ASP.NET MVC 404错误处理

ASP.NET MVC 404 Error Handling

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
How can I properly handle 404 in ASP.NET MVC?

我已经在Asp.Net MVC(RC 5)中的404 Http错误处理程序中进行了更改,我仍然得到标准的404错误页面。 我需要在IIS中更改某些内容吗?


我已经研究了如何在MVC(特别是MVC3)中正确管理404s,而且,恕我直言,这是我提出的最佳解决方案:

在global.asax中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MvcApplication : HttpApplication
{
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            Response.Clear();

            var rd = new RouteData();
            rd.DataTokens["area"] ="AreaName"; // In case controller is in another area
            rd.Values["controller"] ="Errors";
            rd.Values["action"] ="NotFound";

            IController c = new ErrorsController();
            c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
        }
    }
}

ErrorsController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public sealed class ErrorsController : Controller
{
    public ActionResult NotFound()
    {
        ActionResult result;

        object model = Request.Url.PathAndQuery;

        if (!Request.IsAjaxRequest())
            result = View(model);
        else
            result = PartialView("_NotFound", model);

        return result;
    }
}

编辑:

如果您正在使用IoC(例如AutoFac),则应使用以下命令创建控制器:

1
2
3
var rc = new RequestContext(new HttpContextWrapper(Context), rd);
var c = ControllerBuilder.Current.GetControllerFactory().CreateController(rc,"Errors");
c.Execute(rc);

代替

1
2
IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));

(可选的)

说明:

我可以想到有6个场景,ASP.NET MVC3应用程序可以生成404s。

由ASP.NET生成:

  • 场景1:URL与路由表中的路由不匹配。

由ASP.NET MVC生成:

  • 场景2:URL匹配路由,但指定不存在的控制器。

  • 场景3:URL匹配路由,但指定不存在的操作。

手动生成:

  • 场景4:操作使用方法HttpNotFound()返回HttpNotFoundResult。

  • 场景5:操作抛出HttpException,状态代码为404。

  • 方案6:操作手动将Response.StatusCode属性修改为404。

目标

  • (A)向用户显示自定义404错误页面。

  • (B)维护客户端响应的404状态代码(对SEO特别重要)。

  • (C)直接发送响应,不涉及302重定向。

解决方案尝试:自定义错误

1
2
3
4
5
<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customErrors>
</system.web>

此解决方案的问题:

  • 不符合方案(1),(4),(6)中的目标(A)。
  • 不自动符合目标(B)。必须手动编程。
  • 不符合目标(C)。

解决方案尝试:HTTP错误

1
2
3
4
5
6
<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

此解决方案的问题:

  • 仅适用于IIS 7+。
  • 不符合方案(2),(3),(5)中的目标(A)。
  • 不自动符合目标(B)。必须手动编程。

解决方案尝试:带有替换的HTTP错误

1
2
3
4
5
6
<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

此解决方案的问题:

  • 仅适用于IIS 7+。
  • 不自动符合目标(B)。必须手动编程。
  • 它模糊了应用程序级别的http异常。例如。不能使用customErrors部分,System.Web.Mvc.HandleErrorAttribute等。它不仅可以显示通用错误页面。

解决方案尝试自定义错误和HTTP错误

1
2
3
4
5
<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

1
2
3
4
5
6
<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

此解决方案的问题:

  • 仅适用于IIS 7+。
  • 不自动符合目标(B)。必须手动编程。
  • 不符合方案(2),(3),(5)中的目标(C)。

在尝试创建自己的库之前,对此感到困扰的人们(请参阅http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html)。但是之前的解决方案似乎涵盖了所有场景,而没有使用外部库的复杂性。


又一个解决方案。

添加ErrorControllers或静态页面以及404错误信息。

修改web.config(如果是控制器)。

1
2
3
4
5
<system.web>
    <customErrors mode="On">
       <error statusCode="404" redirect="~/Errors/Error404" />
    </customErrors>
</system.web>

或者在静态页面的情况下

1
2
3
4
5
<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Static404.html" />
    </customErrors>
</system.web>

这将处理错过的路线和错过的行动。


Marco的回应是最佳解决方案。我需要控制我的错误处理,我的意思是真的控制它。当然,我已经扩展了一点解决方案并创建了一个管理所有内容的完整错误管理系统。我也在其他博客中读到了这个解决方案,大多数高级开发人员似乎都接受了这个解决方案。

这是我正在使用的最终代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            var exception = Server.GetLastError();
            var httpException = exception as HttpException;
            Response.Clear();
            Server.ClearError();
            var routeData = new RouteData();
            routeData.Values["controller"] ="ErrorManager";
            routeData.Values["action"] ="Fire404Error";
            routeData.Values["exception"] = exception;
            Response.StatusCode = 500;

            if (httpException != null)
            {
                Response.StatusCode = httpException.GetHttpCode();
                switch (Response.StatusCode)
                {
                    case 404:
                        routeData.Values["action"] ="Fire404Error";
                        break;
                }
            }
            // Avoid IIS7 getting in the middle
            Response.TrySkipIisCustomErrors = true;
            IController errormanagerController = new ErrorManagerController();
            HttpContextWrapper wrapper = new HttpContextWrapper(Context);
            var rc = new RequestContext(wrapper, routeData);
            errormanagerController.Execute(rc);
        }
    }

在我的ErrorManagerController里面:

1
2
3
4
5
        public void Fire404Error(HttpException exception)
    {
        //you can place any other error handling code here
        throw new PageNotFoundException("page or resource");
    }

现在,在我的Action中,我正在抛出我创建的自定义异常。我的Controller继承自我创建的自定义Controller Based类。创建自定义基本控制器以覆盖错误处理。这是我的自定义基本控制器类:

1
2
3
4
5
6
7
8
9
10
public class MyBasePageController : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        filterContext.GetType();
        filterContext.ExceptionHandled = true;
        this.View("ErrorManager", filterContext).ExecuteResult(this.ControllerContext);
        base.OnException(filterContext);
    }
}

上面代码中的"ErrorManager"只是一个使用基于ExceptionContext的Model的视图

我的解决方案完美无缺,我可以在我的网站上处理任何错误,并根据任何异常类型显示不同的消息。


看起来这是捕捉一切的最佳方式。

如何在ASP.NET MVC中正确处理404?


我可以推荐的是查看FilterAttribute。例如,MVC已经有HandleErrorAttribute。你可以自定义它只处理404.如果你是朋友,请回复我会看例子。

BTW

您在上一个问题中接受的解决方案(使用最后一条路线)在大多数情况下都不起作用。使用HandleUnknownAction的第二个解决方案可以工作,但需要在每个控制器中进行此更改或具有单个基本控制器。

我的选择是使用HandleUnknownAction的解决方案。


在IIS中,您可以根据错误代码指定重定向到"特定"页面。在您的示例中,您可以配置404 - >您自定义的404错误页面。