【问题标题】:How to return a view for HttpNotFound() in ASP.Net MVC 3?如何在 ASP.Net MVC 3 中返回 HttpNotFound() 的视图?
【发布时间】:2011-06-26 12:18:33
【问题描述】:

有没有办法在每次从控制器返回 HttpNotFoundResult 时返回相同的视图?您如何指定此视图?我猜想在 web.config 中配置 404 页面可能会起作用,但我想知道是否有更好的方法来处理这个结果。

编辑/跟进:

我最终使用了在这个问题的第二个答案中找到的解决方案,并对 ASP.Net MVC 3 进行了一些微调来处理我的 404:How can I properly handle 404s in ASP.Net MVC?

【问题讨论】:

    标签: asp.net asp.net-mvc-3


    【解决方案1】:

    HttpNotFoundResult 不渲染视图。它只是将状态码设置为 404 并返回一个空结果,这对于 AJAX 之类的东西很有用,但如果你想要一个自定义的 404 错误页面,你可以throw new HttpException(404, "Not found") 它将自动在 web.config 中呈现配置的视图:

    <customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
       <error statusCode="404" redirect="/Http404.html" />
    </customErrors>
    

    【讨论】:

    • 如果是这样...返回 HttpNotFound() 有什么作用?
    • 有点不相关,但是您建议如何处理有人试图访问具有错误 id 的页面的情况?例如,stackoverflow 上有一个不好的问题 id? 404 HttpException?
    • @Rifk,这是一种方法。您也可以尝试在 IIS 中为不同的状态代码配置自定义错误页面,然后使用 HttpNotFoundResult
    • @Rifk 目的是:假设站点管理员已删除用户,但仍被搜索引擎索引。因此,在这种情况下,您可以从用户个人资料页面返回“HttpNotFound”,以便搜索引擎更新他们的结果。
    • 自定义错误页面呢?不能定义一个“错误”控制器并为每种错误创建相关的视图吗? (例如 404、500 等)???
    【解决方案2】:

    此解决方案结合了 IResultFilter 和 IExceptionFilter 来捕获从操作中抛出的 HttpException 或返回的 HttpStatusCodeResult。

    public class CustomViewForHttpStatusResultFilter: IResultFilter, IExceptionFilter
    {
        string viewName;
        int statusCode;
    
        public CustomViewForHttpStatusResultFilter(HttpStatusCodeResult prototype, string viewName)
            : this(prototype.StatusCode, viewName) {
        }
    
        public CustomViewForHttpStatusResultFilter(int statusCode, string viewName) {
            this.viewName = viewName;
            this.statusCode = statusCode;
        }
    
        public void OnResultExecuted(ResultExecutedContext filterContext) {
            HttpStatusCodeResult httpStatusCodeResult = filterContext.Result as HttpStatusCodeResult;
    
            if (httpStatusCodeResult != null && httpStatusCodeResult.StatusCode == statusCode) {
                ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
    
            }
        }
    
        public void OnResultExecuting(ResultExecutingContext filterContext) {
        }
    
        public void OnException(ExceptionContext filterContext) {
            HttpException httpException = filterContext.Exception as HttpException;
    
            if (httpException != null && httpException.GetHttpCode() == statusCode) {
                ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
                // This causes ELMAH not to log exceptions, so commented out
                //filterContext.ExceptionHandled = true;
            }
        }
    
        void ExecuteCustomViewResult(ControllerContext controllerContext) {
            ViewResult viewResult = new ViewResult();
            viewResult.ViewName = viewName;
            viewResult.ViewData = controllerContext.Controller.ViewData;
            viewResult.TempData = controllerContext.Controller.TempData;
            viewResult.ExecuteResult(controllerContext);
            controllerContext.HttpContext.Response.TrySkipIisCustomErrors = true;            
        }
    }
    

    您可以注册此过滤器,指定 HttpException 的 http 状态代码或要为其显示自定义视图的具体 HttpStatusCodeResult。

    GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(new HttpNotFoundResult(), "Error404"));
    // alternate syntax
    GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(404, "Error404"));
    

    它处理动作中抛出或返回的异常和 HttpStatusCodeResult。它不会处理在 MVC 选择合适的操作和控制器之前发生的错误,例如以下常见问题:

    • 未知路线
    • 未知控制器
    • 未知操作

    要处理这些类型的 NotFound 错误,请将此解决方案与 other solutions to be found in stackoverflow. 结合使用

    【讨论】:

    • 我将这里提供的解决方案与stackoverflow.com/a/27354140/2310818 结合起来处理各种错误(未知路由、未知控制器、未知操作、控制器操作返回的 HttpNotFound() 结果以及由控制器)。我实现了所有这些,同时根据错误代码的类型实现了正确的状态代码(404、500)。
    【解决方案3】:

    @Darin Dimitrov 提供的有用信息表明 HttpNotFoundResult 实际上返回的是空结果。

    经过一番研究。 MVC 3 的解决方法是派生所有 HttpNotFoundResultHttpUnauthorizedResultHttpStatusCodeResult 类并实现 new(覆盖它)HttpNotFound() 方法BaseController.

    最佳实践是使用基本控制器,这样您就可以“控制”所有派生控制器。

    我创建了新的HttpStatusCodeResult 类,不是从ActionResult 派生,而是从ViewResult 派生,以通过指定ViewName 属性来呈现视图或您想要的任何View。我按照原来的HttpStatusCodeResult 设置HttpContext.Response.StatusCodeHttpContext.Response.StatusDescription 但随后base.ExecuteResult(context) 将呈现合适的视图,因为我再次派生自ViewResult。够简单吧?希望这将在 MVC 核心中实现。

    在下面查看我的BaseController

    using System.Web;
    using System.Web.Mvc;
    
    namespace YourNamespace.Controllers
    {
        public class BaseController : Controller
        {
            public BaseController()
            {
                ViewBag.MetaDescription = Settings.metaDescription;
                ViewBag.MetaKeywords = Settings.metaKeywords;
            }
    
            protected new HttpNotFoundResult HttpNotFound(string statusDescription = null)
            {
                return new HttpNotFoundResult(statusDescription);
            }
    
            protected HttpUnauthorizedResult HttpUnauthorized(string statusDescription = null)
            {
                return new HttpUnauthorizedResult(statusDescription);
            }
    
            protected class HttpNotFoundResult : HttpStatusCodeResult
            {
                public HttpNotFoundResult() : this(null) { }
    
                public HttpNotFoundResult(string statusDescription) : base(404, statusDescription) { }
    
            }
    
            protected class HttpUnauthorizedResult : HttpStatusCodeResult
            {
                public HttpUnauthorizedResult(string statusDescription) : base(401, statusDescription) { }
            }
    
            protected class HttpStatusCodeResult : ViewResult
            {
                public int StatusCode { get; private set; }
                public string StatusDescription { get; private set; }
    
                public HttpStatusCodeResult(int statusCode) : this(statusCode, null) { }
    
                public HttpStatusCodeResult(int statusCode, string statusDescription)
                {
                    this.StatusCode = statusCode;
                    this.StatusDescription = statusDescription;
                }
    
                public override void ExecuteResult(ControllerContext context)
                {
                    if (context == null)
                    {
                        throw new ArgumentNullException("context");
                    }
    
                    context.HttpContext.Response.StatusCode = this.StatusCode;
                    if (this.StatusDescription != null)
                    {
                        context.HttpContext.Response.StatusDescription = this.StatusDescription;
                    }
                    // 1. Uncomment this to use the existing Error.ascx / Error.cshtml to view as an error or
                    // 2. Uncomment this and change to any custom view and set the name here or simply
                    // 3. (Recommended) Let it commented and the ViewName will be the current controller view action and on your view (or layout view even better) show the @ViewBag.Message to produce an inline message that tell the Not Found or Unauthorized
                    //this.ViewName = "Error";
                    this.ViewBag.Message = context.HttpContext.Response.StatusDescription;
                    base.ExecuteResult(context);
                }
            }
        }
    }
    

    要像这样在您的操作中使用:

    public ActionResult Index()
    {
        // Some processing
        if (...)
            return HttpNotFound();
        // Other processing
    }
    

    在 _Layout.cshtml 中(如母版页)

    <div class="content">
        @if (ViewBag.Message != null)
        {
            <div class="inlineMsg"><p>@ViewBag.Message</p></div>
        }
        @RenderBody()
    </div>
    

    此外,您可以使用Error.shtml 之类的自定义视图或创建新的NotFound.cshtml,就像我在代码中评论的那样,您可以为状态描述和其他解释定义视图模型。

    【讨论】:

      【解决方案4】:
      protected override void HandleUnknownAction(string actionName)
      {
          ViewBag.actionName  = actionName;
          View("Unknown").ExecuteResult(this.ControllerContext);
      }
      

      【讨论】:

        【解决方案5】:

        这是真正的答案,它允许在单个位置完全自定义错误页面。 无需修改 web.confiog 或创建复杂的类和代码。 也适用于 MVC 5。

        将此代码添加到控制器:

                if (bad) {
                    Response.Clear();
                    Response.TrySkipIisCustomErrors = true;
                    Response.Write(product + I(" Toodet pole"));
                    Response.StatusCode = (int)HttpStatusCode.NotFound;
                    //Response.ContentType = "text/html; charset=utf-8";
                    Response.End();
                    return null;
                }
        

        基于http://www.eidias.com/blog/2014/7/2/mvc-custom-error-pages

        【讨论】:

          【解决方案6】:

          如果您希望控制器中出现 httpnotfound 错误,请按照此操作

            public ActionResult Contact()
              {
          
                  return HttpNotFound();
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-10-06
            • 2011-10-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多