Как заставить настраиваемые страницы ошибок работать в ASP.NET MVC 4

215

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

  • Включить пользовательские ошибки в файле 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>
    
  • Зарегистрировано HandleErrorAttribute как глобальный фильтр действий в классе FilterConfig следующим образом:

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

  • Объявлено в каждом пользовательском представлении страницы ошибки, что модель для страницы System.Web.Mvc.HandleErrorInfo

В течение 500 страниц отображается страница пользовательской ошибки. Для других это не так.

Есть ли что-то, что мне не хватает?

Похоже, что это не все, что нужно для отображения пользовательских ошибок, когда я читаю код в методе OnException класса HandleErrorAttribute, и он обрабатывает только 500.

Что мне нужно сделать для обработки других ошибок?

  • 20
    Что странно с этой настройкой, так это то, что вы перенаправляете на представления, а не действия контроллера Например, кто должен визуализировать эти представления и передать модель? Просто думаю.
  • 1
    Большинство ответов здесь либо не обрабатывают все случаи, либо приводят к тому, что веб-сервер отвечает «неправильным» образом, то есть перенаправляет на страницу с ошибкой, а не возвращает ответ об ошибке. Если вы заботитесь о том, чтобы сервер реагировал так, как ожидается от веб-серверов, то здесь есть довольно подробная статья об этом: benfoster.io/blog/aspnet-mvc-custom-error-pages . Имейте в виду, что это не так просто, как ответы здесь, поэтому, если вы хотите простой ответ, просто используйте один из приведенных ниже.
Показать ещё 1 комментарий
Теги:
asp.net-mvc
asp.net-mvc-4

11 ответов

287
Лучший ответ

Моя текущая настройка (на 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?
  • 4
    Для этого не нужно никаких дополнительных шагов ... на самом деле Error.cshtml уже должен существовать в Views / Shared, поэтому просто отредактируйте его так, как указано выше.
Показать ещё 20 комментариев
33

Я сделал решение pablo, и у меня всегда была ошибка (MVC4)

Вид "Ошибка" или его мастер не найден или механизм просмотра не поддерживает найденное местоположение.

Чтобы избавиться от этого, удалите строку

 filters.Add(new HandleErrorAttribute());

в FilterConfig.cs

  • 0
    Я искал везде, чтобы решить это. Это, наконец, ответ. Я знал, почему он это делал, но, черт возьми, я не мог, не думая решительно, как то, что говорили другие люди. Я полагаю, что разделяю боль 360Airwalk, когда говорю «спасибо» за указание на это. Легенда!
  • 0
    Это один из вариантов, и ошибка контроллера работает нормально. Но кажется, что когда вы регистрируете фильтры в FilterConfig.cs, он ищет Error.cshtml в общих и оригинальных папках представления контроллеров. Когда вы изменяете Error.cshtml на что-то другое, работает наш пользовательский ErrorController. Но есть место, где вы можете добавить эту регистрацию, и это global.asax.cs. Если вы добавите упомянутую строку в функцию RegisterGlobalFilters (фильтры GlobalFilterCollection) в global.asax.cs и удалите ее из FilterConfig.cs, она будет работать.
Показать ещё 1 комментарий
16

Я делаю то, что требует меньше кодирования, чем другие опубликованные решения.

Во-первых, в моем 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();
    }
}

И, наконец, представление содержит следующее (урезанное для простоты, но оно может быть conta:

@{ 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 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 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 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 - это все, что мне обычно нужно,

  • 0
    Я думаю, что перенаправление в файле .config "~ / ErrorPage / Oops / 404", вероятно, должно быть "~ / ErrorPage / Oops? 404", верно? По крайней мере, это то, что сработало для меня. Может быть, это зависит только от маршрутизации.
  • 0
    Как смоделировать ошибку, выданную IIS. Будь то 500 или 504. Что делать в коде ASP.Net MVC - 5, чтобы смоделировать исключение из IIS, чтобы я мог проверить свою пользовательскую страницу ошибок
10

Я бы рекомендовал использовать файл 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
    {
    }
}
  • 0
    Я не думал, что вы могли бы сделать Server.Transfer () в MVC. Вы думаете, что у ОП смешанный сайт?
  • 0
    почему мы должны использовать Application_Error в mvc? У нас есть такие параметры, как [handleerror] attribut с параметрами URL перенаправления. Есть ли какое-то конкретное преимущество с application_error к этому?
7

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

  • Создайте контроллер ErrorPage

    public class ErrorPageController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    
        public ActionResult Oops(int id)
        {
            Response.StatusCode = id;
            return View();
        }
    }
    
  • Добавить виды для этих двух действий (щелкните правой кнопкой мыши → Добавить вид). Они должны появиться в папке с именем ErrorPage.

  • Внутри 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());
    }
    
  • Внутри 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>
    
  • Тест (конечно). Бросьте необработанное исключение в свой код и посмотрите, как он перейдет на страницу с идентификатором 500, а затем используйте URL-адрес для страницы, которой не существует, чтобы увидеть 404.

Спасибо всем выше. Соответственно поддерживается.

  • 0
    Я получил эту ошибку 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
  • 0
    Как смоделировать ошибку, выданную IIS. Будь то 500 или 504. Что делать в коде ASP.Net MVC - 5, чтобы смоделировать исключение из IIS, чтобы я мог проверить свою пользовательскую страницу ошибок
Показать ещё 3 комментария
6

Основываясь на ответе, опубликованном 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
    }
}

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

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 для отображения того, что вам нужно.

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

Вот мое решение. Использовать [ExportModelStateToTempData]/[ImportModelStateFromTempData] неудобно, на мой взгляд.

~/Views/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.");
        }
    }
}

Желаю вам успешных проектов; -)

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

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

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

Я нашел этот пост в блоге от Рика Стралла, который помог мне.

Мне нужно добавить Response.TrySkipIisCustomErrors = true; в свой код обработки ошибок.

  • 0
    Где это было?
  • 0
    @ Shaun314 Ты имеешь в виду, куда ты положил этот код? В действии, которое обрабатывает запрос. Вы можете увидеть примеры в этом блоге.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню