我如何正确处理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解决方案的要求,下面我展示了如何实现它:

  • 我想处理匹配的行为不好的行为
  • 我想处理匹配的路由坏控制器
  • 我想处理不匹配的路线(我的应用程序无法理解的任意url) - 我不希望这些冒泡到Global.asax或IIS,因为那样我就无法正确回到我的MVC应用程序中
  • 我想要一种方式来处理与上面相同的方式,自定义404s - 就像为一个不存在的对象提交了一个ID(可能被删除)一样,
  • 我希望我所有的404都返回一个MVC视图(不是静态页面),如果需要的话,稍后可以抽出更多数据(好的404设计),并且它们必须返回HTTP 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();
        }
    }
    

    示例项目

  • GitHub上的Test404
  • 现场网站
  • 链接地址: http://www.djcxy.com/p/20541.html

    上一篇: How can I properly handle 404 in ASP.NET MVC?

    下一篇: What problems could arise if I use POST instead of PUT and DELETE?