Saturday, August 3, 2013

Return JSON from asmx when call it using jquery ajax: step by step guide

Some time you still need to configure old asmx web service for working with jquery ajax. Interesting that there is still a lot of questions about different aspects of the subject (even though that people should use WCF with REST endpoints nowadays in .Net stack), so I thought that it will be good idea to summarize all configuration steps in single post.

First of all we need to create asmx service itself which returns some POCO object. Let’s create some abstract QueueService:

   1:  [WebService(Namespace = "http://example.com")]
   2:  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
   3:  [ToolboxItem(false)]
   4:  [ScriptService]
   5:  public class QueueService : WebService
   6:  {
   7:      [WebMethod]
   8:      [ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)]
   9:      public QueueItem GetQueueItem(string itemId)
  10:      {
  11:          var queueRepository = new QueueRepository();
  12:          var queueItem = queueRepository.GetById(new Guid(itemId));
  13:          return queueItem;
  14:      }
  15:  }

QueueItem class may look like this:

   1:  [Serializable]
   2:  public class QueueItem
   3:  {
   4:      public QueueItemStatus Status { get; set; }
   5:      public int Progress { get; set; }
   6:  }

There are several important notes in the code above. First of all web service class is decorated with ScriptService attribute. Then GetQueueItem() web method is decorated with ScriptMethod attribute with UseHttpGet = false, ResponseFormat = ResponseFormat.Json properties. They say that method will be called via HTTP POST verb (you may make it work GET e.g. with JSONP). And ResponseFormat.Json says that result will be returned in JSON format. Note that in the web method we just return object, we don’t serialize it to JSON by ourselves using e.g. standard JavaScriptSerializer or JSON.Net.

The next step will be changing of the web.config, which should be located in the same folder where asmx file is, or location of asmx file should inherit it from parent folders. Here is the minimal required configuration:

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <configuration>
   3:    <system.web>
   4:      <webServices>
   5:        <protocols>
   6:          <add name="HttpPost"/>
   7:        </protocols>
   8:      </webServices>
   9:      <httpHandlers>
  10:        <remove verb="*" path="*.asmx"/>
  11:        <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  12:      </httpHandlers>
  13:    </system.web>
  14:  </configuration>

Here we allow using of HTTP POST verb for calling of our web service (lines 4-8) and replace default handler of asmx files to ScriptHandlerFactory.

This was the server part. Now we need to consume the web service from jquery ajax. Here is the example:

   1:  $.ajax({
   2:      url: "http://example.com/QueueService.asmx/GetQueueItem",
   3:      type: "POST",
   4:      contentType: "application/json; charset=utf-8",
   5:      data: "{'itemId':'...'}",
   6:      dataType: "json",
   7:      success: function(r) {
   8:          if (!r) {
   9:              // handle error
  10:          }
  11:          var result = r.d;
  12:          if (!result) {
  13:              // handle error
  14:          }
  15:          ...
  16:      },
  17:      error: function(xhr, status, error) {
  18:          // handle error
  19:      }
  20:  });

There are several important notes here as well and without understanding them, you will continuously get “There was an error processing the request” with HTTP 500 Internal server error. First of all we call the web service using POST verb (line 3). Second – we need to specify contentType = "application/json; charset=utf-8" (line 4). Without it ASP.Net won’t treat it as JSON request and call will fail (see this article for details: JSON Hijacking and How ASP.NET AJAX 1.0 Avoids these Attacks). Next important thing is that we need to pass parameters to the web method also in JSON format (line 5). Without it you request won’t even reach the service. End the last thing which you need to note is setting dataType = "json" (line 6).

After this you should be able to consume asmx web service which returns JSON from jquery ajax. If you will check requests in fiddler you should see something like this:

request:

POST /QueueService.asmx/GetQueueItem HTTP/1.1
x-requested-with: XMLHttpRequest
Accept-Language: en-US,fi;q=0.5
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/json; charset=utf-8
Content-Length: 52
Connection: Keep-Alive
Pragma: no-cache

{'itemId':'…'}

response:

HTTP/1.1 200 OK
Content-Length: 152
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET

{"d":{"__type":"QueueItem","Status":0,"Progress":0}}

And there are no any additional efforts needed for serializing/deserializing object to JSON neither on server nor on client. Hope this guide will help in you work.

No comments:

Post a Comment