ASP.NET MVC 404 错误处理 [重复]

Posted

技术标签:

【中文标题】ASP.NET MVC 404 错误处理 [重复]【英文标题】:ASP.NET MVC 404 Error Handling [duplicate] 【发布时间】:2010-10-17 14:05:11 【问题描述】:

可能重复:How can I properly handle 404 in ASP.NET MVC?

我已经完成了404 Http error handler in Asp.Net MVC (RC 5) 中列出的更改,但我仍然收到标准的 404 错误页面。我需要更改 IIS 中的某些内容吗?

【问题讨论】:

这是一个很好的阅读主题@How to handle 404 Not Found errors effectively with ASP.NET MVC 4 【参考方案1】:

我研究了 A LOT 关于如何正确管理 MVC 中的 404(特别是 MVC3),恕我直言,这是我想出的最佳解决方案:

在 global.asax 中:

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));
        
    

错误控制器:

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),您应该使用以下方法创建控制器:

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

代替

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

(可选)

说明:

我可以想到 6 种情况,其中 ASP.NET MVC3 应用程序可以生成 404。

由 ASP.NET 生成:

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

由 ASP.NET MVC 生成:

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

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

手动生成:

场景4:动作通过使用HttpNotFound()方法返回一个HttpNotFoundResult。

场景 5: 一个动作抛出一个状态码为 404 的 HttpException。

场景 6: 某个操作手动将 Response.StatusCode 属性修改为 404。

目标

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

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

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

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

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customErrors>
</system.web>

此解决方案的问题:

不符合场景 (1)、(4)、(6) 中的目标 (A)。 不自动符合目标 (B)。必须手动编程。 不符合目标 (C)。

解决方案尝试:HTTP 错误

<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 替换错误

<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 等。它不能只显示通用错误页面。

解决方案尝试 customErrors 和 HTTP 错误

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

<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)。但之前的解决方案似乎涵盖了所有场景,没有使用外部库的复杂性。

【讨论】:

我喜欢您的分析,但不喜欢您的解决方案 :) 等待请求结束的问题是某些请求上下文已被丢弃,例如会话状态。 @RickAndMSFT 那篇文章没有提到 Response.Clear,只有 Response.Redirect。 @RickAndMSFT:Response.Clear 有什么问题?投掷如此神秘的评论和博文,以“不要使用那种方法”作为结尾,然后在被要求澄清时保持沉默是没有帮助的。 嗨@PussInBoots。在 MVC 中,如果您的站点太大(很多控制器、模型和视图),您可以在逻辑上将它们分组到区域中。欲了解更多信息,请参阅msdn.microsoft.com/en-us/library/ee671793(v=vs.100).aspx @Marco,你的总结真的很棒,可能帮助了很多人!我确实认为一个框架(ASP.NET/ASP.NET MVC)创造了如此多的混乱和复杂性来处理 404/500 错误是无法忍受的!对比其他的 web 框架:Yii、Rails、Django 等,这部分在 ASP.NET 中真的很糟糕!【参考方案2】:

另一种解决方案。

添加带有 404 错误信息的 ErrorControllers 或静态页面。

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

<system.web>
    <customErrors mode="On" >
       <error statusCode="404" redirect="~/Errors/Error404" />
    </customErrors>
</system.web>

或者在静态页面的情况下

<system.web>
    <customErrors mode="On" >
        <error statusCode="404" redirect="~/Static404.html" />
    </customErrors>
</system.web>

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

【讨论】:

不错! :) ErrorsController 可以从与所有其他控制器相同的基础继承,因此可以访问某些功能。此外,Error404 视图可以封装在主视图中,为用户提供网站其余部分的整体外观和感觉,而无需任何额外工作。 在开发过程中使用&lt;customErrors mode="RemoteOnly"&gt;查看实际的错误页面。 如果 return HttpNotFound(); 从控制器操作返回为 ActionResult 则不起作用。 此解决方案不起作用。对于“On”和“RemoteOnly”模式,我收到默认的 404 错误。 这会导致 302 重定向,因此您不会保留原始 HTTP 代码。 ASP.NET 确实使控制 HTTP 堆栈变得比需要的更困难。【参考方案3】:

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

这是我使用的最终代码:

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 内部:

        public void Fire404Error(HttpException exception)
    
        //you can place any other error handling code here
        throw new PageNotFoundException("page or resource");
    

现在,在我的操作中,我正在抛出我创建的自定义异常。我的控制器继承自我创建的基于控制器的自定义类。创建自定义基本控制器是为了覆盖错误处理。这是我的自定义基本控制器类:

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的模型的视图

我的解决方案完美运行,我能够处理我网站上的任何错误,并根据任何异常类型显示不同的消息。

【讨论】:

我不同意你认为这是最好的解决方案。像这样的常见任务设置起来不应该这么复杂。马科斯的回答很好,但你真的不想为简单的事情写那么多代码。【参考方案4】:

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

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

【讨论】:

是的,这适用于我的 MVC3 项目。谢谢【参考方案5】:

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

【讨论】:

【参考方案6】:

我可以推荐的是查看FilterAttribute。例如 MVC 已经有 HandleErrorAttribute。您可以将其自定义为仅处理 404。如果您感兴趣,请回复我会看示例。

顺便说一句

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

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

【讨论】:

看起来问题在于“controller/action/id”的标准默认路由捕获了所有内容,因此它不会到达最后一条路由。我认为如果找不到控制器,则会评估下一个路由。 HandleUnknownAction 仅适用于未找到的操作。如果路由匹配但找不到结果控制器怎么办?最好的处理方法是什么? 是的,这是正确的,只有在未找到操作时。您可以尝试将这两种解决方案结合起来。 HandleUnknownAction 用于错过的操作和路线用于错过的控制器。其他可能的解决方案是自定义 RouteHandler。 嗯,RouteHandler 超出范围了。抱歉,您将无法使用自定义 ReouteHandler 执行此操作。

以上是关于ASP.NET MVC 404 错误处理 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

自定义 ASP.NET MVC 404 错误页面的路由

ASP.NET MVC 5 - (HTTP 错误 404.0 - 未找到)具有长的不存在的 URL

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

诊断 IIS 7 和 ASP.NET MVC 上的 404 错误

如何为 ASP.NET MVC 处理“未找到 404 页面”查询的全部路由?

Asp.Net MVC 无法获取 .html 文件或 .xml 文件 404 错误?