【问题标题】:Handling all exceptions within global.asax处理 global.asax 中的所有异常
【发布时间】:2012-05-20 16:17:03
【问题描述】:

我正在尝试为 MVC 3 项目处理 global.asax 中的所有应用程序异常,虽然在 Cassini 中一切正常,但一旦我部署到 IIS 7.5,IIS 就开始从我的应用程序中控制并处理许多异常本身。这会导致绕过我的自定义日志记录并返回丑陋的视图。

我对 Darin 的 answerthis question 有类似的方法。这是我目前正在使用的。

protected void Application_Error(object sender, EventArgs e)
{
    var app = (MvcApplication)sender;
    var context = app.Context;
    var exception = app.Server.GetLastError();

    LogExceptionDetails(exception, Request.Url.PathAndQuery);

    context.Response.Clear();
    context.ClearError();

    string redirectTo = "/error";
    HttpException httpException = exception as HttpException;

    if (httpException != null)
    {
        switch (httpException.GetHttpCode())
        {
            case 403:
                redirectTo += "/forbidden";
                break;
            case 404:
                redirectTo += "/notfound";
                break;
        }
    }

    Response.TrySkipIisCustomErrors = true;

    // I should really change this so I can return a proper statusCode
    Response.Redirect(redirectTo);
}

例如,导航到localhost/app_code 将返回一个丑陋的视图并且不会被记录。我已经设法至少让 IIS 使用以下方法返回我的自定义视图:

<httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="403" />
    <error statusCode="403" path="/error/forbidden" responseMode="ExecuteURL" />
    <remove statusCode="404" />
    <error statusCode="404" path="/error/notfound" responseMode="ExecuteURL" />
</httpErrors>

但这并不能解决日志记录问题。

我尝试过的其他事情包括:

  1. 设置existingResponse="PassThrough"
  2. 使用&lt;clear /&gt;
  3. httpErrors 有和没有customErrors 的各种组合。
  4. 设置&lt;modules runAllManagedModulesForAllRequests="true" /&gt;
  5. Response.TrySkipIisCustomErrors = true;

有没有办法以编程方式处理这个问题,同时将事情集中在 global.asax 中,而不是搞乱 web.config?

【问题讨论】:

  • 您有什么理由不在控制器上使用[HandlesError] 并在那里进行操作? Application_Error 应该是万不得已的后备方案...
  • @bhamlin 几乎我所见过的关于 MVC 3 异常处理的每个示例都使用了Application_Error 和/或ErrorController。话虽如此,我实际上删除了过滤器,因为它正在吞噬远程异常,这使得调试更加困难,但那是在以这种方式实现之后。
  • @bhamlin,HandlesError 只处理发生在控制器动作中的错误,而不是当它例如找不到控制器时。

标签: asp.net-mvc-3 c#-4.0 exception-handling iis-7.5


【解决方案1】:

是的。这就是我正在做的事情,但不幸的是它并不完美。

首先,关闭自定义错误。

<customErrors mode="Off" />

接下来,将 HttpErrors 更改为详细。请注意,这是我不太喜欢的部分,主要是因为如果您这样做,似乎您可能会使堆栈跟踪可访问。我认为只要您在错误处理中处理所有状态代码,通过使用全部捕获,您应该没问题。如果我错了,请纠正我。

<httpErrors errorMode="Detailed" />

您还需要在 global.asax 中使用 catch all route 来捕获所有与您定义的路由不匹配的 MVC 路由,并将它们发送到您的 404。这可能会根据您的路由设置而产生问题,主要是,如果您依靠一条全部路线来处理您当前的非 404 路线。我使用反射来根据控制器中的 Action 方法定义所有路由,因此我不依赖于应用程序路由的全部捕获模式。

最后,处理 global.asax 中的错误。使用 catch all(例如 500)并针对您想要的任何不同执行特殊路由,例如 404 错误。

protected void Application_Error(object sender, EventArgs e)
{
    var ex = Server.GetLastError().GetBaseException();

    Server.ClearError();

    var routeData = new RouteData();
    routeData.Values.Add("controller", "Error");
    routeData.Values.Add("action", "500");

    if (ex.GetType() == typeof (HttpException))
    {
        var httpException = (HttpException) ex;
        var code = httpException.GetHttpCode();

        // Is it a 4xx Error
        if (code % 400 < 100)
        {
            routeData.Values["action"] = "404";
        }
    }

    IController errorController = new ErrorController();
    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}

注意,在我的示例中,我将除 4xx 错误之外的所有错误都视为 500。此外,在此示例中,我使用了一个名为“ErrorController”的控制器和两个名为“500”和“404”的操作。

我希望这会有所帮助,如果您找到需要将 HttpErrors 设置为“详细”的解决方法,请分享!

【讨论】:

  • Server.ClearError(); 这一行似乎很重要。没有这个它在 MVC 4 中不起作用
猜你喜欢
  • 2010-10-09
  • 2019-02-05
  • 2012-09-17
  • 1970-01-01
  • 2014-05-11
  • 2011-03-18
  • 1970-01-01
  • 1970-01-01
  • 2016-02-11
相关资源
最近更新 更多