Application_Error не срабатывает, когда customerrors = "On"
У меня есть код в 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
?
Ответы
Ответ 1
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 искать представление об ошибке и никогда не вызывает метод Application_Error()
. Я не мог найти документацию об этом, но это объясняется в этом ответе на programers.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-адреса.
Есть проблемы вокруг этих проблем, но я не был им достаточно озабочен, чтобы выполнить любую дополнительную работу.
Надеюсь, это поможет всем!
Ответ 2
Я решил это, создав 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());
}
Ответ 3
Я нашел article, в котором описывается гораздо более чистый способ создания пользовательских страниц ошибок в веб-приложении 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; это решение действительно использует его!
Ответ 4
В случае 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
.
Ответ 5
Мне нравится ответ Mark с помощью ExceptionFilter, но другой вариант, если у вас есть все ваши контроллеры от одного и того же базового контроллера, - это просто переопределить OnException в вашем базовом контроллере. Вы можете вести регистрацию и отправлять по электронной почте. Это имеет то преимущество, что вы можете использовать любые зависимости, которые вы уже ввели в ваш базовый контроллер, с помощью контейнера IoC.
Вы все еще можете использовать свой IoC с IExceptionFilter, но немного сложнее настроить привязки.
Ответ 6
Насколько я знаю, вы передаете контроль на страницу, указанную в параметре url, и здесь будет указано ваше уведомление о событии, а не Application_Error
<customErrors defaultRedirect="myErrorPage.aspx"
mode="On">
</customErrors>
Много информации можно найти здесь: http://support.microsoft.com/kb/306355
Ответ 7
Чтобы обойти это, я оставил отключенные пользовательские устройства и обрабатывал все ошибки из события 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();
}
}
Ответ 8
Эта запись в блоге помогла мне:
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 определяется как "Content Length Too Large". Важно отметить, что maxAllowedContentLength
указывается в байтах. Это отличается от параметра maxRequestLength
, который вы найдете в разделе <system.web>
, который указан в килобайтах.
<system.web>
<httpRuntime maxRequestLength="10240" />
</system.web>
Также обратите внимание, что атрибут path
должен быть абсолютным путем, если responseMode
равен Redirect
, поэтому добавьте имя виртуального каталога, если это необходимо. Информативные ответы Джесси Уэбба показывают, как это сделать с помощью responseMode="ExecuteURL"
, и я бы подумал, что этот подход тоже будет хорошо работать.
Этот подход не работает, если вы работаете с Visual Studio Development Server (Cassini, веб-сервер, интегрированный в Visual Studio). Я предполагаю, что это будет работать в IIS Express, но я не тестировал это.
Ответ 9
У меня была та же проблема, когда Application_Error()
не попадал. Я все испробовал, пока, наконец, я не перешел через то, что происходило. У меня был специальный код в событии ELMAH, которое добавляло JSON к отправляемому электронному письму, и там была нулевая ошибка!
Фиксирование внутренней ошибки позволило продолжить код в событии Application_Error()
, как и ожидалось.