Как заставить пользовательские страницы ошибок работать в 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

Ответы:


353

Моя текущая настройка (на 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

Я сделал решение Пабло, и у меня всегда была ошибка (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, она будет работать.
Исаолмез

Я думаю, что это связано с порядком регистрации фильтров. Сохраните контроллер ошибок и перенесите регистрацию фильтра в global.asax.cs. public static void RegisterGlobalFilters (фильтры GlobalFilterCollection) {filters.Add (new HandleErrorAttribute ()); }
Исаолмез

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

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. Тест (конечно). Бросьте необработанное исключение в свой код и увидите, что оно идет на страницу с идентификатором 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"/>и он работал нормально :) Остальная часть кода была из ответа @ Pablo. Я использую MVC 5 и Entity Framework 6. Я не удалил filters.Add(new HandleErrorAttribute())изFilterConfig.cs
sumedha

Как смоделировать ошибку, выданную IIS. Будь то 500 или 504. Что делать в коде ASP.Net MVC - 5, чтобы смоделировать исключение из IIS, чтобы я мог проверить свою пользовательскую страницу ошибок
Unbreakable

Также как сгенерировать необработанное исключение (шаг 5). Я новичок в кодировании, пожалуйста, руководство.
Unbreakable

Все еще не работает для меня? Как насчет маршрутизации? Нужно ли мне добавить страницу Routing for Error? Если я нажимаю на страницу: localhost: 84 / Enforcer / blah, меня перенаправляют на: localhost: 84 / Enforcer / Enforcer / Error / NotFound? Aspxerrorpath = /… Страница ошибок выглядит как стандартная страница ошибок, предоставляемая Asp.NET. Любые идеи?
Радек Стругальский

Элемент customerrors в webconfig должен помешать этому. Ваш (созданный проектом) код маршрута по умолчанию должен работать нормально.
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 к этому?
Kurkula

Мы должны использовать 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] неудобно, на мой взгляд.

~ / Просмотров / Home / 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

@Unbreakable временно изменить ваш код, чтобы вызвать исключение.
Звонят им

Не имеет значения для меня. Я не попал на свою страницу с ошибкой после исключения или 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();
    }
}

и добавить их уважаемые взгляды, это будет работать определенно, я думаю, для всех.

Это решение я нашел его из: Нептун В.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.