Sean Taylor
Sean Taylor

Reputation: 5028

ASP.NET MVC - Catch All Route And Default Route

In trying to get my application to produce 404 errors correctly, I have implemented a catch all route at the end of my route table, as shown below:

 routes.MapRoute(
            "NotFound", _
           "{*url}", _
           New With {.controller = "Error", .action = "PageNotFound"} _
       )

However, to get this working, I had to remove the default route:

{controller}/action/{id}

But now that the default has been removed, most of my action links no longer work, and the only way I have found to get them working again is to add individual routes for each controller/action.

Is there a simpler way of doing this, rather than adding a route for each controller/action?

Is it possible to create a default route that still allows the catch all route to work if the user tries to navigate to an unknown route?

Upvotes: 77

Views: 82454

Answers (7)

Kjeld Poulsen
Kjeld Poulsen

Reputation: 243

I tried all of the above pattern without luck, but finally found out that ASP.NET considered the url I used as a static file, so none of my request was hidding the single controller endpoint. I ended up adding this snipper to the web.config

<modules runAllManagedModulesForAllRequests="true"/>

And then use the below route match pattern, and it solved the issue:

 routes.MapRoute(
            name:  "RouteForAnyRequest",
            url:  "{*url}",
            defaults: new { controller = "RouteForAnyRequest", action = "PageNotFound" }
        );

Upvotes: 0

Chris Halcrow
Chris Halcrow

Reputation: 31950

I would recommend this as the most readable version. You need this in your RouteConfig.cs, and a controller called ErrorController.cs, containing an action 'PageNotFound'. This can return a view. Create a PageNotFound.cshtml, and it'll be returned in response to the 404:

        routes.MapRoute(
            name:  "PageNotFound",
            url:  "{*url}",
            defaults: new { controller = "Error", action = "PageNotFound" }
        );

How to read this:

name: "PageNotFound"

= create a new route template, with the arbitrary name 'PageNotFound'

url:"{*url}"

= use this template to map all otherwise unhandled routes

defaults: new { controller = "Error", action = "PageNotFound" }

= define the action that an incorrect path will map to (the 'PageNotFound' Action Method in the Error controller). This is needed since an incorrectly entered path will not obviously not map to any action method

Upvotes: 1

Robert Koritnik
Robert Koritnik

Reputation: 105029

Use route constraints

In your case you should define your default route {controller}/{action}/{id} and put a constraint on it. Probably related to controller names or maybe even actions. Then put the catch all one after it and it should work just fine.

So when someone would request a resource that fails a constraint the catch-all route would match the request.

So. Define your default route with route constraints first and then the catch all route after it:

routes.MapRoute(
    "Default",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { controller = "Home|Settings|General|..." } // this is basically a regular expression
);
routes.MapRoute(
    "NotFound",
    "{*url}",
    new { controller = "Error", action = "PageNotFound" }
);

Upvotes: 115

Marcus10110
Marcus10110

Reputation: 519

My Solution is 2 steps.

I originally solved this problem by adding this function to my Global.asax.cs file:

protected void Application_Error(Object sender, EventArgs e)

Where I tried casting Server.GetLastError() to a HttpException, and then checked GetHttpCode. This solution is detailed here:

ASP.NET MVC Custom Error Handling Application_Error Global.asax?

This isn't the original source where I got the code. However, this only catches 404 errors which have already been routed. In my case, that ment any 2 level URL.

for instance, these URLs would display the 404 page:

www.site.com/blah

www.site.com/blah/blah

however, www.site.com/blah/blah/blah would simply say page could not be found. Adding your catch all route AFTER all of my other routes solved this:

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

However, the NotFound route does not seem to route requests which have file extensions. This does work when they are captured by different routes.

Upvotes: 1

Haacked
Haacked

Reputation: 59001

Ah, the problem is your default route catches all 3 segment URLs. The issue here is that Routing runs way before we determine who is going to handle the request. Thus any three segment URL will match the default route, even if it ends up later that there's no controller to handle it.

One thing you can do is on your controller override the HandleMissingAction method. You should also use the tag to catch all 404 issues.

Upvotes: 7

Dustin Laine
Dustin Laine

Reputation: 38503

Well, what I have found is that there is no good way to do this. I have set the redirectMode property of the customErrors to ResponseRewrite.

<customErrors mode="On" defaultRedirect="~/Shared/Error" redirectMode="ResponseRewrite">
    <error statusCode="404" redirect="~/Shared/PageNotFound"/>
</customErrors>

This gives me the sought after behavior, but does not display the formatted page.

To me this is poorly done, as far as SEO goes. However, I feel there is a solution that I am missing as SO does exactly what I want to happen. The URL remains on the failed page and throws a 404. Inspect stackoverflow.com/fail in Firebug.

Upvotes: 3

Praveen Prasad
Praveen Prasad

Reputation: 32107

//this catches all  requests
routes.MapRoute(
    "Error",
    "{*.}",
     new { controller = "PublicDisplay", action = "Error404" } 
);

add this route at the end the routes table

Upvotes: 25

Related Questions