首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在ASP.NET MVC中正确处理404?

如何在ASP.NET MVC中正确处理404?
EN

Stack Overflow用户
提问于 2009-03-06 18:21:39
回答 19查看 153.7K关注 0票数 443

我正在使用RC2

使用URL路由的

代码语言:javascript
复制
routes.MapRoute(
    "Error",
     "{*url}",
     new { controller = "Errors", action = "NotFound" }  // 404s
);

上面的代码似乎处理了这样的请求(假设初始MVC项目设置了默认的路由表):“/blah/blah”

在控制器本身中覆盖HandleUnknownAction()的

代码语言:javascript
复制
// 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对直接访问的限制。

EN

回答 19

Stack Overflow用户

回答已采纳

发布于 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异常的方法:

代码语言:javascript
复制
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));
}
票数 278
EN

Stack Overflow用户

发布于 2010-04-05 13:46:08

404的要求

以下是我对404解决方案的要求,下面我展示了如何实现它:

  • 我想用坏的操作处理匹配的路由
  • 我想用坏的控制器处理匹配的路由
  • 我想处理不匹配的路由(我的应用程序无法理解的任意urls )-我不希望这些冒泡到Global.asax或IIS,因为那样i can't redirect back into my MVC app properly
  • I想要以与上面相同的方式处理。自定义404 --就像当一个不存在(可能被删除)的对象的ID被提交时,我希望我的所有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

代码语言:javascript
复制
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吧!

代码语言:javascript
复制
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示例:

代码语言:javascript
复制
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示例:

代码语言:javascript
复制
    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逻辑的原因。

代码语言:javascript
复制
        routes.MapRoute("NotFound", "{*url}", 
            new { controller = "Error", action = "Http404" });

这是在MVC应用程序中捕获404的第三个也是最后一个地方,您不需要自己调用它。如果你在这里没有捕捉到不匹配的路由,那么MVC会把问题传递给ASP.NET (Global.asax),在这种情况下你不会真的想要这样的。

步骤5:最后,当你的应用程序找不到东西时调用404

就像当一个坏的ID被提交给我的Loans控制器时(从MyController派生):

代码语言:javascript
复制
    //
    // 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);
    }

如果所有这些都能用更少的代码连接到更少的地方,那就太好了,但我认为这个解决方案更容易维护,更容易测试,而且相当实用。

感谢你到目前为止的反馈。我很乐意得到更多。

注意:我对原始答案进行了重大修改,但目的/要求是相同的-这就是为什么我没有添加新答案的原因

票数 257
EN

Stack Overflow用户

发布于 2011-04-01 06:27:46

ASP.NET MVC并不能很好地支持自定义的404页面。自定义控制器工厂,通用路由,使用HandleUnknownAction - argh的基本控制器类!

到目前为止,IIS自定义错误页是更好的替代方案:

web.config

代码语言:javascript
复制
<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" />
    <error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
  </httpErrors>
</system.webServer>

ErrorController

代码语言:javascript
复制
public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View();
    }
}

示例项目

票数 240
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/619895

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档