如何在Asp.Net Mvc 3中显示自定义错误页面?

23

我希望所有401错误都能被重定向到自定义错误页面。我最初在我的web.config中设置了以下条目。

<customErrors defaultRedirect="ErrorPage.aspx" mode="On">
  <error statusCode="401" redirect="~/Views/Shared/AccessDenied.aspx" />
</customErrors>

当我使用IIS Express时,我收到了默认的IIS Express 401错误页面。
如果我不使用IIS Express,则返回一个空白页面。使用Google Chrome的网络选项卡检查响应时,我发现虽然页面是空白的,但头部返回了401状态。
到目前为止,我尝试了此SO答案中的建议,因为我正在使用IIS Express,但没有成功。我已经尝试使用<custom errors><httpErrors> 的组合,但仍然无法解决 - 显示标准错误或空白页面。 httpErrors 部分目前看起来像此链接所示,基于上述SO问题(我还找到了另一个非常有希望的答案,但没有成功 - 返回空白)。
<system.webServer>
  <httpErrors  errorMode="DetailedLocalOnly" existingResponse="PassThrough" >
    <remove statusCode="401"  />
    <error statusCode="401" path="/Views/Shared/AccessDenied.htm" />
  </httpErrors>

 <!-- 
 <httpErrors  errorMode="Custom" 
             existingResponse="PassThrough" 
             defaultResponseMode="ExecuteURL">
      <remove statusCode="401"  />
  <error statusCode="401" path="~/Views/Shared/AccessDenied.htm" 
         responseMode="File" />
 </httpErrors>
 -->
</system.webServer>

我甚至修改了applicationhost.config文件,并根据iis.net上的信息将<httpErrors lockAttributes="allowAbsolutePathsWhenDelegated,defaultPath">修改为<httpErrors lockAttributes="allowAbsolutePathsWhenDelegated">。在我的努力过程中,我还发现了another SO question中描述的这个错误。
如何在Asp.Net Mvc 3中显示自定义错误页面?
以下控制器操作已使用Authorize属性为特定用户进行了装饰。
[HttpGet]
[Authorize(Users = "domain\\userXYZ")]
public ActionResult Edit() 
{
   return GetSettings();
}

[HttpPost]
[Authorize(Users = "domain\\userXYZ")]
public ActionResult Edit(ConfigurationModel model, IList<Shift> shifts)
{
    var temp = model;
    model.ConfiguredShifts = shifts;
    EsgConsole config = new EsgConsole();

    config.UpdateConfiguration(model.ToDictionary());
    return RedirectToAction("Index");
}

我也想知道如何显示自定义错误(在我的情况下是403错误)-500错误可以正常工作... - David_001
制作了一个小型库来使这更容易。它可以在以下链接中找到:https://github.com/Buildstarted/Errlusion - Buildstarted
1
想必您会对这个SO帖子感兴趣。 - CBRRacer
@CBRRacer 这篇帖子实际上解决了我遇到的问题,不确定对于原帖作者是否适用。此外,这个问题的所有回答都没有提及所需的所有配置选项,因此很难授予悬赏... - David_001
赏金已经颁发,但是没有答案符合我所要寻找的,@CBRRacer的链接中有更好的答案。 - David_001
我相信这个问题已经在这篇文章中得到了回答:https://dev59.com/Zm435IYBdhLWcg3wxDBp - Lee
4个回答

34

我使用以下步骤:

// in Global.asax.cs:
        protected void Application_Error(object sender, EventArgs e) {

            var ex = Server.GetLastError().GetBaseException();

            Server.ClearError();
            var routeData = new RouteData();
            routeData.Values.Add("controller", "Error");
            routeData.Values.Add("action", "Index");

            if (ex.GetType() == typeof(HttpException)) {
                var httpException = (HttpException)ex;
                var code = httpException.GetHttpCode();
                routeData.Values.Add("status", code);
            } else {
                routeData.Values.Add("status", 500);
            }

            routeData.Values.Add("error", ex);

            IController errorController = new Kavand.Web.Controllers.ErrorController();
            errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
        }

        protected void Application_EndRequest(object sender, EventArgs e) {
            if (Context.Response.StatusCode == 401) { // this is important, because the 401 is not an error by default!!!
                throw new HttpException(401, "You are not authorised");
            }
        }

并且:

// in Error Controller:
    public class ErrorController : Controller {

        public ActionResult  Index(int status, Exception error) {
            Response.StatusCode = status;
            return View(status);
        }

        protected override void Dispose(bool disposing) {
            base.Dispose(disposing);
        }
    }

并且错误文件夹中的索引视图:

@* in ~/Views/Error/Index.cshtml: *@

@model Int32    
@{
    Layout = null;
}    
<!DOCTYPE html>    
<html>
<head>
    <title>Kavand | Error</title>
</head>
<body>
    <div>
        There was an error with your request. The error is:<br />
        <p style=" color: Red;">
        @switch (Model) {
            case 401: {
                    <span>Your message goes here...</span>
                }
                break;
            case 403: {
                    <span>Your message goes here...</span>
                }
                break;
            case 404: {
                    <span>Your message goes here...</span>
                }
                break;
            case 500: {
                    <span>Your message goes here...</span>
                }
                break;
            //and more cases for more error-codes...
            default: {
                    <span>Unknown error!!!</span>
                }
                break;
        }
        </p>
    </div>
</body>
</html>

AND -最后一步:

<!-- in web.config: -->

<customErrors mode="Off"/>

这似乎是有效的,但在使用Windows身份验证进行调试时,异常很烦人。在服务器上测试时,我确实不得不将httpErrors existingResponse="PassThrough"添加到Web配置中。谢谢你的帮助。 - Alex Nolasco
它抛出错误CS0151:switch表达式或case标签必须是bool、char、string、integral、enum或相应的可空类型。请帮忙解决? - codespider
当我在调试时这很有效,但是在服务器上我怎么让它工作呢?我在服务器上仍然看到默认的错误页面。IIS 的错误页面设置模式已关闭。 - Soenhay
我不知道。也许你的服务器配置不同。我在许多网站上使用了三年,一直很好用。抱歉 ): - amiry jd

11

我从未能够让web.config中的CustomErrors和MVC良好地协作,所以我放弃了。我改用下面的方法。

在global.asax中:

protected void Application_Error()
    {
        var exception = Server.GetLastError();
        var httpException = exception as HttpException;
        Response.Clear();
        Server.ClearError();
        var routeData = new RouteData();
        routeData.Values["controller"] = "Errors";
        routeData.Values["action"] = "General";
        routeData.Values["exception"] = exception;
        Response.StatusCode = 500;
        if (httpException != null)
        {
            Response.StatusCode = httpException.GetHttpCode();
            switch (Response.StatusCode)
            {
                case 403:
                    routeData.Values["action"] = "Http403";
                    break;
                case 404:
                    routeData.Values["action"] = "Http404";
                    break;
            }
        }
        // Avoid IIS7 getting in the middle
        Response.TrySkipIisCustomErrors = true;
        IController errorsController = new GNB.LG.StrategicPlanning.Website.Controllers.ErrorsController();
        HttpContextWrapper wrapper = new HttpContextWrapper(Context);
        var rc = new RequestContext(wrapper, routeData);
        errorsController.Execute(rc);
    }

在 ErrorsController 中:

public class ErrorsController
{
    public ActionResult General(Exception exception)
    {
        // log the error here
        return View(exception);
    }

    public ActionResult Http404()
    {
        return View("404");
    }

    public ActionResult Http403()
    {
        return View("403");
    }
}

在 web.config 文件中:

<customErrors mode="Off" />

这对我来说无论错误出现在哪里或者如何创建都有效。目前尚未处理401错误,但您可以相当容易地添加它。


1
谢谢你的回答 - 但是我已经看到了这种方法,但出于某种原因,它并不吸引我。 - Ahmad
1
@Ahmad 我也是,但它确实有效。:) 我发现在 web.config 中尝试使用 CustomErrors 进行设置,取决于错误被抛出的位置/方式,它并不总是起作用。 - Tridus
1
很棒的答案 - 除此之外,我还会在Application_Error中的代码块周围添加if (!HttpContext.Current.IsDebuggingEnabled),这样在调试时仍然可以显示错误。 - NightOwl888
1
+1,非常有帮助且有效的答案,几乎适用于所有类型的错误(我搜索了一个星期来控制web.config中的错误,但没有找到完整的解决方案) - aadi1295

9
也许我漏掉了一些东西,但是MVC有一个默认的全局ErrorHandlerAttribute,它使用自定义错误。这在这里解释得非常清楚。
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
}

您需要做的就是在配置文件中打开自定义错误,然后设置自定义错误重定向,最好重定向到静态的HTML文件(以防应用程序出现错误)。

<customErrors mode="On" defaultRedirect="errors.htm">
    <error statusCode="404" redirect="errors404.htm"/>
</customErrors>

如果您希望,还可以指定自定义的 Controller 来显示错误信息。在以下示例中,我仅使用了默认路由到名为 ErrorController,其中包含一个名为 Index 的操作,并且有一个名为id的字符串参数(用于接收错误代码)。当然,您也可以使用任何所需的路由。您的示例无法正常工作,因为您试图直接链接到 Views 目录,而没有经过 Controller。MVC .NET 不会直接服务于 Views 文件夹中的请求。
<customErrors mode="On" defaultRedirect="/error/index/500">
    <error statusCode="404" redirect="/error/index/404"/>
</customErrors>

ErrorHandlerAttribute可以广泛地与Controllers/Actions一起使用,将错误重定向到与Controller相关的命名Views。例如,当发生ArgumentException类型的异常时,您可以使用以下代码来显示名为MyArgumentErrorView

[ControllerAction,ExceptionHandler("MyArgumentError",typeof(ArgumentException))]
public void Index()
{
   // some code that could throw ArgumentExcepton
}

当然,另一个选择是更新Shared中的库存Error页面。

2
问题是IIS express已经处理了那些其他状态码(不是500),所以你提出的建议并不总是有效的(这取决于IIS的设置)。IIS只显示自己的页面,我们永远无法通过asp查看。 - David_001
没错,那只是IIS配置的问题,与此不同的是MVC错误处理应该按照上述方式完成。 - TheCodeKing

0

看一下你的 web.config 的第一部分,你直接指向了一个 .aspx 页面。当我设置错误页面时,我直接指向控制器和操作。例如:

<customErrors mode="On" defaultRedirect="~/Error/UhOh">
  <error statusCode="404" redirect="~/Error/NotFound" />
  <error statusCode="403" redirect="~/Error/AccessDenied" />
</customErrors>

我有一个包含所有必要操作的错误控制器。我认为MVC不太适合直接调用.aspx页面。


它可以与aspx页面一起使用,事实上,如果您正在使用重写模式,则必须使用静态内容文件(而不是操作)。 - Raul Vejar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接