Як змусити користувацькі сторінки помилок працювати в ASP.NET MVC 4


247

Я хочу, щоб спеціальна сторінка помилок відображалася для 500, 404 та 403. Ось що я зробив:

  1. Увімкнено спеціальні помилки в web.config таким чином:

    <customErrors mode="On" 
                  defaultRedirect="~/Views/Shared/Error.cshtml">
    
        <error statusCode="403" 
               redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />
    
        <error statusCode="404" 
               redirect="~/Views/Shared/FileNotFound.cshtml" />
    
    </customErrors>
  2. Зареєстровано HandleErrorAttributeяк глобальний фільтр дій у FilterConfigкласі наступним чином:

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }
  3. Створено користувацьку сторінку помилок для кожного з перерахованих вище повідомлень. За замовчуванням 500 уже доступні.

  4. У кожному спеціальному перегляді сторінки помилок оголошено, що модель для сторінки є System.Web.Mvc.HandleErrorInfo

Для 500 він показує користувацьку сторінку помилок. Для інших це не так.

Щось мені не вистачає?

Схоже, це ще не все, що стосується відображення користувацьких помилок, коли я читаю код у OnExceptionметоді HandleErrorAttributeкласу, і він обробляє лише 500.

Що мені робити, щоб обробляти інші помилки?


21
Що дивно в цій настройці, це те, що ваше перенаправлення на перегляди, а не дії контролера. Хто, наприклад, повинен викласти ці погляди та передати модель? Просто думаю.
Олівер

2
Більшість відповідей тут або не обробляють усі випадки, або викликають те, що веб-сервер реагує «неправильно», тобто перенаправляє на сторінку помилок, а не повертає відповідь про помилку. Якщо ви дбаєте про те, щоб сервер відповідав так, як очікується від веб-серверів, тут про це є досить детальна стаття: benfoster.io/blog/aspnet-mvc-custom-error-pages . Попереджуйте, що це не так просто, як відповіді тут, тому, якщо ви хочете просту відповідь, просто скористайтеся одним із наведених нижче.
rdans

1
Ось ще одна чудова стаття про різні методики обробки помилок asp.net dusted.codes/…
Godsayah

Відповіді:


352

Моя поточна установка (на MVC3, але я думаю, що це все ще застосовується) покладається на наявність ErrorController, тому я використовую:

<system.web>
    <customErrors mode="On" defaultRedirect="~/Error">
      <error redirect="~/Error/NotFound" statusCode="404" />
    </customErrors>
</system.web>

А контролер містить наступне:

public class ErrorController : Controller
{
    public ViewResult Index()
    {
        return View("Error");
    }
    public ViewResult NotFound()
    {
        Response.StatusCode = 404;  //you may want to set this to 200
        return View("NotFound");
    }
}

І погляди саме так, як ви їх реалізуєте. Я схильний додати трохи логіки, щоб показати трасування стека та інформацію про помилки, якщо програма перебуває в режимі налагодження. Отже Error.cshtml виглядає приблизно так:

@model System.Web.Mvc.HandleErrorInfo
@{
    Layout = "_Layout.cshtml";
    ViewBag.Title = "Error";
}
<div class="list-header clearfix">
    <span>Error</span>
</div>
<div class="list-sfs-holder">
    <div class="alert alert-error">
        An unexpected error has occurred. Please contact the system administrator.
    </div>
    @if (Model != null && HttpContext.Current.IsDebuggingEnabled)
    {
        <div>
            <p>
                <b>Exception:</b> @Model.Exception.Message<br />
                <b>Controller:</b> @Model.ControllerName<br />
                <b>Action:</b> @Model.ActionName
            </p>
            <div style="overflow:scroll">
                <pre>
                    @Model.Exception.StackTrace
                </pre>
            </div>
        </div>
    }
</div>

7
Чи потрібно було щось помістити в Application_Error у своєму Global.asax для цього Pablo?
Алісія

12
Код у контролері, схоже, не виконується з мого досвіду. MVC4 - передача System.Exception в інший контролер призведе до виведення файлу Error.cshtml, але не через ErrorController. Хтось ще відчував це?
Нільзор

53
Для всіх, хто вважає це корисним, але потребує більше контексту; Тег <customErrors> знаходиться всередині <system.web> в web.config.
gooberverse

7
Оновлення для інших - мабуть, у мене виникла проблема, тому що я був redirectMode="ResponseRewrite"на CustomerErrorsелементі
KyleMit

42
Будь ласка, щоб любов до бога проігноруйте коментар //you may want to set this to 200у коді. НЕ РОБИ ЦЬОГО!
Дементік

40

Я робив рішення pablo, і у мене завжди була помилка (MVC4)

Перегляд "Помилка" або його головного пристрою не знайдено або жодна система перегляду не підтримує шукане місце.

Щоб позбутися цього, видаліть рядок

 filters.Add(new HandleErrorAttribute());

у FilterConfig.cs


Я шукав всюди, щоб вирішити це. На це нарешті була відповідь. Я знав, чому це робив, але, до біса, я не міг, не думаючи кардинально, як те, що сказали інші люди. Я уявляю, що ділюсь болем 360Airwalk, коли кажу вам спасибі за те, що вказав на це. Легенда!
Адам

Це один варіант, і контролер помилок працює добре. Але здається, що коли ви реєструєте фільтри в FilterConfig.cs, він шукає Error.cshtml у папках перегляду спільних та оригінальних контролерів. Коли ви змінюєте Error.cshtml на що-небудь, крім того, працює наш користувальницький ErrorController. Але є місце, де ви можете додати цю реєстрацію, і це global.asax.cs. Якщо ви додасте згаданий рядок у функцію RegisterGlobalFilters (фільтрів GlobalFilterCollection) у global.asax.cs та видаліть з FilterConfig.cs, він працює.
isaolmez

Я думаю, це пов'язано з порядком реєстрації фільтрів. Зберігайте контролер помилок і перемістіть реєстрацію фільтра на global.asax.cs. загальнодоступна статична порожнеча RegisterGlobalFilters (фільтрів GlobalFilterCollection) {filters.Add (новий HandleErrorAttribute ()); }
isaolmez

24

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

По-перше, у своєму web.config у мене є таке:

<customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
   <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
   <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
</customErrors>

І контролер (/Controllers/ErrorPageController.cs) містить таке:

public class ErrorPageController : Controller
{
    public ActionResult Oops(int id)
    {
        Response.StatusCode = id;

        return View();
    }
}

І нарешті, представлення містить наступне (зняте для простоти, але воно може контактувати:

@{ ViewBag.Title = "Oops! Error Encountered"; }

<section id="Page">
  <div class="col-xs-12 well">
    <table cellspacing="5" cellpadding="3" style="background-color:#fff;width:100%;" class="table-responsive">
      <tbody>
        <tr>
          <td valign="top" align="left" id="tableProps">
            <img width="25" height="33" src="~/Images/PageError.gif" id="pagerrorImg">
          </td>
          <td width="360" valign="middle" align="left" id="tableProps2">
            <h1 style="COLOR: black; FONT: 13pt/15pt verdana" id="errortype"><span id="errorText">@Response.Status</span></h1>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth"><font style="COLOR: black; FONT: 8pt/11pt verdana">Possible causes:</font>
          </td>
        </tr>
        <tr>
          <td width="400" colspan="2" id="tablePropsWidth2">
            <font style="COLOR: black; FONT: 8pt/11pt verdana" id="LID1">
                            <hr>
                            <ul>
                                <li id="list1">
                                    <span class="infotext">
                                        <strong>Baptist explanation: </strong>There
                                        must be sin in your life. Everyone else opened it fine.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Presbyterian explanation: </strong>It's
                                        not God's will for you to open this link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong> Word of Faith explanation:</strong>
                                        You lack the faith to open this link. Your negative words have prevented
                                        you from realizing this link's fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Charismatic explanation: </strong>Thou
                                        art loosed! Be commanded to OPEN!<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Unitarian explanation:</strong> All
                                        links are equal, so if this link doesn't work for you, feel free to
                                        experiment with other links that might bring you joy and fulfillment.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Buddhist explanation:</strong> .........................<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Episcopalian explanation:</strong>
                                        Are you saying you have something against homosexuals?<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Christian Science explanation: </strong>There
                                        really is no link.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Atheist explanation: </strong>The only
                                        reason you think this link exists is because you needed to invent it.<br>
                                    </span>
                                </li>
                                <li>
                                    <span class="infotext">
                                        <strong>Church counselor's explanation:</strong>
                                        And what did you feel when the link would not open?
                                    </span>
                                </li>
                            </ul>
                            <p>
                                <br>
                            </p>
                            <h2 style="font:8pt/11pt verdana; color:black" id="ietext">
                                <img width="16" height="16" align="top" src="~/Images/Search.gif">
                                HTTP @Response.StatusCode - @Response.StatusDescription <br>
                            </h2>
                        </font>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</section>

Це так само просто. Це може бути легко розширено, щоб запропонувати більш детальну інформацію про помилки, але ELMAH вирішує, що для мене & statusCode & statusDescription - це все, що мені зазвичай потрібно.


Я думаю, що перенаправлення у .config файл "~ / ErrorPage / Oops / 404", ймовірно, має бути "~ / ErrorPage / Oops? 404" правильно? Принаймні, це для мене працювало. Можливо, це просто залежить від маршрутизації.
Джош Саттерфілд

Як імітувати помилку, кинуту IIS. Будь то 500 або 504. Що робити в ASP.Net MVC - 5 коду, щоб імітувати виняток з IIS, щоб я міг перевірити свою власну сторінку помилок
Незламний

12

Здається, кілька кроків тут стрибнуло разом. Я висуну те, що я зробив з нуля.

  1. Створіть ErrorPageконтролер

    public class ErrorPageController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    
        public ActionResult Oops(int id)
        {
            Response.StatusCode = id;
            return View();
        }
    }
  2. Додайте представлення для цих двох дій (клацніть правою кнопкою миші -> Додати перегляд). Вони повинні з’являтися у папці під назвою ErrorPage.

  3. Всередині App_Startвідкрийте FilterConfig.csі прокоментуйте фільтр обробки помилок.

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // Remove this filter because we want to handle errors ourselves via the ErrorPage controller
        //filters.Add(new HandleErrorAttribute());
    }
  4. Всередині web.config додайте наступні <customerErrors>записи підSystem.Web

    <customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
        <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
        <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
    </customErrors>
  5. Тест (звичайно). Введіть у свій код необроблений виняток і перегляньте його, перейшовши на сторінку з id 500, а потім скористайтеся URL-адресою на сторінці, яка не існує, щоб побачити 404.


Я отримував цю помилку An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.Все, що я зібрав з вашого коду, знаходиться у файлі web.config, я додав, <error redirect = "~/ControllerName/ActionName" statusCode="404"/>і він працював чудово :) Решта коду була з відповіді @ Пабло. Я використовую MVC 5 та сутність фреймворку 6. Мене не видаляли filters.Add(new HandleErrorAttribute())зFilterConfig.cs
sumedha

Як імітувати помилку, кинуту IIS. Будь то 500 або 504. Що робити в ASP.Net MVC - 5 коду, щоб імітувати виняток з IIS, щоб я міг перевірити свою власну сторінку помилок
Незламний

Також, як кинути необроблений виняток (крок 5). Я новачок у кодуванні будь-якого посібника.
Незламний

Все ще не працює для мене? А як щодо маршрутизації? Чи потрібно також додавати маршрутизацію для сторінки помилок? Якщо я потрапляю на сторінку: localhost: 84 / Enforcer / blah, я переспрямовуюсь на: localhost: 84 / Enforcer / Enforcer / Error / NotFound? Aspxerrorpath = /… Сторінка помилок виглядає як стандартна сторінка помилок, надана Asp.NET. Будь-які ідеї?
Радек Стругальський

Елемент замовника у вебконфігурі повинен бути цим. Ваш (створений проектом) код маршруту за замовчуванням повинен добре працювати.
VictorySaber

11

Я рекомендую використовувати файл Global.asax.cs.

 protected void Application_Error(Object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    if (exception is HttpUnhandledException)
    {
        Server.Transfer("~/Error.aspx");
    }
    if (exception != null)
    {
        Server.Transfer("~/Error.aspx");
    }
    try
    {
        // This is to stop a problem where we were seeing "gibberish" in the
        // chrome and firefox browsers
        HttpApplication app = sender as HttpApplication;
        app.Response.Filter = null;
    }
    catch
    {
    }
}

1
Я не думав, що ти можеш зробити Server.Transfer () у MVC. Ви думаєте, що в ОП є змішаний сайт?
Реп

1
чому ми повинні використовувати Application_Error в mvc? У нас є такі параметри, як [handleerror] attribut з параметрами URL-адреса переадресації. Чи є якась певна перевага з application_error для цього?
Куркула

Ми повинні використовувати HandleErrorAttribute в MVC і, перекривши метод OnException, ми можемо впоратися з ними набагато краще
Кумар Лаххані,

7

Спираючись на відповідь, опубліковану maxspan, я склав мінімальний зразок проекту на GitHub, який показував усі робочі частини.

В основному, ми просто додаємо Application_Errorметод на global.asax.cs для перехоплення винятку і надаємо можливість перенаправляти (або, правильніше, передати запит ) на користувацьку сторінку помилок.

    protected void Application_Error(Object sender, EventArgs e)
    {
        // See http://stackoverflow.com/questions/13905164/how-to-make-custom-error-pages-work-in-asp-net-mvc-4
        // for additional context on use of this technique

        var exception = Server.GetLastError();
        if (exception != null)
        {
            // This would be a good place to log any relevant details about the exception.
            // Since we are going to pass exception information to our error page via querystring,
            // it will only be practical to issue a short message. Further detail would have to be logged somewhere.

            // This will invoke our error page, passing the exception message via querystring parameter
            // Note that we chose to use Server.TransferRequest, which is only supported in IIS 7 and above.
            // As an alternative, Response.Redirect could be used instead.
            // Server.Transfer does not work (see https://support.microsoft.com/en-us/kb/320439 )
            Server.TransferRequest("~/Error?Message=" + exception.Message);
        }

    }

Контролер помилок:

/// <summary>
/// This controller exists to provide the error page
/// </summary>
public class ErrorController : Controller
{
    /// <summary>
    /// This action represents the error page
    /// </summary>
    /// <param name="Message">Error message to be displayed (provided via querystring parameter - a design choice)</param>
    /// <returns></returns>
    public ActionResult Index(string Message)
    {
        // We choose to use the ViewBag to communicate the error message to the view
        ViewBag.Message = Message;
        return View();
    }

}

Перегляд сторінки помилок:

<!DOCTYPE html>

<html>
<head>
    <title>Error</title>
</head>
<body>

    <h2>My Error</h2>
    <p>@ViewBag.Message</p>
</body>
</html>

Ніщо інше не пов'язане, крім відключення / видалення filters.Add(new HandleErrorAttribute())на FilterConfig.cs

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        //filters.Add(new HandleErrorAttribute()); // <== disable/remove
    }
}

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


3

У мене все було налаштовано, але я все ще не міг побачити належних сторінок помилок для коду статусу 500 на нашому сервері постановки, незважаючи на те, що на локальних серверах розробки все працювало нормально.

Я знайшов цю публікацію в блозі від Ріка Штраля, яка мені допомогла.

Мені потрібно було додати Response.TrySkipIisCustomErrors = true;до власного коду обробки помилок.


@ Shaun314 Ви маєте на увазі, куди ви кладете цей код? У дії, яка обробляє запит. Ви можете побачити приклади в цій публікації в блозі.
DCShannon

2

Ось моє рішення. Використовувати [ExportModelStateToTempData] / [ImportModelStateFromTempData] на мою думку незручно.

~ / Перегляди / Головна / Error.cshtml:

@{
    ViewBag.Title = "Error";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Error</h2>
<hr/>

<div style="min-height: 400px;">

    @Html.ValidationMessage("Error")

    <br />
    <br />

    <button onclick="Error_goBack()" class="k-button">Go Back</button>
    <script>
        function Error_goBack() {
            window.history.back()
        }
    </script>

</div>

~ / Контролери / HomeController.sc:

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Error()
    {
        return this.View();
    }

    ...
}

~ / Контролери / BaseController.sc:

public class BaseController : Controller
{
    public BaseController() { }

    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            if (filterContext.Controller.TempData.ContainsKey("Error"))
            {
                var modelState = filterContext.Controller.TempData["Error"] as ModelState;
                filterContext.Controller.ViewData.ModelState.Merge(new ModelStateDictionary() { new KeyValuePair<string, ModelState>("Error", modelState) });
                filterContext.Controller.TempData.Remove("Error");
            }
        }
        if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
        {
            if (filterContext.Controller.ViewData.ModelState.ContainsKey("Error"))
            {
                filterContext.Controller.TempData["Error"] = filterContext.Controller.ViewData.ModelState["Error"];
            }
        }

        base.OnActionExecuted(filterContext);
    }
}

~ / Контролери / MyController.sc:

public class MyController : BaseController
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Details(int id)
    {
        if (id != 5)
        {
            ModelState.AddModelError("Error", "Specified row does not exist.");
            return RedirectToAction("Error", "Home");
        }
        else
        {
            return View("Specified row exists.");
        }
    }
}

Бажаю вам успішних проектів ;-)


2

Ви можете отримати помилки при правильній роботі, не зламаючи global.cs, возившись з HandleErrorAttribute, виконуючи Response.TrySkipIisCustomErrors, підключивши Application_Error або що завгодно:

У system.web (просто звичайний, увімкнено / вимкнено)

<customErrors mode="On">
  <error redirect="/error/401" statusCode="401" />
  <error redirect="/error/500" statusCode="500" />
</customErrors>

і в system.webServer

<httpErrors existingResponse="PassThrough" />

Тепер все має вестись так, як очікувалося, і ви можете використовувати свій ErrorController для відображення всього необхідного.


Як імітувати помилку, кинуту IIS. Будь то 500 або 504. Що робити в ASP.Net MVC - 5 коду, щоб імітувати виняток з IIS, щоб я міг перевірити свою власну сторінку помилок
Незламний

@Unbreakable тимчасово змінити код, щоб кинути виняток.
theycallmemorty

Не змінив для мене значення. За виключенням або 404 не знайдено помилки, я не переглядаюсь на свою власну сторінку помилок.
pnizzle

0

Здається, я пізно прийшов на вечірку, але вам теж слід перевірити це.

Отже, system.webдля кешування винятків у додатку, такого як return HttpNotFound ()

  <system.web>
    <customErrors mode="RemoteOnly">
      <error statusCode="404" redirect="/page-not-found" />
      <error statusCode="500" redirect="/internal-server-error" />
    </customErrors>
  </system.web>

і для того, system.webServerщоб наздогнати помилки, які виявили IIS і не пробралися до рамки asp.net

 <system.webServer>
    <httpErrors errorMode="DetailedLocalOnly">
      <remove statusCode="404"/>
      <error statusCode="404" path="/page-not-found" responseMode="Redirect"/>
      <remove statusCode="500"/>
      <error statusCode="500" path="/internal-server-error" responseMode="Redirect"/>
  </system.webServer>

В останньому, якщо ви турбуєтесь про відповідь клієнта, тоді змініть responseMode="Redirect"на responseMode="File"та подайте статичний HTML-файл, оскільки цей відображатиме дружню сторінку з кодом відповіді 200.


0

У web.config додайте це під тег system.webserver, як показано нижче,

<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404"/>
  <remove statusCode="500"/>
  <error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound"/>
  <error statusCode="500" responseMode="ExecuteURL"path="/Error/ErrorPage"/>
</httpErrors>

і додайте контролер як

public class ErrorController : Controller
{
    //
    // GET: /Error/
    [GET("/Error/NotFound")]
    public ActionResult NotFound()
    {
        Response.StatusCode = 404;

        return View();
    }

    [GET("/Error/ErrorPage")]
    public ActionResult ErrorPage()
    {
        Response.StatusCode = 500;

        return View();
    }
}

і додайте їх поважних поглядів, це спрацює, безумовно, для всіх.

Це рішення я знайшов у: Neptune Century

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