views:

350

answers:

4

I've been playing with ASP.Net MVC for a while now. I found the most difficult thing to get right is the routing table.

I found most examples leave the default route in place. I found that this leads to a lot of errors where the default route redirects to HomeController with an action that doesnt exist. Leading to strange error messages where you would expect to see a simple 404.

I eventually settled for a routing setup where I explicitly define all controller/action combinations I want to allow with a catch-all at the end to redirect to a 404 page that shows a sensible error message.

Am I missing something here? Or is this indeed a good way to do things?


Looking at the answers I got I think I'd better clarify the question a bit.

I'm trying to fool-proof the routing scheme of the website I'm building. I noticed that when I leave in the default {controller}/{action}/{id} route all kinds of URL's where I would like to display a 404 error actually get routed to the HomeController with an invalid Action and result in some ugly error message instead.

I'm a bit confused because most code examples just leave in the default route. Is there a reason it's there or is it ok to remove it?

The scheme I'm using now looks a bit like this

        routes.MapRoute( "About", "About", new {controller = "Page", action = "About"} );
        routes.MapRoute( "SignIn", "SignIn", new {controller = "Page", action = "SignIn"} );
        routes.MapRoute( "SignOut", "SignOut", new {controller = "Page", action = "SignOut"} );
        routes.MapRoute( "Authenticate", "Authenticate", new { controller = "Authentication", action = "Authenticate" });

        routes.MapRoute("CatchAll", "{*url}", new { controller = "Error", action = "Http404" });

I've got a route specified for every action in the system. And a catchall to display a 404 at the end. Is this a good way to do this or is there an easier way to make the routing scheme fool-proof?

+2  A: 

If this is the default route you are using:

routes.MapRoute(
            "Default",                                             
            "{controller}/{action}/{id}",                           
            new { controller = "Home", action = "Index", id = "" } 
        );

then "HomeController" and the "Index" action will be used by default unless your URL specifies otherwise.

For example:

"http://www.something.com/" will use the Home controller's Index action, because those are the default controller and action.

"http://www.something.com/foo" will use the Foo controller's Index action, because "Index" is the default action.

"http://www.something.com/foo/bar" will use the Foo controller's "bar" action

"http://www.something.com/foo/bar/1" will use the Foo controller's "bar" action, passing "1" as the "id" parameter

If you don't have a "FooController", anything that starts with "http://www.something.com/foo" will fail. Similarly, if your FooController doesn't have a "Bar" action, then "http://www.something.com/foo/bar" will fail.

You may already know everything I posted above. If that is the case, will you post the URLs that are failing, so we can better help?

Scott Ewers
You didn't actually answer my question (I guess that's my fault for asking vague questions) but you did clarify some other things I didnt know, thanks :-) I tried to clear up the question a bit.
Mendelt
+1  A: 

I prefer setting routes explicitly for each action method.

Check out this.

Arnis L.
Thanks for the pointer, will check this out!
Mendelt
A: 

This question is a bit older, but I just came across it, having the same question.

I'm not too keen on defining a ton of routes manually, nor am I keen on adding a bunch of attributes to have the routes automatically created for me (Arnis' link).

After some consideration, I decided to use a simple RouteConstraint that I created. I modified the default route to use it and added my 404 catchall below the default, like so:

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "" },
    new { controller = new ExistingControllerActionConstraint() }
);

routes.MapRoute(
    "CatchAll",
    "{*catchall}",
    new { controller = "Home", action = "NotFound" }
);

And the constraint class:

public class ExistingControllerActionConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
     Type t = Type.GetType("MvcApplication_BareMinimum7.Controllers." + values["controller"] + "Controller");

     if (t == null)
     {
      return false;
     }

     MethodInfo mi = (from m in t.GetMethods() where m.Name == values["action"].ToString() select m).FirstOrDefault();

     return (mi != null);
    }
}

The cost of the Reflection and LINQ used may outweigh the cost of registering tens of hundreds of routes, but this just seems cleaner to me.

Langdon
A: 

This is a very good utility to help you debug routing:

Phil Haack's Route Debugger

Martin