如何使自定义错误页面在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. HandleErrorAttributeFilterConfig类中注册为全局操作过滤器,如下所示:

    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
这里的大多数答案都不能解决所有情况,或者导致Web服务器以“错误”的方式响应,即重定向到错误页面而不是返回错误响应。如果您关心服务器是否以Web服务器期望的方式响应,那么这里有一篇非常详细的文章:benfoster.io/blog/aspnet-mvc-custom-error-pages。请注意,它不像这里的答案那么简单,因此,如果您想要一个简单的答案,请使用以下答案之一。
rdans

1
这是另一篇有关asp.net错误处理dusted.codes/…的
Godsayah

Answers:


351

我当前的设置(在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
您是否需要为此Pablo在Global.asax的Application_Error中放入任何内容?
艾丽西亚(Alicia)2013年

12
根据我的经验,控制器中的代码似乎没有执行。MVC4-在其他控制器中抛出System.Exception将使Error.cshtml文件呈现,但不能通过ErrorController呈现。还有其他人遇到吗?
Nilzor 2014年

53
对于任何其他认为有帮助但需要更多背景信息的人;<customErrors>标记位于web.config中的<system.web>内部。
gooberverse

7
更新为他人 -显然我的问题是怎么回事,因为我有redirectMode="ResponseRewrite"CustomerErrors元素
KyleMit

41
请为上帝的爱而忽略//you may want to set this to 200代码中的注释。不要那样做!
Dementic 2015年

40

我已经完成了pablo解决方案,并且始终出现错误(MVC4)

找不到“错误”视图或其主视图,或者没有视图引擎支持搜索到的位置。

要摆脱这一点,删除行

 filters.Add(new HandleErrorAttribute());

在FilterConfig.cs中


我到处寻找解决此问题的方法。终于有了答案。我知道为什么要这样做,但就我自己而言,如果不像其他人所说的那样剧烈地思考,我就做不到。我想说谢谢您指出这一点,我想我也分享了360Airwalk的痛苦。传说!
亚当

这是一个选项,错误控制器也可以正常工作。但是似乎当您在FilterConfig.cs中注册过滤器时,它会在共享和原始控制器的视图文件夹中查找Error.cshtml。当您将Error.cshtml更改为除我们自定义ErrorController之外的其他任何内容时。但是有一个可以添加此注册的地方,它是global.asax.cs。如果您在global.asax.cs的RegisterGlobalFilters(GlobalFilterCollection过滤器)函数中添加提到的行并从FilterConfig.cs中删除,则该行有效。
isaolmez 2015年

我认为这与过滤器注册的顺序有关。保留错误控制器并将过滤器注册移至global.asax.cs。公共静态无效RegisterGlobalFilters(GlobalFilterCollection过滤器){filter.Add(new HandleErrorAttribute()); }
isaolmez 2015年

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”,对吗?至少那对我有用。也许这仅取决于路由。
Josh Sutterfield

如何模拟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. 测试(当然)。在代码中引发未处理的异常,然后将其转到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"/>它,而且效果很好:)其余代码来自@Pablo的答案。我使用MVC 5和实体框架6.我没有删除filters.Add(new HandleErrorAttribute())FilterConfig.cs
sumedha

如何模拟IIS引发的错误。是500还是504。在ASP.Net MVC中做什么-5代码模拟IIS中的异常,以便我可以测试我的自定义错误页面
Unbreakable

另外,如何引发未处理的异常(步骤5)。我是编码新手,请指导。
牢不可破

还是对我不起作用?那路由呢?我也需要添加“错误路由”页面吗?如果我点击以下页面:localhost:84 / Enforcer / blah,我将重定向到:localhost:84 / Enforcer / Enforcer / Error / NotFound?aspxerrorpath = /…该错误页面看起来像是Asp.NET提供的标准错误页面。有任何想法吗?
Radek Strugalski '17

webconfig中的customerrors元素应该对此有所困扰。您(由项目创建的)默认路由代码应该可以正常工作。
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
我不认为您可以在MVC中执行Server.Transfer()。您是否认为OP具有混合站点?
2014年

1
为什么要在mvc中使用Application_Error?我们有[handleerror]属性之类的选项,带有重定向URL选项。application_error有什么特别的好处吗?
Kurkula

我们应该在MVC中使用HandleErrorAttribute,并通过重写OnException方法,可以更好地处理它们
Kumar Lachhani

7

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

基本上,我们只是Application_Errorglobal.asax.cs添加一个方法来拦截该异常,并为我们提供了将重定向(或更正确地,将request重定向)到自定义错误页面的机会。

    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的正确错误页面。

我发现了这篇博客文章 Rick Strahl的对我有所帮助。

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


@ Shaun314您的意思是您将代码放在哪里?在处理请求的操作中。您可以在该博客文章中看到示例。
DCShannon 2015年

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>

〜/ Controllers / HomeController.sc:

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

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

    ...
}

〜/ Controllers / 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);
    }
}

〜/ Controllers / 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 '19

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.