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

如何在ASP.Net MVC 3中返回HttpNotFound()的视图?,asp.net,asp.net-mvc-3,Asp.net,Asp.net Mvc 3,是否有一种方法可以在每次从控制器返回HttpNotFoundResult时返回相同的视图?如何指定此视图?我猜在web.config中配置404页面可能会有用,但我想知道是否有更好的方法来处理这个结果 编辑/跟进: 最后,我使用了这个问题的第二个答案中的解决方案,对ASP.Net MVC 3进行了一些微调,以处理我的404s:HttpNotFoundResult不呈现视图。它只是将状态代码设置为404,并返回一个空结果,这对AJAX之类的事情很有用,但是如果您想要自定义404错误页面,您可以抛出

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

编辑/跟进:


最后,我使用了这个问题的第二个答案中的解决方案,对ASP.Net MVC 3进行了一些微调,以处理我的404s:

HttpNotFoundResult
不呈现视图。它只是将状态代码设置为404,并返回一个空结果,这对AJAX之类的事情很有用,但是如果您想要自定义404错误页面,您可以
抛出新的HttpException(404,“未找到”)
,它将自动在web.config中呈现配置的视图:

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

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

经过一些研究。MVC 3的解决方法是在
BaseController
中派生所有
HttpNotFoundResult
HttpUnauthorizedResult
HttpStatusCodeResult
类,并实现新的(覆盖它)
HttpNotFound
()方法

最好使用基本控制器,这样您就可以“控制”所有派生控制器

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

请参见下面的my
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中(如母版页)


@如果(ViewBag.Message!=null)
{
@查看包。留言

} @RenderBody()

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

此解决方案结合了IResultFilter和IEExceptionFilter来捕获抛出的HttpException或返回的HttpExceptionHttpStatusCodeResult来自操作中

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错误,请将此解决方案与

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

将此代码添加到控制器:

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

基于

如果希望在控制器中发现httpnotfound错误,请遵循此步骤

  public ActionResult Contact()
    {

        return HttpNotFound();
    }

如果是这样的话。。。返回HttpNotFound()的目的是什么?有点不相关,但您建议如何处理有人试图使用错误id访问页面的情况?例如,stackoverflow上的错误问题id?404 HttpException?@Rifk,这是一种方法。您还可以尝试在IIS中为不同的状态代码配置自定义错误页,然后使用
HttpNotFoundResult
@Rifk。其目的是:假设站点管理员已删除该用户,但该用户仍被搜索引擎索引。因此,在这种情况下,您可以从用户配置文件页面返回“HttpNotFound”,以便搜索引擎更新其结果。那么自定义错误页面呢?无法定义“错误”控制器并为每种错误创建关联视图吗?(例如404、500等)??我将这里提供的解决方案与结合起来,以处理各种错误(未知路由、未知控制器、未知操作、控制器操作返回的HttpNotFound()结果以及控制器引发的HttpException)。我实现了所有这些,同时根据错误代码的类型实现了正确的状态代码(404500)。
        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;
        }
  public ActionResult Contact()
    {

        return HttpNotFound();
    }