Если указан маршрут:
{FeedName} / {ItemPermalink}
пример: / Блог / Hello-World
Если элемент не существует, я хочу вернуть 404. Как правильно сделать это в ASP.NET MVC?
asp.net
asp.net-mvc
http-status-code-404
Даниэль Шаффер
источник
источник
Ответы:
Стрельба от бедра (ковбойское кодирование ;-)), я бы предложил примерно следующее:
Контроллер:
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.
При этом я думаю, что подход Джеффа является прекрасным примером KISS. Если вам не нужна многословность в этом примере, его метод тоже подойдет.
источник
Делаем так; этот код находится в
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?");
источник
web.config
.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
.источник
HttpNotFoundResult
Использование 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
классы и реализовывать новые (перекрывая его)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
как я прокомментировал в коде, и вы можете определить модель представления для описания статуса и других объяснений.источник