Accessing Embedded Resources through a URL using WebResource.axd
By Scott Mitchell
Introduction
Many of the built-in ASP.NET server controls require additional, external resources in order to function properly. For example, when using any of the ASP.NET validation controls, the controls rely on a bevy of JavaScript functions to perform their client-side validation. While each validation control could emit such script directly into the page's content, a more efficient approach would be to package these JavaScript functions into an external JavaScript file and then include that file in the page using
<script src="PathToExternalJavaScriptFile" type="text/javascript" >.
This would reduce the total page size and would allow the browser to cache the external JavaScript file (rather than having to
send the JavaScript code down to the browser on each and every page visit/postback).
Prior to ASP.NET 2.0, such external resources that needed to be accessible to the visitor's browser had to be implemented as
actual files on the file system. If you've worked with ASP.NET 1.x's validation controls, your pages have included a reference
to a JavaScript file /aspnet_client/system_web/version/WebUIValidation.js and there is an actual file
with that name residing in the web application's root. Such external resources hamper deployment - if you deploy your
application from the testing server to production, it's imperative that the production server have the same external resources
(WebUIValidation.js, in this case), in the same locations in the file system.
To remedy this, ASP.NET 2.0 allows for external resources to be embedded within the control's assembly and then be accessed
through a specified URL. With the external images, JavaScript files, CSS files embedded in the control's assembly,
deployment is a breeze, as all of the resources are now contained within the assembly (the .dll file).
There are no external resources whose file names and location on the file system must map up. Once embedded into the assembly,
these resources can be accessed from an ASP.NET 2.0 web page through a special URL (WebResource.axd).
In this article we'll examine how to embed external resources into an assembly and how to retrieve these resources on the web page using a special URL. This technique helps simplify installation and deployment for creating custom, compiled server controls that are shipped to customers. Read on to learn more!
A Insanely Contrived Custom Control Example...
To examine the embedded resources feature in ASP.NET 2.0, we need to build a custom control that includes external resources. For this article, I've decided to create a very simple control that extends the base functionality of the TextBox Web control and adds a rather psychedelic twist to it. This custom control - FunkyTextBox - is configured such that its client-side
onkeypress event the TextBox's background color is changed to a randomly selected color defined in an array.
The net result is that as the user types in text into the TextBox, it cascades through a series of different background colors,
causing the user to say things like, "Grooooovy, man," and "Whoooooaaaaa."
This behavior requires a bit of client-side script: first, we need to add a function that picks a random number and assigns the
TextBox's background color accordingly; second, we need to define the array of possible colors. We could, very easily,
inject all of this JavaScript directly into the ASP.NET page, thereby bypassing the need for embedded resources. The following
code shows the FunkyTextBox control's complete code where the JavaScript is injected directly into the page (using
statements, namespace, and some of the 256 colors defined in the array have been removed for brevity):
public class FunkyTextBox : TextBox
|
This server control derived from the built-in ASP.NET TextBox control, but extends it in two ways. First, in the AddAttributesToRender,
a client-side attribute is added to the rendered <input> tag so that whenever a key is pressed when the user's
cursor is within the rendered textbox, the JavaScript function ChangeBackgroundColor is invoked, passing in the
textbox reference. Second, in the FunkyTextBox's PreRender event, the JavaScript necessary to provide this functionality
is squirted directly into the page through the Page.ClientScript class's RegisterScriptBlock method.
In particular, the ChangeBackgroundColor function and funkyTextBoxColors array are defined. The
ChangeBackgroundColor function picks a random number and then assigns the passed-in HTML element's background color
to the matching background color defined in the funkyTextBoxColors array.
When the FunkyTextBox is added to an ASP.NET page, the resulting page markup contains the following:
<script type="text/javascript">
|
The page contains the JavaScript injected from the OnPreRender method and the FunkyTextBox has been rendered into
an <input type="text"> with its onkeypress event invoking the ChangeBackgroundColor function.
Reducing the Page's Bloat by Moving the JavaScript to an External File
While the JavaScript emitted by the FunkyTextBox control is rather slim, more complex controls could require far more JavaScript to function properly. Embedding such JavaScript directly into the page bloats the page size, adding to the time it takes for users to download your ASP.NET pages. To streamline the page, the JavaScript can be moved to an external file and then included in the web page via a
<script> tag whose src property points to the JavaScript file, like so:
<script src="PathToExternalJavaScriptFile" type="text/javascript" >
|
This single <script> element replaces the JavaScript in the page, thereby reducing the page's size. Moreover,
the browser will cache this external script file so that it only needs to be downloaded once (rather than with each page visit/postback,
as with the JavaScript embedded within the page).
In ASP.NET version 1.x, the only issue with an external JavaScript file - or other external resource files (CSS classes, images, and so on) - is that these external files must be shipped and deployed with the custom control. ASP.NET 2.0 has added the capability for these external files to be embedded within the control's assembly and referenced via a special URL, as we'll see shortly.
Embedding an External Resource in the Control's Assembly
To embed a resource in your control's assembly, start by adding the resource to the server control project in Visual Studio. (At the end of this article you'll find the complete code available for download, which includes the FunkyTextBox Web control Project and a test website.) For FunkyTextBox, I've added a file named
Funky.js that includes the JavaScript that was
previously injected directly into the web page in the OnPreRender method.
Once the resource has been added to the project, select it from the Solution Explorer and then go to the Properties window. There, set the file's Build Action to "Embedded Resource", as shown below:
Once this selection has been made, when you build the solution the file will be embedded within the resulting assembly.
Accessing the Embedded Assembly through a URL
Resources embedded within an assembly can be accessed through the
WebResource.axd HTTP Handler. It can be accessed
through a URL like http://yoursite/WebResource.axd?d=assemblyKey&t=dateTimeOfLastAssemblyWrite,
where assemblyKey is an encrypted string that encodes the name of the assembly that contains the resource to access,
while dateTimeOfLastAssemblyWrite represents the time of the last assembly modification. Given a valid assemblyKey,
the WebResource.axd HTTP Handler returns the embedded resource content as its output.
Using the WebResource.axd HTTP Handler to serve embedded assemblies involves three challenges:
- Marking an embedded resource as being accessible through the
WebResource.axdHTTP Handler, - Determining the precise querystring values to pass the
WebResource.axdHTTP Handler to retrieve a particular embedded resource, and - Including the URL generated in step 2 in the appropriate places within the ASP.NET page
WebResource.axd HTTP Handler.
To override this default behavior and indicate that a resource can be accessed, we need to add an attribute to the server control
project's AssemblyInfo file. In particular, we need to add a WebResource attribute, like so:
[assembly: WebResource(webResource, contentType, performSubstitution)]
|
Note: If you are using Visual Basic, use the less than and greater than characters to delimit the attribute, like:
<assembly: WebResource(...)>. Note that to see this file, you have to choose the "Show All Files" option
in Solution Explorer - it then appears underneath the "My Project" folder. Also, the WebResource attribute is found
in the System.Web.UI namespace, so you may need to add a using System.Web.UI; statement at the top
of AssemblyInfo.cs (use Imports System.Web.UI in VB).
The webResource parameter specifies the name of the resource, and should be named using the following pattern:
RootNamespace.PathToFile. For our example, the root namespace is FunkyTextBox; since the resource file
is in the project's root directory, the PathToFile is simply Funky.js. (If, however, it was in a folder named
Scripts, the PathToFile value would be Scripts.Funky.js.) Therefore, the value to use for the
webResource parameter is: FunkyTextBox.Funky.js.
The contentType parameter specifies the MIME type of the requested resource. When requesting an external resource, the browser
is making a separate HTTP request to the web server. The MIME type instructs the browser of the type of data being sent back.
For external JavaScript files, use a contentType of text/javascript. See the MIME Media Types
registry managed by the Internet Assigned Numbers Authority (IANA) for an official list of MIME types.
Finally, the performSubstitution is an optional Boolean value that specifies whether the requested resource should be
searched for any references to other embedded resources. For example, you might have embedded image files and an embedded JavaScript file.
The script might need to make reference to the embedded images resources. This can be done using the syntax <%=WebResource(webResource)%gt;.
If such substitutions need to be made, set performSubstitution to True.
For our control, we can register the Funky.js embedded resource using the following attribute declaration:
[assembly: WebResource("FunkyTextBox.Funky.js", "text/javascript")]
|
With the resource registered as being accessible through the WebResource.axd HTTP Handler, all that remains is
to write the code that will generate the appropriate URL for the embedded resource. To accomplish this, we use the ClientScriptManager class's
GetWebResourceUrl(type, webResource) method.
For resources embedded within the control's assembly, pass in the control's type as the type parameter and the webResource
value used in the WebResource attribute as the second parameter. For example, to get the URL to access the Funky.js embedded resource
from the server control's code, we'd use:
Page.ClientScript.GetWebResourceUrl(this.GetType(), "FunkyTextBox.Funky.js")
|
Tying it all together, we can add the appropriate JavaScript "include" (<script src="PathToExternalJavaScriptFile" type="text/javascript" >)
by using the RegisterClientScriptInclude method
in conjunction with the GetWebResourceUrl method in the FunkyTextBox control's OnPreRender method, like so:
protected override void OnPreRender(EventArgs e)
|
With this new OnPreRender method, the control adds the following rendered markup to the page:
<script src="/TestWebsite/WebResource.axd?d=NLu6bm6a2XinJZt4M-ujmQ13X5ALig6NEAZa1-AxV0HCbE3M-VHNomDQt_qnxjdT0&t=632902136868023078" type="text/javascript"></script>
|
The JavaScript that was once injected directly into the page is now indirectly referenced through a JavaScript "include."
The external JavaScript file being referenced is accessed through the WebResource.aspx HTTP Handler, which
grabs the embedded information and sends it back to the client.
Conclusion
In this article we saw how to embed resources within the assembly of a custom ASP.NET server control and then how to access those embedded resources remotely through a URL. This technique, which is new to ASP.NET 2.0, enables control developers to bundle external resources within the assembly, which simplifies packaging and deploying the server control.
Happy Programming!
Attachments
Suggested Readings



