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

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

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

任何人都可以build议为什么Application_Error不会触发时,在web.config中启用customErrors

UPDATE
由于这个答案确实提供了一个解决scheme,所以我不会编辑它,但是我find了解决这个问题的更简洁的方法。 看到我的其他答案的细节…

原始答案:
我想出了为什么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文件中有一些逻辑。 这个逻辑用于映射路由和注册filter。 默认情况下,它只注册一个filter:一个HandleErrorAttributefilter。 当customErrors打开(或通过远程请求设置为RemoteOnly时),HandleErrorAttribute告诉MVC查找错误视图,并且从不调用Application_Error()方法。 我找不到这方面的文档,但在程序员.stackexchange.com上的答案中解释了这一点 。

要调用每个未处理的exception的ApplicationError()方法,只需简单地删除注册HandleErrorAttributefilter的行。

现在的问题是:如何configurationcustomErrors得到你想要的…

customErrors部分默认为redirectMode="ResponseRedirect" 。 你可以指定defaultRedirect属性也是一个MVC路由。 我创build了一个非常简单的ErrorController,并改变了我的web.config,看起来像这样…

web.config中

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

这个解决scheme的问题是,它将302redirect到您的错误URL,然后这些页面以200状态码进行响应。 这导致谷歌索引的错误页面是不好的。 它也不是非常符合HTTP规范。 我想要做的不是redirect,并用我的自定义错误视图覆盖原始响应。

我试图改变redirectMode="ResponseRewrite" 。 不幸的是, 这个选项不支持MVC路由 ,只有静态的HTML页面或者ASPX。 我首先尝试使用静态HTML页面,但响应代码仍然是200,但至less没有redirect。 然后我从这个答案得到一个想法…

我决定放弃MVC的error handling。 我创build了一个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()方法来loggingexception
  • 使用带有ResponseRewrite的customErrors,指向ASPX页面
  • 使ASPX页面负责自己的响应状态代码

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

  • ASPX页面不能与Razor模板共享任何标记,为了保持一致的外观,我必须重写我们网站的标准页眉和页脚标记。
  • 可以通过点击他们的URL直接访问* .aspx页面

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

我希望这可以帮助大家!

我通过创build一个ExceptionFilter并logging错误而不是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()); } 

我find了一篇文章 ,描述了在MVC3 Web应用程序中制作自定义错误页面的更简洁的方法,该方法不会阻止loggingexception。

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

我configuration我的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> 

我还configuration了customErrors以使mode="Off" (正如文章所build议的那样)。

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

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

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

单靠这一点就足以让你在MVC中使用自定义错误页面。

我还需要loggingexception,所以我偷了马克的解决scheme ,使用自定义的ExceptionFilter …

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

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

 public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new ExceptionPublisherExceptionFilter()); filters.Add(new HandleErrorAttribute()); } 

这感觉就像我以前的答案一样更清洁的解决scheme,就像我可以说的那样。 我喜欢它,特别是因为它不觉得我正在反对MVC框架; 这个解决scheme实际上是利用它!

在使用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中find它。

我喜欢马克的ExceptionFilter的答案,但另一种select,如果你有所有的控制器派生从相同的基本控制器,只是简单地覆盖您的基地控制器的OnException。 你可以做你的日志和电子邮件在那里。 这具有能够使用已经注入到您的IoC容器的基础控制器的任何依赖关系的优势。

你仍然可以用IExceptionFilter来使用你的IoC,但是configuration你的绑定有点棘手。

据我所知,你将控制权交给了url参数中指定的页面,而你的事件通知将在这里,而不是Application_Error

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

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

为了解决这个问题,我最终closures了customerrors,并处理了global.asax中Application_Error事件的所有错误。 MVC稍微有些棘手,因为我不想返回301redirect,我想返回合适的错误代码。 更多细节可以在我的博客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(); } } 

这个博客条目帮助了我:

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> 

这里有关于这些configuration文件元素的更多细节:

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

状态码404.13被定义为“内容长度太大”。 需要注意的一点是, maxAllowedContentLength是以字节为单位指定的。 这与您在<system.web>节中find的maxRequestLength设置不同,后者以千字节为单位指定。

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

另外请注意,当responseModeRedirectpath属性必须是绝对path,所以如果相关,则预先安装虚拟目录名称。 Jesse Webb的内容丰富的答案显示了如何使用responseMode="ExecuteURL"来做到这一点,我想这种方法也可以。

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

我遇到了同样的问题, Application_Error()没有被击中。 我尝试了所有的东西,直到最后我完成了所发生的事情。 我在ELMAH事件中添加了一些自定义代码,它将JSON添加到它发送的电子邮件中,并且在那里有一个空错误!

修复内部错误使得代码可以像预期的那样继续执行Application_Error()事件。