Upcoming Changes In Routing

Made a few corrections on having default.aspx in the root due to a minor bug we just found. Isn’t preview code so much fun?

We’ve been making some changes to routing to make it more powerful and useful. But as Uncle Ben says, with more power comes more responsibility. I’ll list out the changes first and then discuss some of the implication of the changes.

  • Routes no longer treat the . character as a separator. Currently, routes treat the . and / characters as special. They are separator characters. The upcoming release of routing will only treat the / as separator.
  • Routes may have multiple (non-adjacent) url parameters in a segment. Currently, URL parameters in a route must fill up the space between separators. For example, {param1}/{param2}.{param3}. With the upcoming release, a URL segment may have more than one parameter as long as they are separated by a literal. For example {param1}.{ext}/{param3}-{param4} is now valid.

Passing Parameter Values With Dots

With this change, the dot character becomes just another literal. It’s no longer “special”. What does this buy us? Suppose you are building a site that can present information about other sites. For example, you might want to support URLs like this:

  • http://site-info.example.com/site/www.haacked.com/rss
  • http://site-info.example.com/site/www.haacked.com/stats

That first URL will display information about the RSS feed at www.haacked.com while the second one will show general site stats. To make this happen, you might define a route like this.

routes.Add(new Route("site/{domain}/{action}" 
  , new MvcRouteHandler()) 
{ 
  Defaults=new RouteValueDictionary(new {controller="site"})  
});

Which routes to the following controller and action method.

public class SiteController : Controller
{
  public void Rss(string domain)
  {
    RssData rss = GetRssData(domain);
    RenderView("Rss", rss);
  }

  public void Stats(string domain)
  {
    SiteStatistics stats = GetSiteStatistics(domain);
    RenderView("Stats", stats);
  }
}

The basic idea here is that the domain (such as www.haacked.com in the example URLs above) would get passed to the domain parameter of the action methods. The only problem is, it does not work with the previous routing system because routing considered the dot character in the URL as a separator. You would have had to define routes with URLs like site/{sub}.{domain}.{toplevel}/{action} but then that doesn't work for URLs with two sub-domains or no sub-domain.

Since we no longer treat the dot as special, this scenario is now possible.

Multiple URL Segments

What does adding multiple URL segments buy us? Well it continues to allow using routing with URLs that do have extensions. For example, suppose you want to route a request to the following action method: public void List(string category, string format)

With both the previous and new routing, you can match the request for the URL...

/products/list/beverages.xml

with the route

{controller}/{action}/{category}.{format}

To call that action method. But suppose you don’t want to use file extensions, but still want to specify the format in a special way. With the new routing, you can use any character as a separator. For example, maybe you want to use the dash character to separate the category from the format. You could then match the URL

/products/list/beverages-xml

with the route

{controller}/{action}/{category}-{format}

and still call that action method.

Note that we now allow any character (allowed in the URL and that is not a dash) to pretty much be a separator. So if you really wanted to, though not sure why you would, you could use BLAH to separate out the format. Thus you could match the route

/products/list/beveragesBLAHxml

with the route

{controller}/{action}/{category}BLAH{format}

and it would still route to the same List method above.

Consequences

This makes routing more powerful, but there are consequences to be aware of.

For example, using the default routes as defined in the ASP.NET MVC Preview 2 project template, a request for “/Default.aspx” fails because it can’t find a controller with the name Default.aspx”. Huh? Well “/Default.aspx” now matches the route {controller}/{action}/{id} (because of the defaults for {id} and {action}) because we don’t treat the dot as special.

Not only that, what about a request for /images/jpegs/foo.jpg? Wouldn’t routing try to route that to controller="images", action="jpegs", id="foo.jpg" now?

The decision we made in this case was that by default, routing should not apply to files on disk. That is, routing now checks to see if the file is on disk before attempting to route (via the Virtual Path Provider).

If the file is on disk, we pop out and don’t route and let the web server handle the request normally. If the file doesn’t exist, we attempt to apply routing. This makes sure we don’t screw around with requests for static resources on disk. Of course, this default can be changed by setting the property RouteTable.Routes.RouteExistingFiles to be true.

Why Blog This Now?

The Dynamic Data team is scooping the MVC team on these routing changes. ;) They are releasing a preview of their latest changes to Dynamic Data which includes using our new routing dll.

Check out ScottGu’s post on the subject. I really feel Dynamic Data is the most underrated new technology coming out from the ASP.NET team. When you dig into it, it is really cool. The “scaffolding” part is only the tip of the iceberg.

Installing the Dynamic Data preview requires installing the routing assembly into the GAC. If you install this, it may break existing MVC Preview 2 sites because the assembly loader favors the GAC when the assembly is the same version. And the routing assembly is the same version as the one in Preview 2.

The Dynamic Data Preview readme has the steps to update your MVC Preview 2 project to work with the new routing. You’ll notice that the readme recommends having a Default.aspx file in the root which redirects to /Home. Technically, the Default.aspx file in the root won’t be necessary in the final release because of the suggested routing changes (it is necessary now due to a minor bug). Unfortunately, Cassini doesn’t work correctly when you make a request for "/" and there is no default document. It doesn’t run any of the ASP.NET Modules. So we kept the file in the root, but you can will be able to remove it when deploying to IIS 7. So to recap this last point, the Default.aspx in the project root is to make sure that pre-SP1 Cassini works correctly as well as IIS 6 without star mapping. It's will not be needed for IIS 7 in the future, but is needed for the time being.

We will have a new CodePlex source code push in a couple of weeks with an updated version of MVC that supports the new routing engine.

What others have said

Requesting Gravatar... Kurt Apr 10, 2008 11:49 AM
# re: Upcoming Changes In Routing
Sweet.

Given the "any character as a route separator" thing, are you considering making greedy parameters possible in the future? I might want something like this, for instance:

/{path*}.{extension}

Also, why's the / still considered a separator, rather than treating it like the .?
Requesting Gravatar... Kurt Apr 10, 2008 11:54 AM
# re: Upcoming Changes In Routing
Whoops, to clarify. I meant "greedy parameters that aren't the last in the route". :)
Requesting Gravatar... Chad Lee Apr 10, 2008 12:15 PM
# re: Upcoming Changes In Routing
Any chance of supporting Monorail-style areas in the next release?
Requesting Gravatar... Nick Berardi Apr 10, 2008 1:35 PM
# re: Upcoming Changes In Routing
Phil,

>>So if you really wanted to, though not sure why you would, you >>could use BLAH to separate out the format. Thus you could match >>the route
>>/products/list/beveragesBLAHxml
>>with the route
>>{controller}/{action}/{category}BLAH{format}

This is very wonderful news, and I have already found a use for it: On my current project http://www.ideapipe.com, I have to define pages like this:
http://www.ideapipe.com/pages/2
However now with this new routing I can do the following, which I like the format much better:
http://www.ideapipe.com/page2
By using, as one of my routes:
/page{page}
Requesting Gravatar... Haacked Apr 10, 2008 1:49 PM
# re: Upcoming Changes In Routing
@kurt our parameters are greedy by deafult.
Requesting Gravatar... Kurt Apr 10, 2008 1:53 PM
# re: Upcoming Changes In Routing
@Haacked They suck down forward slashes too? Maybe I should have used the word "wildcard" rather than greedy. Last I read about those, they only worked as the last parameter in a route.
Requesting Gravatar... spootwo Apr 10, 2008 2:49 PM
# re: Upcoming Changes In Routing
I would also like to know if /{path*}.{extension} will work in the future. The old guard at work is cautious moving to a new page model and they require the reassurance that they can still add web pages the old way before I can proceed to bring mvc goodness to my workplace.
Requesting Gravatar... Haacked Apr 10, 2008 4:34 PM
# re: Upcoming Changes In Routing
@Kurt, no they don't suck down slashes. Only catch-all parametres suck down slashes. The catch-all parameter must be at the end.

@spoottwo it should be /{path}.{extension}
No need for a *.

I think I'll try to write up a new blog post with a better explanation of routing. :)
Requesting Gravatar... VIjay Santhanam Apr 10, 2008 7:00 PM
# re: Upcoming Changes In Routing
URL generation is really important to me, and being able to define routes with customised string formatting would be useful.

right now i think spaces are left as is -- and firefox seems to deal okay.
So for,
/Clients/Search/{query} with controller="Clients",action="Search"
public void Search(string query){/*action*/}

and,
ActionLink<ClientsController>(c=> c.Search("John Citiz"), "Search")
returns "/Clients/Search/john citi".

is that url legal? If it is that's great! but otherwise it'd be nice to customize how query gets formatted. Maybe a static char replacement dictionary.






Requesting Gravatar... VIjay Santhanam Apr 10, 2008 7:18 PM
# re: Upcoming Changes In Routing
Nope, sorry, spaces are clearly url encoded. firefox just displays them as spaces in the status bar.

It'd be great to change that space to /clients/search/john_citizen.
Oh so neat!
Requesting Gravatar... tgmdbm Apr 10, 2008 10:16 PM
# re: Upcoming Changes In Routing
@Vijay, yeah spaces are encoded to "%20", i think perhaps they should be encoded to "+" which is what happens when you submit a form with method="get"

That would also mean that routing should decode "+" to space! As it stands at the moment, you would end up with a + in the RouteData.

@Haacked, lovin these changes! Keep it up.
Requesting Gravatar... Chris Cavanagh Apr 10, 2008 10:56 PM
# re: Upcoming Changes In Routing
I'm glad you've added the "RouteTable.Routes.RouteExistingFiles" flag. Please don't remove it! I'm already dependent on every request being routable :o)
Requesting Gravatar... Gokhan Demir Apr 11, 2008 12:52 AM
# re: Upcoming Changes In Routing
phil please also consider to not treat '/' char as separator. i think routing be more nifty with supporting archive.org like urls (e.g. http://web.archive.org/web/*/http://haacked.com)

Requesting Gravatar... Gokhan Demir Apr 11, 2008 1:02 AM
# re: Upcoming Changes In Routing
phil please also consider to not treat / char as separator. i think routing be more nifty with supporting archive.org like urls (e.g. http://web.archive.org/web/*/http://haacked.com)
Requesting Gravatar... Kurt Apr 11, 2008 7:49 AM
# re: Upcoming Changes In Routing
You can do an archive style url like this, I think:

"web/*/{url*}"
Requesting Gravatar... Chance Apr 11, 2008 8:35 AM
# re: Upcoming Changes In Routing
Phil,

Have you guys considered expanding the routing to encompass full URL routing? For example: "{controller}.[domain]/{action}/{id}" which would be great for instances such as forums.website.com or manage.website.com/page/edit/3998

I realize that I could write my own custom routing for this - (which I plan to do) but I think it could potentially benefit the routing system as a whole.
Requesting Gravatar... Haacked Apr 11, 2008 9:04 AM
# re: Upcoming Changes In Routing
@Chance Right now we haven't looked at that only because by the time you've hit our code, the domain has already been determined.

Our code runs at the application level and this could be confusing when you have a route for a domain, but IIS host headers points it elsewhere.

However, I do know some people map multiple host headers to the same website (Subtext does this!) so it wouldn't be hard to write a custom route that does this. If this ends up being a real important scenario for lots of people, I'm sure we'll consider it. :)
Requesting Gravatar... David Alpert Apr 11, 2008 10:04 AM
# re: Upcoming Changes In Routing
Does this mean that i can suddenly drop MVC-style code (using the System.Web.Routing mechanism) into an existing Web Forms application and the routing mechanism will default to Web Forms for files that exist on disk such as my existing pages, yet route into my models and controllers if a URL would otherwise return a 404?
Requesting Gravatar... Chance Apr 11, 2008 4:04 PM
# re: Upcoming Changes In Routing
Thanks Phil, I figured that may have a showstopper for the idea but I had to throw it out there anyways. Subdomain seperation is clean and elegant in a lot of scenarios. I imagine though that if someone is willing to go through the hassle of getting the routing setup on the server, they certainly wouldn't mind writing a few lines of code to handle it in the code.
Requesting Gravatar... Dragan Panjkov Apr 13, 2008 11:03 AM
# re: Upcoming Changes In Routing
What if I download dynamic data preview and manually copy routing dll into bin folder of my mvc sites? It should use that version (I don't plan to install dynamic data preview, so then routing dll will not be deployed into gac)
Requesting Gravatar... Scott May 01, 2008 7:22 AM
# re: Upcoming Changes In Routing
Is there a way to tell routes to ignore period as a separator in a certain part of a url? For instance:

http://whatever.com/files/{filename}
method: DELETE

to delete a certain file?
Requesting Gravatar... Dirk May 06, 2008 9:09 AM
# re: Upcoming Changes In Routing
I'm trying to migrate a "legacy" ASP.NET webforms site bit-by-bit (not literally :/)and some of my "old" webforms pages use ASP.NET Ajax. It seems that certain routes interfere with the /WebResource.axd and /ScriptResource.axd handlers. This leads to Ajax not working correctly or failing completely. I wonder if these handlers could be removed from routing by default, just like the static files.
Requesting Gravatar... mario Jun 04, 2008 5:16 PM
# re: Upcoming Changes In Routing
why not regex? it would make the default mvc so much more flexible. more importantly, we don't have to worry about 'valid' cahracers. only developers will be using MVC and if they can't figure out regular expressions they should seek another profession. maybe use named captures

(?<controller>[^/]+)/(?<action>[^/]+)/(?<id>.*)
Requesting Gravatar... Rodj Jun 18, 2008 1:51 AM
# re: Upcoming Changes In Routing
I have an issue with an url encoded '/' (ie. %2f) being parsed by the routing engine.

for example: simple route "{category}/{product}" however a category is "3/5 door" so after encoding the url returns "3%2f5+door/a-product". The request goes through as "3/5 door/a-product" which will not match any routes.

Is there a way round this?
Requesting Gravatar... haacked Jun 23, 2008 8:51 AM
# re: Upcoming Changes In Routing
@Rodj unfortunately, by the time ASP.NET Hands us the route, that %2f is already unencoded. So there's no way for us to see that as anything other than a "/".

The only workaround I can think of is to replace "/" within categories with a well-known character when generating URLs and after matching URLs. I'll dig into this some more to find out if there are other options.
Requesting Gravatar... Eilon Lipton Jun 23, 2008 10:03 AM
# re: Upcoming Changes In Routing
@Rodj As Phil says you'll have to encode the "/" as some other character because it has a very special meaning.

@mario The problem with regexes is right there in your comment. Even the regex you've shown is not equivalent to the default MVC route. You have to wrap additional "(regex)?" clauses around each segment to make them optional. That makes the regex one huge cluster of punctuation that is incredibly hard for people to read.

I'd like to meet these "developers" who think they understand regexes. Most of the ones I know *think* they understand regexes but end up creating bad regexes that don't entirely solve the original problem.
Requesting Gravatar... Mladen Mihajlovic Sep 09, 2008 5:17 AM
# re: Upcoming Changes In Routing
I'm having the same problem as Rodj (I have a / and \ in a category name) There should really be a way to do this properly. How about adding in some property (or character) to tell it it will have a certain number of slashes. Also shouldn't the {id*} thing work?

There must be a way to do this - / and \ characters should be usable if they're url encoded at least.
Requesting Gravatar... Mohan Sep 24, 2008 6:34 AM
# re: Upcoming Changes In Routing
Am having the same problem as "Dirk", certain routes interfere with the /WebResource.axd and /ScriptResource.axd handlers. This leads to Ajax not working correctly or failing completely. Is thre any solution for the same...?
Even i tried with Routes.IgnoreRoute option also, but no use.
Requesting Gravatar... Mohan Sep 25, 2008 10:50 PM
# re: Upcoming Changes In Routing
Mistake from my side, forgot to put "IgnoreRoute" on top before adding routes.
Requesting Gravatar... borisCallens Mar 23, 2009 1:11 AM
# re: Upcoming Changes In Routing
What I found on blogs, SO and a bit here is that currently there seems to be no way around the "/" thing. @Phil: Are there any plans of solving this issue in the future?
Requesting Gravatar... James Radford, Web Developer Jun 08, 2010 5:12 AM
# re: Upcoming Changes In Routing
so what version of MVC is this in? ASP.NET MVC 2?
Thanks,

What do you have to say?

(will show your gravatar)
Please add 4 and 3 and type the answer here: