asp.netasp.net-mvc-2routeshttp-status-code-404handleerror

ASP.NET MVC - How to throw a 404 page similar to that on StackOverflow


I've currently got a BaseController class that inherits from System.Web.Mvc.Controller. On that class I have the HandleError Attribute that redirects users to the "500 - Oops, we screwed up" page. This is currently working as expected.

THIS WORKS

<HandleError()> _
Public Class BaseController : Inherits System.Web.Mvc.Controller

''# do stuff
End Class

I also have my 404 pages working on a Per-ActionResult basis which is again working as expected.

THIS WORKS

    Function Details(ByVal id As Integer) As ActionResult
        Dim user As Domain.User = UserService.GetUserByID(id)

        If Not user Is Nothing Then
            Dim userviewmodel As Domain.UserViewModel = New Domain.UserViewModel(user)
            Return View(userviewmodel)
        Else
            ''# Because of RESTful URL's, some people will want to "hunt around"
            ''# for other users by entering numbers into the address.  We need to
            ''# gracefully redirect them to a not found page if the user doesn't
            ''# exist.
            Response.StatusCode = CInt(HttpStatusCode.NotFound)
            Return View("NotFound")
        End If

    End Function

Again, this works great. If a user enters something like http://example.com/user/999 (where userID 999 doesn't exist), they will see the appropriate 404 page, and yet the URL will not change (they are not redirected to an error page).

I CAN'T GET THIS IDEA TO WORK

Here's where I'm having an issue. If a user enters http://example.com/asdf- they get kicked over to the generic 404 page. What I want to do is leave the URL in tact (IE: not redirect to any other page), but simply display the "NotFound" view as well as push the HttpStatusCode.NotFound to the client.

For an example, just visit https://stackoverflow.com/asdf where you'll see the custom 404 page and see the URL left in tact.

Obviously I'm missing something, but I can't figure it out. Since "asdf" doesn't actually point to any controller, my base controller class isn't kicking in, so I can't do it in the "HandleError" filter in there.

Thanks in advance for the help.

Note: I absolutely do not want to redirect the user to a 404 page. I want them to stay at the existing URL, and I want MVC to push the 404 VIEW to the user.

Edit:

I have also tried the following to no avail.

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.RouteExistingFiles = False
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
    routes.IgnoreRoute("Assets/{*pathInfo}")
    routes.IgnoreRoute("{*robotstxt}", New With {.robotstxt = "(.*/)?robots.txt(/.*)?"})

    routes.AddCombresRoute("Combres")

    ''# MapRoute allows for a dynamic UserDetails ID
    routes.MapRouteLowercase("UserProfile", _
        "Users/{id}/{slug}", _
        New With {.controller = "Users", .action = "Details", .slug = UrlParameter.Optional}, _
        New With {.id = "\d+"} _
    )


    ''# Default Catch All Valid Routes
    routes.MapRouteLowercase( _
        "Default", _
        "{controller}/{action}/{id}/{slug}", _
        New With {.controller = "Events", .action = "Index", .id = UrlParameter.Optional, .slug = UrlParameter.Optional} _
    )

    ''# Catch All InValid (NotFound) Routes
    routes.MapRoute( _
        "NotFound", _
        "{*url}", _
        New With {.controller = "Error", .action = "NotFound"})

End Sub

My "NotFound" route is doing nothing.


Solution

  • Found my answer on my other SO question. Thanks very much Anh-Kiet Ngo for the solution.

    protected void Application_Error(object sender, EventArgs e)
    {
        Exception exception = Server.GetLastError();
    
        // A good location for any error logging, otherwise, do it inside of the error controller.
    
        Response.Clear();
        HttpException httpException = exception as HttpException;
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "YourErrorController");
    
        if (httpException != null)
        {
            if (httpException.GetHttpCode() == 404)
            {
                routeData.Values.Add("action", "YourErrorAction");
    
                // We can pass the exception to the Action as well, something like
                // routeData.Values.Add("error", exception);
    
                // Clear the error, otherwise, we will always get the default error page.
                Server.ClearError();
    
                // Call the controller with the route
                IController errorController = new ApplicationName.Controllers.YourErrorController();
                errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
            }
        }
    }