1

I'm trying to handle all application exceptions within global.asax for an MVC 3 project and whilst everything is working correctly within Cassini, as soon as I deploy to IIS 7.5, IIS starts taking control away from my application and handles many exceptions itself. This has the consequences of bypassing my custom logging and also returning ugly views.

I have a similar approach to Darin's answer to this question. Here's what I'm using at the moment.

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

As an example, navigating to localhost/app_code will return an ugly view and won't be logged. I have managed to at least make IIS return my custom views by using:

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

That doesn't solve the logging problem though.

Other things I've tried include:

  1. Setting existingResponse="PassThrough".
  2. Using <clear />.
  3. Various combinations of httpErrors with and without customErrors.
  4. Setting <modules runAllManagedModulesForAllRequests="true" />.
  5. Response.TrySkipIisCustomErrors = true;

Is there a way to handle this programmatically, whilst keeping things centralised in global.asax, rather than messing around with the web.config?

4

1 回答 1

1

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

首先,关闭自定义错误。

<customErrors mode="Off" />

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

<httpErrors errorMode="Detailed" />

您还需要catch all route在 global.asax 中捕获所有与您定义的路由不匹配的 MVC 路由,并将它们发送到您的 404。这可能会出现问题,具体取决于您的路由设置,主要是如果您依赖于 catch all route 来处理您当前的非 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 设置为“详细”的解决方法,请分享!

于 2012-08-19T06:46:20.013 回答