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

Posted

技术标签:

【中文标题】当 customerrors = "On" 时,Application_Error 未触发【英文标题】:Application_Error not firing when customerrors = "On" 【发布时间】:2011-09-24 09:42:04 【问题描述】:

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

void Application_Error(object sender, EventArgs e)

    var error = Server.GetLastError();

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


当我在 Visual Studio 中运行它时效果很好,但是当我发布到我们的实时服务器时,Application_Error 事件不会触发。

经过一些测试,当我设置customErrors="Off" 时,我可以触发Application_Error,但是将其设置回customErrors="On" 会阻止事件再次触发。

谁能说明为什么在web.config 中启用customErrorsApplication_Error 不会被触发?

【问题讨论】:

我遇到了同样的问题。我还发现了这个 SO 问题:***.com/questions/3713939/…,它建议将 IIS7 服务器置于经典模式。不幸的是,这不是我们的选择。谁有更好的解决方案? 这是另一个相关问题,它的答案(没有一个被接受)建议不要使用 Application_Error() ...***.com/questions/1194578/… @Gweebz 我已经发布了一个关于我如何解决这个问题的答案,但我仍然没有找到任何关于我为什么会出现这种行为的可靠文档。 我添加了一个答案,解释了为什么没有调用 Application_Error() 方法。我还解释了我的最终解决方案。 真的推荐this article 以使自定义错误正常工作。 【参考方案1】:

更新 由于此答案确实提供了解决方案,因此我不会对其进行编辑,但是我找到了一种更简洁的方法来解决此问题。详情见my other answer...

原答案: 我知道为什么没有调用 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
    
...

默认情况下(生成新项目时),MVC 应用程序在Global.asax.cs 文件中有一些逻辑。此逻辑用于映射路由和注册过滤器。默认情况下,它只注册一个过滤器:HandleErrorAttribute 过滤器。当 customErrors 开启时(或者通过远程请求,当它设置为 RemoteOnly 时),HandleErrorAttribute 告诉 MVC 寻找错误视图并且它从不调用 Application_Error() 方法。我找不到这方面的文档,但在 this answer on 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>

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

我尝试更改redirectMode="ResponseRewrite"。不幸的是,this option does not support MVC routes,只有静态 html 页面或 ASPX。我一开始尝试使用静态 HTML 页面,但响应代码仍然是 200,但至少它没有重定向。然后我从this answer那里得到了一个想法……

我决定放弃 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>

这个块告诉页面提供正确的状态代码。当然,在 PageNotFound.aspx 页面上,我改用了HttpStatusCode.NotFound。我将 web.config 更改为如下所示...

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>

一切都很完美!

总结:

删除线:filters.Add(new HandleErrorAttribute()); 使用Application_Error()方法记录异常 使用带有 ResponseRewrite 的 customErrors,指向 ASPX 页面 让 ASPX 页面负责自己的响应状态代码

我注意到这个解决方案有几个缺点。

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

这些问题有一些变通方法,但我对他们没有足够的关注来做任何额外的工作。

希望对大家有帮助!

【讨论】:

+1!非常好的解决方法,但是,将 MVC 与 aspx 页面混合可能会产生什么后果? 我们在 PROD 中已经有几个月的时间了,我还没有发现负面影响。唯一的“问题”是我们必须更改 CI 并部署以执行 AspCompile MSBUILD 任务,因为 MVC 不需要它,但是当我们添加 .as 文件时,它们需要它。可能还有其他问题,但尚未浮出水面…… 我一直在尝试在 MVC4 中使用这种方法,显然只有当我得到&lt;customErrors mode="Off" /&gt; 时它才对我有效。是否删除filters.Add(new HandleErrorAttribute()); 无效。 在aspx页面上你可以添加一个ajax调用来调用你想要显示的视图。无需复制代码【参考方案2】:

我通过创建一个 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
        
    

Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)

    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());

【讨论】:

这对我来说非常有效,并且比这里接受的答案要整洁得多。不错! 这将用于解决记录异常的问题。您是如何将其与显示自定义错误页面结合起来的? 我试过了,除了 404 之外,它运行良好。对于 404,它不会显示 Errors.cshtml 视图,它只会给我一个 YSoD。如果不需要自定义 404,这个解决方案肯定更干净! 我喜欢。适用于“CustomErrors=On”【参考方案3】:

我找到了一个article,它描述了一种在 MVC3 Web 应用程序中创建自定义错误页面的更简洁的方法,它不会阻止记录异常的能力。

解决方案是使用&lt;system.webServer&gt; 部分的&lt;httpErrors&gt; 元素。

我像这样配置了我的 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>

我还将customErrors 配置为mode="Off"(如文章所建议的那样)。

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

public class ErrorController : Controller

    public ActionResult Index()
    
        return View();
    

    public ActionResult NotFound()
    
        return View();
    

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

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

我还需要记录异常,所以我偷了 Mark's solution 使用自定义 ExceptionFilter...

public class ExceptionPublisherExceptionFilter : IExceptionFilter

    public void OnException(ExceptionContext exceptionContext)
    
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    

您需要做的最后一件事是在您的 Global.asax.cs 文件中注册异常过滤器:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)

    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());

这感觉是比我之前的答案更清洁的解决方案,据我所知,它的效果也很好。我特别喜欢它,因为它感觉不像是在与 MVC 框架作斗争;这个解决方案实际上利用了它!

【讨论】:

值得一提的是,我认为这个解决方案只适用于 IIS 7 及更新版本; httpErrors 元素是最近才添加的。 这个答案对我不起作用,它有我,它让我感到可怕的蓝色:HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. 错误屏幕,不是我请求的/Error/Index 页面 @SerjSagan 我刚刚在一个新项目中尝试了这个来测试这些步骤,它工作正常。您使用的是 IIS、IIS Express 还是 VS Dev Server?这在 VS 开发服务器中不起作用。当我将项目设置为使用 VS Dev Server 而不是 IIS 时,我注意到黄屏错误而不是自定义错误页面。 @SerjSagan 但听起来您遇到的是 IIS 蓝屏错误,而不是经典的黄屏错误。这让我假设您正在使用某种形式的 IIS。如果是这样,请阅读我在第一句话中链接到的文章,特别是最后一节标题为:“几个重要说明”。它说:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: &lt;section name="httpErrors" overrideModeDefault="Allow" /&gt; @SerjSagan 将 errorMode 更改为DetailedLocalOnly 或Custom 将显示您的页面。【参考方案4】:

如果是 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);
        
    

您可以在 App_Start 文件夹的 FilterConfig.cs 中找到它。

【讨论】:

【参考方案5】:

我喜欢 Mark 对 ExceptionFilter 的回答,但如果您的所有控制器都派生自同一个基本控制器,则另一种选择是简单地覆盖基本控制器中的 OnException。您可以在那里进行日志记录和发送电子邮件。这样做的好处是能够使用您已经通过 IoC 容器注入到基本控制器中的任何依赖项。

您仍然可以将 IoC 与 IExceptionFilter 一起使用,但配置绑定有点棘手。

【讨论】:

【参考方案6】:

据我所知,您正在将控制权传递给 url 参数中指定的页面,并且您的事件通知将位于此处,而不是 Application_Error

<customErrors defaultRedirect="myErrorPage.aspx"
              mode="On">
</customErrors>

很多信息可以在这里找到:http://support.microsoft.com/kb/306355

【讨论】:

谢谢 Chris,我阅读了文档,它表明 Application_Error 将在发生未处理的错误(我期望的)时被调用,并且不应将 Server.ClearError() 称为 web.config customErrors 部分将是最终的处理点。但是,启用 customErrors 是阻止 Application_Error 触发的原因。 您链接到的文档是关于 ASP.NET 应用程序的,而且 MVC3 Web 应用程序的行为似乎有所不同。【参考方案7】:

为了解决这个问题,我最终禁用了 customerrors 并处理了 global.asax 中 Application_Error 事件中的所有错误。 MVC 有点棘手,因为我不想返回 301 重定向,我想返回合适的错误代码。更多详细信息可以在我的博客http://www.wduffy.co.uk/blog/using-application_error-in-asp-net-mvcs-global-asax-to-handle-errors/ 上查看,但最终代码在下面列出...

void Application_Error(object sender, EventArgs e)

    var error = Server.GetLastError();
    var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

    if (code != 404)
    
            // Generate email with error details and send to administrator
    

    Response.Clear();
    Server.ClearError();

    string path = Request.Path;
    Context.RewritePath(string.Format("~/Errors/Http0", code), false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(Context);
    Context.RewritePath(path, false);

这里是控制器

public class ErrorsController : Controller


    [HttpGet]
    public ActionResult Http404(string source)
    
            Response.StatusCode = 404;
            return View();
    

    [HttpGet]
    public ActionResult Http500(string source)
    
            Response.StatusCode = 500;
            return View();
    


【讨论】:

我尝试了您的解决方案,但它对我不起作用。使用Response.StatusCode = ###; 行,它在C:\inetpub\custerr\en-US 处显示内置MVC 错误页面。我也不喜欢从我的 Application_Error() 方法手动调用 HttpHandlers 或 Controllers 的想法。我很高兴你找到了解决问题的方法,我知道这让我很头疼。【参考方案8】:

这篇博文帮助了我:

http://asp-net.vexedlogic.com/2011/04/23/asp-net-maximum-request-length-exceeded/

如果您使用的是 IIS 7.0 或更高版本,您可以更改您的 Web.config 文件以处理太大的请求。有一些警告,但这里有一个例子:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="1048576" />
    </requestFiltering>
  </security>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="13" />
    <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="/UploadTooLarge.htm" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

这里有关于这些配置文件元素的其他详细信息:

http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits

状态码 404.13 被定义为“内容长度过大”。需要注意的一件重要事情是maxAllowedContentLength 以字节为单位。这与您在&lt;system.web&gt; 部分中找到的maxRequestLength 设置不同,后者以千字节为单位。

<system.web>
  <httpRuntime maxRequestLength="10240" />
</system.web>

另请注意,当responseModeRedirect 时,path 属性必须是绝对路径,因此如果相关,请添加虚拟目录名称。 Jesse Webb 的信息丰富的答案展示了如何使用 responseMode="ExecuteURL" 来做到这一点,我认为这种方法也很有效。

如果您使用 Visual Studio 开发服务器(Cassini,集成到 Visual Studio 中的 Web 服务器)进行开发,则此方法不起作用。我假设它可以在 IIS Express 中工作,但我还没有测试过。

【讨论】:

【参考方案9】:

我遇到了同样的问题,Application_Error() 没有被击中。我尝试了一切,直到最后我经历了正在发生的事情。我在一个 ELMAH 事件中有一些自定义代码,将 JSON 添加到它发送的电子邮件中,其中有一个 null 错误!

修复内部错误允许代码按预期继续处理Application_Error() 事件。

【讨论】:

以上是关于当 customerrors = "On" 时,Application_Error 未触发的主要内容,如果未能解决你的问题,请参考以下文章

设置 redirectMode="ResponseRewrite" 时,CustomErrors 不起作用

网络问题,伺服务器错误

.net 网页程序怎么设置400页面

无法在网络调用中返回可观察到的 customError

IIS7在设置Response.StatusCode时覆盖customErrors?

C# 自定义异常的总结