如何在ASP.Net MVC 3中返回HttpNotFound()的视图?

时间:2011-02-13 17:09:19

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

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

编辑/跟进:

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

6 个答案:

答案 0 :(得分:64)

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

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

答案 1 :(得分:17)

此解决方案结合了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.

结合使用

答案 2 :(得分:14)

来自@Darin Dimitrov的有用信息HttpNotFoundResult实际上返回空结果。

经过一番研究。此处 MVC 3 的解决方法是派生所有HttpNotFoundResultHttpUnauthorizedResultHttpStatusCodeResult类并实施 new (覆盖它){ HttpNotFound中的{1}}()方法。

最佳做法是使用基本控制器,以便“控制”所有派生的控制器。

我创建了新的BaseController课程,不是来自HttpStatusCodeResult而是来自ActionResult,以通过指定ViewResult来呈现视图或任何View属性。我按原始ViewName设置HttpStatusCodeResultHttpContext.Response.StatusCode,但HttpContext.Response.StatusDescription将呈现合适的视图,因为我再次从base.ExecuteResult(context)派生。这很简单吗?希望这将在MVC核心中实现。

见下文ViewResult

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);
            }
        }
    }
}

在_Layout.cshtml(如母版页)

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

此外,您可以使用自定义视图,例如<div class="content"> @if (ViewBag.Message != null) { <div class="inlineMsg"><p>@ViewBag.Message</p></div> } @RenderBody() </div> 或创建新的Error.shtml,就像我在代码中注释一样,您可以为状态说明和其他说明定义视图模型。

答案 3 :(得分:2)

protected override void HandleUnknownAction(string actionName)
{
    ViewBag.actionName  = actionName;
    View("Unknown").ExecuteResult(this.ControllerContext);
}

答案 4 :(得分:0)

这是真正的答案,它允许在一个地方完全自定义错误页面。 无需修改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

答案 5 :(得分:0)

如果您想在控制器中使用httpnotfound错误,请按照此操作

GetAll()