从ASP.NETMVC操作发送HTTP404响应的正确方法是什么?

lvjbypge  于 2022-12-05  发布在  .NET
关注(0)|答案(6)|浏览(141)

如果给定路线:

{摘要名称}/{项目永久链接}

例如:/博客/Hello-世界
如果这个项目不存在,我想返回一个404。在ASP .NETMVC中做这个的正确方法是什么?

nwwlzxa7

nwwlzxa71#

从臀部射击(牛仔编码;- -译者注),我建议这样:

控制器:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return new HttpNotFoundResult("This doesn't exist");
    }
}

HTTP未找到结果:

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 :)

使用这种方法,您就符合了框架标准。那里已经有一个HttpUnauthorizedResult了,所以这只会在其他开发人员(知道您住在哪里的神经病)的眼中扩展框架。
您可以使用reflector查看程序集,以了解HttpUnauthorizedResult是如何实现的,因为我不知道这种方法是否遗漏了什么(它似乎太简单了)。
我刚才确实用reflector看了一下HttpUnauthorizedResult,好像他们把响应的StatusCode设置成0x 191(401)。虽然这对401有效,使用404作为新值,我在Firefox中看到的似乎只是一个空白页面,而InternetExplorer显示的是默认的404(不是ASP.NET版本)。使用WebDeveloper工具栏,我检查了FF中的头,它确实显示了404未找到响应。可能只是我在FF中配置错误。
话虽如此,我认为Jeff的方法是KISS的一个很好的例子。如果你真的不需要这个例子中的冗长,他的方法也很好用。

7ivaypg9

7ivaypg92#

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

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

这样称呼

public ActionResult ShowUserDetails(int? id)
{        
    // make sure we have a valid ID
    if (!id.HasValue) return PageNotFound();
jutyujz0

jutyujz03#

throw new HttpException(404, "Are you sure you're in the right place?");
wrrgggsh

wrrgggsh4#

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

83qze16e5#

请注意,从MVC3开始,您只能使用HttpStatusCodeResult

igetnqfo

igetnqfo6#

使用ActionFilter*很难维护,因为每当我们抛出错误时,过滤器都需要在属性中设置。如果我们忘记设置它怎么办?一种方法是在基本控制器上派生OnException。您需要定义一个从Controller派生的BaseController,并且所有控制器都必须从BaseController派生。拥有一个基本控制器是最佳实践。
注意如果使用Exception,响应状态代码是500,所以我们需要将其更改为404表示Not Found,401表示Unauthorized。就像我上面提到的,在BaseController上使用OnException覆盖以避免使用filter属性。
新的MVC3也会给浏览器返回一个空的视图,这也让问题变得更麻烦了。经过一些研究,最好的解决方案是基于我在这里的答案。
为了更方便,我把它贴在这里:
经过一些研究,MVC 3的解决方法是派生所有HttpNotFoundResultHttpUnauthorizedResultHttpStatusCodeResult类,并在BaseController中实现new(覆盖它)HttpNotFound()方法。
最好的做法是使用基本控制器,这样您就可以“控制”所有派生的控制器。
我创建了新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
}

和in _Layout.cshtml(类似于母版页)

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

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

相关问题