当customerrors =“ On”时,Application_Error不触发


124

我在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事件不会触发。

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

谁能建议Application_ErrorcustomErrors启用时为什么不触发web.config


我现在正在遇到一模一样的问题。我还发现了一个SO问题:stackoverflow.com/questions/3713939/…,建议将IIS7服务器置于经典模式。不幸的是,这不是我们的选择。有人有更好的解决方案吗?
杰西·韦伯

这里是另一个相关的问题,它的答案(均未接受),建议不要在所有...使用的Application_Error()stackoverflow.com/questions/1194578/...
杰西·韦伯

@Gweebz我已经发布了有关如何解决此问题的答案,但是我仍然找不到关于为什么得到这种行为的任何可靠文档。
WDuffy 2011年

我添加了一个答案,解释了为什么Application_Error()未调用该方法。我还解释了我的最终解决方案。
杰西·韦伯

真的推荐这篇文章,以使自定义错误起作用。
ThomasArdal '16

Answers:


133

更新
由于此答案确实提供了解决方案,因此我将不对其进行编辑,但是我找到了解决此问题的更简洁的方法。查看我的其他答案以获取详细信息...

原始答案:
我想出了为什么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()方法。我找不到有关此操作的文档,但对此内容的解释是在developers.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状态代码进行响应。这导致Google将错误页面编入索引,这是很糟糕的。它也不十分符合HTTP规范。我要做的是不重定向,而是使用我的自定义错误视图覆盖原始响应。

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

我决定放弃使用MVC进行错误处理。我创建了Error.aspxPageNotFound.aspx。这些页面非常简单,但是却具有魔力……

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

该块告诉页面要提供正确的状态代码。粗略地说,我HttpStatusCode.NotFound改用PageNotFound.aspx页。我将web.config更改为如下所示...

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

一切都很好!

摘要:

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

我注意到此解决方案有两个缺点。

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

有解决这些问题的方法,但是我对它们的担心并不足够,无法做任何额外的工作。

希望对大家有帮助!


2
+1!确实是很好的解决方法,但是,将MVC与ASPX页面混合会产生什么后果?
迭戈

2
我们在PROD中已经有几个月了,但是我还没有发现负面影响。唯一的“难题”是我们必须更改CI并部署以执行AspCompile MSBUILD任务,因为MVC不需要它,但是当我们添加.as文件时,他们需要它。可能还有其他问题,但是还没有浮现出来……
Jesse Webb

我一直在尝试在MVC4中使用这种方法,并且显然只有当我获得时,它才对我有用<customErrors mode="Off" />。有filters.Add(new HandleErrorAttribute());删除或不具有任何影响。
2014年

在aspx页面上,您可以添加一个ajax调用来调用您要显示的视图。节省了重复代码的
Mike

71

我通过创建ExceptionFilter并在其中记录错误而不是在Application_Error中解决了此问题。您需要做的就是在RegisterGlobalFilters中添加一个对的调用

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

6
+1如果此答案有效,在我看来,这肯定是处理错误的最优雅且最有可能的方式(通过MVC框架)。
马特·哈姆史密斯,2012年

2
这对我来说非常有效,并且比这里接受的答案更整洁。真好!
亚历克斯·沃伦

3
这将解决记录异常的问题。您如何将其与显示自定义错误页面结合在一起?
杰西·韦伯

3
我试过了,除了404s以外,它都很好用。对于404,它不会显示Errors.cshtml视图,只会给我一个YSoD。如果不需要自定义404,则此解决方案绝对更干净!
杰西·韦伯

1
我喜欢。作品与“的customErrors =开”
伊利丹

36

我发现了一篇文章,描述了在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>

我还配置customErrorsmode="Off"(如文章所建议)。

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

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

    public ActionResult NotFound()
    {
        return View();
    }
}

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

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

我还需要记录异常,因此偷了Mark使用自定义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框架作斗争。该解决方案实际上利用了它!


6
值得一提的是,我认为该解决方案仅在IIS 7和更高版本中有效。httpErrors元素是最近才添加的。
杰西·韦伯

这个答案对我不起作用,它对我有帮助,它使我陷入了可怕的蓝色:HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. 错误屏幕,而不是我请求的/Error/Index页面
Serj Sagan

@SerjSagan我刚刚在一个新项目中对此进行了测试,以测试步骤,并且效果很好。您使用的是IIS,IIS Express还是VS Dev Server?这在VS Dev Server中不起作用。当我将项目设置为使用VS Dev Server而不是IIS时,我注意到黄屏错误而不是自定义错误页面。
杰西·韦伯

1
@SerjSagan听起来像是出现IIS蓝屏错误,而不是经典的黄屏错误。这使我假设您正在使用某种形式的IIS。如果是这样,请通读我在第一句中链接到的文章,特别是最后一节标题为“很少注意”的文章。它说:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
Jesse Webb

3
@SerjSagan将errorMode更改为DetailsLocalOnly或Custom将显示您的页面。
Daniel P

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

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


1

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

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


0

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

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

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


谢谢克里斯,我阅读了文档,它建议在发生未处理的错误时(我期望)将调用Application_Error,不应将不调用Server.ClearError()的web.config customErrors部分作为最终处理点。但是,启用customErrors阻止了Application_Error的触发。
WDuffy 2011年

您链接到的文档正在谈论ASP.NET应用程序,并且看来MVC3 Web应用程序的行为有所不同。
Jesse Webb

0

为了解决这个问题,我最终禁用了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/Http{0}", 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 = ###;上面的几行显示了处的内置MVC错误页面C:\inetpub\custerr\en-US。我也不喜欢从Application_Error()方法中手动调用HttpHandlers或Controllers的想法。我很高兴您找到了解决问题的方法,但我知道这给我带来了什么麻烦。
杰西·韦伯

0

这篇博客文章帮助我:

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以字节为单位指定。这与maxRequestLength在本<system.web>节中找到的设置不同(以千字节为单位指定)。

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

另请注意,pathresponseModeis Redirect为时,该属性必须是绝对路径,因此在虚拟目录名之前(如果相关)。杰西·韦伯(Jesse Webb)的信息丰富的答案说明了如何使用进行此操作responseMode="ExecuteURL",我认为这种方法也很好。

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


0

我遇到了同样的问题,Application_Error()没有受到打击。我尝试了一切,直到最终我逐步完成了所发生的一切。我在ELMAH事件中有一些自定义代码,该事件将JSON添加到发送的电子邮件中,并且那里存在空错误!

修复内部错误后,代码可以Application_Error()按预期继续处理事件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.