我如何正确处理ASP.NET MVC中的404?
我刚刚开始使用ASP.NET MVC,请耐心等待。 我搜索了这个网站和其他各种网站,并看到了一些这样的实现。
编辑:我忘了提及我正在使用RC2
使用URL路由:
routes.MapRoute(
"Error",
"{*url}",
new { controller = "Errors", action = "NotFound" } // 404s
);
上述似乎照顾这样的请求(假设初始MVC项目设置了默认路由表):“/ blah / blah / blah / blah”
在控制器中重写HandleUnknownAction():
// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
ViewData["actionName"] = actionName;
View("NotFound").ExecuteResult(this.ControllerContext);
}
但是之前的策略不处理对Bad / Unknown控制器的请求。 例如,如果我请求这个,我没有“/ IDoNotExist”,如果我使用routing + override,我从Web服务器获得通用404页面,而不是404。
所以最后,我的问题是:有什么办法在MVC框架中使用路由或其他方法来捕获这种请求?
或者我应该只是默认使用Web.Config customErrors作为我的404处理程序,并忘记所有这一切? 我假设如果我使用customErrors,由于Web.Config对直接访问的限制,我必须在/ Views之外存储通用404页面。 无论如何,任何最佳做法或指导表示赞赏。
该代码取自http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx并且工作在ASP.net MVC 1.0中也是如此
以下是我处理http异常的方法:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
// Log the exception.
ILogger logger = Container.Resolve<ILogger>();
logger.Error(exception);
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (httpException == null)
{
routeData.Values.Add("action", "Index");
}
else //It's an Http Exception, Let's handle it.
{
switch (httpException.GetHttpCode())
{
case 404:
// Page not found.
routeData.Values.Add("action", "HttpError404");
break;
case 500:
// Server error.
routeData.Values.Add("action", "HttpError500");
break;
// Here you can handle Views to other error codes.
// I choose a General error template
default:
routeData.Values.Add("action", "General");
break;
}
}
// Pass exception details to the target error View.
routeData.Values.Add("error", exception);
// Clear the error on server.
Server.ClearError();
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
// Call target Controller and pass the routeData.
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));
}
404的要求
以下是我对404解决方案的要求,下面我展示了如何实现它:
解
我认为你应该将Application_Error
保存在Global.asax中,以便处理更高的内容,例如未处理的异常和日志记录(如Shay Jacoby的答案显示),但不处理404。 这就是为什么我的建议保持了Global.asax文件中的404内容。
步骤1:为404错误逻辑建立一个共同的地方
这是一个可维护性的好主意。 使用ErrorController,以便将来对精心设计的404页面的改进可以轻松适应。 另外, 确保你的回复有404代码 !
public class ErrorController : MyController
{
#region Http404
public ActionResult Http404(string url)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
var model = new NotFoundViewModel();
// If the url is relative ('NotFound' route) then replace with Requested path
model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
Request.Url.OriginalString : url;
// Dont get the user stuck in a 'retry loop' by
// allowing the Referrer to be the same as the Request
model.ReferrerUrl = Request.UrlReferrer != null &&
Request.UrlReferrer.OriginalString != model.RequestedUrl ?
Request.UrlReferrer.OriginalString : null;
// TODO: insert ILogger here
return View("NotFound", model);
}
public class NotFoundViewModel
{
public string RequestedUrl { get; set; }
public string ReferrerUrl { get; set; }
}
#endregion
}
第2步:使用基本控制器类,以便您可以轻松调用自定义404操作并连线HandleUnknownAction
ASP.NET MVC中的404s需要在许多地方被捕获。 首先是HandleUnknownAction
。
InvokeHttp404
方法为重新路由到ErrorController
和我们新的Http404
操作创建了一个公共位置。 认为干!
public abstract class MyController : Controller
{
#region Http404 handling
protected override void HandleUnknownAction(string actionName)
{
// If controller is ErrorController dont 'nest' exceptions
if (this.GetType() != typeof(ErrorController))
this.InvokeHttp404(HttpContext);
}
public ActionResult InvokeHttp404(HttpContextBase httpContext)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
var errorRoute = new RouteData();
errorRoute.Values.Add("controller", "Error");
errorRoute.Values.Add("action", "Http404");
errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
errorController.Execute(new RequestContext(
httpContext, errorRoute));
return new EmptyResult();
}
#endregion
}
第3步:在Controller Factory中使用依赖注入并连接404 HttpExceptions
像这样(它不一定是StructureMap):
MVC1.0例子:
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
try
{
if (controllerType == null)
return base.GetControllerInstance(controllerType);
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);
return errorController;
}
else
throw ex;
}
return ObjectFactory.GetInstance(controllerType) as Controller;
}
}
MVC2.0例子:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
return base.GetControllerInstance(requestContext, controllerType);
}
catch (HttpException ex)
{
if (ex.GetHttpCode() == 404)
{
IController errorController = ObjectFactory.GetInstance<ErrorController>();
((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);
return errorController;
}
else
throw ex;
}
return ObjectFactory.GetInstance(controllerType) as Controller;
}
我认为最好能够更接近他们出现的位置。 这就是为什么我更喜欢上面的Application_Error
处理程序。
这是抓住404s的第二个地方。
第4步:为无法解析到您的应用的网址添加一个NotFound路由到Global.asax
这条路线应该指向我们的Http404
动作。 请注意, url
参数将是一个相对url,因为路由引擎正在剥离域部分? 这就是为什么我们在步骤1中具有所有条件网址逻辑的原因。
routes.MapRoute("NotFound", "{*url}",
new { controller = "Error", action = "Http404" });
这是第三个也是最后一个在MVC应用程序中捕获404s的地方,您不会自行调用。 如果你在这里没有捕获到不匹配的路由,那么MVC会将问题传递给ASP.NET(Global.asax),并且在这种情况下你并不真正需要这样做。
第5步:最后,当你的应用找不到东西时调用404s
就像当一个错误的ID被提交给我的Loans控制器(从MyController
派生)一样:
//
// GET: /Detail/ID
public ActionResult Detail(int ID)
{
Loan loan = this._svc.GetLoans().WithID(ID);
if (loan == null)
return this.InvokeHttp404(HttpContext);
else
return View(loan);
}
如果所有这些都可以用更少的代码连接到更少的地方,那将是非常好的,但我认为这个解决方案更易于维护,更具可测试性和相当实用。
感谢迄今为止的反馈。 我很想得到更多。
注意:这已经从我原来的答案中大幅编辑,但目的/要求是相同的 - 这就是为什么我没有添加新的答案
ASP.NET MVC不支持自定义404页面。 自定义控制器工厂,捕获所有路径,使用HandleUnknownAction
基础控制器类 - argh!
到目前为止IIS自定义错误页面是更好的选择:
web.config中
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
</httpErrors>
</system.webServer>
ErrorController
public class ErrorController : Controller
{
public ActionResult PageNotFound()
{
Response.StatusCode = 404;
return View();
}
}
示例项目
上一篇: How can I properly handle 404 in ASP.NET MVC?
下一篇: What problems could arise if I use POST instead of PUT and DELETE?