Microsoft Most Valuable Professional

Chris Pietschmann

An MVP From Wisconsin



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:

1.       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.

2.       Put the most frequently used Url Mappings first in the list so that they the ones that are parsed first.

3.       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&amp;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 - Scoth Guthrie 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.

 

Currently rated 4.2 by 6 people

  • Currently 4.166667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: General
Posted by crpietschmann on Saturday, November 12, 2005 1:36 PM
Permalink | Comments (29) | Post RSSRSS comment feed

Related posts

Comments

Jonathan Kind

Tuesday, November 15, 2005 5:12 AM

Jonathan Kind

Have you thought about caching the positive matches?

Glen

Wednesday, November 30, 2005 4:07 PM

Glen

Hi,

Good job! This works fine but I have a question...

If I use a masterpage and a theme that automaticaly insert a css file, if I try to rewrite a url like "/english/mypage/p123l1.aspx" to "/default.aspx?idpage=123&idlanguage=1" my css don't work, because it takes the path of the rewrited url.

My rule is this one:

<add url="~/.*p(.*)l(.*)\.aspx" mappedUrl="~/default.aspx?idpage=$1&amp;idlanguage=$2" />

So, how can I make this working?

Maybe this is impossible because the theme automaticaly include the css with a relative path, so that's why http://Microsoft.com" target="_blank">Microsoft didn't include regular expression support?

Thanks in advance for your answer.

Daniel

Saturday, December 03, 2005 3:38 AM

Daniel

Great Work with this! Just what I searched for.

Thanks alot.

Steve

Monday, December 05, 2005 5:37 AM

Steve

Also very pleased with how easy it was to implement this code...

One question though... how/where do i capture the QueryString values though?

I am mapping

http://mysite/books/list.aspx

to

http://mysite/list.aspx?Type=books

but don't know where/how to get "books" from.... the page trace shows REQUEST_QUERYSTRING empty inside the header variables

Steve

Monday, December 05, 2005 5:41 AM

Steve

nvm...

the trace is showing the "pre" mapped header information, actually pulling the values in the code brings "post" mapped

Mike

Friday, December 30, 2005 4:20 PM

Mike

I converted the above code to C# for any that prefer's to use C# over VB.NET

Compilied it and Tested it on my current project and everything seems to work without a problem.

You can download the converted code here.

http://www.nfscars.net/UrlRewriting-2.0-C.zip" rel="nofollow">http://www.nfscars.net/UrlRewriting-2.0-C.zip">http://www.nfscars.net/UrlRewriting-2.0-C.zip" rel="nofollow">http://www.nfscars.net/UrlRewriting-2.0-C.zip

Have Fun, Mike

antares

Saturday, December 31, 2005 11:37 AM

antares

there will be error while the page post back...

Christopher Pietschmann, MCSD, MCAD

Tuesday, January 03, 2006 2:58 AM

Christopher Pietschmann, MCSD, MCAD

I know this isn't a 100% solution. I haven't tested out some reported issues related to using this with Masterpages and Themes. I have thought about caching the mapping results, but I this post was about how to do Regex URL Mapping. I basically wanted to keep it simple.

If you are interested in fixing the issue of the pages posting back to the actually page executed instead of the mapped url; look at the "Finally - the BIG RewritePath issue" section of this page: http://edsid.com/blog/articles/160.aspx" rel="nofollow">http://edsid.com/blog/articles/160.aspx">http://edsid.com/blog/articles/160.aspx" rel="nofollow">http://edsid.com/blog/articles/160.aspx

Glyn Simpson

Monday, January 09, 2006 12:36 AM

Glyn Simpson

What happens if there is no match? All I see is

[NullReferenceException: Object reference not set to an instance of an object.]
RegExUrlMapping_HTTPModule.RegExUrlMappingConfigHandler.MappedUrl(String url) +129
RegExUrlMapping_HTTPModule.RegExUrlMappingModule.Rewrite(String requestedPath, HttpApplication app) +562
RegExUrlMapping_HTTPModule.RegExUrlMappingBaseModule.BaseModuleRewriter_AuthorizeRequest(Object sender, EventArgs e) +61
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +138
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +65

when nothing matches. What should the "default" string be?

Brian Bauer

Tuesday, January 10, 2006 8:29 AM

Brian Bauer

First of all... thanks for the code. This has solved a problem we're going to have on a web site I'm developing where different organization names will be part of the URL. Those organization names need to be stripped off of the URL and saved but are not part of the underlying structure of the web site.

For Glen who asked how to make this work with master pages and themes... we're doing the same thing and I experienced the same sort of problem where the rewritten URL gets you to the correct page, but the underlying theme elements hang on to the "extra" portions of the URL and therefore don't resolve correctly. What I did was change the final line in the RegExUrlMappingModule so that instead of doing a RewritePath I do a Response.Redirect to the new path. This incurs some overhead, but all my theme references then resolve appropriately. Hope this helps.

Albert Weinert

Wednesday, January 18, 2006 5:22 AM

Albert Weinert

Hi,

Thomas Bandt and i developed a Version which handles Themes, Postbacks am more.

Free free to download and use it. It's free.

http://www.urlrewriting.net

We think we solved all problems.

Justin Wignall

Wednesday, January 18, 2006 4:25 PM

Justin Wignall

After using your code for a very short while (only stated .net 2.0 recently) I want to thank you for it but point out to everyone the above link to http://www.urlrewriting.net/ It rocks!

Only been using for 5 minutes and already just want to rush over to Germany and hug those guys

Glyn Simpson

Saturday, January 21, 2006 12:52 AM

Glyn Simpson

I've just tried urlrewriting.net - yeah it's a good replacement to virtually everything.

The only thing I can do is my datestamp on the physical file, ie:

Dim dateString As String
Dim objFile As System.IO.FileInfo
objFile = New System.IO.FileInfo(Request.PhysicalPath)
dateString = objFile.LastWriteTime.ToLongDateString()

Doesn't return the value of the physical file - I can't seem to work out how to refer to it now..

Will Asrari

Thursday, March 02, 2006 12:55 AM

Will Asrari

Check out my URL Rewriting code via Global.asax. There are also functions on my page written in C# for determining the rewritten URL during postbacks.

www.willasrari.com/.../00043.aspx

www.willasrari.com/.../00015.aspx

John

Monday, March 20, 2006 8:28 AM

John

the code was easy to implement, thanks

china

Thursday, March 23, 2006 12:44 PM

china

I fall accross a question,when I debug at zhe development environment ,zhe urlmapping can be run,but when I publish at zhe IIS,It has no effect.Why?
could you help me? thank you!

Philip

Sunday, April 23, 2006 8:58 PM

Philip

I have had problems in using the various HttpHandler/ Http Module URL Rewritting solutions floating around the Internet when the destination page is in a sub-folder and is a page derived from a ASP.NET V2 Master Page. The master page and other user controls can not be found.

Have you seen such a problem ?... and can you suggest an effective workaround ?

K.Senthil Kumar (Sensoft2000)

Friday, May 19, 2006 2:34 PM

K.Senthil Kumar (Sensoft2000)

Its cool.. But if it is in C#, it will be more helpful

Hosam

Thursday, June 15, 2006 6:47 PM

Hosam

good work, i have a question how can i support subdomains rewriting example
http://subdomain.domain.com to ~default.aspx?subDomain = subdomain

Hosam

Thursday, June 15, 2006 6:48 PM

Hosam

good work, i have a question how can i support subdomains rewriting example
http://subdomain.domain.com to ~default.aspx?subDomain = subdomain

SEO Articles

Wednesday, July 05, 2006 8:43 PM

SEO Articles

hi
does subdomain mapping to one of your application subdirectory works with URL mapping

thanks

Helen C

Friday, July 07, 2006 4:40 AM

Helen C

Ive used the code supplied above and although Im not getting any error messages, its not making the slightest bit of difference. Can anybody suggest a reason why? Im using master pages - does that make a difference? And if so, what do I do to implement it with Master pages. Im nearly pulling my hair out at this stahe, any help would be appreciated

Helen C

Friday, July 07, 2006 5:35 AM

Helen C

Hi, Ive used the above code and although it doesnt give any errors, it makes no difference to the url displayed. Can anyone suggest a reason why?

Kyle Shea

Monday, October 23, 2006 9:24 AM

Kyle Shea

Any idea how to modify the code to support a "dynamic" list of replacement URL's? Namely - I'd like to take a URL, i.e. "Products/FooProducts.aspx", and do a run-time looking in the database to determine the unique identifier of the "FooProducts" product category. Then I can dynmaically replace the URL with "Products.aspx?category={query result}. Would this make sense to do? I have a lengthy list of product categories that change constantly so doing rewriting dynamically is a must.. What do you guys think?

Ken

Friday, November 10, 2006 2:01 PM

Ken

Kyle
if you look to the code you find this function...

Change it to get "the Dynamically created url from whatever source - for example your Database of Products " I am doing exactly this - as I have over 10,000 products to list.
''' 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

Rico

Monday, January 01, 2007 7:19 AM

Rico

Ken, is there any chance that you could post your code up?

PQP

Tuesday, January 02, 2007 10:14 AM

PQP

I use Url Mapping module in WebPart Website, so I want to personalization pages but I can't.
Ex I map: ~/(.*)/default.aspx -> default.aspx?ChannelID=$1. When I query 1/default.aspx then make personalization and then query 2/default.aspx, it's same previous page.
How can I do it now?

Chris Pietschmann

Wednesday, January 10, 2007 7:38 PM

Chris Pietschmann

Yeah, it sounds logical that it might work that way, but it doesn’t. I actually tried the same thing myself. The issue is the WebParts see the same page being executed no matter what URL is being mapped to it (and it’s logical that it works this way), however to get it to work the way you want you have to write your own WebPartManager for that page (I believe). I started working on this a few months ago, but I haven’t had time to finish figuring it out. Let me know if you make any progress.

webhostchat.co.uk

Monday, August 11, 2008 12:10 PM

pingback

Pingback from webhostchat.co.uk

Write permissions on root folder - good or bad? - Web Host Chat

Comments are closed

About the author

I'm Chris Pietschmann, go to the About Me page to learn more about me.

Search

Sponsors

Web.Maps.VE - ASP.NET AJAX Virtual Earth Mapping Server Control

Recent comments

Disclaimer


This work is licensed under a Creative Commons Attribution 3.0 United States License, unless explicitly stated otherwise within the posted content.
© Copyright 2004 - 2008 Chris Pietschmann