【发布时间】:2010-10-17 14:05:11
【问题描述】:
【问题讨论】:
标签: asp.net-mvc error-handling
【问题讨论】:
标签: asp.net-mvc error-handling
我研究了 A LOT 关于如何正确管理 MVC 中的 404(特别是 MVC3),恕我直言,这是我想出的最佳解决方案:
在 global.asax 中:
public class MvcApplication : HttpApplication
{
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 404)
{
Response.Clear();
var rd = new RouteData();
rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
rd.Values["controller"] = "Errors";
rd.Values["action"] = "NotFound";
IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
}
}
}
错误控制器:
public sealed class ErrorsController : Controller
{
public ActionResult NotFound()
{
ActionResult result;
object model = Request.Url.PathAndQuery;
if (!Request.IsAjaxRequest())
result = View(model);
else
result = PartialView("_NotFound", model);
return result;
}
}
编辑:
如果您使用 IoC(例如 AutoFac),您应该使用以下方法创建控制器:
var rc = new RequestContext(new HttpContextWrapper(Context), rd);
var c = ControllerBuilder.Current.GetControllerFactory().CreateController(rc, "Errors");
c.Execute(rc);
代替
IController c = new ErrorsController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
(可选)
说明:
我可以想到 6 种情况,其中 ASP.NET MVC3 应用程序可以生成 404。
由 ASP.NET 生成:
由 ASP.NET MVC 生成:
场景 2: URL 匹配路由,但指定的控制器不存在。
场景 3: URL 匹配一个路由,但指定了一个不存在的操作。
手动生成:
场景4:动作通过使用HttpNotFound()方法返回一个HttpNotFoundResult。
场景 5: 一个动作抛出一个状态码为 404 的 HttpException。
场景 6: 某个操作手动将 Response.StatusCode 属性修改为 404。
目标
(A)向用户显示自定义 404 错误页面。
(B) 维护客户端响应中的 404 状态代码(对 SEO 特别重要)。
(C) 直接发送响应,不涉及 302 重定向。
解决方案尝试:自定义错误
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/Errors/NotFound"/>
</customErrors>
</system.web>
此解决方案的问题:
解决方案尝试:HTTP 错误
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
此解决方案的问题:
解决方案尝试:HTTP 替换错误
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
此解决方案的问题:
解决方案尝试 customErrors 和 HTTP 错误
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/Errors/NotFound"/>
</customError>
</system.web>
和
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
此解决方案的问题:
以前对此感到困扰的人甚至尝试创建自己的库(请参阅http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html)。但之前的解决方案似乎涵盖了所有场景,没有使用外部库的复杂性。
【讨论】:
Response.Clear 有什么问题?投掷如此神秘的评论和博文,以“不要使用那种方法”作为结尾,然后在被要求澄清时保持沉默是没有帮助的。
另一种解决方案。
添加带有 404 错误信息的 ErrorControllers 或静态页面。
修改您的 web.config(如果是控制器)。
<system.web>
<customErrors mode="On" >
<error statusCode="404" redirect="~/Errors/Error404" />
</customErrors>
</system.web>
或者在静态页面的情况下
<system.web>
<customErrors mode="On" >
<error statusCode="404" redirect="~/Static404.html" />
</customErrors>
</system.web>
这将处理错过的路线和错过的动作。
【讨论】:
<customErrors mode="RemoteOnly">查看实际的错误页面。
return HttpNotFound(); 从控制器操作返回为 ActionResult 则不起作用。
Marco 的回应是最好的解决方案。我需要控制我的错误处理,我的意思是真正控制它。当然,我稍微扩展了解决方案并创建了一个完整的错误管理系统来管理所有内容。我也在其他博客中读到过这个解决方案,它似乎很容易被大多数高级开发人员接受。
这是我使用的最终代码:
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 404)
{
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.Clear();
Server.ClearError();
var routeData = new RouteData();
routeData.Values["controller"] = "ErrorManager";
routeData.Values["action"] = "Fire404Error";
routeData.Values["exception"] = exception;
Response.StatusCode = 500;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (Response.StatusCode)
{
case 404:
routeData.Values["action"] = "Fire404Error";
break;
}
}
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
IController errormanagerController = new ErrorManagerController();
HttpContextWrapper wrapper = new HttpContextWrapper(Context);
var rc = new RequestContext(wrapper, routeData);
errormanagerController.Execute(rc);
}
}
在我的 ErrorManagerController 内部:
public void Fire404Error(HttpException exception)
{
//you can place any other error handling code here
throw new PageNotFoundException("page or resource");
}
现在,在我的操作中,我正在抛出我创建的自定义异常。我的控制器继承自我创建的基于控制器的自定义类。创建自定义基本控制器是为了覆盖错误处理。这是我的自定义基本控制器类:
public class MyBasePageController : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
filterContext.GetType();
filterContext.ExceptionHandled = true;
this.View("ErrorManager", filterContext).ExecuteResult(this.ControllerContext);
base.OnException(filterContext);
}
}
上面代码中的“ErrorManager”只是一个使用基于ExceptionContext的模型的视图
我的解决方案完美运行,我能够处理我网站上的任何错误,并根据任何异常类型显示不同的消息。
【讨论】:
看起来这是捕捉一切的最佳方式。
【讨论】:
在 IIS 中,您可以根据错误代码指定重定向到“特定”页面。在您的示例中,您可以配置 404 -> 您自定义的 404 错误页面。
【讨论】:
我可以推荐的是查看FilterAttribute。例如 MVC 已经有 HandleErrorAttribute。您可以将其自定义为仅处理 404。如果您感兴趣,请回复我会看示例。
顺便说一句
您在上一个问题中接受的解决方案(最后一条路线)在大多数情况下都不起作用。使用 HandleUnknownAction 的第二种解决方案将起作用,但需要在每个控制器中进行此更改或具有单个基本控制器。
我的选择是使用 HandleUnknownAction 的解决方案。
【讨论】: