I need to send a simple JSON object to a Restful datasnap server (Delphi) from a Delphi client. I am using Delphi XE. Can anybody help me out with the code? I am trying for hours but not getting it.. Please ask if details are not sufficient
Edit:
Here is server side method declaration:
procedure updatemethodnme(str:string):string;
and here is client side code:
function PostData(request: string): boolean;
var
param: TStringList;
url, Text,str: string;
code: Integer;
http: TIDHttp;
begin
Result:= false;
http:= TIDHttp.Create(nil);
http.HandleRedirects:= true;
http.ReadTimeout:= 50000;
http.request.Connection:= 'keep-alive';
str:= '{"lamp":"'+lamp+'","floor":"'+floor+'","op":"'+request+'"}';
param:= TStringList.Create;
param.Clear;
param.Add(str);
url:= 'h***p://xx2.168.xx.xx:xxxx/Datasnap/rest/TserverMethods1/methdname/';
try
Text:= http.Post(url, param);
Result:= true;
except on E: Exception do
begin
Result := false;
end;
end;
end;
Here's some simple XE2 test code sending JSON data through HTTP Post using SuperObject (using Indy's TIdHTTP):
procedure TFrmTTWebserviceTester.Button1Click(Sender: TObject);
var
lJSO : ISuperObject;
lRequest: TStringStream;
lResponse: String;
begin
// Next 2 lines for Fiddler HTTP intercept:
IdHTTP.ProxyParams.ProxyServer := '127.0.0.1';
IdHTTP.ProxyParams.ProxyPort := 8888;
lJSO := SO('{"name": "Henri Gourvest", "vip": true, "telephones": ["000000000", "111111111111"], "age": 33, "size": 1.83, "adresses": [ { "adress": "blabla", "city": "Metz", "pc": 57000 }, { "adress": "blabla", "city": "Nantes", "pc": 44000 } ]}');
lRequest := TStringStream.Create(lJSO.AsString, TEncoding.UTF8);
try
IdHTTP.Request.ContentType := 'application/json';
IdHTTP.Request.Charset := 'utf-8';
try
lResponse := IdHTTP.Post('http://127.0.0.1:8085/ttposttest', lRequest);
ShowMessage(lResponse);
except
on E: Exception do
ShowMessage('Error on request:'#13#10 + E.Message);
end;
finally
lRequest.Free;
end;
lJSO := nil;
end;
This is the data that goes out:
POST http://127.0.0.1:8085/ttposttest HTTP/1.0
Content-Type: application/json; charset=utf-8
Content-Length: 204
Connection: keep-alive
Host: 127.0.0.1:8085
Accept: text/html, */*
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)
{"vip":true,"age":33,"telephones":["000000000","111111111111"],"adresses":[{"adress":"blabla","pc":57000,"city":"Metz"},{"adress":"blabla","pc":44000,"city":"Nantes"}],"size":1.83,"name":"Henri Gourvest"}
Receiver is a TWebAction on a TWebModule, with handler:
procedure TWebModuleWebServices.WebModuleWebServicesTTPostTestAction(
Sender: TObject; Request: TWebRequest; Response: TWebResponse;
var Handled: Boolean);
var
S : String;
lJSO: ISuperObject;
begin
S := Request.Content;
if S <> '' then
lJSO := SO('{"result": "OK", "encodingtestcharacters": "Typed € with Alt-0128 Will be escaped to \u20ac"}')
else
lJSO := SO('{"result": "Error", "message": "No data received"}');
Response.ContentType := 'application/json'; // Designating the encoding is somewhat redundant for JSON (http://stackoverflow.com/questions/9254891/what-does-content-type-application-json-charset-utf-8-really-mean)
Response.Charset := 'utf-8';
Response.Content := lJSO.AsJSON;
Handled := true;
end; { WebModuleWebServicesTTPostTestAction }
It uses TIdHTTPWebBrokerBridge:
FWebBrokerBridge := TIdHTTPWebBrokerBridge.Create(Self);
// Register web module class.
FWebBrokerBridge.RegisterWebModuleClass(TWebModuleWebServices);
// Settings:
FWebBrokerBridge.DefaultPort := 8085;
This is the actual response:
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json; charset=utf-8
Content-Length: 92
{"encodingtestcharacters":"Typed\u20acwithAlt0128FollowedBy8364escaped\u8364","result":"OK"}
You can use Indy's TIdHTTP component to Post() JSON-encoded data to a REST server. You would simply have to encode and decode the JSON data in your own code.
From Delphi6 posting to a datasnap server built in XE5 the above works for me, but I had found out the server method needed the parameter to be defined as a TJSONObject instead of TStream. Something odd in addition to this, the Datansap server changes the lookup method when you do a post instead of a get. For example I had a method named ImportData. But when you debug the server it was looking for an UpdateImportData method. So when I added the new method and changed the parameter type to Data.DBXJSON.TJSONObject it worked.
Related
I'm trying to send an email via AWS pinpoint containing attachments. To send attachments with an email, you must use the 'RAW' email content. The only documentation I can find about this is here: https://docs.aws.amazon.com/pinpoint-email/latest/APIReference/API_RawMessage.html, but it is missing quite a few things (like, what are the required headers?)
When I send an email using the 'simple' content, it works fine:
emailInput := &pinpointemail.SendEmailInput{
Destination: &pinpointemail.Destination{
ToAddresses: []*string{&address},
},
FromEmailAddress: &sender,
Content: &pinpointemail.EmailContent{
Simple: &pinpointemail.Message{
Body: &pinpointemail.Body{
Html: &pinpointemail.Content{
Charset: &charset,
Data: &emailHTML,
},
Text: &pinpointemail.Content{
Charset: &charset,
Data: &emailText,
},
},
Subject: &pinpointemail.Content{
Charset: &charset,
Data: &emailSubject,
},
},
}
Since I want to add attachments, I have to use the 'RAW' content type. I have written a function which generates the email content, based on: https://gist.github.com/douglasmakey/90753ecf37ac10c25873825097f46300:
func generateRawEmailContent(subject, to, from, HTMLBody string, attachments *[]EmailAttachment) []byte {
buf := bytes.NewBuffer(nil)
buf.WriteString(fmt.Sprintf("Subject: %s\n", subject))
buf.WriteString(fmt.Sprintf("To: %s\n", to))
buf.WriteString(fmt.Sprintf("From: %s\n\n", from))
buf.WriteString("MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n")
buf.WriteString(HTMLBody)
writer := multipart.NewWriter(buf)
boundary := writer.Boundary()
buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", boundary))
buf.WriteString(fmt.Sprintf("--%s\n", boundary))
for _, attachment := range *attachments {
buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
buf.WriteString(fmt.Sprintf("Content-Type: %s\n", http.DetectContentType(attachment.Data)))
buf.WriteString("Content-Transfer-Encoding: base64\n")
buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", attachment.FileName))
b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
base64.StdEncoding.Encode(b, attachment.Data)
buf.Write(b)
buf.WriteString(fmt.Sprintf("\n--%s", boundary))
}
buf.WriteString("--")
log.Println(string(buf.Bytes()))
return buf.Bytes()
}
This generates the following (emails changed):
Subject: Welcome \nTo: xxxxx#gmail.com\nFrom: xxxxx#gmail.com\n\nMIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n\u003ch1\u003eHello ,\u003c/h1\u003e\u003cp\u003eYou now have an account.\u003c/p\u003e\nContent-Type: multipart/mixed; boundary=8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\n\n\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: base64\nContent-Disposition: attachment; filename=test.json\newogICJ0ZXN0IjogdHJ1ZQp9\n--8f6b2cc498b79f5a99550b930ba1ecab1fc1ee2d3425a0a69ab67b83b647--
I then construct the email as follows:
&pinpointemail.SendEmailInput{
Destination: &pinpointemail.Destination{
ToAddresses: []*string{&address},
},
FromEmailAddress: &sender,
Content: &pinpointemail.EmailContent{
Raw: &pinpointemail.RawMessage{
Data: generateRawEmailContent(emailSubject, address, sender, emailHTML, emailAttachments),
},
}
When sending this email via the github.com/aws/aws-sdk-go/service/pinpoint package, I get a 403 returned, and I have no idea why. A 403 means that the resource I'm trying to access is forbidden, but I don't see how that is relevant here? Also, there is no documentation about a 403 even being a possible response. Any help would be greatly appreciated!
I have also tried using libraries, like for instance the gomail-v2 library as follows:
m := gomail.NewMessage()
m.SetHeader("From", from)
m.SetHeader("To", to)
m.SetHeader("Subject", subject)
m.SetBody("text/plain", textBody)
m.AddAlternative("text/html", HTMLBody)
m.Attach("foo.txt", gomail.SetCopyFunc(func(w io.Writer) error {
_, err := w.Write((*attachments)[0].Data)
return err
}))
buf := bytes.NewBuffer(make([]byte, 0, 2048))
_, werr := m.WriteTo(buf)
if werr != nil {
return nil, common.NewStackError(werr)
}
But that still gives me a 403 error.
I'm not a Go person, so this is just a brutal attempt to shuffle around code lines to hopefully produce a valid MIME structure.
func generateRawEmailContent(subject, to, from, HTMLBody string, attachments *[]EmailAttachment) []byte {
buf := bytes.NewBuffer(nil)
// Creating headers by gluing together strings is precarious.
// I'm sure there must be a better way.
buf.WriteString(fmt.Sprintf("Subject: %s\n", subject))
buf.WriteString(fmt.Sprintf("To: %s\n", to))
// Remove spurious newline
buf.WriteString(fmt.Sprintf("From: %s\n", from))
writer := multipart.NewWriter(buf)
boundary := writer.Boundary()
buf.WriteString(fmt.Sprintf("MIME-Version: 1.0\n", boundary))
buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", boundary))
// End of headers
buf.WriteString("\n")
buf.WriteString(fmt.Sprintf("--%s\n", boundary))
buf.WriteString("Content-Type: text/html; charset=\"UTF-8\";\n\n")
buf.WriteString(HTMLBody)
for _, attachment := range *attachments {
buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
buf.WriteString(fmt.Sprintf("Content-Type: %s\n", http.DetectContentType(attachment.Data)))
buf.WriteString("Content-Transfer-Encoding: base64\n")
buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", attachment.FileName))
b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
base64.StdEncoding.Encode(b, attachment.Data)
buf.Write(b)
// Don't add a second boundary here
buf.WriteString("\n")
}
// Final terminating boundary, notice -- after
buf.WriteString(fmt.Sprintf("\n--%s--\n", boundary))
log.Println(string(buf.Bytes()))
return buf.Bytes()
}
The resulting output should look something like
Subject: subject
To: recipient <victim#example.org>
From: me <sender#example.net>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=foobar
--foobar
Content-Type: text/html; charset="UTF-8"
<h1>Tremble, victim</h1>
<p>We don't send <tt>text/plain</tt> because we
hate our users.</p>
--foobar
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=skull_crossbones.jpg
YmluYXJ5ZGF0YQ==
--foobar--
Okay, found the issue. Turns out that this 403 error has nothing to do with my code, but rather with IAM permissions in AWS. Apparently an IAM permission has to be turned on to enable RAW email content.
I have a post request where i need to send x-www-form-urlencoded keyValue pair parameters and content-type should be x-www-form-urlencoded.
Before coding ,i've tried in postman successfully,just adding Header"Content-Type=application/x-www-form-urlencoded" with x-www-form-urlencoded body .
Here is my code:`
RestAssured.baseURI="****"
RequestSpecification request = RestAssured.given().config(RestAssured.config()
.encoderConfig(EncoderConfig.encoderConfig()
.encodeContentTypeAs("x-www-form-urlencoded",
ContentType.URLENC)))
.contentType(ContentType.URLENC.withCharset("UTF-8"))
.formParam("grant_type", *)
.formParam("code", *)
.formParam("client_id",*)
.when().log().all()
.then().log().all().request()
request.post("/oauth2/token")`
I guess rest assured posted as formParam not "x-www-form-urlencoded"?
This is rest assured log: `
Request method: POST
Request URI: ***
Proxy: <none>
Request params: <none>
Query params: <none>
Form params: grant_type=***
code=***
client_id=***
Path params: <none>
Headers: Accept=image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, application/x-ms-xbap
Content-Type=application/x-www-form-urlencoded; charset=UTF-8
Cookies: <none>
Multiparts: <none>
Body: <none>
HTTP/1.1 405 Method Not Allowed
Content-Length: 61
Date: Tue, 30 Jan 2018 06:59:20 GMT
X-Correlationid: 5d155b6f-0d85-4775-5f50-82c397e5b44b
X-Smp-Log-Correlation-Id: 5d155b6f-0d85-4775-5f50-82c397e5b44b
X-Vcap-Request-Id: 5d155b6f-0d85-4775-5f50-82c397e5b44b
Only support Content-Type:application/x-www-form-urlencoded
`
This problem drives me crazy for a couple f days .
Please do let me know is there any other way to send x-www-form-urlencoded parameters or some updation required in code.
Thanks a lot!
Response response = RestAssured
.given()
.contentType("application/x-www-form-urlencoded; charset=utf-8")
.formParam("grant_type", "password")
.formParam("username", user_email)
.formParam("password", user_password)
.formParam("audience", audience)
.formParam("scope", "openid email")
.formParam("client_id", REGULAR_APP_CLIENT_ID)
.formParam("client_secret", REGULAR_APP_SECRET_ID)
.when()
.post(AUTH0_URL);
If you need send request with params in body:
String body = String.format("grant_type=%s&code=%s&clientid=%s", grantType, code, clientId);
Response response = given().with().
header("Content-Type", "application/x-www-form-urlencoded").
body(body).
post("/oauth2/token");
Case for params in URL:
Response response = given().with().
header("Content-Type", "application/x-www-form-urlencoded").
post("/oauth2/token?grant_type={type}&code={code}&clientid={id}");
Case for params in header (also might use Header object io.restassured.http.Header):
Response response = given().with().
header("Content-Type", "application/x-www-form-urlencoded").
header("grant_type", type).
header("code", code).
header("clientid", id).
post("/oauth2/token");
BTW use static give() for don't duplicate Config
public static RequestSpecification given() {
RestAssured.config = RestAssured.config().
...;
return given().baseUrl(BASE_URL).contentType(ContentType.URLENC);
}
This Method is working for me
public String getAuthForKeyCloak() {
Response response = RestAssured.given().with().auth().preemptive()
.basic(props.get("keycloak_username"), props.get("keycloak_password"))
.header("Content-Type", "application/x-www-form-urlencoded")
.formParam("grant_type", props.get("keycloak_granttype"))
.formParam("client_id", props.get("keycloak_clientid"))
.formParam("username", props.get("keycloak_username"))
.formParam("password", props.get("keycloak_password")).when()
.post(ApplnURIForKeyCloak + props.get("keycloakAuthURL"));
System.out.println(response.getBody().asString());
String responseBody = response.getBody().asString();
String token = new org.json.JSONObject(responseBody).getString("access_token");
System.out.println(token);
return token;
}
Use RA EncoderConfig to encode the content type for content-type x-www-form-urlencoded. Also, post the payload as form parameters
RequestSpecBuilder.setConfig(RestAssured.config().sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation())
.encoderConfig(EncoderConfig.encoderConfig()
.encodeContentTypeAs("x-www-form-urlencoded",
ContentType.URLENC)))
.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
I had the same issue and I finally found the solution:
Understanding the issue:
Rest Assured automatically concatenates a charset after the content type, even if you do not specify one.
i.e if you set the Content-Type to be application/x-www-form-urlencoded it will automatically set a default charset to it and your log will display:
Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1 or some other charset.
My problem was that I needed the content type to be without any charset, otherwise it was not accepted and returned status code 400.
The solution is to make sure you send the content type WITHOUT ANY charset.
How to use:
Before you set the content type, add this line:
RequestSpecification rs= RestAssured.given();
rs.config(RestAssured.config().encoderConfig(encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false)));
this part :
appendDefaultContentCharsetToContentTypeIfUndefined(false)))
will make sure that no charset will be appended to your content type.
Then set the header:
rs.header("Content-Type", "application/x-www-form-urlencoded");
I have something that looks like so:
func (client *MyCustomClient) CheckURL(url string, json_response *MyCustomResponseStruct) bool {
r, err = http.Get(url)
if err != nil {
return false
}
defer r.Body.Close()
.... do stuff with json_response
And in my test, I have the following:
func TestCheckURL(t *test.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
fmt.Fprintln(w, `{"status": "success"}`)
}))
defer ts.Close()
json_response := new(MyCustomResponseStruct)
client := NewMyCustomClient() // returns instance of MyCustomClient
done := client.CheckURL("test.com", json_response)
However, it does not appear as if the HTTP test server is working and that it actually goes out to test.com, as evidenced by the log output:
Get http:/test.com: dial tcp X.Y.Z.A: i/o timeout
My question is how to properly use the httptest package to mock out this request... I read through the docs and this helpful SO Answer but I'm still stuck.
Your client only calls the URL you provided as the first argument to the CheckURL method. Give your client the URL of your test server:
done := client.CheckURL(ts.URL, json_response)
I'm posting the login-data from delphi on a django-created-form which runs on my localhost. Like this:
procedure TForm1.btnPostClick(Sender: TObject);
var
IdHTTP: TidHTTP;
auth: TStringList;
test,token:string;
begin
IdHTTP := TidHTTP.Create(nil);
IdHTTP.Request.Accept :='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
IdHTTP.Request.AcceptCharSet :='iso-8859-1, utf-8, utf-16, *;q=0.1';
IdHTTP.Request.AcceptEncoding :='deflate, gzip, identity, *;q=0';
IdHTTP.Request.Connection :='Keep-Alive';
IdHTTP.Request.ContentType :='application/x-www-form-urlencoded';
token := IdHTTP.Get('http://localhost:8000/accounts/signup/');
token := copy(token, AnsiPos('csrfmiddlewaretoken', token) + 28, 32);
IdHTTP.Request.CustomHeaders.Clear;
with IdHTTP.Request.CustomHeaders do
begin
AddValue('X-CSRFToken',token);
Values['COOKIE']:='';
//if IdHTTP2.CookieManager.CookieCollection.count > 0 then
// Add('COOKIE: '+token);
end;
try
auth := TStringList.Create;
auth.Add('csrfmiddlewaretoken='+ token);
auth.Add('first_name=' + edtVorname.Text);
auth.Add('last_name=' + edtName.Text);
auth.Add('function=' + edtFunction.Text);
auth.Add('company=' + edtCompany.Text);
auth.Add('country=' + edtCountry.Text);
auth.Add('email=' + edtEmail.Text);
auth.Add('password1=' + edtPassword.Text);
auth.Add('password2=' + edtPasswordAgain.Text);
//IdHTTP.Request.CustomHeaders.AddValue('Accept-Language', 'en-EN');
//IdHTTP.Request.CustomHeaders.AddValue('Referer',
IdHTTP.post('http://127.0.0.1:8000/accounts/signup/', auth);
except
end;
end;
Whenever it gets until the Line with the post
IdHTTP.post('http://127.0.0.1:8000/accounts/signup/', auth);
Im getting the 403 error "Forbidden". My guess is im sending the CSRF-Token wrong
because from the python debugger I see being caught in
if csrf_token is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
return self._reject(request, REASON_NO_CSRF_COOKIE)
But HOW is it supposed to be? How do I need to send this csrf-token?
P.S Well I think the problem was that per default HandleRedirects is set to True and it gave me the 403. The cookievalue in the latest Django versions is usually called csrftoken and not X-CSRFToken how I did here.
You shouldn't set cookie by yourself. When Django response with a form and django.middleware.csrf.CsrfViewMiddleware is in your list of middleware classes (more on https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/), it should already set csrftoken cookie. So you only have to read value of this cookie and set it to csrfmiddlewaretoken form field.
Also don't forget to set UserAgent in your request. Without UserAgent Indy (Delphi 2010) and Django 1.8 won't cooperate with each other.
I don't know how to add csrfmiddlewaretoken field on redirects so I did it without redirects!
Here is my code:
uses
IdURI, IdCookie;
...
procedure TForm1.btnLoginClick(Sender: TObject);
var
idHTTP: TIdHTTP;
token: AnsiString;
stream: TStringStream;
params: TStringList;
cookie: TidCookieRFC2109;
begin
idHTTP := TIdHTTP.Create(nil);
stream := TStringStream.Create;
params := TStringList.Create;
try
idHTTP.AllowCookies := True;
idHTTP.HandleRedirects := False;
//even if HandleRedirects := False, you must have OnRedirect event (don't know why)
idHTTP.OnRedirect := IdHttpOnRedirect;
IdHTTP.Request.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
IdHTTP.Request.AcceptCharSet := 'iso-8859-1, utf-8, utf-16, *;q=0.1';
IdHTTP.Request.AcceptEncoding := 'gzip, deflate, sdch';
IdHTTP.Request.Connection := 'Keep-Alive';
IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
idHTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.107 Safari/537.36';
idHTTP.Request.CharSet := 'utf-8';
idHTTP.Get('http://www.ttaksa.si/login/');
cookie := idHTTP.CookieManager.CookieCollection.Cookie['csrftoken', '.www.ttaksa.si'];
token := cookie.Value;
params.Values['csrfmiddlewaretoken'] := token;
params.Values['username'] := txtUporabniskoIme.Text;
params.Values['password'] := txtGeslo.Text;
idHTTP.Post('http://www.ttaksa.si/login/', params, stream);
idHTTP.Get('http://www.ttaksa.si/objekti/export/?zacetni_datum=23.7.2015-12:00:00&format=xml&indent=2', stream);
stream.SaveToFile(txtFilename.Text);
wbUvoz.Navigate('File://' + txtFilename.Text);
finally
idHTTP.Free;
stream.Free;
params.Free;
end;
end;
procedure TForm1.IdHTTPOnRedirect(Sender: TObject; var dest: string;
var NumRedirect: Integer; var Handled: boolean; var VMethod: TIdHTTPMethod);
begin
Handled := True;
end;
I'm trying to connect to a https web service (not .NET as far as I know).
I can't control the other side in any way, I just got some standards and a wsdl to operate
with it.
I have created at first the client using Add Service Reference, tried some things until I get through some problems, where one most serious was that I couldn't add the Authentication header to the message which was resulting in fail.
Added the service using old Add Web Reference and seemed more easily managed and appropriate, using a partial class and override the GetWebRequest, I added this code so I can preauthenticate with the service and add the security header, which they don't mention in the wsdl link. I know that it is not mandatory for services to tell this but it would be nice my Web Service creators fellow developers.
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
if (PreAuthenticate)
{
NetworkCredential networkCredentials = Credentials.GetCredential(uri, "Basic");
if (networkCredentials != null)
{
byte[] credentialBuffer = new UTF8Encoding()
.GetBytes(networkCredentials.UserName + ":" + networkCredentials.Password);
request.Headers["Authorization"] = "Basic" + Convert.ToBase64String(credentialBuffer);
}
else
{
throw new ApplicationException("No network credentials");
}
}
return request;
}
To call the service I added this code:
using (Service client = new Service()) // autogenerated Service class
{
client.EnableDecompression = true;
// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("test1", "test1");
Uri uri = new Uri(client.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
client.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
client.PreAuthenticate = true;
// Make the web service call.
Request req = new Request { UserName = "test2", Password = "test2"; // an object created from autogenerated code
RequestResult result = client.processMessage(req); // autogenerated code
}
While testing this call and checking with fiddler my request. I see 2 calls a keep alive call with these header, nothing special.
CONNECT server:443 HTTP/1.1
Host: server
Connection: Keep-Alive
Sending 570 returning a 200 result.
HTTP/1.1 200 Connection Established
FiddlerGateway: Direct
StartTime: 00:05:13.743
Connection: close
And the call with the data sending 571 result 500 error:
POST /host/Service HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.5448)
Authorization: BasicdXNlcOTc3MzQyMGTDFTR4dftfrdg5 // changed this hash for security reasons
VsDebuggerCausalityData: // Removed this hash for security reasons
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Host: server-host-url
Content-Length: 7238
Expect: 100-continue
Accept-Encoding: gzip
Connection: Keep-Alive
The error exception in .NET client:
Error on verifying message against security policy Error code:1000
As you see the Authorization header exist. I also tried with adding a space after Basic you can see above where exactly in the overriden method, and seemed fiddler recognized it better and also decoded the username:password header.
This results into that response:
HTTP/1.1 500 Internal Server Error
Date: Sat, 21 Apr 2012 21:05:22 GMT
Server: Oracle-Application-Server-11g
X-Powered-By: Servlet/2.5 JSP/2.1
X-Cnection: close
Transfer-Encoding: chunked
Content-Type: text/xml;charset="utf-8"
Content-Language: en
X-Pad: avoid browser bug
Set-Cookie: BIGipServerpoolXoas243_254_9999=437682499.99988.0000; path=/
The strange thing I wonder first is if the first call should be preauthenticated, the handshake keep alive one? This 500 error I know that causes when authentication header is not present, but mine is. Preauthentication is what I need to know how it should happen and I guess it's not working if it should appear in the 1st message.
Another strange thing is that if I change the 2 pairs of passwords opposite, I see on fiddler that I get 3 messages, 1 with the handshake and result in 200 and "TWO" others with 401 Authorization Required.
This drives me crazy. Any help appreciated to save my soul.
Thank you!