Question

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?

Was it helpful?

Solution

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?

OTHER TIPS

I prefer setting routes explicitly for each action method.

Check out this.

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

Phil Haack's Route Debugger

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top