当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:一个HandleErrorAttribute
filter。 当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>
另外请注意,当responseMode
是Redirect
, path
属性必须是绝对path,所以如果相关,则预先安装虚拟目录名称。 Jesse Webb的内容丰富的答案显示了如何使用responseMode="ExecuteURL"
来做到这一点,我想这种方法也可以。
如果您使用Visual Studio开发服务器(Cassini,集成到Visual Studio中的Web服务器)进行开发,则此方法不起作用。 我假设它将在IIS Express中工作,但我没有testing过。
我遇到了同样的问题, Application_Error()
没有被击中。 我尝试了所有的东西,直到最后我完成了所发生的事情。 我在ELMAH事件中添加了一些自定义代码,它将JSON添加到它发送的电子邮件中,并且在那里有一个空错误!
修复内部错误使得代码可以像预期的那样继续执行Application_Error()
事件。