我调查了很多关于如何在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)); } } }
ErrorsController:
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应用程序可以生成404s.
由ASP.NET生成:
场景1: URL与路由表中的路由不匹配.
由ASP.NET MVC生成:
场景2: URL匹配路由,但指定不存在的控制器.
场景3: URL匹配路由,但指定不存在的操作.
手动生成:
场景4:操作使用方法HttpNotFound()返回HttpNotFoundResult.
场景5:操作抛出HttpException,状态代码为404.
方案6:操作手动将Response.StatusCode属性修改为404.
目标
(A)向用户显示自定义404错误页面.
(B)维护客户端响应的404状态代码(对SEO特别重要).
(C)直接发送响应,不涉及302重定向.
解决方案尝试:自定义错误
此解决方案的问题:
不符合方案(1),(4),(6)中的目标(A).
不自动符合目标(B).必须手动编程.
不符合目标(C).
解决方案尝试:HTTP错误
此解决方案的问题:
仅适用于IIS 7+.
不符合方案(2),(3),(5)中的目标(A).
不自动符合目标(B).必须手动编程.
解决方案尝试:带有替换的HTTP错误
此解决方案的问题:
仅适用于IIS 7+.
不自动符合目标(B).必须手动编程.
它模糊了应用程序级别的http异常.例如,不能使用customErrors部分,System.Web.Mvc.HandleErrorAttribute等.它不仅可以显示通用错误页面.
解决方案尝试customErrors和HTTP错误
和
此解决方案的问题:
仅适用于IIS 7+.
不自动符合目标(B).必须手动编程.
不符合方案(2),(3),(5)中的目标(C).
在尝试创建自己的库之前,对此感到困扰的人们(请参阅http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html).但是之前的解决方案似乎涵盖了所有场景,而没有使用外部库的复杂性.
我调查了很多关于如何在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)); } } }
ErrorsController:
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应用程序可以生成404s.
由ASP.NET生成:
场景1: URL与路由表中的路由不匹配.
由ASP.NET MVC生成:
场景2: URL匹配路由,但指定不存在的控制器.
场景3: URL匹配路由,但指定不存在的操作.
手动生成:
场景4:操作使用方法HttpNotFound()返回HttpNotFoundResult.
场景5:操作抛出HttpException,状态代码为404.
方案6:操作手动将Response.StatusCode属性修改为404.
目标
(A)向用户显示自定义404错误页面.
(B)维护客户端响应的404状态代码(对SEO特别重要).
(C)直接发送响应,不涉及302重定向.
解决方案尝试:自定义错误
此解决方案的问题:
不符合方案(1),(4),(6)中的目标(A).
不自动符合目标(B).必须手动编程.
不符合目标(C).
解决方案尝试:HTTP错误
此解决方案的问题:
仅适用于IIS 7+.
不符合方案(2),(3),(5)中的目标(A).
不自动符合目标(B).必须手动编程.
解决方案尝试:带有替换的HTTP错误
此解决方案的问题:
仅适用于IIS 7+.
不自动符合目标(B).必须手动编程.
它模糊了应用程序级别的http异常.例如,不能使用customErrors部分,System.Web.Mvc.HandleErrorAttribute等.它不仅可以显示通用错误页面.
解决方案尝试customErrors和HTTP错误
和
此解决方案的问题:
仅适用于IIS 7+.
不自动符合目标(B).必须手动编程.
不符合方案(2),(3),(5)中的目标(C).
在尝试创建自己的库之前,对此感到困扰的人们(请参阅http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html).但是之前的解决方案似乎涵盖了所有场景,而没有使用外部库的复杂性.
又一个解决方案.
添加ErrorControllers或静态页面以及404错误信息.
修改web.config(如果是控制器).
或者在静态页面的情况下
这将处理错过的路线和错过的行动.
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"); }
现在,在我的Action中,我正在抛出我创建的自定义异常.我的Controller继承自我创建的自定义Controller Based类.创建自定义基本控制器以覆盖错误处理.这是我的自定义基本控制器类:
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的Model的视图
我的解决方案完美无缺,我可以在我的网站上处理任何错误,并根据任何异常类型显示不同的消息.