Tuesday, April 23, 2013

Auto Generated WCF Client Not Able to Direct Call WebHttpBinding Service

I have a WCF service hosted with WebHttpBinding. The service is very simple, an operation contract which accept multiple parameters. My WCF client, auto generated after using the "Add Service Reference", is not able to directly consume the WCF service. The error only occur for WebHttpBinding but not the others.

Here's the story. This is how I decorate my operation contract:


[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "Submit2String", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
string Submit2String(string input1, string input2);


After that, I created a unit test project. And then, add the service reference to my unit test project. Finally, I wrote the following code to call my service.


ExpenseServiceClient proxy = new ExpenseServiceClient();
proxy.Submit2String("test1", "test2");


When I test run my above code, I get the following error:

Error: InvalidOperationException was unhandled by user code
Manual addressing is enabled on this factory, so all messages sent must be pre-addressed.



Here is how my auto generated configuration file look like after using the "Add Service Reference":


<system.serviceModel>
  <bindings>
    <webHttpBinding>
      <binding name="webHttp">
        <security mode="None">
          <transport clientCredentialType="None" />
        </security>
      </binding>
    </webHttpBinding>
  </bindings>
  <client>
    <endpoint binding="webHttpBinding" bindingConfiguration="webHttp"
        contract="ExpenseService.IExpenseService" address="http://localhost:65000/ExpenseService.svc">
    </endpoint>
  </client>
</system.serviceModel>


I realize that only WebHttpBinding has this problem. The solution is simple, just add a behavior configuration as follow:


<behaviors>
  <endpointBehaviors>
    <behavior name="webEndpoint">
      <webHttp defaultBodyStyle="Wrapped" defaultOutgoingResponseFormat="Json" helpEnabled="true"/>
    </behavior>
  </endpointBehaviors>
</behaviors>


Then, update the client endpoint to use the above endpoint behavior.


<client>
  <endpoint binding="webHttpBinding" bindingConfiguration="webHttp" behaviorConfiguration="webEndpoint"
      contract="ExpenseService.IExpenseService" address="http://localhost:65000/ExpenseService.svc">
  </endpoint>
</client>


The problem is solved.



Monday, April 22, 2013

ASP.net WebAPI Weird Behavior

I have a simple WebAPI service with a method that accept and return single string parameter. The code look like this:


[HttpPost]
public string SubmitString(string input)
{
    return "Success";
}


Then, I use the following code to call my WebAPI:


using (HttpClient client = GetClientProxy())
{
    HttpResponseMessage response = client.PostAsync<string>("api/expense/SubmitString", input,
        new System.Net.Http.Formatting.JsonMediaTypeFormatter()
        ).Result;

    if (response.IsSuccessStatusCode)
    {
        // Parse the response body. Blocking!
        result = response.Content.ReadAsAsync<string>().Result;
    }
    else
    {
        // throw exception;
    }
}

It is weird that I keep getting HTTP404 error whenever I try to invoke the WebAPI method. Here is how my HTTP RAW content look like:


POST http://127.0.0.1:65000/api/expense/SubmitString HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
Host: 127.0.0.1:65000
Content-Length: 6
Expect: 100-continue
Connection: Keep-Alive

"test"

After "googling" around, and found that WebAPI somehow not picking up simple value parameter automatically. I need to add [FromBody] attribute to the parameter, like this:


[HttpPost]
public string SubmitString([FromBody]string input)
{
    return "Success";
}

Yes, it works now. But, it behave differently when I create a WebAPI method which accept object parameter, I do not need to specify the [FromBody] attribute.

Also, note that WebAPI method only accept object type as parameter and the media formatter type are restricted to XML and JSON only.

In my previous post, I mentioned that the HttpClient.PostAsync<string> and HttpClient.PostAsync with StringContent are different. PostAsync<string> is posting whole string as an object, while the PostAsync with StringContent is posting HTTP RAW content.

What if I have the following WebAPI method which accept more than one parameter? Like this:


[HttpPost]
public string Submit2String(string input1, string input2)
{
    return "Success";
}


Unfortunately, it is not supported. The HTTP POST content can contain one object only. Therefore, we have to form an object that wrap both string objects into one object then only pass it to WebAPI method. WCF or web service developer like me would hate this kind of extra work and limitation. WCF has its built in SOAP serializer to parse the request packet and bind them correctly to the service method parameter a.k.a. the operation contract and data contract. However, the extra steps to wrap objects into one object before passing to WebAPI, somehow I feel it is similar to form SOAP envelope with wrapped objects. I only see one advantage of using WebAPI is the object (POX or JSON) passing via the wire is lightweight compare to SOAP.

Back to the problem, if you wish to accept more than one parameter in your WebAPI, you have to do the workaround like this:


[HttpPost]
public string Submit2String()
{
    string httpContent = this.Request.Content.ReadAsStringAsync().Result;
    string[] contents = httpContent.Split('&');

    Dictionary<string, string> parameterValue = new Dictionary<string, string>();
    foreach (string content in contents)
    {
        var temp = content.Split('=');
        parameterValue.Add(temp[0], temp[1]);
    }

    string input1 = parameterValue["input1"];
    string input2 = parameterValue["input2"];

    //Alternatively, if you hate the above string splitting code, you can have shortcut by using HttpUtility.ParseQueryString
    //Because the HTTP RAW content look like a query string anyway

    var option2 = HttpUtility.ParseQueryString(httpContent);

    string input_1 = option2["input1"];
    string input_2 = option2["input2"];

    return "Success";
}


Then, for your client, this is how you post your content:


using (HttpClient client = GetClientProxy())
{
    //Note: use content type application/x-www-form-urlencoded
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));

    //Option 1:
    HttpResponseMessage option1 = client.PostAsync("api/expense/Submit2String",
        new StringContent("input1=" + input1 + "&input2=" + input2)
        ).Result;

    //Option 2:
    var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>()
        {
            new KeyValuePair<string, string>("input1", input1),
            new KeyValuePair<string, string>("input2", input2)
        });

    HttpResponseMessage option2 = client.PostAsync("api/expense/Submit2String", content).Result;

}


As you can see, you have to post the content with FormUrlEncodedContent and specify the content type as "application/x-www-form-urlencoded". The HTTP content must be in this format: parameter1=value1&parameter2=value2&parameter3=value3. When the content has been posted to WebAPI method, you can manipulate or parse the content by splitting it with &, and then convert the value into any other object type you wish. Or, you can use HttpUtility.ParseQueryString to convert the content into NameValueCollection since the value format look like query string.

If you wish to post more than one parameter with object type, you can do the same workaround by posting the HTTP content with the serialized object XML or JSON string.


Monday, April 8, 2013

HttpClient & HttpWebRequest Confusion with HTTP POST

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]
[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&amp;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.

Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...