从ASP.NET MVC操作发送HTTP 404响应的正确方式是什么?

从ASP.NET MVC操作发送HTTP 404响应的正确方式是什么?,asp.net,asp.net-mvc,http-status-code-404,Asp.net,Asp.net Mvc,Http Status Code 404,如果给定路线: {FeedName}/{ItemPermalink} ex:/Blog/Hello World 如果该项目不存在,我想返回404。在ASP.NET MVC中,正确的方法是什么?从臀部拍摄(牛仔编码;-),我建议如下: throw new HttpException(404, "Are you sure you're in the right place?"); public ActionResult Index() { // Some processing if

如果给定路线:

{FeedName}/{ItemPermalink}

ex:/Blog/Hello World

如果该项目不存在,我想返回404。在ASP.NET MVC中,正确的方法是什么?

从臀部拍摄(牛仔编码;-),我建议如下:

throw new HttpException(404, "Are you sure you're in the right place?");
public ActionResult Index()
{
    // Some processing
    if (...)
        return HttpNotFound();
    // Other processing
}
控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return new HttpNotFoundResult("This doesn't exist");
    }
}
using System;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace YourNamespaceHere
{
    /// <summary>An implementation of <see cref="ActionResult" /> that throws an <see cref="HttpException" />.</summary>
    public class HttpNotFoundResult : ActionResult
    {
        /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with the specified <paramref name="message"/>.</summary>
        /// <param name="message"></param>
        public HttpNotFoundResult(String message)
        {
            this.Message = message;
        }

        /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with an empty message.</summary>
        public HttpNotFoundResult()
            : this(String.Empty) { }

        /// <summary>Gets or sets the message that will be passed to the thrown <see cref="HttpException" />.</summary>
        public String Message { get; set; }

        /// <summary>Overrides the base <see cref="ActionResult.ExecuteResult" /> functionality to throw an <see cref="HttpException" />.</summary>
        public override void ExecuteResult(ControllerContext context)
        {
            throw new HttpException((Int32)HttpStatusCode.NotFound, this.Message);
        }
    }
}
// By Erik van Brakel, with edits from Daniel Schaffer :)

HttpNotFoundResult:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return new HttpNotFoundResult("This doesn't exist");
    }
}
using System;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace YourNamespaceHere
{
    /// <summary>An implementation of <see cref="ActionResult" /> that throws an <see cref="HttpException" />.</summary>
    public class HttpNotFoundResult : ActionResult
    {
        /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with the specified <paramref name="message"/>.</summary>
        /// <param name="message"></param>
        public HttpNotFoundResult(String message)
        {
            this.Message = message;
        }

        /// <summary>Initializes a new instance of <see cref="HttpNotFoundResult" /> with an empty message.</summary>
        public HttpNotFoundResult()
            : this(String.Empty) { }

        /// <summary>Gets or sets the message that will be passed to the thrown <see cref="HttpException" />.</summary>
        public String Message { get; set; }

        /// <summary>Overrides the base <see cref="ActionResult.ExecuteResult" /> functionality to throw an <see cref="HttpException" />.</summary>
        public override void ExecuteResult(ControllerContext context)
        {
            throw new HttpException((Int32)HttpStatusCode.NotFound, this.Message);
        }
    }
}
// By Erik van Brakel, with edits from Daniel Schaffer :)
使用系统;
Net系统;
使用System.Web;
使用System.Web.Mvc;
名称空间YourNamespaceHere
{
///它的一个实现抛出了一个错误。
公共类HttpNotFoundResult:ActionResult
{
///使用指定的初始化的新实例。
/// 
公共HttpNotFoundResult(字符串消息)
{
this.Message=消息;
}
///使用空消息初始化的新实例。
公共HttpNotFoundResult()
:this(String.Empty){}
///获取或设置将传递给抛出方的消息。
公共字符串消息{get;set;}
///覆盖基本功能以引发错误。
公共覆盖无效ExecuteSult(ControllerContext上下文)
{
抛出新的HttpException((Int32)HttpStatusCode.NotFound,this.Message);
}
}
}
//埃里克·范·布拉克尔,丹尼尔·谢弗编辑:)
使用这种方法,您可以遵守框架标准。那里已经有一个HttpUnauthorizedResult,因此这将在以后维护代码的其他开发人员(你知道,知道你住在哪里的神经病)眼中简单地扩展框架

您可以使用reflector查看组件,以了解如何实现HttpUnauthorizedResult,因为我不知道这种方法是否遗漏了任何内容(它似乎太简单了)


我刚才确实使用reflector查看了HttpUnauthorizedResult。似乎他们正在将响应的状态码设置为0x191(401)。虽然这适用于401,但使用404作为新值,我在Firefox中似乎只得到了一个空白页。Internet Explorer显示默认的404(不是ASP.NET版本)。使用webdeveloper工具栏,我检查了FF中的标题,它确实显示了404NotFound响应。可能只是我在FF中的错误配置



尽管如此,我认为杰夫的方法是接吻的一个很好的例子。如果您真的不需要这个示例中的详细信息,那么他的方法也可以很好地工作。

我们就是这样做的;此代码位于
BaseController

/// <summary>
/// returns our standard page not found view
/// </summary>
protected ViewResult PageNotFound()
{
    Response.StatusCode = 404;
    return View("PageNotFound");
}

HttpNotFoundResult是实现我所使用的第一步。返回HttpNotFoundResult是好的。那么问题是,下一步是什么

我创建了一个名为HandleNotFoundAttribute的操作过滤器,然后显示404错误页面。由于它返回一个视图,您可以为每个控制器创建一个特殊的404视图,或者使用默认的共享404视图。当控制器不存在指定的操作时,甚至会调用它,因为框架抛出状态代码为404的HttpException

public class HandleNotFoundAttribute : ActionFilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        var httpException = filterContext.Exception.GetBaseException() as HttpException;
        if (httpException != null && httpException.GetHttpCode() == (int)HttpStatusCode.NotFound)
        {
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; // Prevents IIS from intercepting the error and displaying its own content.
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.StatusCode = (int) HttpStatusCode.NotFound;
            filterContext.Result = new ViewResult
                                        {
                                            ViewName = "404",
                                            ViewData = filterContext.Controller.ViewData,
                                            TempData = filterContext.Controller.TempData
                                        };
        }
    }
}

请注意,从MVC3开始,您可以使用
HttpStatusCodeResult

使用ActionFilter很难维护,因为每当我们抛出错误时,都需要在属性中设置过滤器。如果我们忘了设定呢?一种方法是在基本控制器上导出
OnException
。您需要定义从
Controller
派生的
BaseController
,并且所有控制器都必须从
BaseController
派生。最好有一个基地控制器

注意:如果使用
Exception
响应状态代码为500,那么我们需要将其更改为404(未找到)和401(未授权)。正如我上面提到的,在
BaseController
上使用
OnException
覆盖以避免使用filter属性

新的MVC 3还通过将空视图返回到浏览器而使问题更加棘手。经过一些研究后,最好的解决方案是基于我的回答

为了更方便,我将其粘贴在此处:


经过一些研究。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
,如我在代码中的注释,您可以为状态描述和其他解释定义一个视图模型。

谢谢您提出这个问题。这是我的标准项目补充:DYeah,我也注意到了枚举。正如我所说,这只是一个粗略的例子,请随意改进。这毕竟应该是一个知识库;-)我想我有点过火了。。。享受:DFWIW,杰夫的例子也要求你