8

When attempting to create a catch all route in MVC 4 (something I've found several examples of, and based my code on) it returns a 404 error. I'm running this on IIS 7.5. This seems like a straight forward solution, so what am I missing?

One note, if I move the "CatchAll" route above the "Default" route it works. But of course then none of the other controllers are ever reached.

Here is the code:

Route.Config:

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );

        routes.MapRoute(
            "CatchAll",
            "{*dynamicRoute}",
            new { controller = "CatchAll", action = "ChoosePage" }
        );

Controller:

public class CatchAllController : Controller
{

    public ActionResult ChoosePage(string dynamicRoute)
    {
        ViewBag.Path = dynamicRoute;
        return View();
    }

}
tereško
  • 58,060
  • 25
  • 98
  • 150
gsxrboy73
  • 1,382
  • 14
  • 19

3 Answers3

9

Since the ultimate goal of creating the catchall route was to be able to handle dynamic urls and I was unable to find a direct answer to the original issue above, I approached my research from a different perspective. In doing so I came across this blog post: Custom 404 when no route matches

This solution allows handling of multiple sections within a given url (i.e. www.mysite.com/this/is/a/dynamic/route)

Here is the final custom controller code:

public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
 {
     if (requestContext == null)
     {
         throw new ArgumentNullException("requestContext");
     }

     if (String.IsNullOrEmpty(controllerName))
     {
         throw new ArgumentException("MissingControllerName");
     }

     var controllerType = GetControllerType(requestContext, controllerName);

     // This is where a 404 is normally returned
     // Replaced with route to catchall controller
     if (controllerType == null)
     {
        // Build the dynamic route variable with all segments
        var dynamicRoute = string.Join("/", requestContext.RouteData.Values.Values);

        // Route to the Catchall controller
        controllerName = "CatchAll";
        controllerType = GetControllerType(requestContext, controllerName);
        requestContext.RouteData.Values["Controller"] = controllerName;
        requestContext.RouteData.Values["action"] = "ChoosePage";
        requestContext.RouteData.Values["dynamicRoute"] = dynamicRoute;
     }

     IController controller = GetControllerInstance(requestContext, controllerType);
     return controller;
 }
gsxrboy73
  • 1,382
  • 14
  • 19
  • 2
    This works well. You can avoid the foreach and substring if you do this: `var dynamicRoute = string.Join("/", requestContext.RouteData.Values.Values);` – Nathan Ratcliff May 19 '14 at 21:37
  • Good call Nathan. Been awhile since I looked at this code. I always find ways to improve old code. This is certainly one of those times. I've updated the answer with your suggestion. Thanks! – gsxrboy73 May 20 '14 at 22:06
  • SO is great for that, I learn something almost every time I come here. This code was great, I was trying to figure out if I wanted to handle this in a base controller, but pushing it up to the controller factory is a better idea! – Nathan Ratcliff May 22 '14 at 15:59
4

It's probably because whatever route your're testing this with is matching your 1st - Default route. The way the routing in MVC works, any address you pass in will try to match routes in your routes collection in order of appearance. Once it find the 1st matching route it aborts further execution. In this case your Default route is 1st one in the list so if it is matched your second route will never be examined.

Basically write something like http://www.mysite.com/Home/Testing/Item/Page in your address bar and this should fail to match to your Default route and then try to match the CatchAll route.

Marko
  • 12,543
  • 10
  • 48
  • 58
  • 1
    This is the route I'm testing: http://mysite.com/hello, which I have no controllers for. – gsxrboy73 May 13 '13 at 17:44
  • 1
    It doesn't matter if you don't have a controller for the route, its still going to pick the first sequential matching route regardless ("Default" in this case). It will look for a class HelloController : Controller with Index action and if it doesn't find it, you will get the 404. – cchamberlain Mar 07 '15 at 20:33
0

Try defining the optional string dynamicRoute parameter on your route:

 routes.MapRoute( 
      "CatchAll", 
      "{*dynamicRoute}", 
      new { controller = "CatchAll", action = "ChoosePage", dynamicRoute = UrlParameter.Optional } );
Marko
  • 12,543
  • 10
  • 48
  • 58