Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

The Asp.NET 2.0 allows to provide JS files in embedded resources using WebResource.axd.

The procedure is well described here and here. Note that adding [assembly: WebResourceAttribute] attibute is important.

However  the methods ClientScript.RegisterClientScriptResource and ClientScript.GetWebResourceUrl do not throw exceptions, if embedded resource  is missing. They just generate invalid urls that can't be visually checked because parameters are encrypted.
For easier identification of the error it is a good practice to check existence of resource prior to call using Debug.Assert or throw exception.

Type rstype = typeof(ClassInAssemblyWithResource);
Debug.Assert(null != rstype.Assembly.GetManifestResourceInfo(sFullName));
return page.ClientScript.GetWebResourceUrl(rstype, sFullName);
 

OR better way
StreamHelper.EnsureWebResourceValid(sFullName, rsType.Assembly,true)
page.ClientScript.RegisterClientScriptResource(rsType, FileName)

where EnsureWebResourceValid is the  function shown below.

Update: Jon in comments pointed about potential performance penalties.
To avoid it, call the function in Debug mode only,e.g.
 
Debug.Assert(StreamHelper.EnsureWebResourceValid(sFullName, rsType.Assembly,true));

        public static bool EnsureWebResourceValid(string ResName, Assembly Asm,bool ThrowException)

        {

            bool bRet = StreamHelper.EnsureManifestResourceExist(ResName, Asm, ThrowException);

            if(bRet==true)

            { //find the attribute

                bRet = false;

                // Iterate through the attributes for the assembly.

                foreach (Attribute attr in Attribute.GetCustomAttributes(Asm))

                {

                    //Check for WebResource attributes.

                    if (attr.GetType() == typeof(WebResourceAttribute))

                    {

                        WebResourceAttribute wra = (WebResourceAttribute)attr;

                        Debug.WriteLine ("Resource in the assembly: " + wra.WebResource.ToString() +

                          " with ContentType = " + wra.ContentType.ToString() +

                          " and PerformsSubstitution = " + wra.PerformSubstitution.ToString() );

                        if (wra.WebResource== ResName)

                        {

                            bRet = true;

                            break;

                        }

                    }

                }//foreach

            } //ManifestResourceExist

            if(bRet==false)

            {

                string sMsg="Embedded resource " + ResName + " in assembly " + Asm.FullName + " doesn't have WebResourceAttribute ";

                if (ThrowException == true)

                {

                   throw new ApplicationException(sMsg);

                }

                else

                {

                    Debug.Assert(false, sMsg);

                }

            }

            return bRet;

        }

        //'if ThrowException=true, then Throw exception if resource not found

        public static bool EnsureManifestResourceExist(string ResName, Assembly Asm, bool ThrowException)

        {   //NOTE: If resource is located in subfolder of C# project, path of subfolder should be specified (it is included as part of namespace)

            //' Resources are named using a fully qualified name ((including namespace).

            bool bRet=true;  

            ManifestResourceInfo info = Asm.GetManifestResourceInfo(ResName);

            if (info == null)

            {

                bRet=false;

                string sMsg = "Couldn't find embedded resource " + ResName + " in assembly " + Asm.FullName;

                if (ThrowException)

                {

                    throw new ApplicationException(sMsg);

                }

                else

                {

                    Debug.Assert(false, sMsg);

                }

            }

            return bRet;

        }

 

 

 I've submitted a suggestion to MS.

posted @ Friday, May 26, 2006 10:21 AM
Print

Comments on this entry:

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Sloan at 2/28/2007 9:08 AM
Gravatar
Well played Michael.

This has shaved alot of time of my development effort, being able to validate every .js / gif , etc up front. Thanks for the post!

MS, if you're listening, ADD IT IN PLEASE.

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Michael Freidgeim at 2/28/2007 9:56 PM
Gravatar

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Terry at 4/15/2007 10:56 PM
Gravatar
Great tip. One more for you...any ideas on how to override the <% =WebResource("ResourceName") %> functionality when using PerformSubstitution=true (i.e. in a js/css file). I've wrapped your code in a helper so that I just call WebResourceHelper.RegisterClientScriptResource( page, type, resourceName ) and it first checks to see if the resource is valid then just registers it (just to save me from writing the same two lines of code over and over (I'm lazy)).

So that code works great but right now I have no method of validating resources that are generated during PerformSubstitution=true.

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Michael Freidgeim at 4/18/2007 11:31 PM
Gravatar
Terry,
Sorry but I haven't work with PerformSubstitution=true

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Nestor at 5/9/2007 1:54 AM
Gravatar
Hi Michael,

You did a great job on this.

I am currently using it to be sure that resources are server as expected.

The only addition that I did inside the &quot;EnsureWebResourceValid&quot; function was to a parameter to the call &quot;Attribute.GetCustomAttributes&quot; for the assemby and the custom attribute type.

changed &quot;Attribute.GetCustomAttributes(Asm)&quot;
to &quot;Attribute.GetCustomAttributes(Asm, typeof(WebResourceAttribute))&quot;

Doing this you only get a collection of attributes for that type instead of getting all of them and checking the attribute type inside the iteration.

I must to say thanks to your perfect solution.

Regards

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by FOR at 5/19/2007 2:35 AM
Gravatar
After 2 days of scavenging the web for info on the topic of RegisterClientScriptResource and RegisterClientScriptInclude I am doing the rounds and sharing what I discovered with the people that posted on the same topic:

RegisterClientScriptInclude will not work unless your page has a <form runat='server"> somewhere.

Hope this helps all the people having problems with these new APIs,
/cheer

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by andrew at 4/7/2008 6:31 AM
Gravatar
I was banging my head against the wall about all of this as well. In my case I wanted to use the web resources in a control that was defined inside a web project (instead of as a stand-alone control). The issue was the type I was using when calling GetWebResourceUrl. That method requires a type; you should check your type to ensure the embedded resources are there. Add a PreRender method to the control and check to see if this.GetType().Assembly.GetManifestResourceNames() actually returns a list of assemblies. In my case it didn't because the control was put into a seperate .dll (even though it's part of the web project!) than the "main" website. In my case I have a Header control that I declare and use in my website, when doing "this.GetType()" in the Header control's codebehind I noticed that the temporary .dll generated by .net was different than the main .dll. To get the correct type I declared an instance of my homepage (called Home.aspx) and got its type instead:

Project1.Home h = new Home();

Now, when I do h.GetType().Assembly.GetManifestResourceNames() I see the correct list of resources. I then pass h.GetType() to the GetWebResourceUrl method and all works as expected.

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by CodeSlappy at 9/2/2008 11:34 PM
Gravatar
Thanks for the article. I now have my GetWebResourceUrl code working. Also thanks to comment left by andrew, which was also a big help.

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Jon at 10/29/2008 5:23 AM
Gravatar
Here's my one issue with this. Why would i want to incur the expense of reflection each and every time I am adding a web resource? For a control that has many embedded resources, I could see this potentially causing performance problems. Especially if these are resources embedded in your own assembly. This is really a check that I should think could be handled via some offline automated testing so you don't incur performance penalities.

Why not have a WATIR test or something of that nature that you run, and it calls a page that is coded to include all your resources. If a 404 error code is returned, it would alert you.

Its interesting code nonetheless.

I'd much suggest viewing this blog article:
http://www.west-wind.com/WebLog/posts/9837.aspx

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Michael Freidgeim at 10/29/2008 7:43 AM
Gravatar
Jon,
Thanks for comment, I've updated the post with suggestion to call the function in Debug mode only,e.g.
Debug.Assert(StreamHelper.EnsureWebResourceValid(sFullName, rsType.Assembly,true));

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Frederik Schøning at 11/13/2008 3:15 AM
Gravatar
Hello Michael,

I just want to thank you for this simple script that removes my headache of figuring out, whether or not an embedded resource is actually there. Good job!

All best,
Frederik

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Frederik Schøning at 11/13/2008 3:48 AM
Gravatar
One more thing -

I think andrew already pointed this out, but to clarify:

The ClientScriptManager.GetWebResourceUrl method takes 2 arguments, a type and a string, that is the name of the resource. The type passed in the method, must be *in the same assembly* as the resource itself, orelse you will pass the EnsureWebResourceValid alright, but the resources will display something like:

"The resource cannot be found".

This caused me some unnecessary further headache. :S

I was tipped off by this post:
http://forums.asp.net/t/1020915.aspx

/Frederik

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Ish at 8/12/2009 3:21 PM
Gravatar
I get a error when I compile at public static bool EnsureWebResourceValid(string ResName, Assembly Asm,bool..

on the Assembly Asm parameter, I'm I missing something?

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Michael Freidgeim at 8/13/2009 7:04 AM
Gravatar
Have you added:
using System.Reflection;
?

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Ish... at 8/13/2009 12:40 PM
Gravatar
That did it...thanks. I figured it out once I posted the question.

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Grégory at 11/16/2009 8:34 AM
Gravatar
To follow up on andrew's post. Another solution is to not use GetType() to get the type and then the assembly object. It seems that when you do that you will get a reference to a dynamically compiled assembly that doesn't contain the resources. Use typeof(UserControlType) instead. (As Michael did in the post in fact !).

Anyways, thanks for this article, it saved my day :-)

Grégory

# re: Check that embedded resource exist before calling ClientScript.RegisterClientScriptResource or GetWebResourceUrl

Left by Pietro Scionti at 9/17/2010 4:12 AM
Gravatar
Your idea is just what I needed, it finally led me to understand why my code wasn't working the way I wanted. Thank you!
A note for everyone: a dash (hyphen, minus sign, whatever you call it) ("-") in a resource's folder name (but apparently not in the file name itself) is converted to underscores ("_")

Your comment:



(not displayed)

 
 
 
 
 

Live Comment Preview:

 
«August»
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456