A developer came to me and need my help to troubleshoot a problem with HttpClient and HttpWebRequest. Imagine that I have a WCF REST service, the operation contract is a logical invoke operation which expect POST method and accepting a string parameter.
[OperationContract]
The developer tried to invoke the web method by using HttpClient. It is a success with the following code:
HttpClient client = new HttpClient();
However, the developer tried to invoke the same web method by using HttpWebRequest and it failed and getting HTTP400 bad request error with the following code:
So, I tried to play around with the RAW data by using SOAP UI. I am posting same RAW data to the service and I get the same HTTP400 error too.
I find something is not right here. The WCF service is accepting the same HTTP request, why would HttpClient success but not HttpWebRequest and posting raw HTTP with SOAP UI. Therefore, I proceed to checkout what are the HTTP header and content difference between HttpClient and HttpWebRequest by using Fiddler.
This is what I got from HttpClient:
This is what I got from HttpWebRequest:
Ha! So, you notice the missing charset=utf-8 header in the HttpWebRequest. And then, the content is different as highlighted above.
I suppose the reason why serialized <string> content is working fine while calling my WCF service is because the operation contract parameter is a string data type. If we want to post RAW content to the service, we have to make sure the service input parameter data type is a Stream type. Otherwise, I actually can use HttpWebRequest post the same content as HttpClient did, and then I will get HTTP200.
The following HttpClient will post the string content with serialized <string> which I do not want:
In order to post the exact string or RAW content to the service like what I did with HttpWebRequest or SOAP UI, I need to modify the HttpClient code as follow:
In summary, there was a little confusion with the HttpClient.PostAsync method. The PostAsync<string> method does not post the same content as StringContent.
[OperationContract]
[WebInvoke(UriTemplate="Push",
Method="POST", BodyStyle=WebMessageBodyStyle.Bare)]
string PushPost(string
input);
The developer tried to invoke the web method by using HttpClient. It is a success with the following code:
HttpClient client = new HttpClient();
var post = client.PostAsync<string>("http://localhost:58072/ResearchAPI.aspx/push",
"input1=abc&input2=cde", new XmlMediaTypeFormatter()).Result;
string result =
post.Content.ReadAsStringAsync().Result;
However, the developer tried to invoke the same web method by using HttpWebRequest and it failed and getting HTTP400 bad request error with the following code:
string input = "input1=abc&input2=cde";
var request = (HttpWebRequest)WebRequest.Create("http://localhost:58072/ResearchAPI.aspx/push");
request.Method
= "POST";
byte[] data = Encoding.UTF8.GetBytes(input);
request.ContentLength
= input.Length;
request.ContentType
= "application/xml";
Stream newStream = request.GetRequestStream();
newStream.Write(data,
0, data.Length);
newStream.Close();
newStream.Flush();
var response = (HttpWebResponse)request.GetResponse();
var stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
So, I tried to play around with the RAW data by using SOAP UI. I am posting same RAW data to the service and I get the same HTTP400 error too.
I find something is not right here. The WCF service is accepting the same HTTP request, why would HttpClient success but not HttpWebRequest and posting raw HTTP with SOAP UI. Therefore, I proceed to checkout what are the HTTP header and content difference between HttpClient and HttpWebRequest by using Fiddler.
This is what I got from HttpClient:
POST http://ipv4.fiddler:58072/ResearchAPI.aspx/push
HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: ipv4.fiddler:58072
Content-Length: 102
Expect: 100-continue
Connection: Keep-Alive
<string
xmlns="http://schemas.microsoft.com/2003/10/Serialization/">input1=abc&input2=cde</string>
This is what I got from HttpWebRequest:
POST http://ipv4.fiddler:58072/ResearchAPI.aspx/push HTTP/1.1
Content-Type: application/xml
Host: ipv4.fiddler:58072
Content-Length: 21
Expect: 100-continue
Connection: Keep-Alive
input1=abc&input2=cde
Ha! So, you notice the missing charset=utf-8 header in the HttpWebRequest. And then, the content is different as highlighted above.
I suppose the reason why serialized <string> content is working fine while calling my WCF service is because the operation contract parameter is a string data type. If we want to post RAW content to the service, we have to make sure the service input parameter data type is a Stream type. Otherwise, I actually can use HttpWebRequest post the same content as HttpClient did, and then I will get HTTP200.
The following HttpClient will post the string content with serialized <string> which I do not want:
HttpClient client = new HttpClient();
var post = client.PostAsync<string>("http://localhost:58072/ResearchAPI.aspx/push",
"input1=abc&input2=cde", new XmlMediaTypeFormatter()).Result;
In order to post the exact string or RAW content to the service like what I did with HttpWebRequest or SOAP UI, I need to modify the HttpClient code as follow:
HttpClient client = new HttpClient();
var post = client.PostAsync("http://localhost:58072/ResearchAPI.aspx/push",
new StringContent("input1=abc&input2=cde")).Result;
In summary, there was a little confusion with the HttpClient.PostAsync method. The PostAsync<string> method does not post the same content as StringContent.
Very interesting. There is so little documentation around the newer web apis in .NET 4.5 these days and they are so full of these subtle ambiguities!
ReplyDeleteThanks!