How will you make it if you never even try?

December 13, 2006

Understanding the “Atlas” ScriptResourceHandler

Filed under: Atlas — Tags: , , — charlieflowers @ 2:48 pm

Whew! Been very busy the past several months on an Atlas consulting engagement. There has been a lot to learn with Atlas and a lot of changes from Microsoft to adapt to (yes, I know the name changed, but “Atlas” is still the clearest term to use right now).

I recently had to dig into the internals of ScriptResourceHandler.axd, and I wanted to write my findings here for others and for my own sake. Atlas now comes with a new HTTP handler that serves JavaScript code — called “ScriptResourceHandler.axd”. This is a nice, feature-packed little thing, but it also obscures some things a little bit. It is worth it, but to get the most out of it you need a lot more info than I could find anywhere else on the web.

The Big Picture

In a nutshell, the ScriptResourceHandler.axd is meant to give you the following benefits:

1. Let you embed JavaScript code into your assemblies, and have it included from those assemblies into your web pages.

2. Take care of details of serving that script efficiently for you (primarily, making sure the browser caches it, and then compressing it using GZip).

3. Helping you with internationalization, by automatically reading a set of resources (ie, strings that you want to localize), and putting them into an object in JavaScript so your code can reference them.

It is fantastic to have all of this at your fingertips. Atlas itself uses this heavily. For example, the ScriptManager control uses the ScriptResourceHandler to load the “MicrosoftAjax.js” code that defines the foundation of the Microsoft Ajax ClientFx.

Now for some under-the-hood details…

What in the world is that QueryString??

If you view source on an Atlas page that has a ScriptManager, you will see at least one <script> reference whose “src” attribute refers to ScriptResourceHandler.axd. For example, you might see the following…


<span style="font-family:Courier New;font-size:xx-small;">
    <script src="/MsAjaxClientStuff/ScriptResource.axd?d=9uZzWonH9Yffv82x9JBEOboR-Z7Vtw-        y48sFq7UKtpiuxzt2r1pnjhJ9asp1Z8z4gK8HT0imILHAeWKHaiW1FZB5kueEppforSccLkW4LwX0I-    Yz-dsEC885R_smzSRH6U9TUvmoTDMWxfIIioA6AzzuM23jKe9-G5HkLa5GW5w1&t=633004023929547001" 
        type="text/javascript">
    </script></span>

The QueryString is obviously not meant for human consumption. It is encrypted, and it contains three key pieces of information:

1) The name of the assembly that contains an embedded resource which is the text of the JavaScript that you want to include in the page.

2) The name of the resource within that assembly that contains the JavaScript code you want to include in the page.

3) The culture to be used to select the correct bundle of resources (to over-simplify, this indicates which language you want). If this is not present, then you will get the InvariantCulture.

How is the QueryString encrypted / decrypted?

There is a pair of internal static methods on System.Web.UI.Page, called “EncryptString” and “DecryptString”. These encrypt and decrypt a string that is passed. Atlas uses reflection to call these methods since it otherwise would not be able to access them due to the internal modifier.

How does it find the resource within the assembly?

The primary usage pattern (the only one I plan to cover here ) is that you have placed some ScriptResourceAttribute’s onto your assembly. Each ScriptResourceAttribute lets you declare one particular script resource, and it lets you specify some information that applies to each script resource. At request time, the ScriptResourceHandler then reflects upon your assembly to find the ScriptResourceAttribute that matches the script it is being asked for. In particular, the attribute has a property called “ScriptName” which must match the request’s script name (the second thing in the QueryString from above).

Once it has found the matching ScriptResourceAttribute, it uses info from its properties to proceed. In particular, there are 3 key properties of the ScriptResourceAttribute:

1) ScriptName —  (must match the resource name from the QueryString). In the case of the main Atlas file, the ScriptName property is “Microsoft.Web.Resources.ScriptLibrary.MicrosoftAjax.js“

2) ScriptResourceName — this is the name of a .resources file in your assembly that contains a set of resource name/value pairs that your script needs to refer to. In the case of the main Atlas file, the ScriptResourceName property is “Microsoft.Web.Resources.ScriptLibrary.Res“

3) TypeName — the name of this property is confusing. This is the name of a JavaScript object that will contain all of the resource values collected for your application. (This will become more clear below).

How does it construct the final JavaScript to include in the page?

It sets up an output stream that uses GZip compression, and it configures the HTTP caching policy so that the browser will properly cache the JavaScript. Then, it begins to write to the stream. First, it writes the complete contents of the JavaScript text that was in the resource named by ScriptResourceAttribute.ScriptName.

Then, it writes your resource values. To do this, it generates JavaScript that instantiates an object. The object’s name comes directly from your ScriptResourceAttribute’s “TypeName” property. For the Atlas primary file, this TypeName property is set to “Sys.Res”. Therefore, you get something like the following:


Sys.Res = {

someResourceName: “Some value for the resource”,

// etc

};

The values that are set inside of the object (inside of Sys.Res, for example), come directly from an embedded .resources file in your assembly whose name matches the ScriptResourceAttribute’s ScriptResourceName property. Each entry in a .resources file is a name-value pair, and ScriptResourceHandler merely translates each pair into a property declaration on the object (it uses the JavaScriptSerializer to write the value of each resource).

So, now your client code has had the correct set of resources selected for the culture you want, and those values are “automagically” available to your client code.

One little extra thing here — if the script that was requested ends in “.debug.js”, the ScriptResourceHandler will do everything mentioned thus far, and also it will find a second ScriptResourceAttribute, for the same script without “.debug.js”. It will then get the .resources file for it, and include all of those resources in the same JavaScript object. This looks like a little hack that Microsoft added when they decided they wanted to support debug and non-debug versions of scripts. Makes sense — keeps you from having to duplicate all of the name/value pairs when providing a debug script and a non-debug script.

Finally, ScriptResourceHandler does one more nice little thing for you: it includes the “end-of-script script” that Atlas would like all script files to include. This is as follows:

<span style="color:#800000;">if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();</span>

This script basically says, “If I am on an Atlas page, then let me call the Application.notifyScriptLoaded() method to tell Atlas that my script has finished loading.”

Summary

So, the ScriptResourceHandler has found your JavaScript from within your assembly, served it with compression and proper configuration for HTTP caching, and also extracted all the resource name/value pairs that your script needs and made those values available to your JavaScript on the page.

Note that circumventing the ScriptResourceHandler can be one of the causes of “Sys.Res.xxx is not an object” errors. Why? Because “Sys.Res” is the name of the Atlas client-side object that contains the resource values that Atlas wants to use. If you bypass the ScriptResourceHandler, then nothing will create the Sys.Res object for you. (Here’s a post that touches on that a little bit, because it talks about what you have to do to use the MS Ajax client-side framework (ClientFx) without using ASP.NET on the server).

Blog at WordPress.com.