Saturday, May 10, 2008

JSON services in ASP.NET Ajax

Lately the use of client centric development model in .NET is getting increasingly popular. In contrast to the traditional server model where html tags are rendered in server side and then sent to the browser, the client centric approach consist of the use of asynchronous HTTP requests to retrieve data from server side and then do the html rendering in client side code.

The main advantage of this client centric approach is that the communication between the browser and the server gets much lighter; the browser doesn't need to post the page to get information from the server, it rather issues asynchronous calls to Web Services, which implement the JSON protocol, for retrieving the data. You might be wondering why XML-SOAP or other XML based RPC protocols are not used instead of that JSON...

JSON is a non-markup based language which has some limitations comparing to XML languages (i.e: lack of namespaces) but that simplicity makes it lighter, easier to handle by browsers, and overall more performant and more suitable for this web client scenario.

Unfortunately there is a catch :( ; since the rendering is done in client side, you'd better forget about using your automatically rendered ASP.NET controls and be prepared to start messing around extensively with javascript code for the rendering... No need to go through that pain though, there are some third party client based controls that implement rendering through javascript libraries, particularly you might want to check what this open source control library can offer you.

Well, now that we have gone through the theory, how can we put that in practice in ASP.NET? How can we implement and consume a JSON service?
ASP.NET Ajax framework provides some built-in mechanisms to handle JSON communication and serialization automatically.
These mechanisms are Page Methods, Ajax Web Services and Ajax WCF Services and I'm going to spend the rest of this post to do a brief walkthrough on them.


Page methods

A page method is a static function marked as a web method and declared within the page code behind. This function can be called from javascript code through a predefined class/keyword called PageMethods. Let's take a look in a sample step by step.

First the Page method execution has to be enabled in the asp.net ScriptManager.
<asp:ScriptManager ID="scriptManager" runat="server" EnablePageMethods="true" />
Now it's time to implement the static web method in the page's code behind.
public partial class _Default : System.Web.UI.Page
{
[WebMethod]
public static String ToUpper(string name)
{
return name.ToUpper();
}
}
Finally, the method is consumed from a script included in the page. As I said before the call execution follows an asynchronous pattern, therefore in the sample below if the operation succeds the resulting string in upper case is passed to the OnSucceded method, otherwise the OnFailed method receives the .NET exception.
function ToUpper(name)
{
PageMethods.ToUpper(name, OnSuccedeed, OnFailed);
}
function OnSuccedeed(res)
{
alert(res);
}
function OnFailed(error)
{
// Alert user to the error.
alert(error.get_message());
}

Web Services


One of the main differences between Page Methods and Ajax Web Services is that Page Methods can only be used by a single Page and Web Services can be used from as many pages as it’s needed. Needlees to say, both Ajax Web Services and Page Methods use JSON as communication protocol. Let's rework the previous example to use Web Services.

First the HTTP Handler that will serve AJAX Web services requests and responses needs to be included in the web.config file.
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
Next step is to create a web service, which I'll call StringHandler.asmx. The Web Service's class needs to be marked with the ScriptService attribute and ToUpper web method to be implemented.
namespace JsonServicesSample
{
[ScriptService]
public class StringHandler : System.Web.Services.WebService
{
[WebMethod]
public string ToUpper(String input)
{
return input.ToUpper();
}
}
}
We are getting there, now it's just to include the server through the ScriptManager...
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference
Path="~/StringHandler.asmx" />
</Services>
</asp:ScriptManager>
and everything is ready to retrieve data from the client script.
function ToUpper(name)
{
JsonServicesSample.StringHandler
.ToUpper(name, OnSuccedeed, OnFailed);
}
function OnSuccedeed(res)
{
alert(res);
}
function OnFailed(error)
{
// Alert user to the error.
alert(error.get_message());
}

WCF Services

The .NET 3.5 framework release came along with some fixes and extensions upon the early WCF .NET 3.0 version; one of those extensions is the webHttpBinding binding which implements JSON serialization and communication protocol.
So for those who use VS2008 and .NET 3.5 let's rework the sample for the last time.

First of all the service implementation has to be changed to include the WCF contract attributes; for the sake of simplicity I won't create a separate interface for the contract, I'd rather apply the contract attributes in the implementation itself.
namespace JsonServicesSample
{
[ServiceContract
(Namespace="JsonServices.Contract.Sample")]
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class StringHandler : System.Web.Services.WebService
{
[OperationContract]
public string ToUpper(String input)
{
return input.ToUpper();
}
}
}
On top of the WCF contract attributes, the AspNetCommpatibilityRequirements attribute is provided. That attribute will allow the WCF service to have access to the HTTP context (i.e.: ASP.NET Session variable) and besides, it will tell the runtime to ensure at Service Host creation time that all the enpoints defined for the JSON contract use the Web http binding.
Now it's time to define the service and its endpoint in Web.config file, notice that the AspNetCompatibilityRequirements attribute needs to be defined at the ServiceHost level as well.
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="StringHandlerBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<services>
<service name="JsonServicesSample.StringHandler">
<endpoint address=""
behaviorConfiguration="StringHandlerBehavior"
binding="webHttpBinding"
contract="JsonServicesSample.StringHandler"/>
</service>
</services>
</system.serviceModel>
Since the service will be deployed in a IIS environment, the StringHandler.svc is created.
<%@ServiceHost
Service="JsonServicesSample.StringHandler" %>
The server configuration tasks are done, now the service reference needs to be included in the page's Script Manager
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference
Path="~/StringHandler.svc" />
</Services>
</asp:ScriptManager>
And everything is ready to start using the service from client side script code.
function ToUpper(name)
{
JsonServices.Contract.Sample.StringHandler
.ToUpper(name, OnSuccedeed, OnFailed);
}
function OnSuccedeed(res)
{
alert(res);
}
function OnFailed(error)
{
// Alert user to the error.
alert(error.get_message());
}
You may have already noticed that instead of using the service .NET class namespace (JsonServicesSample) as we did in the Web Services sample, this proxy uses JsonService.Contract.Sample, but what the ** is that? Well if you go back to the StringHandler implementation you'll notice that the ServiceContract defines a namespace...
[ServiceContract
(Namespace="JsonServices.Contract.Sample")]
So the bottom line is that WCF client-side proxys use the contract namespace in contrast with Ajax Web services which use the .NET class namespace instead..
And that's it, our WCF service and client are ready. Before finishing though, it's worth mentioning the WebScriptServiceHostFactory Service Host Factory and how it can help us to remove configuration from Json WCF services.
Comparing to the Web service solution, in WCF sample an additional step was taken in order to define the service and endpoint configuration. If you want to keep it simple and skip that step you can do it using the WebScriptServiceHostFactory Service Host factory. This factory creates ServiceHosts with the default services, endpoints and behaviours suitable for this Json web environment so explicit configuration is no longer needeed
In order to achieve it, if we look at the sample then, the service factory is declared in the .svc file...
<%@ServiceHost
Service="JsonServicesSample.StringHandler"
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"%>
...so the WCF configuration can be now wiped out from the web.config file.

That's all folks! I hope that if you weren't familiar with this subject this rather long post has given you an idea of what it takes to implement basic Json services in ASP.NET.

kick it on DotNetKicks.com

7 comments:

  1. I started with asp.net update panels and page methods being called asynchronously and disliked the fact that the whole page gets executed server side as for my application this was not necessary.

    I also disliked that unless you have control over your web server (e.g.) you are on shared hosting, there is no way to shrink the javascript that asp.net produces (other than setting release mode) however that does not minify the scripts as much as it could.

    In the end I started using jQuery instead of the script manager and update panels, you have to do a bit more javascript programming than your approach, but you can combine the effects with no additional overhead. The other nice thing is that I went from 3 javascript files that without any hacking about of the aspx rendering are splatted throughout the source to 2 which I can put at the end of the page, but also the size of the javascript has been reduced by about 30% too.

    ReplyDelete
  2. Hi tr3v,

    You're right about that the page gets executed server side with update panels and up to some level with page methods, but to avoid that situation you can use Json enabled Web Services or WCF Services as I explained in this post; those are pure JSON services completely dettached from the page and its asp.net lifecycle.
    I really appreciate you comment though, I haven't had the chance to use jQuery but I feel it's time for me to get started :)

    ReplyDelete
  3. I have been working with JSON Services for quite a while now. Its working ok for me to start with, However there are things which i am concerned about, would appreciate yoiur views/solutions on the same.

    1) Security - I have been using some encryption model to transfer crucial data, but am not very convinced. Would like to hear the best practice for same.

    2) Service registering at Client - There have been occasions when i have seen that service registration fails at client side, if we restart the IIS, it works fine. This happens in only few sites. We use configless approach.

    Will be waiting for your reply.

    -Wali.

    ReplyDelete
  4. Hi Wali,

    First of all from the sentence "We use configless approach." I assume that you are using WCF services.
    To be honest I haven't had to meet the requirements you expose in 1) and I haven't run across the behaviour you describe in 2)
    so I'm afraid I may not be very helpfull on this... but

    1)If there is a requirement to protect transfered data in your site maybe you should consider the option of protecting
    your site with SSL. The WCF JSON calls are nothing more than POSTS/GETS over HTTP, and SSL along with HTTP work pretty well
    (Don't forget to check this blog entry if you go for this approach
    http://modelus.com/Blog/post/2008/04/NET-352c-WCF2c-JSON2c-SSL2c-and-HTTP-Error-Code-401.aspx)

    2)Like I said before I've never had that problem but if you mention that restarting IIS does the trick maybe you should
    start looking at IIS logs and look for any call from any client that might shed some light on this.

    Hope that helps!

    Javi

    ReplyDelete
  5. This might be a pretty dumb question to ask but do you think JSON can be used in the following scenario:

    I am dynamically adding text box controls at the server side. However I require their client ids to access their values at client side. Now I haven't been able to find any approach so that I can let the client side know about the dynamically added text box id.

    Do you think JSON is a recommended solution for this?

    ReplyDelete
  6. Hi nEEbz,

    JSON is just a data interchange format... I'm afraid that what you're asking has nothing to do with this post!

    I'm not sure that I quite understand what you are trying to accomplish, but you can try getting and client ids of all the controls you generate and then use the Page.ClientScript to register an array declaration which contains those id's so you can access the array in client-side... just brainstorming!

    Thanks,

    Javi

    ReplyDelete
  7. Which of 3 methods Page Methods, Ajax Web Services and Ajax WCF Services will you recommend?
    Can you see any reasons, why to choose more old Page Methods or ASPX insteaed of latest WCF services?

    ReplyDelete