About your host

Photo of your host - Charles Vallance Charles Vallance is a web developer with a slight case of OCD when it comes to nice clean standards compliant html and code.

Badges

twitter / cvallance
My articles have been featured in The Morning Brew - Daily .NET News and Views

Tag Cloud

more tags...
posted @ Friday, January 11, 2008 9:01 PM

Ok, after a slight push from Travis, I decided to put aside some time to live up to my promise and post an entry on how to enable extensionless requests on the new ASP.NET MVC framework using IIS 5.1 & 6.

I'm starting where my last post, ASP.NET MVC on IIS 5.1 (XP Pro), left off. So if you haven't got the MVC framework working on IIS 5.1 or 6 with extensions, have a gander at my last post and make sure its all working.

What we currently have (following after my last post) is links like /Home.mvc/About which are working because we've mapped the .mvc extension to the ASP.NET application, so IIS knows it's for ASP.NET and can fire it off in that direction, but what we'd ideally want is just having the link look like /Home/About (without any extension). Currently if you try an extensionless request like that, IIS just doesn't know what to do with the request or where to send it, so it returns a 404.

Enter URL rewriting!

I'm going to use Helicon Tech's ISAPI_Rewrite 3. The lite version, which I'm going to use, is free but has limitations (which you can read about here) but should be fine for what we want to do. The full version is only $99 US per server... cheap as chips if you ask me!

ISAPI_Rewrite offers the same syntax and behavior as mod_rewrite on Apache (which is a good thing :-). If you don't know anything about mod_rewrite or are completely new to the whole URL Rewriting idea, I would recommend you read up on some material - here would be a good place to start, or good old wikipedia.

Anyway, lets start.

First off you'll want to download ISAPI_Rewrite 3 from here (just grab the lite version) and install it. You shouldn't run into any problems... touch wood. If you do, go back to their documentation for troubleshooting advice.

Once it's installed, open up the httpd.conf file in your favourite text editor (Notepad++ is my fav :-)) and add the following lines. Note that if you used the .msi installer, the httpd.conf file should be in the Program Files\Helicon\ISAPI_Rewrite3\ directory.

RewriteEngine on 
RewriteBase 
RewriteRule ^Home/(.*?)$ Home.mvc/$1

What this RewriteRule is doing is intercepting any request that starts with "Home/" and replaces the "Home/" part with "Home.mvc/". Once it's done this, IIS handles the request and sees the .mvc extension, so it fires that request off to ASP.NET.

Brilliant.

So go back to your browser and try and visit the http://localhost/Home/About address on your local machine. Hopefully you should be met with the same page as if you would have written http://localhost/Home.mvc/About. Yay! Now do that for all your different controllers... Just make sure none of your controllers are called Styles, CSS, Scripts etc etc ;-)

But there is still a problem... anyone picked it yet?

You see, our incoming request, "/Home/About", is being rewritten, by ISAPI_Rewrite, to "/Home.mvc/About" and is then being passed to ASP.NET and it's then being handled by the ASP.NET MVC routing engine. It should then match the following routing rule:

RouteTable.Routes.Add(new Route
{
    Url = "[controller].mvc/[action]/[id]",
    Defaults = new { action = "Index", id = (string)null },
    RouteHandler = typeof(MvcRouteHandler)
});

So this will go off to the "About" action on the "Home" controller. Fantastic... but what happens when you go the other way? What happens when you get the ASP.NET MVC routing engine to write the links for you? That's right, it'll have the .mvc extension thrown onto the end of any requests that match the rule.

Hmmm, not cool because it basically renders all of what we've done with the URL rewriting useless!

Not all is lost though. You see, the ASP.NET MVC routing engine tests each incoming and outgoing URL against the RouteTable from top to bottom (which is why you need to write them from most specific to least specific)... So if we throw in a different rule, say the exact same rule without the .mvc part, that should be evaluated first and find it as a match and then the outbound links shouldn't have .mvc in them.

Something like this:

protected void Application_Start(object sender, EventArgs e)
{
    RouteTable.Routes.Add(new Route
    {
        Url = "[controller]/[action]/[id]",
        Defaults = new { action = "Index", id = (string)null },
        RouteHandler = typeof(MvcRouteHandler)
    });

    RouteTable.Routes.Add(new Route
    {
        Url = "[controller].mvc/[action]/[id]",
        Defaults = new { action = "Index", id = (string)null },
        RouteHandler = typeof(MvcRouteHandler)
    });

    RouteTable.Routes.Add(new Route
    {
        Url = "Default.aspx",
        Defaults = new { controller = "Home", action = "Index", id = (string)null },
        RouteHandler = typeof(MvcRouteHandler)
    });
}

Glorious.

So, that's a start... it's nice and simple and hopefully a good place to get you going. I'm open to suggestion as to different ways of going about it... I'm no mod_rewrite or regex guru by a long shot!

Technorati Tags: ,

And then he said...

# re: ASP.NET MVC Extensionless Requests on IIS 5.1 & 6

Gravatar
Posted by Bryan
on 4/25/2008 11:04 AM
You should use something like this instead. One rule that handles all controllers. The [L] tells the rewrite engine to stop processing after this rule (so if you have additional rules, you won't incur the overhead of testing against them

RewriteRule ^/([^?/]+)(.*) /$1.mvc$2 [L]

Bryan

# re: ASP.NET MVC Extensionless Requests on IIS 5.1 & 6

Gravatar
Posted by Bryan
on 4/25/2008 12:04 PM
One other thing, you'll also want to have another rule that does not rewrite your static content. Put it before the previous rule:

RewriteRule ^/Content/ $0 [L]
RewriteRule ^/([^?/]+)(.*) /$1.mvc$2 [L]

(I came up with these off of the top of my head, so forgive me if there is a minor error in them)

Bryan

# re: ASP.NET MVC Extensionless Requests on IIS 5.1 & 6

Gravatar
Posted by charles
on 4/25/2008 12:04 PM
Hi Bryan,

Your rewrite rule for all general requests is alot nicer and could save alot of donkey work. Thanks for that!

Regards,
Charles

Ps. Can you tell I'm no regex guru? ;-)

# re: ASP.NET MVC Extensionless Requests on IIS 5.1 & 6

Gravatar
Posted by v.c.
on 5/15/2008 7:05 AM
hi charles,
I've followed your blog, but i still receive 404 error, i use iis5 on xp with sp3.

# re: ASP.NET MVC Extensionless Requests on IIS 5.1 & 6

Gravatar
Posted by Gerald
on 7/27/2008 5:07 PM
Hi,

Thanks for the resource. It has been pretty helpful getting MVC to work correctly on my XP system for development purposes.

I figured I would give back a bit and provide a regex that works a little better. It's similar to what Bryan posted, but eliminates some potential glitches that I ran into (i.e. trying to rewrite Home.mvc to Home.mvc.mvc)

RewriteRule ^(?!/Content)(/[A-Za-z0-9_-]+)(/.*)?$ $1.mvc$2 [L]

# re: ASP.NET MVC Extensionless Requests on IIS 5.1 & 6

Gravatar
Posted by kyle
on 12/15/2009 10:37 AM
In case anyone is wondering. The last step you cannot do. Because it will evaluate the controller as home.mvc and thus not find it.

You need to leave the controllers in the correct order

routes.MapRoute( _
"Default2", _
"mvc/{controller}.mvc/{action}/{id}", _
New With {.controller = "Home", .action = "Index", .id = ""} _
)

routes.MapRoute( _
"Default", _
"mvc/{controller}/{action}/{id}", _
New With {.controller = "Home", .action = "Index", .id = ""} _
)

And then I said...

Title *
Name *
Email
Url
Comment *  
Please add 3 and 8 and type the answer here: