ASP.NET MVC 4 - 异常处理不起作用

11
在我的ASP.NET MVC 4应用程序中出现错误时,我希望根据错误类型为用户自定义一个视图。例如,找不到页面或发生异常(包含一些用户友好的异常详细信息)。我已经查看了StackOverflow和其他在线资源上如何做到这一点的其他示例,但是没有一个答案适用于我。
基本的[HandleError]属性似乎无法在针对.NET 4.5的MVC 4应用程序中使用VS2012。以下是我在主控制器中的代码:
[HandleError]
public ActionResult Index()
{
    Response.TrySkipIisCustomErrors = true; //doesn't work with or without this
    throw new NullReferenceException("Uh oh, something broke.");
}

它只是抛出了一个异常,我期望由于[HandleError]属性,返回默认的~/Shared/Error.cshtml视图,但是我得到的是HTTP 500内部服务器错误,表示无法显示页面。我检查了我的web.config文件,不同的配置似乎表现出奇怪的行为。在部分中,它当前包含:

<customErrors mode="On" />

我尝试添加了defaultRedirect和自定义错误模式mode="Off",但是都没有任何效果……无论是共享的Error视图还是我拥有的CustomError视图都没有被渲染。如果我将customErrors mode更改为off,则可以按预期查看异常详细信息,因此它确实正确地抛出了“糟糕,出错了”异常。

我还尝试在HomeController中添加OnException处理程序,虽然可以通过调试看到OnException事件已被触发,但它并没有产生任何影响:

protected override void OnException(ExceptionContext filterContext)
{
    base.OnException(filterContext);
    filterContext.ExceptionHandled = true;
    if (filterContext == null)
    {
        filterContext.Result = View("CustomError");
        return;
    }
    Exception e = filterContext.Exception;
    // TODO: Log the exception here
    ViewData["Exception"] = e; // pass the exception to the view
    filterContext.Result = View("CustomError");
}

我还尝试过更改[HandleError]以指定视图,但似乎也没有任何作用:
[HandleError(View="CustomError")]

非常感谢您的帮助。如果您需要更多详细信息,请告诉我。

4个回答

5

我曾经阅读了无数的stackoverflow答案和各种博客文章,试图让自定义错误页面起作用。以下是最终适合我的方法。

第一步是使用IIS Express进行调试,而不是内置的Cassini Web服务器,以“保证”调试体验与实际环境相同。

创建一个控制器来处理应用程序错误,并为每个自定义错误添加一个操作。 操作应该做任何日志记录、设置状态代码并返回视图。

public class ErrorsController : Controller
{
    // 404
    [HttpGet]
    public ActionResult NotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    // I also have test actions so that I can verify it's working in production.
    [HttpGet]
    public ActionResult Throw404()
    {
        throw new HttpException((int)HttpStatusCode.NotFound, "demo");
    }
}

在web.config文件中配置customErrors部分,以便重定向到您的自定义错误操作。

  <system.web>
    <customErrors mode="RemoteOnly" defaultRedirect="Errors/InternalServerError">
      <error statusCode="400" redirect="Errors/BadRequest" />
      <error statusCode="403" redirect="Errors/Forbidden" />
      <error statusCode="404" redirect="Errors/NotFound" />
      <error statusCode="500" redirect="Errors/InternalServerError" />
    </customErrors>

在web.config文件中,将httpErrors部分添加到system.webServer中,并将errorMode设置为Detailed。为什么这样做能起作用我不清楚,但这是关键。
  <system.webServer>
    <httpErrors errorMode="Detailed" />

将一个“catchall”路由添加到定义的路由的最后,将404重定向到自定义页面。
            // catchall for 404s
        routes.MapRoute(
            "Error",
            "{*url}",
            new {controller = "Errors", action = "NotFound"});

你可能还想创建一个自定义的HandleErrorAttribute并将其注册为全局过滤器以记录500错误。
这些步骤对我在开发(IIS Express)和生产(IIS7)环境中都有效。你需要将customErrors mode="On"更改为在开发中看到效果。

我检查了一下,从“项目属性> Web”中,默认设置为“使用本地IIS Web服务器”,并选中了IIS Express。检查其余部分需要一点时间。 - Robert Becker
1
删除了我的404的catch-all路由,修改了web.config,创建了ErrorsController,并连接了Errors/{Action}路由后,它似乎没有按预期工作。就好像IIS服务器在出现这些状态码时没有正确地重定向到Errors/<path>。导航到<site>/Errors/<action>可以正常工作,但是当我设置Response.StatusCode时,我没有得到自定义页面。如果我删除StatusCode,那么我会得到自定义页面,但它不会触发其他未找到路径或异常(<site>/blah)。我已经进行了本地和远程测试。 - Robert Becker
如果可以找到重定向不起作用的原因,那么这种方法可能是可行的(除非需要将实际异常传递给InternalServerError视图以呈现自定义错误页面,否则可能无法通过)。 - Robert Becker
为了使自定义错误页面起作用,需要发生错误,设置Response.StatusCode不会触发它们(例如返回HttpNotFoundResult()也不会)。应用程序需要像我的ErrorsController中的Throw404操作一样抛出异常。如果您需要解释错误并提供特定页面,请使用自定义HandleErrorAttribute捕获500并重定向。 - Jamie Ide
这并不真正起作用。最终的错误URL仍然发送200 OK状态代码。 - Matthew Lock

4

我记得你需要从一个非localhost的IP地址(通常是另一台计算机)调用页面。而且它必须是基于IIS的服务器,而不是内置的开发服务器(因此需要IIS或IIS Express,但是您必须配置IIS Express以进行外部访问,这很麻烦)。

实际上可以对其进行调试,您需要在调试计算机上配置本地服务器以接受外部请求,然后从远程服务器调用本地服务器。


2
尽管我在 web.config 中没有使用 RemoteOnly,但我的原始代码似乎在远程工作,但在本地却无法正常工作!这对我来说非常令人惊讶。为了渲染视图,我确实不得不在 web.config 的 <system.webserver> 部分中添加了 <modules runAllManagedModulesForAllRequests="true" />,但这似乎很正常。这使得在本地进行调试更加困难,而且似乎是一个 bug,因为没有使用 mode=RemoteOnly。如果有人知道如何在本地使其正常工作,请告诉我。(我可能会开另一个问题专门讨论这个问题。) - Robert Becker

2

我曾经遇到过类似的问题,花了一些时间来弄清楚发生了什么。因此,以防其他人遇到类似的问题,这就是我的问题所在。

错误页面试图使用我的_Layout页面。只需确保您的Error.cshtml页面具有以下内容:

@{ Layout = null; }


1

尝试将以下属性添加到customErrors标记中:

defaultRedirect="Error"

这明确定义了当错误被抛出且在属性中未指定视图时,MVC 应将用户重定向到哪个视图。当然,这仅在 Shared 视图文件夹中存在 Error.cshtml 文件时才能起作用。


我终于开始尝试这些建议了。看起来使用<customErrors mode="On" defaultRedirect="Error" />返回了我的PageNotFound错误页面,而不是共享错误视图的内容,并且ViewData中没有异常数据。我以为这是因为服务器上实际上并不存在/Error,web.config也不知道实际视图的任何信息...但是当我修改OnException处理程序也使用"Error"视图时,结果与一直看到的HTTP 500错误相同,我不再看到PageNotFound页面。 :( - Robert Becker

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