Как вернуть представление для HttpNotFound() в ASP.Net MVC 3?
Есть ли способ вернуть один и тот же вид каждый раз, когда HttpNotFoundResult возвращается с контроллера? Как вы указываете это представление? Я предполагаю, что настройка страницы 404 в web.config может работать, но я хотел знать, есть ли лучший способ справиться с этим результатом.
Изменить/продолжить:
В результате я нашел решение, найденное во втором ответе на этот вопрос, с некоторыми небольшими трюками для ASP.Net MVC 3 для обработки моих 404-х: Как я могу правильно обрабатывать 404 в ASP.Net MVC?
Ответы
Ответ 1
HttpNotFoundResult
не отображает представление. Он просто устанавливает код состояния 404 и возвращает пустой результат, который полезен для таких вещей, как AJAX, но если вы хотите создать страницу с ошибкой 404, вы можете throw new HttpException(404, "Not found")
, которая автоматически отобразит настроенное представление в web.config:
<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="/Http404.html" />
</customErrors>
Ответ 2
Это решение объединяет IResultFilter и IExceptionFilter, чтобы поймать либо брошенное HttpException, либо вернуть HttpStatusCodeResult из действия.
public class CustomViewForHttpStatusResultFilter: IResultFilter, IExceptionFilter
{
string viewName;
int statusCode;
public CustomViewForHttpStatusResultFilter(HttpStatusCodeResult prototype, string viewName)
: this(prototype.StatusCode, viewName) {
}
public CustomViewForHttpStatusResultFilter(int statusCode, string viewName) {
this.viewName = viewName;
this.statusCode = statusCode;
}
public void OnResultExecuted(ResultExecutedContext filterContext) {
HttpStatusCodeResult httpStatusCodeResult = filterContext.Result as HttpStatusCodeResult;
if (httpStatusCodeResult != null && httpStatusCodeResult.StatusCode == statusCode) {
ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
}
}
public void OnResultExecuting(ResultExecutingContext filterContext) {
}
public void OnException(ExceptionContext filterContext) {
HttpException httpException = filterContext.Exception as HttpException;
if (httpException != null && httpException.GetHttpCode() == statusCode) {
ExecuteCustomViewResult(filterContext.Controller.ControllerContext);
// This causes ELMAH not to log exceptions, so commented out
//filterContext.ExceptionHandled = true;
}
}
void ExecuteCustomViewResult(ControllerContext controllerContext) {
ViewResult viewResult = new ViewResult();
viewResult.ViewName = viewName;
viewResult.ViewData = controllerContext.Controller.ViewData;
viewResult.TempData = controllerContext.Controller.TempData;
viewResult.ExecuteResult(controllerContext);
controllerContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
Вы можете зарегистрировать этот фильтр так, указав либо код состояния http для HttpException, либо конкретный HttpStatusCodeResult, для которого вы не хотите отображать пользовательский вид.
GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(new HttpNotFoundResult(), "Error404"));
// alternate syntax
GlobalFilters.Filters.Add(new CustomViewForHttpStatusResultFilter(404, "Error404"));
Он обрабатывает исключения и HttpStatusCodeResult, которые были выбраны или возвращены в действие. Он не будет обрабатывать ошибки, которые возникают до того, как MVC выберет подходящее действие и контроллер, как эта общая проблема:
- Неизвестные маршруты
- Неизвестные контроллеры
- Неизвестные действия
Для обработки этих типов ошибок NotFound объедините это решение с fooobar.com/questions/16780/...
Ответ 3
Полезная информация от @Darin Dimitrov, что HttpNotFoundResult
фактически возвращает пустой результат.
После некоторого исследования. Обходной путь для 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
, как я прокомментировал код, и вы можете определить модель представления для описания состояния и других объяснений.
Ответ 4
protected override void HandleUnknownAction(string actionName)
{
ViewBag.actionName = actionName;
View("Unknown").ExecuteResult(this.ControllerContext);
}
Ответ 5
Вот верный ответ, который позволяет полностью настраивать страницу ошибок в одном месте.
Не нужно изменять web.confiog или создавать сложные классы и код.
Работает также в MVC 5.
Добавьте этот код в контроллер:
if (bad) {
Response.Clear();
Response.TrySkipIisCustomErrors = true;
Response.Write(product + I(" Toodet pole"));
Response.StatusCode = (int)HttpStatusCode.NotFound;
//Response.ContentType = "text/html; charset=utf-8";
Response.End();
return null;
}
На основе http://www.eidias.com/blog/2014/7/2/mvc-custom-error-pages
Ответ 6
Пожалуйста, следуйте этому примеру, если вы хотите httpnotfound Error в вашем контроллере
public ActionResult Contact()
{
return HttpNotFound();
}