ASP.NET 2.0: URL Mapping with RegEx Support
The one big limitation of the URL Mapping functionality built in to ASP.NET 2.0 is that it doesn’t support regular expressions. I ported my v1.1 URL Mapping implementation over to v2.0 and added support for regular expressions. It really wasn’t too difficult; I only had to modify two lines of code to port it over to ASP.NET 2.0. Then I had to modify 4 lines of code to add RegEx support. My implementation works just like the ASP.NET 2.0 URL Mapping functionality with the addition of RegEx.
My code allows to create Url Mappings similar to the following:
~/Chris.aspx
to~/Default.aspx?p=chris
~/Show154.aspx
to~/Default.aspx?p=154
Some Performance tips for using this code:
- Use as few Url Mapping definitions as possible since it parses them from the first to the last and stops when it finds a match. If a page requested doesn’t match any of the definitions it will go through all of them before moving on to complete the page request.
- Put the most frequently used Url Mappings first in the list so that they the ones that are parsed first.
- The first Url Mapping in placed in my code (
<add url="~/(.*)default\.aspx" mappedUrl="~/$1default.aspx" />
) is a little trick that allows all requests for the Default.aspx page in the root or any sub-folder of the application to be completed with out having to parse through the entire list of Url Mappings. Regular expression support just seems logical in the v2.0 URL Mapping functionality, but I don’t know why Microsoft didn’t add it. I guess they wanted me to have something to do.
Enjoy!
Code for this implementation listed below:
/App_Code/RegExUrlMappingBaseModule.vb
Imports Microsoft.VisualBasic
Imports System.Web
Namespace RegExUrlMapping_HTTPModule
Public Class RegExUrlMappingBaseModule
Implements System.Web.IHttpModule
Sub Init(ByVal app As HttpApplication) Implements IHttpModule.Init
AddHandler app.AuthorizeRequest, AddressOf Me.BaseModuleRewriter_AuthorizeRequest
End Sub
Sub Dispose() Implements System.Web.IHttpModule.Dispose
End Sub
Sub BaseModuleRewriter_AuthorizeRequest(ByVal sender As Object, ByVal e As EventArgs)
Dim app As HttpApplication = CType(sender, HttpApplication)
Rewrite(app.Request.Path, app)
End Sub
Overridable Sub Rewrite(ByVal requestedPath As String, ByVal app As HttpApplication)
End Sub
End Class
End Namespace
/App_Code/RegExUrlMappingConfigHandler.vb
Imports Microsoft.VisualBasic
Imports System.Configuration
Imports System.Xml
Namespace RegExUrlMapping_HTTPModule
Public Class RegExUrlMappingConfigHandler
Implements IConfigurationSectionHandler
Dim _Section As XmlNode
Public Function Create(ByVal parent As Object, ByVal configContext As Object, ByVal section As System.Xml.XmlNode) As Object Implements System.Configuration.IConfigurationSectionHandler.Create
_Section = section
Return Me
End Function
''' Get whether url mapping is enabled in the app.config
Friend Function Enabled() As Boolean
If _Section.Attributes("enabled").Value.ToLower = "true" Then
Return True
Else
Return False
End If
End Function
''' Get the matching "mapped Url" from the web.config file if there is one.
Friend Function MappedUrl(ByVal url As String) As String
Dim x As XmlNode
Dim oReg As Regex
For Each x In _Section.ChildNodes
oReg = New Regex(x.Attributes("url").Value.ToLower)
If oReg.Match(url).Success Then
Return oReg.Replace(url, x.Attributes("mappedUrl").Value.ToLower)
End If
Next
Return ""
End Function
End Class
End Namespace
/App_Code/RegExUrlMappingModule.vb
Imports Microsoft.VisualBasic
Imports System.Web
Imports System.Configuration
Namespace RegExUrlMapping_HTTPModule
Public Class RegExUrlMappingModule
Inherits RegExUrlMappingBaseModule
Overrides Sub Rewrite(ByVal requestedPath As String, ByVal app As HttpApplication)
''Implement functionality here that mimics the 'URL Mapping' features of ASP.NET 2.0
Dim config As RegExUrlMappingConfigHandler = CType(ConfigurationManager.GetSection("system.web/RegExUrlMapping"), RegExUrlMappingConfigHandler)
Dim pathOld As String, pathNew As String = ""
If config.Enabled Then
pathOld = app.Request.RawUrl
''Get the request page without the querystring parameters
Dim requestedPage As String = app.Request.RawUrl.ToLower
If requestedPage.IndexOf("?") > -1 Then
requestedPage = requestedPage.Substring(0, requestedPage.IndexOf("?"))
End If
''Format the requested page (url) to have a ~ instead of the virtual path of the app
Dim appVirtualPath As String = app.Request.ApplicationPath
If requestedPage.Length >= appVirtualPath.Length Then
If requestedPage.Substring(0, appVirtualPath.Length).ToLower = appVirtualPath.ToLower Then
requestedPage = requestedPage.Substring(appVirtualPath.Length)
If requestedPage.Substring(0, 1) = "/" Then
requestedPage = "~" & requestedPage
Else
requestedPage = "~/" & requestedPage
End If
End If
End If
''Get the new path to rewrite the url to if it meets one
''of the defined virtual urls.
pathNew = config.MappedUrl(requestedPage)
''If the requested url matches one of the virtual one
''the lets go and rewrite it.
If pathNew.Length > 0 Then
If pathNew.IndexOf("?") > -1 Then
''The matched page has a querystring defined
If pathOld.IndexOf("?") > -1 Then
pathNew += "&" & Right(pathOld, pathOld.Length - pathOld.IndexOf("?") - 1)
End If
Else
''The matched page doesn't have a querystring defined
If pathOld.IndexOf("?") > -1 Then
pathNew += Right(pathOld, pathOld.Length - pathOld.IndexOf("?"))
End If
End If
''Rewrite to the new url
HttpContext.Current.RewritePath(pathNew, false)
End If
End If
End Sub
End Class
End Namespace
/Web.Config
<configuration>
<!-- Declare the custom 'RegExUrlMapping' section and handler -->
<configSections>
<sectionGroup name="system.web">
<section name="RegExUrlMapping" type="RegExUrlMapping_HTTPModule.RegExUrlMappingConfigHandler"/>
</sectionGroup>
</configSections>
<system.web>
<!-- Tell ASP.NET to use the RegEx URL Mapping HTTP Module -->
<httpModules>
<add type="RegExUrlMapping_HTTPModule.RegExUrlMappingModule" name="RegExUrlMappingModule"/>
</httpModules>
<!-- The RegEx URL Mapping parser goes through these in sequential order. -->
<RegExUrlMapping enabled="true">
<add url="~/(.*)default\.aspx" mappedUrl="~/$1default.aspx" />
<add url="~/Chris.aspx" mappedUrl="~/Default.aspx?p=chris"/>
<add url="~/show(.*)\.aspx" mappedUrl="~/Default.aspx?p=$1&section=3"/>
</RegExUrlMapping>
</system.web>
</configuration>
ASP.NET 1.1 Url Mapping code can be found here: http://pietschsoft.com/Blog/archive/2005/07/04/717.aspx
Update 11/14/2005 - Scott Guthrie (ScottGu) has posted about why they didn’t add Regular Expression support to the ASP.NET 2.0 URL Mapping on his blog. He has also posted a link to this blog entry in that blog post. It’s pretty cool that my blog has gotten noticed by him. http://weblogs.asp.net/scottgu/archive/2005/11/14/430493.aspx
Update 9/10/2007 - I fixed a bug in the RegExUrlMapping code that was preventing ASP.NET Themes from correctly loading.
In the RegExUrlMappingModule class change the following line of the Rewrite method:
HttpContext.Current.RewritePath(pathNew)
To be the following instead:
HttpContext.Current.RewritePath(pathNew, false)
I already made this change to the code above so anyone copying it from now on will get this fix.
Update 3/23/2009 - I just found that this post is mentioned in the “ASP.NET 2.0 MVP Hacks and Tips” book that was published in May 2006 on Page 357. I find that one of my blog posts being mentioned in a book like this to be really interesting. Thanks for the props guys!!