当customerrors ="On"时,Application_Error未触发

WDu*_*ffy 123 application-error asp.net-mvc-3

我在global.asax文件的Application_Error事件中有代码,它在发生错误时执行,并将错误的详细信息发送给我自己.

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

    if (error.Message != "Not Found")
    {
        // Send email here...
    }

}
Run Code Online (Sandbox Code Playgroud)

我在Visual Studio中运行它时工作正常,但是当我发布到我们的实时服务器时,Application_Error事件不会触发.

经过一些测试后,我可以Application_Error在设置时进行射击customErrors="Off",但是将其设置回来可以customErrors="On"阻止事件再次发射.

任何人都可以建议为什么Application_ErrorcustomErrors启用时不会被解雇web.config

Jes*_*ebb 132

更新
由于这个答案确实提供了解决方案,我不会编辑它,但我找到了一种更清晰的方法来解决这个问题.请参阅我的其他答案了解详情......

原始答案:
我弄清楚为什么Application_Error()没有调用该方法...

的Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // this line is the culprit
    }
...
}
Run Code Online (Sandbox Code Playgroud)

默认情况下(生成新项目时),MVC应用程序在Global.asax.cs文件中有一些逻辑.该逻辑用于映射路由和注册过滤器.默认情况下,它只注册一个过滤器:HandleErrorAttribute过滤器.当customErrors打开时(或当设置为RemoteOnly时通过远程请求),HandleErrorAttribute告诉MVC查找错误视图,它从不调用该Application_Error()方法.我找不到这方面的文档,但在programmers.stackexchange.com上的答案对此进行了解释.

要获取为每个未处理的异常调用的ApplicationError()方法,请简单地删除注册HandleErrorAttribute过滤器的行.

现在的问题是:如何配置customErrors以获得您想要的...

customErrors部分默认为redirectMode="ResponseRedirect".您也可以将defaultRedirect属性指定为MVC路由.我创建了一个非常简单的ErrorController,并将我的web.config更改为这样......

web.config中

<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
  <error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>
Run Code Online (Sandbox Code Playgroud)

此解决方案的问题在于它会对您的错误URL进行302重定向,然后这些页面以200状态代码进行响应.这导致谷歌索引错误页面,这是不好的.它也不符合HTTP规范.我想要做的不是重定向,而是使用我的自定义错误视图覆盖原始响应.

我试图改变redirectMode="ResponseRewrite".不幸的是,此选项不支持MVC路由,仅支持静态HTML页面或ASPX.我最初尝试使用静态HTML页面,但响应代码仍然是200但是,至少它没有重定向.然后我从这个答案中得到了一个想法......

我决定放弃MVC进行错误处理.我创造了一个Error.aspx和一个PageNotFound.aspx.这些页面非常简单,但它们有一块魔法......

<script type="text/C#" runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
    }
</script>
Run Code Online (Sandbox Code Playgroud)

此块告诉页面使用正确的状态代码.粗略的,在PageNotFound.aspx页面上,我使用了HttpStatusCode.NotFound.我改变了我的web.config看起来像这样......

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>
Run Code Online (Sandbox Code Playgroud)

这一切都很完美!

摘要:

  • 删除行: filters.Add(new HandleErrorAttribute());
  • 使用Application_Error()方法记录异常
  • 将customErrors与ResponseRewrite一起使用,指向ASPX页面
  • 使ASPX页面负责自己的响应状态代码

我已经注意到这个解决方案有几个缺点.

  • ASPX页面不能与Razor模板共享任何标记,我不得不重写我们网站的标准页眉和页脚标记,以获得一致的外观和感觉.
  • *.aspx页面可以通过点击其URL直接访问

这些问题有解决办法,但我并不关心他们做任何额外的工作.

我希望这对大家有所帮助!

  • +1!确实很好的解决方法但是,将MVC与aspx页面混合的后果可能是什么? (2认同)
  • 我们已经在PROD中使用了几个月,而且我没有发现并产生负面影响.唯一的"问题"是我们必须更改我们的CI并部署以执行AspCompile MSBUILD任务,因为MVC不需要它,但是当我们添加*.as*文件时,他们需要它.可能还有其他问题但尚未浮出水面...... (2认同)

小智 69

我通过创建ExceptionFilter并在那里记录错误而不是Application_Error来解决这个问题.您需要做的就是在RegisterGlobalFilters中添加对in的调用

log4netExceptionFilter.cs

using System
using System.Web.Mvc;

public class log4netExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        if (!(ex is HttpException)) //ignore "file not found"
        {
            //Log error here
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

的Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());
}
Run Code Online (Sandbox Code Playgroud)

  • +1如果这个答案有效,那么在我看来,它确实是最优雅的,也许是预期的方式(通过MVC框架)来处理错误. (6认同)
  • 这将有助于解决记录异常的问题.你是如何将它与显示自定义错误页面结合起来的? (3认同)
  • 我试过这个,除了在404s的情况下,它工作得很好.对于404s,它不会显示Errors.cshtml视图,它只会给我一个YSoD.如果不需要自定义404,这个解决方案绝对更清洁! (3认同)
  • 这对我来说非常合适,并且比这里接受的答案更加整洁.太好了! (2认同)

Jes*_*ebb 35

我发现了一篇文章,它描述了一种在MVC3 Web应用程序中制作自定义错误页面的更简洁的方法,它不会阻止记录异常的能力.

解决方案是使用<httpErrors><system.webServer>部分的元素.

我像我这样配置了我的Web.config ......

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>
Run Code Online (Sandbox Code Playgroud)

我也配置customErrorsmode="Off"(正如文章所建议的那样).

这使得响应被ErrorController的操作覆盖.这是控制器:

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
}
Run Code Online (Sandbox Code Playgroud)

这些视图非常简单,我只是使用标准的Razor语法来创建页面.

仅此一项就足以让您在MVC中使用自定义错误页面.

我还需要记录Exceptions,所以我偷了Mark使用自定义ExceptionFilter 的解决方案 ......

public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要做的最后一件事是在Global.asax.cs文件中注册Exception Filter :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());
}
Run Code Online (Sandbox Code Playgroud)

这感觉就像一个比我之前的答案更清晰的解决方案,并且就我所知的情况而言同样有效.我喜欢它,特别是因为我觉得我不喜欢与MVC框架作斗争; 这个解决方案实际上利用了它!

  • 值得一提的是,我认为此解决方案仅适用于IIS 7及更新版本; httpErrors元素最近才被添加. (5认同)
  • @SerjSagan将errorMode更改为DetailedLocalOnly或Custom将显示您的页面. (3认同)

Dev*_*per 8

ASP.NET MVC5的情况下使用

public class ExceptionPublisherExceptionFilter : IExceptionFilter
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        public void OnException(ExceptionContext exceptionContext)
        {
            var exception = exceptionContext.Exception;
            var request = exceptionContext.HttpContext.Request;
            // HttpException httpException = exception as HttpException;
            // Log this exception with your logger
            _logger.Error(exception.Message);
        }
    }
Run Code Online (Sandbox Code Playgroud)

你可以找到它FilterConfig.csApp_Start文件夹中.