Dashboard > MonoRail > Home > Routing Overview
Routing Overview
Added by Colin Ramsay, last edited by Colin Ramsay on Nov 23, 2008  (view change)
Labels: 


This is a scratchpad for documentation on the new Routing Engine. Please add anything you see fit and when it reaches a near-complete stage I will pull it out as a patch for the Castle documentation.

Configuration

Simply add the following to your HttpModules section in web.config:

<add name="routing" type="Castle.MonoRail.Framework.Routing.RoutingModuleEx, Castle.MonoRail.Framework" />

Registering a Route

RoutingModuleEx is the static class used for registering a route. Generally you'll want to set up your route in the Global.asax Application_Start, and here's an example of that to get us started:

public class Global : HttpApplication
{
   public void Application_OnStart()
   {
      RoutingModuleEx.Engine.Add(
         new PatternRoute("/<controller>/<id>/view.aspx")
         .DefaultForAction().Is("view")
      );
   }
}

In this case we are handling URLs which take the form /category/5/view.aspx or /product/2/view.aspx. The <controller> part of the PatternRoute constructor parameter indicates that we wish to capture that part of the URL and use it as the controller name. Similarly, the <id> part indicates that we want to capture a parameter called "id". We then register the action for this particular URL, in order to tell the routing engine which URL should be used as the default. Without this last step, it will not know which action to use.

Restrictions

In the above example, we're using integers for our ids. We can express this in the route:

RoutingModuleEx.Engine.Add(
new PatternRoute("/<controller>/<id>/view.aspx")
   .DefaultForAction().Is("view")
   .Restrict("id").ValidInteger
);

We've used the Restrict() method with the "id" parameter name and specified that only valid integers can be used here.

Named Routes

In previous examples we have accessed routes by supplying controller, action and parameter data to trigger a match with a registered route. There is a shortcut to this: named routes. By registering a route with an identifier, we can refer to it more easily:

RoutingModuleEx.Engine.Add("adminhome",
   new PatternRoute("/admin/home/index.aspx")
   .DefaultForAction().Is("view")
);

And here's a before and after look at how we refer to this route:

(velocity syntax)
$Url.Link('Admin Home', "%{area='admin', controller='home', action='index'}")
...
$Url.Link('Admin Home', "%{named='adminhome'}")

This is a little more concise and it also enables a URL shorthand for template developers.

Generating URLs

The default URL building services within Monorail are fully routing aware. Given the rule:

RoutingModuleEx.Engine.Add(
   new PatternRoute("/<controller>/<id>/view.aspx")
   .DefaultForAction().Is("view")
);

We can use the following NVelocity template code to generate a URL:

(velocity syntax)
$Url.Link('Product Name', "%{controller='product', action='list', params={id=15}}")

This will produce:

/product/15/list.aspx

We can add further parameters into the URL:

RoutingModuleEx.Engine.Add(
   new PatternRoute("/<controller>/<name>/<id>/view.aspx")
   .DefaultForAction().Is("view")
   .Restrict("id").ValidInteger
);

Now template code like this:

(velocity syntax)
$Url.Link('Product Name', "%{controller='product', action='view', params={id=15, name='ProductName'}}")

This will produce:

/product/ProductName/15/list.aspx

Other Integration

Any part of Monorail which uses the DefaultUrlBulder service will automatically be aware of your routes - so helpers and redirections will work as expected with no adjustments:

(velocity syntax)
$Form.FormTag("%{named='login'}") ## generate an opening form tag with the action attribute populated with the URL for the "login" named route
...
RedirectUsingNamedRoute("adminhome") // Redirect to the URL for the named route "adminhome"

Further Notes on Default URLs

Such as the homepage.

URL Extensions

Discuss how to deal with extensions when building routes and approaches for using Monorail extension-free.

Gotchas

There are a couple of scenarios in which solutions aren't immediately clear:

Reserved Extensions

  • On Windows, if the extension is equal to a DOS 1.0 legacy reserved filename (aux, con, com, lpt, etc) it will not match
  • If the extension is equal to an asp.net forbidden extension (.cs, .java, .asax, etc ) it will not match

Advanced Features

ForDomain and ForSubDomain are methods on RoutingEngine which are yet to be implemented but should provide support for per-domain and per-subdomain routes. This may be useful in multi-tenanted applications. AddFirst is available on RoutingEngine and allows a routing rule to be inserted as the first rule to be considered. Due to the way in which ordering of rules is important within the routing system, this can be used to "prioritize" particular rules.

Unit Testing Routes

The routing engine can be isolated and tested stand-alone by using the RouteMatch class:

[Test]
public void CatchAllRouteTest() {
	PatternRoute route = new PatternRoute("/<controller>/<id>/<action>.aspx");

	RouteMatch match = new RouteMatch();

	Assert.IsTrue(route.Matches("/product/123/index.aspx", new RouteContext(new StubRequest("GET"), null, "/", new Hashtable(), match) > 0);
	Assert.AreEqual("product", match.Parameters["controller"]);
	Assert.AreEqual("123", match.Parameters["id"]);
	Assert.AreEqual("index", match.Parameters["action"]);
}

Known Limitations

We are aware of a few situations in which the behavior of the routing engine is non-optimal.

Dots Could be present in url parts

But a work-around has to be implemented, see Routing tips for a working solution

having possibility to manage domain route match/dispatch in some way would be a killer feature (multi tenancy sides of a single application)

Isn't this what the ForDomain and ForSubdomain stuff (see Advanced Features) is supposed to be? I'm not sure if that's even implemented though. Regarding the issues with dots - why didn't you produce a test case against the trunk and try and get it fixed there?

Seems ForDomain and ForSubdomain are on the todo list, we may add them to the routing spec (either as goals or non goals for v1)

On the dots, I've tried to do this, but worked around by asking making a method virtual because there was lack of interested parts / consensus on the topic.

See http://support.castleproject.org/projects/MR/issues/view/MR-ISSUE-394 and http://groups.google.com/group/castle-project-devel/browse_frm/thread/f1f33b83f6275e2a

I will gladly provide/enhance the solution if interest come back for this being supported

Site running on a free Atlassian Confluence Community License granted to Castle Project. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.4 Build:#809 Jun 12, 2007) - Bug/feature request - Contact Administrators