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.

Monday, March 25, 2013

Unit Test with Exception

There was a developer came to me, asking me how to perform unit test that check for exception. If the expected exception is thrown, it should consider the test as a success.

We can use ExpectedException attribute to assert exception.

So, here are the sample code:


[TestMethod(), ExpectedException(typeof(ApplicationException),
    "Something is wrong, no exception had been thrown.")]
public void TestMethod1()
{
    //write some code here which will throw ApplicationException
    throw new ApplicationException("Test assert exception");
}

Friday, March 22, 2013

WCF with MSMQ

I want to share how to use netMsmqBinding in WCF today. I need to create a solution which able to support asynchronous operation, which mean the WCF service is down but the client still able to make request. The client request will be kept in a queue in MSMQ, and then when the WCF service is back online, it will process whatever requests which are pending in the queue.

First, we need to configure MSMQ. But before that, we need to make sure MSMQ had already been installed. My operating system is Windows 2008 R2, here are the steps to check whether MSMQ had been installed, otherwise we need to install it.

1. Open up Server Manager.
2. Check whether you see Message Queuing is shown in the features list? If yes, skip to step 6.
3. Click the Add Features link button.
4. Check the Message Queuing checkbox.

5. Click the [Install] button. You may need to reboot your server.
6. After the installation is done, you will notice "Message Queuing" is appearing under the Features tree.
7. Proceed to collapse it and then try to create a new queue by right clicking the Private Queue, and then click the New -> Private Queue.
8. Give a name for your new queue and then click the [OK] button.
9. Right click the newly created queue, and then click the Properties.
10. If your server is not joined with any domain and it is running in WORKGROUP, you need to set the Privacy level to None. And, un-check the Authenticated box.


11. Then, go to Security tab, set Allow Full Control to Everyone. For better security control, it is recommended to grant Allow Full Control permission to the application pool identity account only (for example, IIS APPPOOL\AppPoolName) which is going to be used to host the WCF service.



Now, it is time to code your WCF service. After you are done with your coding, here are the web configuration that you need for netMsmqBinding. For WORKGROUP server, you need to set security mode, protection level as None.


<bindings>
  <netMsmqBinding>
    <binding name="basicMsmq" exactlyOnce="false" >
      <security mode="None">
        <transport msmqAuthenticationMode="None" msmqProtectionLevel="None"/>
      </security>
    </binding>
  </netMsmqBinding>
</bindings>


Then, for the service endpoint configuration, you need to set your queue name which you had created just now at the endpoint address:


<services>
  <service name="<YourServiceName>"
            behaviorConfiguration="<YourServiceBehavior>">

    <endpoint name="netMsmqServiceEndpoint"
              address="net.msmq://localhost/private/<YourQueueName>"
              binding="netMsmqBinding" bindingConfiguration="basicMsmq"
              contract="<YourServiceContractName>" />

    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>


When you are done, host your WCF service to IIS. Before that, you need to make sure IIS had been enabled with net.msmq binding. You can perform the following steps to check.

1. Open up IIS Manager.
2. Right click the Default Web Site (assume you are going to host your application in a virtual directory in default website).
3. Click the Edit Bindings... menu.
3. If you see net.msmq binding type is in the list, you skip to steps 6. Otherwise, click the [Add] button.
4. Select net.msmq type from the drop down list, and then enter localhost as binding information.

5. Click the [OK] button.
6. Now, create a new application in default website that host your WCF service.
7. Select the newly created application, and then open Advanced Settings.
8. At the Enabled Protocols properties, append net.msmq to the existing value with comma delimiter.
9. Click the [OK] button, and your application is enabled with MSMQ binding.

Before proceed for testing, need to make sure Net.Msmq Listener Adapter windows service is running.


Now, you can start testing your WCF service with a browser, go to the service url and try to get the WSDL. After tested everything works fine, you are good to go to create a WCF client application.

However, there are a few common errors that you may encounter during the service setup, but the given error message is actually not really meant for it.

Common Error 1:

An error occurred while opening the queue:Access is denied. (-1072824283, 0xc00e0025). The message cannot be sent or received from the queue. Ensure that MSMQ is installed and running. Also ensure that the queue is available to open with the required access mode and authorization.


Root Cause:
The IIS process worker has insufficient permission to read the queue.

Resolution:
You need to make sure the application pool identity account or everyone is given appropriate access.

Common Error 2:

An error occurred while opening the queue:This operation is not supported for Message Queuing installed in workgroup mode. (-1072824214, 0xc00e006a). The message cannot be sent or received from the queue. Ensure that MSMQ is installed and running. Also ensure that the queue is available to open with the required access mode and authorization.


Root Cause:
The service endpoint address is invalid.

Resolution:
Make sure the endpoint address is in the following format:
For example: net.msmq://localhost/private/<YourQueueName>


    <endpoint name="netMsmqServiceEndpoint"
              address="net.msmq://localhost/private/<YourQueueName>"
              binding="netMsmqBinding" bindingConfiguration="basicMsmq"
              contract="<YourServiceContractName>" />



Back for the testing, after you have created a service client and added the service reference to your WCF service host. Make a few requests first from your client to make sure the WCF service is working fine.
After that, you can purposely stop the IIS service for your WCF service, and then use your client to submit a few more requests. At this time, you should expect to see your client is still working fine and the queue is piling up with requests.



Tuesday, March 5, 2013

Performance Profiler in Visual Studio 2012

Today I want to share how to use the performance profiler tool which is available in Visual Studio 2012. I had been using 3rd party profiler tool such as Red Gate all these while, and you know that we have to pay some money for the license. Since Visual Studio 2012 comes with the performance profiler, we can save some money although I find the user friendliness of the tool is not as good as Red Gate's ANTS Performance Profiler.

Note: this tool only available in Visual Studio 2012 Premium and Ultimate edition.

A bit of intro about what is a performance profiler. It is a tool that monitor your running application and capture the information like the time of execution between individual line of codes. Besides, it also has the capability to capture objects in the memory, CPU utilization at that time or any resource contention (deadlock). With these information, it will be easy for us to identify which line of code is causing bottleneck, and the objects in the memory are really get collected by garbage collector or not, if not, it mean memory leak has occurred.



In your Visual Studio 2012, after clicking the Analyze menu, you can straight away start the performance profiler tool by clicking at the "Start Performance Analysis". It will run the profiler with default settings. After the profiler tool is started, it will fire up your application and begin profiling your application. Then, you can proceed to play around with your application. If you wish to track a certain area of code, you can just use your application in a way that you know how it would trigger that area of code, and then quit the application. The profiler tool will start its analysis job then provide the report later.

My experience is I have a web application to be profiled, but it is in layered architecture, means that I have multiple projects need to be profiled at the same time. The default profiler setting is not able to capture the information that I want. Therefore, I have to click the "Lauch Performance Wizard" menu to configure my profiler.


I have selected "Instrumentation" in order to capture the response time between lines of code.


Then, select the web project that I wish to profile. Here is the tricky part, by default it would select all the projects for you, and for the first time user like me, I would assume that it will profile everything for me even the selected class library projects. If your application architecture design is similar to mine, multiple class library projects are referenced by your web project, you can skip from selecting the class library projects in this window. Later, there is a place for you to select which assemblies (dll) to be instrumented together with your web project.



Next, "Enable Tier Interaction Profiling", the description look interesting, just enable it if your project is a web project.


Uncheck the checkbox, do not launch profiling yet because I have not add the assemblies to be instrumented together with my web project.

After clicking the Finish button, a new profiler session will be created in your Performance Explorer window. Right click the profiler session, then open the "Properties".


I would suggest to change the "Report Location" because when the first time I use this tool, all the reports are generated into my solution root folder, it look messy when getting more and more performance reports generated. Hence, I have a folder created call PerformanceReport in my solution folder to keep those performance reports. Then, click the Apply and OK button to close this window.




Now, back to Performance Explorer window, right click the Targets folder, then "Add Target Binary". Browse through your web project's bin folder, then select all the assemblies (dll) which are referenced by your project.


In order to start profiling, right click the Profile, then click the "Start Profiling" menu. You should expect to see your web project get built and default browser will be opened up for you automatically. Then, start to play around with your application. Once you are done with it, click the "Stop Profiling" or close the web browser.

Later, a performance report will be generated.


The graph is about the CPU utilization during the profiling, I personally do not find it useful.
The Hot Path is the suggested functions that you need to pay attention.
The Functions With Most Individual Work is the list of functions that take very long time to process in sorted order.

Play around with the views, there is a call tree view where display top to bottom of a function to another function call, the data is actually quite well self-explained. I personally find the "Avg Elapsed Exclusive Time" value is more useful which tell a particular function response time.


If you go to the modules view, you would find the screen like above. It tells you how times of your assemblies had been called, and I would normally do is to sort the Avg Elapsed Exclusive Time in descending order, and then double click the class.


Then, change the performance metric if you like. Click the function which show long elapsed time. At the bottom, you will see your code. It is the hint where you need to tune your code.

Enjoy your code tuning~

Monday, February 4, 2013

ASP.NET 4.0 - Request Validation

Today, I encounter an error: A potentially dangerous Request.QueryString value was detected from the client.

If you submit any HTTP request which contain XML string, it will be detected as a potential threat to the web application, the above message is to tell you that someone may intends to inject some cross site scripts to your web application. By default, the request validation is turned on automatically, the dangerous string will be truncated. However, due to some business requirements, somehow you want to make your application to accept XML string request. Therefore, you want to disable the request validation.

If you had experience in ASP.net 2.0, you know that you can simply disable the request validation by adding directive RequestValidation="false" to your aspx page. But, in ASP.net 4.0, there are something new. Even though you have added the directive, you still get the same warning message.

There is something extra need to be done for ASP.net 4.0. We need to specify <httpRuntime> as highlighted below in the web.config file. If you wish to disable the request validation for all the pages, you do not need to add directive to every individual page, instead, just use <pages> for all pages.


  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" requestValidationMode="2.0" />
    <pages validateRequest="false" />
  </system.web>


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...