An (Improved) ASP.NET Script Resource Manager

ASP.NET

The web’s biggest asset is also it’s biggest problem. The web provides an infinite amount of information – but it all needs to be retrieved from remote web servers.

Web browsers (clients) are completely dependent on web servers for their content. This content must be retrieved from a web server through one or more HTTP Requests. Depending on how resource intensive a web site is this can lead to be big latency issues. Every single script, stylesheet, and image is retrieved using a seperate HTTP “GET” Request. Impacting the problem even more is that most web browsers will only make a handful of HTTP “GET” Requests at a time.

Resource Handling Guidelines

There are a few things we can do to improve a web browser’s performance. Steven Sounders offers four suggestions in his book, “High Performance Web Sites”

  1. Consolidate HTTP Requests

    Javascript (and stylesheets) can easily be merged into a single resource file. A merged resource file consolidates multiple HTTP Requests into one.

  2. Ensure that Javascript is retrieved as late as possible

    A web browser will fully render a web page prior to retrieving and processing Javascript if the scripts are located at the end of the HTML document. Putting Javascript as “far down” as possible will defer processing and result in a faster load time.

  3. Ensure that stylesheets are retrieved as early as possible

    A web browser will retrieve and process all stylesheets prior to rendering a web page. As such stylesheets should be put as “far up” in an HTML document as possible.

  4. Eliminate Redundant Resources

    This one should be obvious. Never request the same resource twice.

Content Handling Using a Resource Manager

Given the guidelines above it makes sense to automate the processing of resources through a “Resource Manager”. Microsoft has realized such a necessity and provided us with the ASP.NET ScriptManager.

Microsoft ASP.NET ScriptManager

Microsoft’s ASP.NET ScriptManager automates resource handling with varying degrees of success. As our own “improved” implementation will closely follow the ScriptManager’s design it’s important to understand how it works.

The ScriptManager is composed of three basic components – the ScriptManager, the ScriptManagerProxy, and a specialized HttpHandler.

The ScriptManager is an ASP.NET Web Control which optimizes how Javascript is rendered to a web page. The ScriptManager eliminates redundant Javascript and consolidates rendering to a single location in the HTML document. To do this the ScriptManager follows the singleton pattern – only one can exist per page heirarchy.

The ScriptManagerProxy is an ASP.NET Web Control which supplements th ScriptManager. Multiple instances of the ScriptManagerProxy can exist within a page heirarchy. The ScriptManagerProxy should be used in places where a reference to the ScriptManager is unavailable such as nested UserControl’s and MasterPage’s. Any Javascript resources registered to the ScriptManagerProxy are managed and rendered by the ScriptManager. It is essentially a pass-through.

When a ScriptManager is rendered to a web page it’s Javascript resources are injected into the HTML as standard <script> elements – with a twist. The <script> element’s “src” attribute points to a special ASP.NET HttpHandler. When the web browser encounters the <script> element it makes an HTTP Request against the web server which in turn invokes the HttpHandler. The HttpHandler processes the request and returns the desired Javascript back to to the web browser.

<script src="/YourSite/WebResource.axd?d=fs7zUa...&t=6342..." type="text/javascript"></script>
<script src="/YourSite/WebResource.axd?d=EqSMSn...&t=6342..." type="text/javascript"></script>
HTML <script> elements injected by am ASP.NET ScriptManager. WebResource.axd is a custom HttpHandler.

Why Re-Invent the Wheel?

Microsoft’s ASP.NET ScriptManager is good but could be made better. Here is a list of shortcomings that I would like to improve upon …

It Only Supports Javascript

The ScriptManager only supports Javascript. The same resource handling guidelines that apply to Javascript also apply to stylesheets. Redundant stylsheets should be eliminated and unique stylesheets merged to reduce the number of HTTP Requests. The ScriptManager does a great job with Javascript – why can’t it do the same with stylesheets?

All Javascript is Deferred (or Not)

The ScriptManager does a horrible job with Javascript deferment. It takes an all-or-nothing approach. What happens if you don’t want to defer all of your Javascript resources to the end of the web page? Sometimes Javascript needs to be executed prior to rendering.

<asp:ScriptManagerProxy LoadScriptsBeforeUI="false" runat="server"> 
   <Scripts> 
      <asp:ScriptReference Path="~/Scripts/Script1.js" /> 
      <asp:ScriptReference Path="~/Scripts/Script2.js" /> 
      <asp:ScriptReference Path="~/Scripts/Script3.js" /> 
   </Scripts> 
</asp:ScriptManagerProxy>

Note: The ScriptManager’s LoadScriptsBeforeUI=”false” option defers all scripts to the end of the page.

AJAX Javascript Libraries? No Thanks

The ScriptManager automatically includes Microsoft’s robust AJAX Javascript library. The problem? Although most web pages require Javascript not all of them need AJAX. Unfortunately, there’s no way to get the cake without the icing.

The (Improved) Resource Manager

The ResourceManager consists of three main components – the ResourceManager, the ResourceManagerProxy, and the ResourceHttpHandler. Sound familiar? The functionality of the ResourceManager’s components closely resembles that of their (similarly named) ScriptManager counterpart. The biggest difference? The ResourceManager’s components are all generic. This allows them to be custom-tailored for any resource type.

Within the ScottsJewels.Web.UI namespace you will find the ResourceManager as well as a StyleManager (for managing stylesheets) and a ScriptManager (for managing Javascript). In retrospect, ScriptManager was a poor name choice as it overlaps with Microsoft’s own implementation. Please make sure that you refer to the ScottsJewels.Web.UI namespace when using it to avoid conflicts.

Resource Manager Control

The ResourceManager Control is very similar to Microsoft’s ScriptManager Control. Both are singleton controls responsible for organizing and rendering resources to a web page. Unlike Microsoft’s ScriptManager the ResourceManager allows you more granularity when configuring where a resource should render on a web page. Resources contained within a <DeferredResources> container are rendered at the end of a web page. All other resources are rendered in the web page’s <head>.

Here is an example of how to register some Javascript resources using the “improved” ScriptManager in ScottsJewels.Web.UI.

<scottJewels:ScriptManager runat="server">
	<Resources>
		<scottJewels:Script Path="~/scripts/script1.js" />
		<scottJewels:Script Path="~/scripts/script2.js" />
	</Resources>
	<DeferredResources>
		<Resources>
			<scottJewels:Script Path="~/scripts/script3.js" />
		</Resources>
	</DeferredResources>
	<CompositeResource>
		<Resources>
			 <scottJewels:Script Path="~/scripts/script4.js" />
			 <scottJewels:Script Path="~/scripts/script5.js" />
		</Resources>
	</CompositeResource>
</scottJewels:StyleManager>
Registering scripts using the ScottsJewels.Web.UI.ScriptManager

Here is an example of the HTML output produced by the “improved” ScriptManager. Notice that the location of the <script> elements corresponds to how the scripts were registered in the ScriptManager. Also notice that the <script> elements’ “src” points to ScriptHttpHandler.ashx. The web server will invoke the ScriptHttpHandler when the web browser makes an HTTP Request for the Javascript resource.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<script src="ScriptHttpHandler.ashx?Resource=~/scripts/script1.js" type="text/javascript" ></script>
	<script src="ScriptHttpHandler.ashx?Resource=~/scripts/script2.js" type="text/javascript" ></script>
	<script src="ScriptHttpHandler.ashx?Resource=~/scripts/script4.css,~/scripts/script5.js" type="text/javascript" ></script>
	<title></title>
</head>
<body>
</body>
</html>
<script src="ScriptHttpHandler.ashx?Resource=~/scripts/script3.js" type="text/javascript" </script>
HTML Output produced by ScottsJewels.Web.UI.ScriptManager

The “improved” ResourceManager was created from scratch and does not derive from Microsoft’s ScriptManager. As my experience with custom ASP.NET Web Controls was relatively sparse up until this experiment it was only natural that I would ecounter some new problems and forge (at least for me) some new solutions. For example …

How do I enforce the singleton pattern on an ASP.NET Web Control?

The ResourceManager needs to follow the singleton pattern. Resource management and rendering needs to be consolidated to a single control to eliminate redundancy.

The secret to implementing the singleton pattern in an ASP.NET Web Control lies in the HttpContext object. Every time an HTTP Request is made against the web server an HttpContext object is created. This object is unique to the request and accessible through the static HttpContext.Current property. HttpContext exposes an Items property bag. By checking the bag for an instance of the ResourceManager within the ResourceManger’s own constructor we can enforce the singleton pattern. If a ResourceManager doesn’t exist in the bag we will add it. Otherwise we will throw an exception notifying the user that more than once ResourceManager exists in the Page heirarchy.

static ResourceManager()
{
	CachIdentifier = typeof(ResourceManager<TResource, TResourceComparer>).ToString();
}
Declaring a unique identifier for the ResourceManager (ResourceManager.cs)
protected ResourceManager()
{
	...

	if (HttpContext.Current != null)
	{
		if (HttpContext.Current.Items.Contains(CachIdentifier))
		{
			throw new InvalidOperationException(string.Format("Only one {0} is allowed per page!", GetType()));
		}
		HttpContext.Current.Items[CachIdentifier] = this;
	}
}
Checking to see if the ResourceManager already exists in HttpContext.Current.Items (ResourceManager.cs)

By providing a static Current property on the ResourceManager we can expose it’s singleton instance everywhere.

internal static ResourceManager<TResource, TResourceComparer> Current
{
	get
	{
		ResourceManager<TResource, TResourceComparer> result = null;

		if (((HttpContext.Current != null) && (HttpContext.Current.Items[CachIdentifier] != null)) &&
			(HttpContext.Current.Items[CachIdentifier] is ResourceManager<TResource, TResourceComparer>))
		{
			result = HttpContext.Current.Items[CachIdentifier] as ResourceManager<TResource, TResourceComparer>;
		}

		return result;
	}
}
Exposing the singleton ResourceManager (ResourceManager.cs)

How to I force an ASP.NET Web Control to render somewhere else?

When creating a custom ASP.NET Web Control you will typically override the Render method to inject the desired HTML elements into the web page that is returned to the web browser. The ResourceManager, however, requires more control over where the HTML is injected. Deferred resources need to be rendered at the end of the web page. Non-deferred resources need to be rendered in the web page’s <head>.

The ASP.NET Page Lifecycle specifies that a Page will fire a PreRender event prior to rendering it’s control heirarchy. By inserting Literal Controls into a Page’s control heirarchy during it’s PreRender event we can set up “placeholders”. Later, when the ResourceManager’s PreRender event is fired it can leverage these placeholders to surgically inject it’s resources into the web page.

Why can’t we just add the resources to the web page during the ResourceManager’s Render event? Because ASP.NET will throw a nasty error, “The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.” Apparently a Page doesn’t have the same restrictions as a Control.

private readonly LiteralControl _headerPlaceholder;
private readonly LiteralControl _footerPlaceholder;

protected override void OnInit(EventArgs e)
{
	Page.PreRender += OnPagePreRender;
}

private void OnPagePreRender(object sender, EventArgs e)
{
	Page.Header.Controls.Add(_headerPlaceholder);
	Page.Controls.Add(_footerPlaceholder);
}

protected override void OnPreRender(EventArgs e)
{
	headerPlaceholder.Text = "My resources";
	footerPlaceholder.Text = "My resources";
}
Using stragtegically placed Literal Controls to render resources (ResourceManager.cs)

Resource Manager Proxy Control

The ResourceManagerProxy is very similar to Microsoft’s ScriptManagerProxy. It is used when an instance of the ResourceManager is not available. Multiple instances of the ResourceManagerProxy can co-exist within a page hierarchy. The ResourceManagerProxy is just that – a proxy. It doesn’t really do much on it’s own and relies upon the ResourceManager to manage and render the resources assigned to it. The ScriptManagerProxy’s <Resources> property is just a façade that exposes the singleton instance of the ResourceManager.

[PersistenceMode(PersistenceMode.InnerProperty)]
public List<TResource> Resources
{
	get
	{
		if (ResourceManager<TResource, TResourceComparer>.Current != null)
			return ResourceManager<TResource, TResourceComparer>.Current.Resources;

		throw new InvalidOperationException(
			string.Format("No {0} ResourceManager has been declared.", typeof(TResource)));
	}
}
The ResourceManagerProxy’s resources are actually managed by the ResourceManager (ResourceManagerProxy.cs)

Here is an example of how to register some stylesheets in a Page code-behind using the StyleManagerProxy.

styleManagerProxy.CompositeResource.Resources.Add(new Style() { Media = "screen", Path = "~/styles/style1.css" });
styleManagerProxy.CompositeResource.Resources.Add(new Style() { Media = "screen", Path = "~/styles/style1.css" });
styleManagerProxy.CompositeResource.Resources.Add(new Style() { Media = "screen", Path = "~/styles/style2.css" });
styleManagerProxy.CompositeResource.Resources.Add(new Style() { Media = "screen", Path = "~/styles/style2.css" });
styleManagerProxy.CompositeResource.Resources.Add(new Style() { Media = "print", Path = "~/styles/style3.css" });
styleManagerProxy.CompositeResource.Resources.Add(new Style() { Media = "print", Path = "~/styles/style4.css" });
Registering stylsheets in a Page code-behind using the ScottsJewels.Web.UI.StyleManagerProxy (StyleManagerProxy.cs)

Here is an example of the HTML output produced by the StyleManager. Notice that the StyleManager consolidates and groups the stylesheets prior to rendering them.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<link media="screen" href="StyleHttpHandler.ashx?Resource=~/styles/style1.css,~/styles/style2.css" type="text/css" rel="stylesheet" />
	<link media="print" href="StyleHttpHandler.ashx?Resource=~/styles/style3.css,~/styles/style4.css" type="text/css" rel="stylesheet" />
   <title></title>
</head>
<body>
</body>
</html>
HTML output produced by ScottsJewels.Web.UI.StyleManager (StyleManager.cs)

Resource Manager HTTP Handler

The ResourceHttpHandler is very similar to the HttpHandler used by Microsoft’s ScriptManager. Upon encountering an HTML element rendered by the ResourceManager a web browser will make an HTTP Request against the web server. The ResourceHttpHandler’s job is to intercept this request, interpret it, and return the appropriate resource.

The underlying logic of the ResourceHttpHandler is very simple. Find the requested resources, merge them if necessary, and stream them back to the web browser.

public void ProcessRequest(HttpContext context)
{
	if (context.Request.QueryString["Resource"] != null)
	{
		string[] requestedResources = context.Request.QueryString["Resource"].Split(new[] { ',' }, Int32.MaxValue, StringSplitOptions.RemoveEmptyEntries);

		string resourcePhysicalPath;

		foreach (string resource in requestedResources)
		{
			resourcePhysicalPath = context.Server.MapPath(resource);

			if (File.Exists(resourcePhysicalPath))
			{
				using (StreamReader reader = new StreamReader(resourcePhysicalPath))
				{
					context.Response.BinaryWrite(StringToByteArray(reader.ReadToEnd()));
					context.Response.ContentType = Resource.ContentType;
					context.Response.Flush();
				}
			}
			else
			{
				context.Response.StatusCode = 404;
			}
		}
	}
}
The ProcessRequest function of the ResourceHttpHandler (ResourceHttpHandler.cs)

What is an ASP.NET HttpHandler?

When a web browser requests an ASP.NET resource such as a Web Page (.aspx), a User Control (.ascx), or a Web Service (.asmx/.svc) the request is routed to an ASP.NET HttpHandler. The HttpHandler processes the request and returns a response to the web browser. An ASP.NET resource is mapped to an HttpHandler in the web.config file.

Here are some HttpHandler mappings from the .NET 4.0 global web.config file. This configuration maps requests for WebResource.axd to the AssemblyResourceLoader HttpHandler. As shown earlier WebResource.axd is requested by HTML <script> elements injected by Microsoft’s ScriptManager. The other two mappings are for ASP.NET Web Forms and User Controls respectively.

<httpHandlers>
	<add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True" />
	<add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True" />
	<add path="*.ascx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
	...
</httpHandlers>
Sample of HttpHandler mappings from C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config

You can create your own custom HttpHandler by deriving a new class from IHttpHandler. To enable a custom HttpHandler you need to register it in your Web Application’s web.config. Registration varies depending on what version of web server that you are running.

If you are running IIS 6, 7 Classic, or Visual Studio’s built-in web server you should register your custom HttpHandler like this …

<?xml version="1.0"?>
<configuration>
	<system.web>
		<httpHandlers>
			<add verb="*" path="StyleHttpHandler.ashx" type="ScottsJewels.Web.StyleHttpHandler, StyleManager" />
			<add verb="*" path="ScriptHttpHandler.ashx" type="ScottsJewels.Web.ScriptHttpHandler, ScriptManager" />
		</httpHandlers>
	</system.web>
</configuration>
HttpHandler configuration for IIS 6, 7 Class, or Visual Studio built-in web server

If you are running IIS 7 Integrated you should register your custom HttpHandler like this …

<?xml version="1.0"?>
<configuration>
	<system.webServer>
		<handlers>
			<add name="StyleHttpHandler" verb="*"path="StyleHttpHandler.ashx" type=" ScottsJewels.Web.StyleHttpHandler, StyleManager" resourceType="Unspecified" />
			<add name="ScriptHttpHandler" verb="*" path="ScriptHttpHandler.ashx" type=" ScottsJewels.Web.ScriptHttpHandler, ScriptManager" resourceType="Unspecified" />
		</handlers>
	</system.webServer>
</configuration>
HttpHandler configuration for IIS 7

Conveniently, Microsoft offers a “compatibility mode” which allows both types of configuration to co-exist within the same web.config file…

<?xml version="1.0"?>
<configuration>
	<system.web>
		<httpHandlers>
			<add verb="*" path="StyleHttpHandler.ashx" type="ScottsJewels.Web.StyleHttpHandler, StyleManager" />
			<add verb="*" path="ScriptHttpHandler.ashx" type="ScottsJewels.Web.ScriptHttpHandler, ScriptManager" />
		</httpHandlers>
	</system.web>
	<system.webServer>
		<validation validateIntegratedModeConfiguration="false"/>
		<handlers>
			<add name="StyleHttpHandler" verb="*" path="StyleHttpHandler.ashx" type=" ScottsJewels.Web.StyleHttpHandler, StyleManager" resourceType="Unspecified" />
			<add name="ScriptHttpHandler" verb="*" path="ScriptHttpHandler.ashx" type=" ScottsJewels.Web.ScriptHttpHandler, ScriptManager" resourceType="Unspecified" />
		</handlers>
	</system.webServer>
</configuration>
HttpHandler configuration for all web servers using <validation validateIntegratedModeConfiguration=”false”/>

How does the HttpResourceHandler retrieve resources?

The ResourceManager renders relative paths to the web page for all of the resources. When the web browser requests these resources from the ResourceHttpHandler the handler needs to be able to reconcile the relative path to the physical location on the web server. This is accomplished by using the Server.MapPath function. One limitation of the ResourceManager is that it can only process local resources. You can not register resources from sources other than the web application itself.

Conclusion

So there we have it. A robust yet extendable improvement to Microsoft’s ASP.NET ScriptManager Control.

The source code is available here. Please let me know if you have any questions or suggestions on how it can be improved.


#.NET #CSharp #ASP.NET #Programming #SoftwareDevelopment #SoftwareEngineer #DrawnAndCoded

Simple AJAX Using JQuery, JSON, ASP.NET, and WCF

AJAX

I know what you’re thinking. Oh great, another article on AJAX! Okay, sure. You’re right. But seriously, when I was originally attempting to learn this stuff I found myself jumping through hoops just trying to get a simple example to work…

  • How do I configure a web service to return JSON?
  • What in the heck is JSON?
  • Why not use XML?
  • What is JQuery?
  • How do I call a Web Service from JavaScript?

Yeah, there were articles. Lots of articles. But I found that no single article presented a complete example. Some would focus on the client. Some the server. Some would gloss over the “why” and emphasize the “how”. Others would pull in obscure technology that, while cool, would be completely at odds in a business environment.

So, here’s my own entry into wide assortment of AJAX articles – a simple Echo Service.

Why an Echo Service?

It’s a very simple way to demonstrate how to send/receive JSON objects between a client browser and a Web Service. This is common pattern that is used in modern websites. A simple example makes it easier to showcase a bunch of cool (yet essential) technologies such as…

ASP.NETMicrosoft’s web application framework.
JavascriptThe defacto standard for manipulating the HTML DOM.
JQueryAn open-source Javascript Library for manipulating the HTML DOM. Endorsed by Microsoft and included with Visual Studio 2010.
JSONLike XML but with a (much) smaller footprint. JSON is Javascript. It can be serialized into a human-readable string or deserialized into a Javascript object that can be used programatically in Javascript code.
Windows Communication Foundation (WCF)Microsoft’s implementation of a SOA framework. We’ll use it to create a web service.

The Echo Service

Overview

First, we’re going to create an EchoService Web Service using Microsoft’s Windows Communication Foundation (WCF). A Message class will be used to facilitate communication with the EchoService. The Message will be automatically converted to/from JSON by WCF.

Next, we’re going to create an ASP.NET EchoPage Web Form. A blank JSON representation of the Message class will be exposed to the EchoPage’s client-side. This JSON Message will be populated with some text and submitted to the EchoService using JQuery’s AJAX implementation.

Finally, we’re going to apply some enhancements to the EchoService and EchoPage to make them a little more robust – while eliminating unecessary code.

EchoService WCF Web Service

The EchoService exposes a single Echo() method that receives and returns a JSON object. Why a JSON object and not just a bunch of params? Because parameters are sloppy and difficult to manage. Also, it’s easier to deal with the same data object on both the server and the client.

The EchoService’s handling of JSON is declared in the attributes for the Echo() method. Notice the ResponseFormat and RequestFormat indicators. This tells WCF to implicitly convert between the Message class and JSON.

[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
public Message Echo (Message message)
{
   message.Text = string.Format("Echo {0}", message.Text);
   return message;
}
EchoService.svc.cs

EchoPage ASP.NET Web Form

Client-Side (.aspx)

The client-side portion of the EchoPage uses JQuery’s AJAX implementation to submit (and receive) Messages to the EchoService. The “data” parameter specifies the data that you want to submit to the EchoService. The “dataType : json” instructs JQuery to expect a JSON result from the EchoService. JQuery will implicitly convert the EchoService’s response. The “success” function is called upon a successful transaction.

Be very careful when specifying your “data” parameter. It should be in the form of a JSON (name/value pair) object where the name corresponds to the parameter name on the EchoService’s Echo() method. The value is your actual data – serialized using the JSON.Stringify() function. Also, take special notice of the quotation marks. I’ve had problems before with single versus double-quotes.

$.ajax({
   type: "POST",
   url: "EchoService.svc/Echo",
   data: '{ "message" : ' + JSON.stringify(jsonMessage) + '}',
   dataType: "json",
   contentType: "application/json; charset=utf-8",
   success: function(data, textStatus, httpRequest) {
         data = data.hasOwnProperty('d') ? data.d :  data;										
         $("#responseMessage").html(data.Text); 
   },
   error: function(httpRequest, status, errorThrown) {
         $("#responseMessage").addClass("error");
         $("#responseMessage").html("There was a problem calling the Echo Service. Error : " + errorThrown + "!");
   }
});
EchoPage.svc.cs
What’s the deal with the .d in the AJAX success function?

Since ASP.NET 3.5 Microsoft’s WCF Service encapsulates all JSON responses in a “d” name-value pair for security reasons. You can read more about it here and here. A side-effect of this is that we need to handle the encapsulation in our client-side Javascript …

data = data.hasOwnProperty('d') ? data.d :  data;
EchoPage.aspx
What’s the deal with the JSON.stringify() ?

When submitting a request to the EchoService using JQuery’s AJAX the data needs to be in the form of a string. JSON.stringify() serializes our Message (which is a JSON object) so that it can be submitted properly.

$.ajax({
   type  : "POST",
   url   : "EchoService.svc/Echo",
   data  : '{ "message" : ' + JSON.stringify(jsonMessage) + '}',
...
EchoPage.aspx

Server-Side (.aspx.cs)

The server-side portion of the EchoPage is relatively sparse. When we start optimizing our code later it will disappear entirely. For now, the server-side portion of the EchoPage creates a JSON representation of an empty Message.

protected string jsonMessage;

protected void Page_Load(object sender, EventArgs e)
{
   DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Message));
   using (MemoryStream ms = new MemoryStream())
   {
      serializer.WriteObject(ms, new Message());
      jsonMessage = Encoding.Default.GetString(ms.ToArray()); // Viola!
   }
}
EchoPage.aspx.cs

This JSON Message is exposed to the client-side Javascript as a var and is used in the JQuery AJAX submission to the EchoService.

var jsonMessage = <%= jsonMessage %>;
EchoPage.aspx

Okay, great. Is that it?

No, although the above code works there are a few issues that we should probably look into …

  1. ASP.NET’s JSON security is a nice touch. However, our handling of the “d” encapsulation on the client-side of the EchoPage is clunky.
  2. Our serialization of the Message object into JSON on the server-side of the EchoPage is very specific to a particular class.
  3. If an error occurs while the EchoService is processing a Message there is no way to properly notify the EchoPage so that it might handle the error gracefully.

Polishing the Echo Service

JQuery’s AJAX dataFilter

JQuery exposes a customizable “dataFilter” for it’s AJAX calls. Declaring a dataType instructed JQuery to implicitly handle the response from a Web Service. Declaring a dataFilter puts the responsibility solely on the developer.

We can leverage JQuery’s AJAX dataFilter when handling ASP.NET’s “d” security feature on the EchoPage. Notice that we need to programatically deserialize the EchoService’s response into a Javascript object using JSON.parse().

$.ajax({
   type : "POST",
   url : "EchoService.svc/Echo",
   data : '{ "message" : ' + JSON.stringify(jsonMessage) + '}',
   /* dataType: "json",  */
   contentType : "application/json; charset=utf-8",
   dataFilter : function(data) {
            data = JSON.parse(data);
            return data.hasOwnProperty("d") ? data.d : data;
         },
   success: function(data, textStatus, httpRequest) {
            /* data = data.hasOwnProperty('d') ? data.d :  data; */										
            $("#responseMessage").html(data.Text);
         },
   error: function(httpRequest, status, errorThrown) {
            $("#responseMessage").addClass("error");
            $("#responseMessage").html("There was a problem calling the Echo Service. Error : " + errorThrown + "!");
         }
});
EchoPage.aspx

JSON Serialization Extension Method

NET 3.5 introduced Extension Methods. Extension Methods allow you to add new methods to an existing type. Adding an extension method to the .NET base object class exposes the method to all classes.

Currently the EchoPage can only serialize a Message into JSON. However, by moving the code into an Extension Method we can clean up the EchoPage and eliminate a lot of (potentially) redundant code. This is what the new .GetJsonString() Extension Method looks like …

/// <summary>
/// Extension methods to .NET classes.
/// </summary>
public static class Extensions
{
   /// <summary>
   /// Extends object to with a JSON serializer.
   /// </summary>

   /// The object as a serialized JSON string.
   public static string GetJsonString(this object obj)
   {
      DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
      using (MemoryStream ms = new MemoryStream())
      {
         serializer.WriteObject(ms, obj);
         return Encoding.Default.GetString(ms.ToArray());
      }
   }
}
Extensions.cs

The EchoPage can now call .GetJsonString() directly from the Message data object.

var jsonMessage = <%= (new SimpleAJAXEchoService.Message()).GetJsonString() %>
EchoPage.aspx

As far as the server-side code for the EchoPage? Gone.

/// <summary>
/// An Web Page that "talks" to the Echo Service using AJAX.
/// </summary>

public partial class EchoPage : System.Web.UI.Page
{
   // The addition of the .GetJsonString() method eliminates all of this code. Furthermore, it can be called against any class derived from
   // object (which is pretty much anything).

/* Gone!
   /// <summary>
   /// A serialized JSON representation of the Message class. Will be exposed to the EchoPage's Javascript and used to submit data to
   /// the EchoService.
   /// </summary>
   protected void Page_Load(object sender, EventArgs e)
   {
      // We'll need to submit an instance of the Message class to the EchoService from the EchoPage using Javascript. JSON is an ideal format to
      // work with. Let's serialize an instance of Message into a JSON string and expose it to the EchoPage's Javascript.
      DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Message));
      using (MemoryStream ms = new MemoryStream())
      {
         serializer.WriteObject(ms, new Message());
         jsonMessage = Encoding.Default.GetString(ms.ToArray()); // Viola!
      }
    }
*/
}
EchoPage.aspx.cs

Encapsulating the Response

By wrapping the response from the EchoService we can more effectively handle errors that it might throw on the EchoPage. We can wrap up the EchoService’s response Message in a new ClientResponse wrapper. In addition to a Payload (the Message) the ClientResponse exposes an IsSuccessful and ErrorMessage.

/// <summary>
/// A generic client response wrapper. Wraps a web service response so that
/// additional (troubleshooting) information can be returned alongside the payload.
/// </summary>

/// The payload type to encapsulate.
[DataContract]
public class ClientResponse
{
   /// <summary>
   /// The data to return to the client.
   /// </summary>
   [DataMember]
   public T Payload { get; set; }

   /// <summary>
   /// True, if the data was retrieved sucessfully.
   /// </summary>
   [DataMember]
   public bool IsSuccessful { get; set; }

   /// <summary>
   /// The error message if the data was not retrieved sucessfully.
   /// </summary>
   [DataMember]
   public string ErrorMessage { get; set; }

   /// <summary>
   /// Constructor.
   /// </summary>
   public ClientResponse()
   {
      IsSuccessful = true;
      ErrorMessage = string.Empty;
   }

   /// <summary>
   /// Constructor.
   /// </summary>
   /// True, if the data was retrieved sucessfully.
   /// The data to return to the client.
   /// The error message if the data was not retrieved sucessfully.
   public ClientResponse(bool isSuccessful, T payload, string errorMessage) : this()
   {
      IsSuccessful = isSuccessful;
      Payload = payload;
      ErrorMessage = errorMessage;
   }
}
ClientResponse.cs

We can leverage JQuery’s AJAX dataFilter to implictly handle the ClientResponse wrapper. Any errors incurred will now be passed onto JQuery’s AJAX error() function.

$.ajax({
   type: "POST",
   url: "EchoService.svc/Echo",
   data: '{ "message" : ' + JSON.stringify(jsonMessage) + '}',
   /* dataType: "json",  */
   contentType: "application/json; charset=utf-8",
   dataFilter: function(data) {
           data = JSON.parse(data);
           data = data.hasOwnProperty("d") ? data.d : data;
           if (data.hasOwnProperty("IsSuccessful")) {
             if (data.IsSuccessful == true) {
               return data.Payload;
             }
             else {
               var errorMessage = "Error";

               if (data.hasOwnProperty("ErrorMessage") && data.ErrorMessage !== null) {
                  errorMessage = data.ErrorMessage;
               }
               throw errorMessage;
             }
           }					

           return data;
         },
   error:   function(httpRequest, status, errorThrown) {
         $("#responseMessage").addClass("error");
         $("#responseMessage").html("There was a problem calling the Echo Service. Error : " + errorThrown + "!");
      }
});
EchoPage.aspx

Conclusion

Okay, sure. The Echo Service was a simple example. But hey! It showcased a whole bunch of cool technologies in a nice little package. Now, next time someone asks you on the street, “Excuse me, but what is AJAX?” you can look them in the eyes and reply, “Let me tell you about a simple little Echo Service that I know…”.

The source code is available here. Please let me know if you have and questions or comments.


#.NET #WCF #JSON #Webservices #EchoService #ASP.NET #JQuery #Javascript #Programming #SoftwareDevelopment #SoftwareEngineer #DrawnAndCoded