Making PathInfo And ASP.NET Themes Work Together
Updated on 2008.07.28 – The code was done in a hurry and, talking to my friend Luís, I noticed that I had forgotten to make a case insensitive comparison and that the code was not so obvious. So, I updated the code and added an explanation.
On my last post I wrote about the problem that arises when we try the use path infos and ASP.NET Themes and Skins together.
But most of the times you don’t care about the why you can’t. You just want to know how you can.
The way I see it, the right solution would be to render the URLs for the stylesheets rooted.
But since I can’t do that, the next best thing is the serve the wrongly addressed request properly.
But how can we do that?
The only way I could come up with, was an HTTP Module:
public class AppThemesModule : global::System.Web.IHttpModule { private const string LocalThemesFolderName = "/App_Themes/"; private static readonly int searchStartIndex; private static readonly int minimumLenghtForSearch;<SPAN style="COLOR: blue">static</SPAN> AppThemesModule() { <SPAN style="COLOR: blue">int</SPAN> searchStartIndex = System.Web.<SPAN style="COLOR: #2b91af">HttpRuntime</SPAN>.AppDomainAppVirtualPath.Length; <SPAN style="COLOR: #2b91af">AppThemesModule</SPAN>.searchStartIndex = ((searchStartIndex == 1) ? 0 : searchStartIndex) + 2; <SPAN style="COLOR: #2b91af">AppThemesModule</SPAN>.minimumLenghtForSearch = <SPAN style="COLOR: #2b91af">AppThemesModule</SPAN>.searchStartIndex + <SPAN style="COLOR: #2b91af">AppThemesModule</SPAN>.LocalThemesFolderName.Length; } <SPAN style="COLOR: blue">#region </SPAN>IHttpModule Members <SPAN style="COLOR: blue">public void </SPAN>Dispose() { } <SPAN style="COLOR: blue">public void </SPAN>Init(System.Web.<SPAN style="COLOR: #2b91af">HttpApplication </SPAN>context) { context.BeginRequest += HttpApplicationBeginRequest; } <SPAN style="COLOR: blue">#endregion void </SPAN>HttpApplicationBeginRequest(<SPAN style="COLOR: blue">object </SPAN>sender, System.<SPAN style="COLOR: #2b91af">EventArgs </SPAN>e) { System.Web.<SPAN style="COLOR: #2b91af">HttpApplication </SPAN>httpApplication = sender <SPAN style="COLOR: blue">as </SPAN>System.Web.<SPAN style="COLOR: #2b91af">HttpApplication</SPAN>; <SPAN style="COLOR: blue">string </SPAN>path = httpApplication.Request.Path; <SPAN style="COLOR: blue">if </SPAN>(path.Length > searchStartIndex) { <SPAN style="COLOR: blue">int </SPAN>appThemesStartIndex = path.IndexOf(<SPAN style="COLOR: #2b91af">AppThemesModule</SPAN>.LocalThemesFolderName, searchStartIndex, System.<SPAN style="COLOR: #2b91af">StringComparison</SPAN>.OrdinalIgnoreCase); <SPAN style="COLOR: blue">if </SPAN>(appThemesStartIndex > 0) { httpApplication.Context.RewritePath(<SPAN style="COLOR: #a31515">"~" </SPAN>+ path.Substring(appThemesStartIndex)); } } }
}
The code starts by initializing the static read-only field searchStartIndex with the start index of the search for the /App_Themes/ pattern. If the length of the application’s virtual path is 1, that means that it’s the root of the site and search start index is 0 instead of 1; otherwise the search start index will be the length of the application’s virtual path. 2 is added because there is no need to start searching the path just after the application’s virtual path (if the pattern was found just after the application’s virtual path, no replacement would be needed).
Than, the static read-only field minimumLenghtForSearch is initialized with the minimum length of the path to search for the pattern. There is no need to search for the pattern on paths shorter than the search start index plus the length of the pattern because, if found, no replacement would be needed.
Besides registering the module, you’ll have to configure your virtual directory so that all the files to be served out of the themes are handled by a StaticFileHandler.