21

我的目标是在应用程序中创建一个错误处理来处理所有托管错误,而不仅仅是与 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);
    }
}

我有一个that ErrorControllerhasGeneral和Actions。我有对应于每个动作的视图。Http403Http404

在我的 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 错误消息。

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

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

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

申请详情:

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

有没有办法不使用<httpErrors errorMode="Detailed" />并使这项工作?

4

2 回答 2

36

您看到该行为的原因可能是您的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; runAllManagedModulesForAllRequests在你的 modules 节点上设置web.config可以帮助你捕获否则会漏掉的错误。

我已经在 Azure 网站预览版上运行了这个设置(虽然我个人使用 elmah 来进行日志记录),并且它可以捕获所有内容,除了明显来自于 failed to parse 的任何错误web.config

我已经在 github 上放了一个使用 httpErrors 自定义错误页面的完整示例。您还可以在 azure 网站上实时看到它

于 2013-01-29T08:41:01.640 回答
0

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

于 2013-01-30T09:28:31.027 回答