Ошибка Application_Error, когда customerrors = "On" - PullRequest
123 голосов
/ 28 июня 2011

У меня есть код в Application_Error файле global.asax, который выполняется при возникновении ошибки и отправляет подробности об ошибке по электронной почте.

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 ]

130 голосов
/ 29 июля 2011

UPDATE
Поскольку этот ответ дает решение, я не буду его редактировать, но я нашел более чистый способ решения этой проблемы. Подробнее см. мой другой ответ ...

Оригинальный ответ:
Я понял, почему метод 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 искать представление Error и никогда не вызывает метод Application_Error(). Я не смог найти документацию по этому вопросу, но это объяснено в этом ответе на 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 на ваши URL-адреса ошибок, а затем эти страницы отвечают кодом состояния 200. Это приводит к тому, что Google индексирует страницы ошибок, что плохо. Это также не очень соответствует спецификации 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() для регистрации исключений
  • Использовать customErrors с ResponseRewrite, указывая на страницы ASPX
  • Сделать страницы ASPX ответственными за собственные коды статуса ответов

Есть несколько недостатков, которые я заметил с этим решением.

  • Страницы ASPX не могут делиться разметкой с помощью шаблонов Razor, мне пришлось переписать стандартную разметку заголовка и нижнего колонтитула нашего сайта для согласованного внешнего вида.
  • Доступ к страницам * .aspx можно получить, нажав их URL-адреса

Существуют обходные пути для этих проблем, но они меня не слишком волновали, чтобы выполнять какую-либо дополнительную работу.

Надеюсь, это поможет всем!

68 голосов
/ 26 сентября 2011

Я решил эту проблему, создав ExceptionFilter и записав туда ошибку вместо 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());
}
35 голосов
/ 05 марта 2012

Я нашел статью , в которой описан более понятный способ создания пользовательских страниц ошибок в веб-приложении MVC3, который не препятствует возможности регистрировать исключения.

Решение заключается в использовании элемента <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.

Мне также нужно было регистрировать исключения, поэтому я украл решение Марка об использовании пользовательского 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; это решение фактически использует его!

8 голосов
/ 16 января 2014

В случае 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.

1 голос
/ 12 января 2012

Мне нравится ответ Марка с ExceptionFilter, но другой вариант, если у вас все ваши контроллеры происходят от одного базового контроллера, это просто переопределить OnException в вашем базовом контроллере.Вы можете сделать свою регистрацию и электронную почту там.Преимущество этого заключается в возможности использования любых зависимостей, которые вы уже внедрили в базовый контроллер с вашим контейнером IoC.

Вы все еще можете использовать свой IoC с IExceptionFilter, но это немного сложнее настроитьваши привязки.

0 голосов
/ 23 августа 2016

У меня была такая же проблема, когда Application_Error() не получил удар.Я попробовал все, пока, наконец, я не прошел через то, что происходило.У меня был какой-то пользовательский код в событии ELMAH, который добавлял JSON к отправляемому им электронному письму, и там была нулевая ошибка!

Исправление внутренней ошибки позволило коду перейти к событию Application_Error()как и ожидалось.

0 голосов
/ 08 июня 2012

Эта запись в блоге помогла мне:

http://asp -net.vexedlogic.com / 2011/04/23 / asp-net-максимальная-длина-запроса-превышена /

Если вы используете 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 isопределяется как «слишком длинная длина содержимого».Важно отметить, что maxAllowedContentLength указан в байтах.Это отличается от параметра maxRequestLength, который вы найдете в разделе <system.web>, который указан в килобайтах.

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

Также обратите внимание, что атрибут path должен быть абсолютным путем, когда responseMode is Redirect, поэтому добавьте имя виртуального каталога, если это необходимо.Информативные ответы Джесси Уэбба показывают, как это сделать с responseMode="ExecuteURL", и я думаю, что этот подход тоже будет работать хорошо.

Этот подход не работает, если вы разрабатываете с использованием Visual Studio Development Server (Cassini)Веб-сервер, интегрированный в Visual Studio).Я предполагаю, что это будет работать в IIS Express, но я не проверял это.

0 голосов
/ 29 июля 2011

Чтобы обойти это, я отключил ошибки клиентов и обработал все ошибки из события Application_Error в global.asax.С 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();
    }

}
0 голосов
/ 28 июня 2011

Насколько я знаю, вы передаете управление странице, указанной в параметре url, и ваше уведомление о событии будет находиться здесь, а не Application_Error

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

Здесь можно найти много информации: http://support.microsoft.com/kb/306355

...