views:

170

answers:

2

If I have a route:

routes.MapRoute(
    "RouteName",                                            // route name
    "{action}",                                             // url with parameters
    new { controller = "Home", action = "Index", id = "" }  // parameter defaults
);

I want to be able to catch URL's of the form:

http://sitename.com/about  
http://sitename.com/contact  
http://sitename.com/others

This obviously works when an action exists within the Home controller that carries the name of the desired URL. If I enter an erroneous URL, such as http://sitename.com/foo, and the foo action does not exist within the Home controller, I would like to direct the application to a 404 page not found, but obviously if I enter foo, it is looking for the foo action within the home controller. Is there any way to remain generic without hardcoding all the subpages into the global.asax. If at all possible, I want to refrain from:

routes.MapRoute(
    "About",                                                // route name
    "about",                                                // url with parameters
    new { controller = "Home", action = "About", id = "" }  // parameter defaults
);

routes.MapRoute(
    "Contact",                                                // route name
    "contact",                                                // url with parameters
    new { controller = "Home", action = "Contact", id = "" }  // parameter defaults
);

Thanks.

A: 

Maybe I'm not understanding your question correctly, but the route that you have at the top of your question will already cover the cases you're typing out at the bottom. There is no need to define them explicitly like that. If they type in an action that doesn't exist, a 404 error will be thrown.

If you want to do something special when someone matches a valid controller but doesn't supply a valid action, you can override HandleUnknownError in your controller.

womp
+2  A: 

Have you tried using a constraint with a single route? This would allow you to match any action that exists on your Home (or other) controller at the top level.

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

Where MatchesHomeControllerConstraint is:

public class MatchesHomeControllerConstraint : IRouteConstraint
{
     public bool Match( HttpContextBase httpContext, 
                        Route route, 
                        string parameterName, 
                        RouteValueDictionary values, 
                        RouteDirection routeDirection )
     {
           var name = values[parameterName] as string;
           var method =  typeof(HomeController).GetMethod(name,BindingFlags.IgnoreCase);
           return method != null
                  && method.ReturnType.IsAssignableFrom( typeof(ActionResult) );
     }
}
tvanfosson
In practice, I'd probably cache the set of controller actions and match against the cached set. I'm leaving the implementation as-is, though, for simplicity.
tvanfosson
No I haven't tried this. Very clever though. Thanks!