Friday, April 4, 2008

Defensive programming when using ClientScript's GetWebResourceUrl method

Anyone who has done a substantial amount of work with custom controls in ASP.NET has probably needed to make a call similar to this in an ASP Web Form:


Page.ClientScript.GetWebResourceUrl(this.GetType(), "MyAssembly.Images.AnImageResource");


Without going into to much detail here, this generates a URL that can be used to reference a file, typically an image, that is embedded inside an assembly being included in the web application. This comes in very handy because you can create a custom control with all of its dependent JavaScript, styles, and images and put it all inside one DLL mimimizing deployment. ASP.NET will generate and return the URL of them dynamically in code. If you want a primer on this, check here for a good how-to.

There is a huge caveat to be aware of: if the resource cannot be loaded for any reason the method returns null. When the control gets rendered, you'll immediately notice missing images at a minimum which obviously can be a pain to hunt down, especially when there are several points of failure when using GetWebResourceUrl. It would be better to throw an exception if for some reason the resource cannot be loaded in most cases.

So, at Apprenda we wrote our own loader of web resources, which covers a couple points of failures when loading the web resource and throws a useful exception. I included the function here and hopefully this will help someone locate issues faster when developing their custom controls.


public string GetWebResourceUrl(string resourceName, ClientScriptManager clientScriptManager)
{
ManifestResourceInfo info = this.GetType().Assembly.GetManifestResourceInfo(resourceName);

if (null == info)
{
throw new ApplicationException(string.Format(
"The resource named '{0}' was not found in the assembly '{1}'.",
resourceName, this.GetType().Assembly.ToString()));
}

foreach (WebResourceAttribute attribute in Attribute.GetCustomAttributes(this.GetType().Assembly, typeof(WebResourceAttribute)))
{
if (attribute.WebResource.Equals(resourceName))
{
return clientScriptManager.GetWebResourceUrl(this.GetType(), resourceName);
}
}
throw new ApplicationException(string.Format(
"The resource named '{0}' was found in the assembly '{1}', but is not specified as a WebResource in the assembly info.",
resourceName, this.GetType().Assembly.ToString()));
}



We put our function in a utility class called WebResourceChecker.

No comments: