Correct 404 message for a missing ASP.Net MVC controller

限于喜欢 提交于 2019-12-18 18:27:49

问题


I have an MVC 2 application that should always give a 'nice' 404 page.

However currently I get a low level .Net one: "Server Error in '/sitename' Application..."

I have a base controller that has a NotFound action that will render the nice 404 page.

Missing actions are handled:

protected override void HandleUnknownAction(string actionName)
{
    this.NotFound(actionName).ExecuteResult(this.ControllerContext);
}

So a visit to {site}/ValidController/NotAnAction gets routed correctly.

However a visit to {site}/NotAController doesn't.

I have routes set up with a catch all:

routes.MapRoute(
    "MVC routes",
    "{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional });

routes.MapRoute(
    "Catch All", 
    "{*url}",
    new { controller = "System", action = "NotFound" });

The catch all correctly catches routes that don't match.

So {site}/Invalid/Action/id/extra is correctly routed via the catch all.

However {site}/Invalid gets picked up via the "MVC routes" route and ASP.Net goes looking for InvalidController, and throws a dumb exception when it doesn't find it.

I know that I can override this at the web.config level, but that just redirects to the page. I'd like to know when the route pattern matches but the controller is not a valid controller name.

Where can I catch and change this behaviour?


回答1:


I finally found the answer to this, though it's still not ideal.

You can restrict the controller names that are allowed to match a route using a regex, so if we assume the default implementation of the controller factory we can figure out all the possible class names that are supported:

// build up a list of known controllers, so that we don't let users hit ones that don't exist
var allMvcControllers = 
    from t in typeof(Global).Assembly.GetTypes()
    where t != null &&
        t.IsPublic &&
        !t.IsAbstract &&
        t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
        typeof(IController).IsAssignableFrom(t)
    select t.Name.Substring(0, t.Name.Length - 10);

// create a route constraint that requires the controller to be one of the reflected class names
var controllerConstraint = new
{
    controller = "(" + string.Join("|", allMvcControllers.ToArray()) + ")"
};

// default MVC route
routes.MapRoute(
    "MVC",
    "{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional },
    controllerConstraint);

// fall back route for unmatched patterns or invalid controller names
routes.MapRoute(
    "Catch All", 
    "{*url}",
    new { controller = "System", action = "NotFound" });

This isn't ideal, it adds a hit on the application start and still feels far too complicated, but it does have the desired effect.




回答2:


Setting it up at web.config level does not "just redirect to the page". On an MVC app, you give it a "{controller/action}" url an it will actually call that action:

<customErrors mode="On" defaultRedirect="/system/problem"> 
    <error statusCode="404" redirect="/system/notfound" />
</customErrors>

This will call the NotFound on the SystemController.

In your action you can then for instance get the value of HttpContext.Request.RawUrl to see what the faulty request was: "/system/notfound?aspxerrorpath=/Invalid". In this case I tried to go to the InvalidController.

A nice way to handle this things, by the way, is implementing ELMAH (or Error Logging Modules and Handlers. Scott Hanselman wrote an "introductory" post about it, but ELMAH is nowadays available as a NuGet package.

You might want to take a look at this question/ansers on how to use it with ASP.NET MVC: How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?



来源:https://stackoverflow.com/questions/5701922/correct-404-message-for-a-missing-asp-net-mvc-controller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!