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

时间:2011-06-28 14:50:40

标签: asp.net-mvc-3 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事件不会触发。

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

Application_Error启用customErrors后,有人可以建议为什么web.config不会被解雇?

9 个答案:

答案 0 :(得分:130)

<强>更新
由于这个答案确实提供了解决方案,我不会编辑它,但我找到了一种更清晰的方法来解决这个问题。有关详细信息,请参阅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重定向,然后这些网页会以200状态代码进行响应。这导致谷歌索引错误页面,这是不好的。它也不符合HTTP规范。我想做的不是重定向,而是使用我的自定义错误视图覆盖原始响应。

我尝试更改redirectMode="ResponseRewrite"。不幸的是,this option does not support MVC routes,只有静态HTML页面或ASPX。我最初尝试使用静态HTML页面,但响应代码仍然是200但是,至少它没有重定向。然后我从this answer ...

得到了一个想法

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

此块告诉页面使用正确的状态代码。粗略的,在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()方法记录例外
  • 将customErrors与ResponseRewrite一起使用,指向ASPX页面
  • 让ASPX页面负责自己的响应状态代码

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

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

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

我希望这对每个人都有帮助!

答案 1 :(得分:67)

我通过创建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());
}

答案 2 :(得分:35)

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

解决方案是使用<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"(正如文章所建议的那样)。

这使得响应被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框架作斗争;这个解决方案实际上利用了它!

答案 3 :(得分: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.cs文件夹App_Start中找到它。

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

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

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

可在此处找到大量信息:http://support.microsoft.com/kb/306355

答案 6 :(得分: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();
    }

}

答案 7 :(得分: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>

另请注意,当pathresponseMode时,Redirect属性必须是绝对路径,因此,如果相关,则添加虚拟目录名称。 Jesse Webb的内容丰富的答案显示了如何使用responseMode="ExecuteURL"来做到这一点,我认为这种方法也能很好地运作。

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

答案 8 :(得分:0)

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

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