Application_Error не запускається, коли customerrors = "Увімкнено"


124

У мене є код у події 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?


У мене є точно така ж проблема. Я також знайшов це питання ТАК: stackoverflow.com/questions/3713939/…, який пропонує перевести сервер IIS7 в класичний режим. На жаль, це не варіант для нас. Хтось має кращі рішення?
Джессі Вебб

Ось ще одне пов'язане питання, і його відповіді (жодна з яких не прийнята) пропонують взагалі не використовувати Application_Error () ... stackoverflow.com/questions/1194578/…
Jesse Webb

@Gweebz Я опублікував відповідь про те, як мені це подолати, але все ще не знайшов жодної надійної документації щодо того, чому я отримую таку поведінку.
WDuffy

Я додав відповідь, яка пояснює, чому Application_Error()метод не викликався. Я також пояснив своє остаточне рішення.
Джессі Вебб

Дуже рекомендуємо цю статтю , щоб користувацькі помилки працювали.
ThomasArdal

Відповіді:


133

ОНОВЛЕННЯ
Оскільки ця відповідь дає рішення, я не буду її редагувати, але я знайшов набагато більш чистий спосіб вирішення цієї проблеми. Дивіться іншу відповідь для деталей ...

Оригінальна відповідь:
Я зрозумів, чому 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фільтр. Коли користувальницькі помилки включені (або за допомогою віддалених запитів, коли для нього встановлено RemoteOnly), HandleErrorAttribute повідомляє MVC шукати перегляд помилок, і він ніколи не викликає 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-адреси

Для цих проблем існують робочі місця, але я не був достатньо стурбований ними, щоб виконувати зайву роботу.

Я сподіваюся, що це допомагає всім!


2
+1! Насправді гарне рішення, але які можуть бути наслідки змішування MVC із сторінками aspx?
Дієго

2
У нас це було в PROD вже пару місяців, і я не знайшов і негативного впливу. Єдиний "gotcha" - це те, що нам довелося змінити CI і розгорнути, щоб виконати завдання AspCompile MSBUILD, оскільки MVC не потрібен, але коли ми додали файли .as , вони вимагали цього. Можуть бути й інші проблеми, але вони ще не з’явилися…
Jesse Webb

Я намагався використовувати такий підхід у MVC4, і, мабуть, він працює для мене лише тоді, коли я потрапив <customErrors mode="Off" />. Наявність filters.Add(new HandleErrorAttribute());видалені чи ні , не має ніякого ефекту.
Grzegorz Sławecki

на сторінці aspx можна додати дзвінок ajax, щоб зателефонувати до подання, яке ви хочете показати. Економить необхідність дублювання коду
Майк

71

Я вирішив це, створивши 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());
}

6
+1 Якщо ця відповідь спрацює, я впевнена, що мені здається, найелегантніший і, можливо, призначений спосіб (за рамками MVC) вирішувати помилки.
Метт Хамсміт

2
Це прекрасно працює для мене і набагато акуратніше, ніж тут прийнята відповідь. Приємно!
Алекс Уоррен

3
Це допоможе вирішити проблему винятків реєстрації журналів. Як ви поєднали це із відображенням користувацьких сторінок помилок?
Джессі Вебб

3
Я спробував це, і це спрацювало чудово, за винятком 404-х років. Протягом 404-х він не відображав би вигляд Errors.cshtml, він би дав мені лише YSoD. Якщо користувацькі 404 не потрібні, це рішення, безумовно, більш чисте!
Джессі Вебб

1
Мені це подобається. Працює з "CustomErrors = On"
Іллідан

36

Я знайшов статтю, яка описує набагато більш чистий спосіб створення користувацьких сторінок помилок у веб-додатку 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; це рішення фактично використовує його!


6
Варто зазначити, що я думаю, що це рішення працює лише в IIS 7 і новіших; елемент httpErrors був доданий лише нещодавно.
Джессі Вебб

Ця відповідь не спрацювала для мене, це мене, це грає мені жахливо синім: HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. Екран помилок, не моя запитувана /Error/Indexсторінка
Serj Sagan

@SerjSagan Я щойно спробував це в новому проекті, щоб перевірити кроки, і він прекрасно працює. Використовуєте IIS, IIS Express або VS Dev Server? Це не працюватиме в VS Dev Server. Коли в мене був встановлений проект використовувати VS Dev Server, а не IIS, я помітив помилки на жовтому екрані замість користувацьких сторінок помилок.
Джессі Вебб

1
@SerjSagan Але це здається, що ви отримуєте помилки на синьому екрані IIS на відміну від класичних помилок на жовтому екрані. Це змушує мене припускати, що ви використовуєте якусь форму IIS. Якщо це так, прочитайте статтю, до якої я посилався в першому реченні, зокрема останній розділ під назвою: «Мало важливих приміток». У ньому сказано:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
Джессі Вебб

3
@SerjSagan Змінення помилкиMode на DetailedLocalOnly або Custom відобразить вашу сторінку.
Даніель П

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папки.


1

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

Ви все одно можете використовувати ваш IoC за допомогою IExceptionFilter, але налаштувати свої прив’язки трохи складніше.


0

Наскільки я знаю, ви передаєте контроль над Сторіною, вказаною в параметрі URL, і ваше повідомлення про подію буде тут, а не Application_Error

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

Багато інформації можна знайти тут: http://support.microsoft.com/kb/306355


Дякую Крісу, я прочитав документацію, і це дозволяє припустити, що Application_Error буде викликаний, коли станеться непоправлена ​​помилка (яку я очікую), і що сервер Server.ClearError () не повинен називатися розділом web.config customErrors буде остаточним пунктом обробки. Однак увімкнення customErrors - це те, що зупиняє програму Application_Error від запуску.
WDuffy

Документація, до якої ви посилаєтесь, говорить про додаток ASP.NET, і, схоже, веб-додатки MVC3 поводяться по-різному.
Джессі Вебб

0

Щоб уникнути цього, я в кінцевому підсумку залишив клієнтів відключеними та обробляв усі помилки події 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();
    }

}

Я спробував ваше рішення, але у мене це не вийшло. З допомогою рядків Response.StatusCode = ###;він показував вбудовані сторінки помилок MVC на C:\inetpub\custerr\en-US. Мені також не сподобалася ідея вручну викликати HttpHandlers або Controllers з мого методу Application_Error (). Я радий, що ти знайшов рішення своєї проблеми, але я знаю, які головні болі мені це дали.
Джессі Вебб

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>

Також зауважте, що pathатрибут повинен бути абсолютним шляхом, коли responseModeє Redirect, тому додайте ім'я віртуальної каталогів, якщо це доречно. Інформативні відповіді Джессі Вебба показують, як це зробити responseMode="ExecuteURL", і я думаю, що такий підхід також буде добре працювати.

Цей підхід не працює, якщо ви розробляєте сервер розвитку Visual Studio (Cassini, веб-сервер, інтегрований у Visual Studio). Я припускаю, що це буде працювати в IIS Express, але я цього не перевіряв.


0

У мене була та сама проблема, де Application_Error()не потрапили. Я спробував усе, поки нарешті не переступив через те, що відбувається. У мене був якийсь спеціальний код у події ELMAH, який додав JSON до електронного листа, який він надсилає, і там була нульова помилка!

Виправлення внутрішньої помилки дозволило коду продовжувати роботу до Application_Error()події, як очікувалося.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.