Как правильно отправить ответ HTTP 404 от действия ASP.NET MVC? - PullRequest
90 голосов
/ 01 февраля 2009

Если указан маршрут:

{FeedName} / {ItemPermalink}

ex: / Blog / Hello-World

Если элемент не существует, я хочу вернуть 404. Как правильно сделать это в ASP.NET MVC?

Ответы [ 6 ]

68 голосов
/ 01 февраля 2009

Съемка с бедра (ковбойское кодирование ;-)), я бы предложил что-то вроде этого:

Контроллер:

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

HttpNotFoundResult:

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, так что это просто расширит рамки в глазах другого разработчика, который будет поддерживать ваш код позже (вы знаете, псих, который знает, где вы живете).

Вы можете использовать рефлектор, чтобы взглянуть на сборку, чтобы увидеть, как достигается HttpUnauthorizedResult, потому что я не знаю, пропускает ли этот подход что-то (он кажется слишком простым).


Я использовал рефлектор, чтобы взглянуть на HttpUnauthorizedResult прямо сейчас. Кажется, они устанавливают StatusCode в ответ на 0x191 (401). Хотя это работает для 401, используя 404 в качестве нового значения, я, кажется, получаю просто пустую страницу в Firefox. Хотя Internet Explorer показывает значение по умолчанию 404 (не версия ASP.NET). Используя панель инструментов веб-разработчика, я проверил заголовки в FF, которые показывают ответ 404 Not Found. Это может быть просто то, что я неправильно настроил в FF.


Как говорится, подход Джеффа - прекрасный пример ПОЦЕЛУЯ. Если вам не нужно многословие в этом примере, его метод также работает отлично.

46 голосов
/ 01 февраля 2009

Мы делаем это так; этот код находится в 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();
19 голосов
/ 01 февраля 2009
throw new HttpException(404, "Are you sure you're in the right place?");
7 голосов
/ 04 февраля 2011

Обратите внимание, что с MVC3 вы можете просто использовать HttpStatusCodeResult.

7 голосов
/ 22 июня 2010

HttpNotFoundResult - отличный первый шаг к тому, что я использую. Возвращение HttpNotFoundResult - это хорошо. Тогда вопрос в том, что дальше?

Я создал фильтр действий с именем HandleNotFoundAttribute, который затем отображает страницу ошибки 404. Поскольку он возвращает представление, вы можете создать специальное представление 404 для каждого контроллера или разрешить использовать общее представление 404 по умолчанию. Это даже будет вызвано, когда у контроллера нет указанного действия, потому что платформа выдает HttpException с кодом состояния 404.

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
                                        };
        }
    }
}
5 голосов
/ 10 марта 2011

Использование ActionFilter трудно поддерживать , потому что всякий раз, когда мы выдаем ошибку, фильтр должен быть установлен в атрибуте. Что если мы забудем установить его? Одним из способов является получение OnException на базовом контроллере. Вам необходимо определить BaseController, полученный из Controller, а все ваши контроллеры должны быть получены из BaseController. Рекомендуется иметь базовый контроллер.

Обратите внимание, что при использовании Exception код состояния ответа равен 500, поэтому нам нужно изменить его на 404 для Not Found и 401 для Unauthorized. Как я упоминал выше, используйте OnException переопределения для BaseController, чтобы избежать использования атрибута фильтра.

Новый MVC 3 также создает проблемы, возвращая пустой вид в браузер. Лучшее решение после некоторых исследований основано на моем ответе здесь Как вернуть представление для HttpNotFound () в ASP.Net MVC 3?

Для большего удобства я вставляю это здесь:


После некоторого изучения. Обходной путь для MVC 3 здесь заключается в извлечении всех HttpNotFoundResult, HttpUnauthorizedResult, HttpStatusCodeResult классов и реализации new (переопределяя его) HttpNotFound () в методе BaseController.

Рекомендуется использовать базовый контроллер, чтобы у вас был «контроль» над всеми производными контроллерами.

Я создаю новый класс HttpStatusCodeResult, не производный от ActionResult, а от ViewResult для визуализации представления или любого View, который вы хотите, указав свойство ViewName. Я следую исходному HttpStatusCodeResult, чтобы установить HttpContext.Response.StatusCode и HttpContext.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, как я прокомментировал в коде, и вы можете определить модель представления для описания статуса и других объяснений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...