5/01/2012

Consuming WCF Service with Ext JS

We decided to use WCF as our primary source for Ajax requests coming from our Ext JS application. However, there are 3 major steps that must be set before using WCF along with any Ajax requests.

1)WCF Service must be configured as WebHttpBinding. In other words, we need a restful service.Example web.config for your service:

First you need to define a behaviour name in serviceBehaviors section and use it in service behaviorConfiguration property.(RN.Service.PopulationServiceBehavior) You need to fill endpoint name property and Contract name property as your service name with its namespace prefix.(RN.Service is namespace and PopulationService is method name). Endpointconfiguration must be defined in endpointBehaviors.(jsonBehavior)

2)You need to add following settings before defining your service method.(you can either use interface or method itself)

        [WebInvoke(Method = "*",
           BodyStyle = WebMessageBodyStyle.Bare,
           //RequestFormat = WebMessageFormat.Json,
           //ResponseFormat = WebMessageFormat.Json,
           UriTemplate = "/GetExtJSList")]
Method can be GET or POST. It allows both. For BodyStyle I used Bare because i dont have any complex returning or parameter. I also want JSON string. UriTemplate is vital since you will be calling your method using these name. (service url/uriTemplate name)

3)You need to allow Ajax requests from different ports. This concept is related with Cross-Domain Call. When you try to test your WCF service along with a web application, you will probably get the following error since these two projects will be run in different ports:
XMLHttpRequest cannot load ... Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers.
The problem is that, Ajax request uses OPTION method instead of GET or POST in request header. If it manages to get a success response a second request is sent with GET or POST. The solution that i provided is working in IE. You can also bypass this problem with opening browsers in cmd with certain parameters.
For Chrome:

chrome.exe --disable-web-security

In order to handle OPTION request header once for all, you need to add a global.asax(if not exist in your poject) and add the following code to Application_BeginRequest:

            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin",
                          "*");
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods",
                              "GET, POST");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers",
                              "Content-Type, Accept");
                HttpContext.Current.Response.AddHeader("Access-Control-Max-Age",
                              "1728000");
                HttpContext.Current.Response.End();
            }

Instead of *, you can define your application domain.(since * may cause security problems. You accept all domain requests in that case).

At that point, I suggest you to use Fiddler like programs to see what is going on behind the scene.

Now you are ready to consume WCF. I write a basic Ext JS Tree Panel example.(asynchronization approach is not used) You need to consider Tree Store requirements if you want to use JSON Serialize class(children,leaf properties etc.)

                Ext.define('PopulationMenuModel', {
                    extend: 'Ext.data.Model',
                    fields: [
                    { name: 'text', type: 'string' },
                    { name: 'id', type: 'string' },
                    { name: 'leaf', type: 'bool' }
                ]
                });
                var TreePopStore = Ext.create('Ext.data.TreeStore', {
                    proxy: { url: 'http://localhost:50081/PopulationService.svc/GetExtJSList'
                    , type: 'ajax',method:'GET'                    
                     },
                    model: PopulationMenuModel
                });
                var Tree = new Ext.tree.TreePanel(
                                 {
                                     height: 400,
                                     useArrows: true,
                                     title: 'deneme',
                                     autoScroll: true,
                                     animate: true,
                                     renderTo: 'tree-div',
                                     enableDD: true,
                                     containerScroll: true,
                                     border: false,
                                     rootVisible: false,
                                     store: TreePopStore
                                 });

As a last warning, if you plan to return string instead of JSON Serialize + IList approach, you need to be extra careful since we set WebMessageFormat.Json as response format. It may cause double serialize issues. In my case, i get "[{id:12,text:'test'}]" instead of [{id:12,text:'test'}] which is wrong since Ext JS cannot parse JSON with "" tags. I solved this problem using Stream instead of string.

Stream mymethod(){

byte[] byResponse = Encoding.UTF8.GetBytes(mystring);
            return new MemoryStream(byResponse);

}

For JSONP Users:
I also want to explain how to use JSONP since it is a powerful method to consume services in different domain. In WCF side, we need to encapsulate our JSON string with a callback function.
callback1( [your json here] )
Then we can use jsonp instead of json.

 proxy: {
          url: 'http://localhost:50081/PopulationService.svc/GetExtJSList',
          type: 'jsonp'
}

I try to include all problems and solutions that I encountered during 3-4 days.

References:






1 comment:

  1. Excellent summary. However, I still get the XMLHttpRequest cannot load ... Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers error calling my service in Sencha Touch 2. Could you help?

    ReplyDelete