When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, August 9, 2006

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!

- continued -

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
{
    protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
    {
        // Wire up the onkeypress event handler to the ChangeBackgroundColor() JavaScript function
        writer.AddAttribute("onkeypress", "ChangeBackgroundColor(this);");

        base.AddAttributesToRender(writer);
    }


    protected override void OnPreRender(EventArgs e)
    {
        // Dump in the JavaScript directly into the page
        // (Although the external JavaScript approach is more efficient)
        Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "FunkyTextBox",
@"
function ChangeBackgroundColor(ctrl)
{
  var rndNum = Math.floor(Math.random() * 256);
  if (ctrl && funkyTextBoxColors.length > rndNum)
    ctrl.style.backgroundColor = funkyTextBoxColors[rndNum];
}

var funkyTextBoxColors =  new Array('#89C142', '#EF14E5', '#75762C', '#BB9E24', '#BF4A44', '#9D4A77', ...);
", true);

        base.OnPreRender(e);
    }
}

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">
<!--

function ChangeBackgroundColor(ctrl)
{
    var rndNum = Math.floor(Math.random() * 256);
    if (ctrl && funkyTextBoxColors.length > rndNum)
        ctrl.style.backgroundColor = funkyTextBoxColors[rndNum];
}

var funkyTextBoxColors =  new Array('#89C142', '#EF14E5', '#75762C', '#BB9E24', '#BF4A44', '#9D4A77', ...);
// -->
</script>


Here is a FunkyTextBox:
<input onkeypress="ChangeBackgroundColor(this);" name="MyTextBox" type="text" id="MyTextBox" />

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:

Choose the 'Embedded Resource' option from the Build Action drop-down list.

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:

  1. Marking an embedded resource as being accessible through the WebResource.axd HTTP Handler,
  2. Determining the precise querystring values to pass the WebResource.axd HTTP Handler to retrieve a particular embedded resource, and
  3. Including the URL generated in step 2 in the appropriate places within the ASP.NET page
By default, embedded resources within an assembly cannot be accessed through the 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)
{
    // When pre-rendering, add in external JavaScript file
    Page.ClientScript.RegisterClientScriptInclude("FunkyJavaScript",
                 Page.ClientScript.GetWebResourceUrl(this.GetType(),
                                             "FunkyTextBox.Funky.js"));

    base.OnPreRender(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>

Here is a FunkyTextBox:
<input onkeypress="ChangeBackgroundColor(this);" name="MyTextBox" type="text" id="MyTextBox" />

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!

  • By Scott Mitchell


    Attachments


  • Download the complete code samples examined in this article (in ZIP format)
  • Suggested Readings


  • Using ASP.NET 2.0 Web Resources in WebParts
  • Embedding Resources in ASP.NET 2.0 Assemblies
  • Working with Web Resources in ASP.NET 2.0


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article