我在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"
会阻止事件再次触发。
谁能建议为什么Application_Error
在web.config
中启用customErrors
时不会触发?
UPDATE
由于这个答案确实提供了解决方案,所以我不会编辑它,但我找到了解决这个问题的更干净的方法。有关详细信息,请参阅我的另一个答案...
原答案:
我想出了为什么没有调用Application_Error()
方法......
全球.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()
方法。我找不到这方面的文档,但在 programmers.stackexchange.com 的这个答案中进行了解释。
若要获取为每个未处理的异常调用的 ApplicationError(( 方法,只需删除注册 HandleErrorAttribute 筛选器的行。
现在的问题是:如何配置自定义错误以获得您想要的内容......
自定义错误部分默认为 redirectMode="ResponseRedirect"
。您也可以将默认重定向属性指定为 MVC 路由。我创建了一个非常简单的错误控制器,并将我的 web.config 更改为如下所示......
网络.config
<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
<error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>
此解决方案的问题在于,它会执行 302 重定向到您的错误 URL,然后这些页面以 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>
此块告知页面使用正确的状态代码提供。当然,在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()
方法记录异常 - 将自定义错误与响应重写一起使用,指向 ASPX 页
- 使 ASPX 页负责自己的响应状态代码
我注意到此解决方案有几个缺点。
- ASPX 页面不能与 Razor 模板共享任何标记,我不得不重写我们网站的标准页眉和页脚标记以获得一致的外观和感觉。
- *.aspx页面可以通过点击其URL直接访问
这些问题有解决方法,但我对它们的关注不够多,无法做任何额外的工作。
我通过创建一个 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
}
}
}
全球.asax.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
filters.Add(new HandleErrorAttribute());
}
我找到了一篇文章,其中描述了在 MVC3 Web 应用程序中创建自定义错误页面的更简洁的方法,该方法不会阻止记录异常的能力。
解决方案是使用 <system.webServer>
部分的 <httpErrors>
元素。
我像这样配置了我的 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"
(如文章所建议的那样(。
这使得响应被错误控制器的操作覆盖。这是该控制器:
public class ErrorController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult NotFound()
{
return View();
}
}
视图非常简单,我只是使用标准的 Razor 语法来创建页面。
仅此一项就足以让您在 MVC 中使用自定义错误页面。
我还需要记录异常,所以我偷了 Mark 使用自定义异常过滤器的解决方案......
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 框架作斗争;这个解决方案实际上利用了它!
在 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
中找到它。
为了解决这个问题,我最终禁用了自定义错误,并在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();
}
}
我喜欢 Mark 对 ExceptionFilter 的回答,但如果所有控制器都派生自同一个基本控制器,另一种选择是简单地覆盖基本控制器中的 OnException。 您可以在那里进行日志记录和发送电子邮件。 这样做的好处是,能够将已注入到基本控制器中的任何依赖项与 IoC 容器一起使用。
您仍然可以将 IoC 与 IExceptionFilter 一起使用,但配置绑定会更加棘手。
我所知,您正在将控制权传递给 url 参数中指定的页面,您的事件通知将位于此处,而不是Application_Error
<customErrors defaultRedirect="myErrorPage.aspx"
mode="On">
</customErrors>
很多信息可以在这里找到:http://support.microsoft.com/kb/306355
这篇博文帮助了我:
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
是以字节为单位指定的。 这与您在<system.web>
部分中找到的maxRequestLength
设置不同,后者以 KB 为单位指定。
<system.web>
<httpRuntime maxRequestLength="10240" />
</system.web>
另请注意,当responseMode
Redirect
时,path
属性必须是绝对路径,因此请在虚拟目录名称前面加上相关。 杰西·韦伯(Jesse Webb(内容丰富的回答展示了如何使用responseMode="ExecuteURL"
来做到这一点,我认为这种方法也会奏效。
(Cassini,集成到 Visual Studio 中的 Web 服务器(进行开发,则此方法不起作用。 我假设它可以在IIS Express中工作,但我还没有测试过。
我遇到了同样的问题,Application_Error()
没有被击中。我尝试了一切,直到最后我踏入了正在发生的事情。我在 ELMAH 事件中有一些自定义代码,该代码将 JSON 添加到它发送的电子邮件中,并且那里有一个空错误!
修复内部错误允许代码按预期继续执行Application_Error()
事件。