我正在使用RC2
使用URL路由的:
routes.MapRoute(
"Error",
"{*url}",
new { controller = "Errors", action = "NotFound" } // 404s
);
上面的代码似乎处理了这样的请求(假设初始MVC项目设置了默认的路由表):“/blah/blah”
在控制器本身中覆盖HandleUnknownAction()的:
// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
ViewData["actionName"] = actionName;
View("NotFound").ExecuteResult(this.ControllerContext);
}
但是,前面的策略不能处理对错误/未知控制器的请求。例如,我没有"/IDoNotExist",如果我请求它,我会从web服务器得到通用的404页面,而不是我的404,如果我使用routing + override的话。
所以最后,我的问题是:有没有办法使用MVC框架本身中的路由或其他东西来捕获这种类型的请求?
或者,我应该默认使用Web.Config customErrors作为我的404处理程序,然后忘记这一切吗?我假设如果我使用customErrors,我将不得不在/Views之外存储通用的404页面,因为Web.Config对直接访问的限制。
发布于 2009-03-06 21:49:27
代码取自http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net-mvc-rc2.aspx,也适用于ASP.net MVC1.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));
}
发布于 2010-04-05 13:46:08
404的要求
以下是我对404解决方案的要求,下面我展示了如何实现它:
视图(而不是一个静态页面),以后如果需要(good 404 designs),我可以向这个视图注入更多的数据,并且它们必须返回HTTP404状态码
解决方案
我认为您应该将Application_Error
保存在Global.asax中用于更高级的事情,比如未处理的异常和日志记录(就像Shay Jacoby's answer显示的那样),而不是404处理。这就是为什么我的建议将404内容排除在Global.asax文件之外的原因。
第1步:为404-错误逻辑找到一个公共位置
这对于可维护性来说是个好主意。使用ErrorController,以便将来对well designed 404 page的改进可以很容易地适应。此外,确保您的响应具有404code!
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中的404需要在许多地方捕获。第一个是HandleUnknownAction
。
InvokeHttp404
方法创建了一个公共位置,用于重新路由到ErrorController
和我们的新Http404
操作。想想DRY吧!
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:在您的控制器工厂中使用依赖注入,并连接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
处理程序。
这是第二个捕获404的地方。
第4步:为无法解析到应用程序中的urls添加到Global.asax的NotFound路由
此路由应指向我们的Http404
操作。注意到url
参数将是一个相对url,因为路由引擎在这里剥离了域部分?这就是为什么我们在步骤1中具有所有条件url逻辑的原因。
routes.MapRoute("NotFound", "{*url}",
new { controller = "Error", action = "Http404" });
这是在MVC应用程序中捕获404的第三个也是最后一个地方,您不需要自己调用它。如果你在这里没有捕捉到不匹配的路由,那么MVC会把问题传递给ASP.NET (Global.asax),在这种情况下你不会真的想要这样的。
步骤5:最后,当你的应用程序找不到东西时调用404
就像当一个坏的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);
}
如果所有这些都能用更少的代码连接到更少的地方,那就太好了,但我认为这个解决方案更容易维护,更容易测试,而且相当实用。
感谢你到目前为止的反馈。我很乐意得到更多。
注意:我对原始答案进行了重大修改,但目的/要求是相同的-这就是为什么我没有添加新答案的原因
发布于 2011-04-01 06:27:46
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();
}
}
示例项目
https://stackoverflow.com/questions/619895
复制相似问题