【问题标题】:ASP.NET MVC Custom ErrorsASP.NET MVC 自定义错误
【发布时间】:2013-01-10 00:30:07
【问题描述】:

我的目标是在应用程序中创建一个错误处理来处理所有托管错误,而不仅仅是与 MVC 相关的错误。所以我没有使用 HandleErrorAttribute 模式,因为它仅适用于 MVC 错误,而不适用于其他托管错误。

在我的 Global.asax 我有:

protected void Application_Error()
{
    string displayError = ConfigurationManager.AppSettings["DisplayError"];
    NameValueCollection serverVariables;
    int loop1, loop2;
    StringBuilder serverVariableKeys = new StringBuilder();
    Exception exception = Server.GetLastError();
    HttpException httpException = exception as HttpException;

    if (HttpContext.Current != null)
    {
            // loop through and get server vars
    }

    if (exception != null)
        // Log Error

    // Redirect to Error page if set to basic mode
    if (!string.IsNullOrWhiteSpace(displayError) && displayError.ToLower() == "basic")
    {
        Response.Clear();
    Server.ClearError(); // needed for redirect to work

    var routeData = new RouteData();
    routeData.Values["controller"] = "Error";
    routeData.Values["action"] = "General";
    routeData.Values["exception"] = exception;
    Response.StatusCode = 500;

    if (httpException != null)
    {
       Response.StatusCode = httpException.GetHttpCode();
       switch (Response.StatusCode)
        {
        case 403:
          routeData.Values["action"] = "Http403";
          break;
            case 404:
           routeData.Values["action"] = "Http404";
           break;
        }
    }

    IController errorsController = new Controllers.ErrorController();
    var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
    errorsController.Execute(rc);
    }
}

我有一个ErrorController,其中包含GeneralHttp403Http404 操作。我有对应于每个动作的视图。

在我的 web.config 我有:

  <system.web>  
    <!-- removed bits to reduce space -->
    <customErrors mode="On" />
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules >
      <remove name="UrlRoutingModule-4.0" />
      <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
      <remove name="Session"/>
      <add name="Session" type="System.Web.SessionState.SessionStateModule" preCondition=""/>
    </modules>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <httpErrors errorMode="Detailed" />
  </system.webServer>

这适用于与托管代码相关的许多场景,但是当 IIS 接管时,例如错误的静态文件调用 (example.com/BadFileName.gif),它将从 IIS 返回一个错误,该错误会泄露服务器详细信息,例如本地文件路径.这是因为 web.config 中的以下行:

<httpErrors errorMode="Detailed" />

但是,当我将 errorMode 设置为其他值(例如自定义)或仅删除该行时,我的自定义错误处理将停止运行,并且会为包括托管代码错误在内的所有内容返回默认的 IIS 错误消息。

我已经尝试了很多方法来让自定义错误在没有此设置的情况下正常工作,但没有任何运气,包括:

  • CustomError 设置为mode="Off"
  • customError 下定义错误节点以重定向到我的控制器
  • httpErrors 下定义错误节点以重定向到我的控制器

当我逐步调试时,我可以看到对控制器的调用,但最终 IIS 发送了响应。错误日志确实被正确调用 Application_Error,因此代码确实让应用程序将其处理到某个点。

申请详情:

  • ASP.NET MVC 4.0
  • 托管在 IIS 7.5(Azure 网站预览版)上

有没有办法不使用&lt;httpErrors errorMode="Detailed" /&gt; 来完成这项工作?

【问题讨论】:

  • 处理所有托管错误 如果 Razer 引发异常怎么办(配置错误、缺少程序集等)。您希望呈现错误页面的内容是什么?
  • 如果 Razor 出现错误,则默认为 IIS 错误。这是一个有效的观点,因为您可能会遇到运行时错误,但我的错误页面保持简单,大约 3 行 Razor 代码。
  • 顺便说一句,你考虑过Elmah吗? (related blogpost)
  • @Jeroen 我做了,但我有自己的日志记录代码,想捕获更多细节。

标签: c# asp.net asp.net-mvc asp.net-mvc-4


【解决方案1】:

您看到该行为的原因可能是您的 Application_Error 处理程序被正确调用(并调用您的控制器),但随后 IIS 正在拦截 500 错误并显示其自己的错误页面。 (尽管您的 Application_Error 处理程序本身也有可能抛出 Exception,但您需要排除这种情况)

因为您想要处理所有错误(例如您提到的静态文件错误),您可能无法依赖 ASP.NET 或 ASP.NET MVC 为您进行初始错误检测。 并非所有错误都来自您的应用程序!

最好的办法是尽可能多地依赖 IIS 7.5 的错误处理,而不是过早地尝试自己参与其中。让 IIS 为您做最初的错误检测,这会容易得多。

尝试将以下内容放入您的 web.config,替换您现有的 httpErrors 节点:-

<httpErrors errorMode="Custom" existingResponse="Replace">
  <clear />
  <error statusCode="400" responseMode="ExecuteURL" path="/Error" />
  <error statusCode="403" responseMode="ExecuteURL" path="/Error/Forbidden" />
  <error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound" />
  <error statusCode="500" responseMode="ExecuteURL" path="/Error" />
</httpErrors>

完全删除customErrors 节点。没必要。

记住并确保您的错误控制器设置了正确的状态码:-

public ActionResult Forbidden()
{
    Response.StatusCode = 403;
    return this.View();
}

然后,您应该能够放弃除注销您的 Application_Error 处理程序之外的所有内容 - 尽管我认为您最好创建自己的 HttpModule;在web.config 中的模块节点上设置runAllManagedModulesForAllRequests 可以帮助您捕获否则会漏掉的错误。

我已经在 Azure 网站预览版上运行了这个设置(尽管我个人使用 elmah 来进行日志记录),它可以捕获所有内容,除了显然无法解析 web.config 导致的任何错误。

我已经输入了full working example of using httpErrors for custom error pages up on github。你也可以see it live on azure web sites

【讨论】:

  • 关键似乎是我缺少的 existingResponse="Replace" 属性。很棒的建议,我唯一要反对的是 runAllManagedModulesForAllRequests="true" 建议。我读过该设置会损害性能并产生一些错误。建议改为设置 preCondition="" - 缺点是您必须引用每个模块。
  • existingResponse="Replace" 拯救了这一天!
  • existingResponse="Replace" 将强制 IIS 替换错误响应,即使 Response.TrySkipIisCustomErrors 设置为 true。从 AJAX 请求返回 JSON 时,通常使用TrySkipIisCustomErrors = true...否则,IIS 将用它自己的响应替换 JSON 响应。参考:Response.TrySkipIisCustomErrors not working
  • 问题--当你使用IIS拦截错误时,有没有地方可以记录错误?我发现当我使用这种方法时,我的 asp.net Application_Error 方法永远不会被调用,并且在我的错误控制器中“Server.GetLastError”为空。
  • 未来用户注意事项:使用 http 错误时,路径中不要有波浪号。它行不通。例如。 /Error/NotFound 将查看与您的 web.config 文件相关的错误控制器和操作。 ~/Error/NotFound 将不起作用。
【解决方案2】:

如果您想在您的应用程序中处理此问题,您可以编写自定义处理程序或使用 HandleError 属性 here 是解决此问题的一个很好的示例,而不必使用 IIS 作为拐杖。因为并非所有 IIS 主机提供商都允许在服务器级别进行这种类型的自定义,而 IMO 是一种不好的方法,因为它会在部署时创建额外的配置。

【讨论】:

    猜你喜欢
    • 2014-06-27
    • 1970-01-01
    • 2010-11-13
    • 2010-12-24
    • 2018-12-03
    • 1970-01-01
    • 2019-01-22
    • 2014-06-07
    相关资源
    最近更新 更多