如何使自定义错误页在ASP.NET MVC 4中工作

How to make custom error pages work in ASP.NET MVC 4

我想要显示500,404和403的自定义错误页面。这是我所做的:

  • 在web.config中启用了自定义错误,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <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类中的全局操作过滤器,如下所示:

    1
    2
    3
    4
    5
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new CustomHandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }
  • 为上述每条消息创建了自定义错误页面。 500的默认值已经开箱即用。

  • 在每个自定义错误页面视图中声明该页面的模型是System.Web.Mvc.HandleErrorInfo

  • 对于500,它显示自定义错误页面。 对于其他人,它没有。

    有什么我想念的吗?

    看起来这并不是显示自定义错误的全部,因为我在HandleErrorAttribute类的OnException方法中读取代码并且它只处理500。

    我该怎么做才能处理其他错误?


    我当前的设置(在MVC3上,但我认为它仍然适用)依赖于ErrorController,所以我使用:

    1
    2
    3
    4
    5
    <system.web>
        <customErrors mode="On" defaultRedirect="~/Error">
          <error redirect="~/Error/NotFound" statusCode="404" />
        </customErrors>
    </system.web>

    控制器包含以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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看起来像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @model System.Web.Mvc.HandleErrorInfo
    @{
        Layout ="_Layout.cshtml";
        ViewBag.Title ="Error";
    }

        <span>Error</span>


       
            An unexpected error has occurred. Please contact the system administrator.
       
        @if (Model != null && HttpContext.Current.IsDebuggingEnabled)
        {
           
                <p>

                    Exception: @Model.Exception.Message<br />
                    Controller: @Model.ControllerName<br />
                    Action: @Model.ActionName
               
    </p>
               
                    [cc]
                        @Model.Exception.StackTrace

    }


    我已经完成了pablo解决方案而且我总是遇到错误(MVC4)

    The view 'Error' or its master was not found or no view engine supports the searched location.

    要摆脱这一点,删除该行

    1
     filters.Add(new HandleErrorAttribute());

    在FilterConfig.cs中


    我做的事情需要的代码少于发布的其他解决方案。

    首先,在我的web.config中,我有以下内容:

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

    控制器(/Controllers/ErrorPageController.cs)包含以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class ErrorPageController : Controller
    {
        public ActionResult Oops(int id)
        {
            Response.StatusCode = id;

            return View();
        }
    }

    最后,视图包含以下内容(为简单起见,它被删除,但它可以包含:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    @{ ViewBag.Title ="Oops! Error Encountered"; }

    <section id="Page">
     
        <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>
              </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">
                               
                               
    <ul>

                                    <li id="list1">
                                        <span class="infotext">
                                            Baptist explanation: There
                                            must be sin in your life. Everyone else opened it fine.
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Presbyterian explanation: It's
                                            not God's will for you to open this link.
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                             Word of Faith explanation:
                                            You lack the faith to open this link. Your negative words have prevented
                                            you from realizing this link's fulfillment.
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Charismatic explanation: Thou
                                            art loosed! Be commanded to OPEN!
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Unitarian explanation: 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.
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Buddhist explanation: .........................
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Episcopalian explanation:
                                            Are you saying you have something against homosexuals?
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Christian Science explanation: There
                                            really is no link.
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Atheist explanation: The only
                                            reason you think this link exists is because you needed to invent it.
                                        </span>
                                   
    </li>

                                   
    <li>

                                        <span class="infotext">
                                            Church counselor's explanation:
                                            And what did you feel when the link would not open?
                                        </span>
                                   
    </li>

                               
    </ul>

                                <p>

                                   
                               
    </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
                               
                            </font>
              </td>
            </tr>
          </tbody>
        </table>
     
    </section>

    就这么简单。它可以很容易地扩展,以提供更详细的错误信息,但ELMAH为我处理,statusCode和statusDescription是我通常需要的。


    我建议使用Global.asax.cs文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     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
        {
        }
    }


    这里似乎有许多步骤混杂在一起。我会提出我从头开始做的事情。

  • 创建ErrorPage控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class ErrorPageController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Oops(int id)
        {
            Response.StatusCode = id;
            return View();
        }
    }
  • 添加这两个操作的视图(右键单击 - >添加视图)。这些应出现在名为ErrorPage的文件夹中。

  • App_Start内打开FilterConfig.cs并注释掉错误处理过滤器。

    1
    2
    3
    4
    5
    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中,在System.Web下添加以下条目

    1
    2
    3
    4
    <customErrors mode="On" defaultRedirect="~/ErrorPage/Oops">
        <error redirect="~/ErrorPage/Oops/404" statusCode="404" />
        <error redirect="~/ErrorPage/Oops/500" statusCode="500" />
    </customErrors>
  • 测试(当然)。在代码中抛出未处理的异常并看到它转到ID为500的页面,然后使用URL到不存在的页面以查看404。


  • 在maxspan发布的答案的基础上,我在GitHub上整理了一个最小的示例项目,展示了所有工作部分。

    基本上,我们只是在global.asax.cs中添加一个Application_Error方法来拦截异常,并让我们有机会将(或更准确地说,转移请求)重定向到自定义错误页面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
        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);
            }

        }

    错误控制器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /// <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();
        }

    }

    错误页面查看:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!DOCTYPE html>

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

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

    除了在FilterConfig.cs中禁用/删除filters.Add(new HandleErrorAttribute())之外,不涉及任何其他内容

    1
    2
    3
    4
    5
    6
    7
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            //filters.Add(new HandleErrorAttribute()); // <== disable/remove
        }
    }

    虽然实现起来非常简单,但我在这种方法中看到的一个缺点是使用查询字符串将异常信息传递到目标错误页面。


    我已经完成了所有设置,但仍然无法在我们的登台服务器上看到状态代码500的正确错误页面,尽管事实上在本地开发服务器上一切正常。

    我发现Rick Strahl的这篇博文对我有帮助。

    我需要将Response.TrySkipIisCustomErrors = true;添加到我的自定义错误处理代码中。


    这是我的解决方案。在我看来,使用[ExportModelStateToTempData] / [ImportModelStateFromTempData]是不舒服的。

    ?/查看/主页/ Error.cshtml:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @{
        ViewBag.Title ="Error";
        Layout ="~/Views/Shared/_Layout.cshtml";
    }

    Error
    <hr/>



        @Html.ValidationMessage("Error")

        <br />
        <br />

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

    ?/控制器/ HomeController.sc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            return View();
        }

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

        ...
    }

    ?/控制器/ BaseController.sc:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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.");
            }
        }
    }

    祝你项目成功;-)


    您可以在不破坏global.cs的情况下正确地处理错误,弄乱HandleErrorAttribute,执行Response.TrySkipIisCustomErrors,挂钩Application_Error或其他:

    在system.web(只是通常,开/关)

    1
    2
    3
    4
    <customErrors mode="On">
      <error redirect="/error/401" statusCode="401" />
      <error redirect="/error/500" statusCode="500" />
    </customErrors>

    并在system.webServer中

    1
    <httpErrors existingResponse="PassThrough" />

    现在事情应该按预期运行,您可以使用ErrorController来显示您需要的任何内容。


    在web.config中,在system.webserver标记下添加,如下所示,

    1
    2
    3
    4
    5
    6
    7
    <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>

    并添加一个控制器,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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();
        }
    }

    并添加他们尊重的视图,这绝对是我猜的所有。

    这个解决方案我发现它来自:海王星世纪


    我似乎来晚了,但你最好还是检查一下。

    所以在system.web中缓存应用程序中的异常,例如返回HttpNotFound()

    1
    2
    3
    4
    5
    6
      <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框架

    1
    2
    3
    4
    5
    6
    7
     <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响应代码的友好页面。