Как вернуть представление для HttpNotFound() в ASP.Net MVC 3?
Есть ли способ возвращать одно и то же представление каждый раз, когда HttpNotFoundResult возвращается из контроллера? Как вы определяете эту точку зрения? Я предполагаю, что настройка страницы 404 в web.config может сработать, но я хотел знать, есть ли лучший способ обработать этот результат.
Редактировать / следить за:
В итоге я использовал решение, найденное во втором ответе на этот вопрос, с небольшими изменениями для ASP.Net MVC 3 для обработки моих 404-х: Как правильно обрабатывать 404-е в ASP.Net MVC?
6 ответов
HttpNotFoundResult
не отображать вид Он просто устанавливает код состояния на 404 и возвращает пустой результат, который полезен для таких вещей, как AJAX, но если вам нужна пользовательская страница с ошибкой 404, вы можете throw new HttpException(404, "Not found")
который будет автоматически отображать настроенное представление в web.config:
<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="/Http404.html" />
</customErrors>
Это решение объединяет 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 объедините это решение с другими решениями, которые можно найти в stackru.
Полезная информация от @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
как я прокомментировал в коде, и вы можете определить модель представления для описания статуса и других объяснений.
protected override void HandleUnknownAction(string actionName)
{
ViewBag.actionName = actionName;
View("Unknown").ExecuteResult(this.ControllerContext);
}
Пожалуйста, следуйте этому, если вы хотите httpnotfound Error в вашем контроллере
public ActionResult Contact()
{
return HttpNotFound();
}
Вот верный ответ, который позволяет полностью настроить страницу ошибки в одном месте. Нет необходимости изменять 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