Some weeks ago I wrote how to minify and compress the WebResource.axd handler. In the comments of that post someone asked how to do the same with ScriptResource.axd. I thought about it and realized it could be done much smarter.

If you take a look at the HTML source at the CodePlex Issue Tracker page, you’ll see it references 13 WebResource.axd and ScriptResource.axd. That’s 13 different web requests on each page load. The ScriptResource.axd is using HTTP compression but WebResource.axd does not. None of them minifies the scripts.

Therefore it would be cool to create a plug ‘n play HttpModule that combines all resource.axd scripts into one single web request and then minify and compress them. It would result in smaller HTML, fewer web requests and optimized JavaScript code. If you’re a YSlow nazi like me, this is a must-have.

How it works

To make it work, we need both an HttpModule and an HttpHandler.

The module

The module looks for resource scripts in the HTML code and collects all the references from the src tag. It then constructs a new script tag containing all the references and points it to the HttpHandler. The new script tag is injected into the HTML where the first resource script was found. It keeps the order of the scripts intact.

All the original scripts are now removed and a single new script pointing to the handler is injected.

The handler

When the handler is requested by the browser it looks for a URL parameter containing the references to the original resource script. They are separated by a comma in the URL parameter. It then retrieves the content of each script using a HTTP request. Then content is then aggregated into a single string which then get’s minified. Minifying means that all comments and whitespace is removed from the script.

The string is then being cached so the handler only has to do it once per application life cycle. The output is then compressed and the appropriate header set so the browser will cache the file for 30 days. You can easily set the number of days in cache to whatever you see fit.

Implementation

Download the ScriptCompressor.cs file below and put it in your App_Code folder. Then add the following lines to your web.config:

<httpModules>
  <add type="ScriptCompressorModule" name="ScriptCompressorModule" />
</httpModules>
<httpHandlers>
  <add verb="*" path="*js.axd" type="ScriptCompressorHandler" />
</httpHandlers>

Download

ScriptCompressor.zip (3.43 kb)

Comments

Rajiv

Have you checked out the ToolScriptManager http://www.asp.net/AJAX/AjaxControlToolkit/Samples/Walkthrough/OtherNeatStuff.aspx

Rajiv

Mads Kristensen

I've never seen that before. That's cool but overkill compared to this solution IMHO.

Mads Kristensen

Josh Stodola

Nicely done! However, I'm still going to continue to wave my middle finger at script tags that point to an .axd file.

Josh Stodola

Mads Kristensen

@Josh, If you can control the IIS, you can just change the extension to .js

Mads Kristensen

Chris Pietschmann

Very nice. However I have a couple suggestions that would make this even better... 1) The CodePlex Issue Tracker page has 13 ScriptResource/WebResource .axd script includes. But it also has 9 other javascript includes in the page too. A neat addition to this would be to combine all of them into a single minified script. 2) Maybe have an option to not combine and minify the scripts when the application is running in Debug. This way it's still a little simpler when debugging. Just a couple thoughts.

Chris Pietschmann

DotnetShadow

Hi there, Cool code definitely will use it... almost is similar to this: http://www.codeproject.com/KB/aspnet/httpcompression.aspx

DotnetShadow

Daze

I added this to my current web application project and everything stopped working. I get Javascript errors as soon as the page loads :(

Daze

Z&#233; Pedro

It seems to be adding the images embedded in the controls and distributed also by the WebResource.axd. Why not check if the Request.ContentType == "application/x-javascript"? Ps. Nice work...

Z&#233; Pedro

Z&#233; Pedro

And how about some type of url shrinking? Let's say js.axd?34234234233? It also reduces the ammount of bytes sent to the browser.

Z&#233; Pedro

Z&#233; Pedro

Sorry for the spam guys, but forget my first remark. Did not see the regular expression searching for script tags :) Damn it, code folding :)

Z&#233; Pedro

Petr Snobelt

Hi Mads, Try look at http://www.codeplex.com/MbCompression I think it does something similar, and if you put your developer skill into improving this project and then use it for BlogEngine.NET, others can use it too (even without Blogengine) Petr

Petr Snobelt

Rush

Does this work for HTTPS sites? For my non-https sites it is working fine but when I move it to my production server with HTTPS it does not work.

Rush

Miron

Hi Mads, Nice idea. Two things: The Regex you use in your filter will not match if the type="text/javascript" will come before the src="" part. (as appear in the Blogengine.NET 1.2 release). Also, if the first WebResource appears in the header (because it contains script for 'onload' event), you will and up with huge script request on the header that will block all the page content until it done. This will perform slower page load than 5 small requests in the page body. The combining must be with more care.

Miron

Rush

I had to comment out an IF statement to get it to work on HTTPS sites. void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; if (app.Context.CurrentHandler is Page &amp;&amp; !app.Request.RawUrl.Contains("serviceframe")) { //if (!app.Context.Request.Url.Scheme.Contains("https")) { app.Response.Filter = new WebResourceFilter(app.Response.Filter); //} } }

Rush

Rush

Also this solution is limited in IE because links in IE can only be 2048 characters so if you have a lot of webresource.axd files it will not load the ones that come at or after 2048 chars.

Rush

Rush

In the Write method change this If statement to this for the IE fix. if (index &gt; 0) { string script = "&lt;script type=\"text/javascript\" src=\"js.axd?path={0}\"&gt;&lt;/script&gt;"; string path = string.Empty; string temp = string.Empty; foreach (string s in list) { //limit the js.axd?path= link string to 2048 characters in IE temp += HttpUtility.UrlEncode(s) + ","; if (HttpContext.Current.Request.UserAgent.Contains("MSIE") &amp;&amp; temp.Length &gt; 2036) { temp = string.Format(script, path) + Environment.NewLine; html = html.Insert(index, temp); index += temp.Length; temp = HttpUtility.UrlEncode(s) + ","; } path = temp; } html = html.Insert(index, string.Format(script, path)); }

Rush

Rush

OK last post on this... I also created a version of the ScriptCompressor for CSS. You use it the same exact way. It uses file dependency for the cache so you should be good when things change. I made a zip file with my changes to ScriptCompressor and CssCompressor. http://files.rushfrisby.com/dotnetrush/code/CompressorClasses.zip Have fun :-)

Rush

Tolisss

I get a Sys is not defined error when using it with telerik controls any thoughs on this one?

Tolisss

Sam Yong

yep same here. i am getting "Sys is undefined error" when using AJAX Toolkit. any idea how to resolve it? at first it was the URL too long issue, but suddenly this same along.

Sam Yong

gowrishankar

Hi mads thanks forthe post but I have a small problem i able to compress all scripts into a single new script pointing to the handler. but iam getting an error saying Sys is undefined please help me out

gowrishankar

Cassiano

Hello there! Brilliant idea! Helped me a lot! Well... for the "Sys is undefined" error, it's very simple =P The problem is that the link generated (js.axd?path=/WebResource.axd?blablabla) has this damn slash after the "path="! Just had to remove it from the module, something like: string relative = match.Groups[1].Value; if (relative[0] == '/') relative = relative.Substring(1); list.Add(relative); (Yeah... I know I could have done this in the Regex pattern... but regular expression is one of the things that my brain refuses to understand =P) AND I needed to add this slash in the handler: content += RetrieveScript(root + "/" + script) + Environment.NewLine; Now it's working beatifully! Cheers! Cassiano

Cassiano

GreenShoes

Hi Mads, I've been scouring the net for something just like this and surprised at the lack of 'lightweight' options as you have presented! Well done! ... well kinda,.. I can't seem to load any ScriptResource.axd references; The response I get is directing me straight to my login page for authentication. Any ideas? PS: Thanks Cassiano with the WebResource.Axd splash fix: Worked a treat! PPS: Thanks Rush for the IE fix, but I can't seem to get the CSS implementation working at all :( Cheers!

GreenShoes

al

hello everyone, Thank you everybody for putting your minds into this post, especially, Mads! i stuck with the problem though, wonder if somebody has a hint/idea how to fix it.. i understand we combining those webresources given that all of them JS.. but how about images? In particular, i am suing ajaxtoolkit tab control and it USES webresources to store there images that it require... So, there are banch of js errors pop up when i try to look at the page with tab control.. using Response.ContentType == "application/x-javascript" as was suggested ealier does not help (or may be i am using it incorrectly).. Thanks for any help, hf

al

Gopinath

Hi Mads Kristensen I have used your scriptcompressor.cs file but it didn't work. I got the same error as friends posted above like "Sys is Undefined" in IE and "Sys is not defined" in Firefox. I couldn't do anything in my site. The site was jammed. Please help me how to overcome this issue. Please take a look on this site http:www.anytimerooms.eu which shows an error in IE as well as error console of Firefox. Advance thanks for your help. I hope u come with solution asap.

Gopinath

R

Brilliant bit of code, but I have an issue. I get a js error Expected ';' and have eventually tracked this down to a missing semicolon in Microsoft's WebForm_DoCallback function xmlRequestFrame.style.position = "absolute"; xmlRequestFrame.style.top = "-100px" xmlRequestFrame.style.left = "-100px"; Anybody else come up against this issue?

R

Nawaf

Hi, I don't think you need to handle the ScriptResource case. Because in the scriptmanager you can set the scriptmode="Release". It will pick the minified version. The value by default is set to auto, which will look at the config and see if the debug mode is on or not and set it based on that.

Nawaf

Babu Kumarasamy

Hi, I had changed what Cassiano said. But till now also I am getting "Sys is undefined". [b]This is the script that I am getting from SERVER[/b] js.axd?path=anytimerooms/WebResource.axd?d=mee-Go1B7DJthBqLdDhfSQ2&amp;amp;t=633598415816665082,anytimerooms/ScriptResource.axd?d=dP1w54zng6-_VbdnzgGa9fmEBpcIf1DhhTcBB_ck8J3DEDIbvQoUmMD4K3K3fHU1qOlAAso9IGan3cllA3y5t7a0iQHaNNd_xKjM4b9_y_81&amp;amp;t=633598434759103750,anytimerooms/ScriptResource.axd?d=dP1w54zng6-_VbdnzgGa9fmEBpcIf1DhhTcBB_ck8J3DEDIbvQoUmMD4K3K3fHU1qOlAAso9IGan3cllA3y5t_OxDBS2tePvdBtJ91BOBF_41ojyWdmT-D2hA8TdHXti0&amp;amp;t=633598434759103750, [b]This is from LOCAL[/b] [b]FROM LOCAL I DIDN'T GET THE ERROR[/b] js.axd?path=WebResource.axd?d=hMGWEcMxH5OxwLGvITx8Bw2&amp;amp;t=633633194134236166,ScriptResource.axd?d=sSIqFd-6lYJr9TEobXdLGR9AdBXO0p8SORUVVzFokmLt6jhhv0a3AcThYf8zcQ_c8R6foqoVOKn4q6HYY5L1kNNRJXYi6BLMxnHI3UU1osk1&amp;amp;t=633633656961406250,ScriptResource.axd?d=sSIqFd-6lYJr9TEobXdLGR9AdBXO0p8SORUVVzFokmLt6jhhv0a3AcThYf8zcQ_c8R6foqoVOKn4q6HYY5L1kOpMqjpvPaTF9NzH48ILqJ01&amp;amp;t=633633656961406250, Thanks

Babu Kumarasamy

Betty

Possible exploit, although I haven't figured out a use for it yet. Your security check simply checks if resource.axd is in the url not the path, meaning it can be in the query string. Meaning the following can be used to get past the check. http://website/js.axd?path=/robots.txt%3FRESOURCE.AXD That said you can only get files that the webserver is willing to serve up, so it can't be used to get the web.config or anything. However the request does count as the webserver itself rather then the user doing the request meaning you may be able to get items you can't normally. I'm also having another problem with this seeing as my web host seems to limit the length of my query strings. I tried compressing the querystring with no real results, so I was considering caching the current querystring and using a Guid instead but that wouldn't exactly be ideal (or reliable if the cache dies). Anyone have some brilliant idea around this? (Accidentally posted this comment on the wrong page http://blog.madskristensen.dk/post/Optimize-WebResourceaxd-and-ScriptResourceaxd.aspx)

Betty

John

wonder this script perform a whole page compress or only the js?

John

ASP.NET AJAX Toolkit Forum Posts

how to stop autogenerated Scripts when using AjaxControltoolkit hi , i m using ajaxcontrol toolkit on my applicaiton, application working fine but at a time of page

ASP.NET AJAX Toolkit Forum Posts

tuts9.com

Pingback from tuts9.com ScriptResource.axd-urgent | The Largest Forum Archive

tuts9.com

base10.net.br

Pingback from base10.net.br Macroblog Base10 &raquo; Select tool to minimize JavaScript and CSS size

base10.net.br

base10.net.br

Pingback from base10.net.br Macroblog Base10 &raquo; Select tool to minimize JavaScript and CSS size

base10.net.br

Comments are closed