Что такое правильный способ, чтобы отправить ответ HTTP 404 от ASP.NET действие MVC?



если задан маршрут:



{FeedName} / {ItemPermalink}



ex: / Blog / Hello-World



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

641   6  

6 ответов:

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

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

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

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

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

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

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

чтобы сделать более удобным я вставляю его здесь:


после некоторого изучения. Этот обходной путь для MVC 3 вот вывести все HttpNotFoundResult,HttpUnauthorizedResult,HttpStatusCodeResult классы и реализовать новая (перекрывая его) 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 как я прокомментировано в коде, и вы можете определить модель представления для описания состояния и других объяснений.

Comments

    Ничего не найдено.