126

我在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时不会触发?customErrorsweb.config

4

9 回答 9

135

更新
由于此答案确实提供了解决方案,因此我不会对其进行编辑,但是我找到了一种解决此问题的更简洁的方法。有关详细信息,请参阅我的其他答案...

原始答案:
我弄清楚为什么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 过滤器的行。

现在的问题是:如何配置 customErrors 以获得你想要的......

customErrors 部分默认为redirectMode="ResponseRedirect". 您也可以将 defaultRedirect 属性指定为 MVC 路由。我创建了一个非常简单的 ErrorController 并将我的 web.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()方法记录异常
  • 使用带有 ResponseRewrite 的 customErrors,指向 ASPX 页面
  • 让 ASPX 页面对自己的响应状态代码负责

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

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

这些问题有一些变通方法,但我对他们没有足够的关注来做任何额外的工作。

我希望这对大家有帮助!

于 2011-07-29T15:06:26.927 回答
75

我通过创建一个 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());
}
于 2011-09-26T06:18:13.630 回答
37

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

我还配置customErrors为拥有mode="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 框架作斗争;这个解决方案实际上利用了它!

于 2012-03-05T19:41:38.830 回答
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

于 2014-01-16T18:06:40.743 回答
1

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

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

于 2012-01-11T20:32:56.730 回答
0

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

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

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

于 2011-06-28T14:54:11.397 回答
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();
    }

}
于 2011-07-29T10:18:26.007 回答
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>

另请注意,当is时,该path属性必须是绝对路径,因此如果相关,请在前面加上虚拟目录名称。Jesse Webb 的信息丰富的答案展示了如何使用.responseModeRedirectresponseMode="ExecuteURL"

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

于 2012-06-08T18:05:17.280 回答
0

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

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

于 2016-08-23T04:52:23.607 回答