Whats with System.Web.UI.WebResourceAttribute?

A bit about WebResourceAttribute in ASP.NET Whidbey and the related Web Resources feature.
Jesse questions the reasons behind this attribute, and that gives me a reason to blog...

For those who haven't seen this attribute, its an assembly-level metadata attribute that allows server control developers to mark embedded resources as URL-accessible, and ties in with the "Web Resources" feature introduced in ASP.NET "Whidbey". Briefly said, Web resources allow control developers to package up client files such as scripts, images, style sheets etc. and use them in the generated rendering simply by embedding them into the assembly, rather than having to scatter them into the file system (such as in the aspnet_client folder). More manageable, cleaner, and simpler deployment!

One reason for this attribute is security. Without the attribute, any embedded resource in any assembly (from GAC or bin) would be automatically URL-accessible. Admittedly, there might not be many security concerns exposing a script file or an image file, but conceptually, its not exactly what you want. Also, the framework that we create now cannot guess what resources (or what assemblies) may appear down the road, and therefore we placed the restriction of requiring Web resources to be explicitely marked upfront (remember adding restrictions down the road is a breaking-change).

Another reason for the attribute is to be able to specify content type. As Jesse mentioned, the content type could be derived from the file name (note to self: Look into making the content type optional). However, we'd like to give the control developer the option of specifying content type, and therefore created a mechanism to do so without depending on a mapping registered elsewhere on the system.

Final reason for the attribute: Extensibility. Basically the ability to specify additional metadata about the resource is interesting. Again, we're putting a system in place that allows us to do cool things now, and more in the future. For an example of a cool trick, read on...

Using Web Resources
Imagine I am writing an HtmlEditor control and I want to use a stock bold button icon. Here's what I'd do:
  1. Embed "Bold.gif" as a resource with the same name into my control's assembly.
  2. Mark it as Web-accessible via the attribute:
        [assembly: WebResource("Bold.gif", ContentType.ImageGif)]
  3. Use the GetWebResourceUrl() method on Page to get a URL that can be rendered out to the client.
        boldButton.ImageUrl = Page.GetWebResourceUrl(typeof(HtmlEditor), "Bold.gif");
This results in a URL of the form:
    WebResource.axd?a=MyControls&r=Bold.gif&t=632059604175183419

The "a" and "r" query string parameters stand for the assembly and resource names respectively. I'll get to the foofy-looking "t" parameter in a moment.

One rather nice fallout of Web resources is that you automatically benefit from resource localization. If I had a satellite resource assembly with a resource named "Bold.gif", then that would be served up, and not the culture-neutral one. Works great if you've got images or error messages in script files that are culture sensitive.

Perf?
Early on, when we first prototyped this feature, there was concern about perf when using Web resources. Imagine hitting the Web server once per Web resource. With a large number of pages using the WebForms.js script file packaged as a resource, we'd be processing two requests for each page. Ack!

So, what we did was set the appropriate cache headers, so that a) the browser would indefinitely cache it, and not even issue a request to check if the content was updated and b) we use output cache on the server (which automatically uses disk output cache also added in Whidbey). The downside was that the development experience wasn't great. Each time I'd change, WebForms.js, I'd have to flush out the cache to pick up the changes. That's where the the "t" parameter comes in. Its basically an assembly timestamp. When the assembly is re-built, the rendered URL contains a different "t" parameter value, and the browser does make a request and picks up the updated content. Otherwise, the same "t" value keeps getting rendered, and the browser's (or the server) cache is used.

And finally, a cool trick...
Imagine that I writing a WebPart control that ships with its own help file embedded as a resource (each WebPart can offer up a Help URL). The help page I want to serve up contains my logo image, and I'd like to embed the image as a resource as well. However, theres no way to statically specify the src attribute of the image inside the .htm embedded resource. Enter WebResourceAttribute.
    [assembly: WebResource("Help.htm", "text/html", /* performSubstitution */ true)]

The third parameter tells the framework that this resource contains macros that need to be substituted in with real values dynamically. In Help.htm, I'd have the following:
    <img src="<%= WebResource(Logo.gif) %>" />

Voila! Now, if you look at the served up Help.htm file, you'd see the image pointing at another WebResource.axd URL. For now, "WebResource()" is the only thing you can place inside the <%= %> syntax within an embedded resource, but perhaps there would be something more in the future. Cool huh?
Posted on Tuesday, 12/2/2003 @ 5:08 PM | #ASP.NET


Comments

11 comments have been posted.

Kevin Dente

Posted on 12/2/2003 @ 5:55 PM
The webresource stuff is very cool. What I wouldn't give to have it in 1.1 :) - I had to implement a similar thing myself for one of my server controls. On a related note - has VS.NET been fixed so that it recognizes changes in embedded assembly resources and rebuilds accordingly? That little bug is driving me bonkers.

Jason Alexander

Posted on 12/3/2003 @ 6:27 AM
Yes, that's very cool stuff. We had to booger up a similar framework for our controls on Match.com. Can't wait to have real support for this in Whidbey!

Michael Stuart

Posted on 12/3/2003 @ 11:01 AM
I agree with Kevin...it gets quite annoying in 1.1 to explicitely click 'Rebuild' on my project just to get it to pick up a change in an embedded JavaScript resource file when working on a server control. I can't wait for the WebResource attribute though. Every single control developer should LOVE that one!

Venkat

Posted on 9/7/2004 @ 2:19 PM
I am working with asp.net 2.0 menu controls. Menu.js and other images are rendering perfectly with IE using WebResource.axd?xyz... but WebResource.axd feature is not working Netscape browsers. anybody overcome this kind of issues?

Eric Gehring

Posted on 1/26/2005 @ 4:59 AM
Something similar. My aspx.files with menu and treeview controls work fine with IE. But it is not working in IE started from within the Visual Web Developer 2005 Express. Or not working anymore ...
Anybody an idea what can be the cause?

Bine

Posted on 11/7/2005 @ 3:21 AM
I think this is a very cool feature. After I solved the problem with the default namespace it works fine. (The default namespace must be empty)

The GetWebResourceURL generates the following link:
/WebTest/WebResource.axd?d=8XF3gMpcMyVWaK5vc7kgdwkIBTZRbMdx-aLZFH7lYB01&amp;t=632544343905703125
t-parameter means it looks for any type of files with the specified name.
But what means the d parameter?

Tim Cochran

Posted on 1/2/2006 @ 3:24 PM
Is there any method of using the performSubstitution trick to substitute within a javascript file embedded resource? I have some img references in the js file that I would like to sub from my embedded images.

James Driscoll

Posted on 2/15/2006 @ 8:22 AM
I've had problems with this where the WebResource for the part of the ImageSet in a Treeview control has been rendered as:

WebResource.axd?d=9G92-mY7CscAg6O7lwv8PsnId---TqhIqRaQpDEdAXI1&t=632745594511093750

Which is a problem due to the --- in the middle. This is because the Javascript emitted is not enclosed in a CDATA section, this breaks some Atlas functionality of mine.
However, it only applies to the current instance of the project. As soon as I make a copy of the project, the resources are "re-mangled", and a new version of the "d" parameter is created which doesn't contain ---!!

I know it's a weird bug that does have a workaround, but shouldn't the Treeview control emit all its Javascript enclosed in CDATA sections as per Microsoft's own guidelines??!!!

humble.apprentice

Posted on 3/30/2006 @ 3:46 PM
I am trying to implement your example but I always get NullReferenceException in anyway I try to run the code. Basically what I am trying to do is a button that will change its background whenever is pressed (two images, one for pressed and another for unpressed) and remember the change. Whenever I do what many of the postings say to do, it does not work! Could anybody help me figure this one out? Here is the code

//--- Start: AssemblyInfo.cs
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Web.UI;

...

[assembly: WebResourceAttribute("ChangingImageButton.minussign.gif", "image/gif")]
[assembly: WebResourceAttribute("ChangingImageButton.plussign.gif", "image/gif")]
//--- End: AssemblyInfo.cs

//--- Start: ChangingButton.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ChangingImageButton
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ChangingButton runat=server></{0}:ChangingButton>")]
public class ChangingButton : Button
{
//Assembly ass;
Image plus, minus;

public ChangingButton()
{
this.Text = "";
this.Attributes.Add("Width", "16px");
this.Attributes.Add("Height", "16px");
//ass = Assembly.GetExecutingAssembly();
plus = new Image();
//plus.ImageUrl =
// this.Page.ClientScript.GetWebResourceUrl(typeof(ChangingImageButton.ChangingButton),
// "ChangingImageButton.plussign.gif");
plus.ImageUrl =
this.Page.ClientScript.GetWebResourceUrl(typeof(Image),
"ChangingImageButton.plussign.gif");
minus = new Image();
//minus.ImageUrl =
// this.Page.ClientScript.GetWebResourceUrl(typeof(ChangingImageButton.ChangingButton),
// "ChangingImageButton.minussign.gif");
plus.ImageUrl =
this.Page.ClientScript.GetWebResourceUrl(typeof(Image),
"ChangingImageButton.minussign.gif");
}
}
}
//--- End: ChangingButton.cs

I've added the images straight into the project, set them as "Embedded Resource" and even, in desperation, set them to "Copy always" in "Copy to Output Directory".

I would really appreciate some help in this, I have spend a day trying to solve this guy and nothing has come out!

Yours,

humble.apprentice

Alan Weltch

Posted on 4/3/2006 @ 3:12 AM
Take a look at http://www.codeproject.com/aspnet/MyWebResourceProj.asp for a working example

Natasha

Posted on 5/20/2006 @ 1:47 AM
Would do u think about united blogs for topics?
The discussion on this post has been closed. Please use my contact form to provide comments.