it-swarm-ru.tech

Как вернуть представление для HttpNotFound () в ASP.Net MVC 3?

Есть ли способ возвращать одно и то же представление каждый раз, когда HttpNotFoundResult возвращается из контроллера? Как вы определяете эту точку зрения? Я предполагаю, что настройка страницы 404 в web.config может сработать, но я хотел знать, есть ли лучший способ обработать этот результат.

Редактировать/следить за:

В итоге я использовал решение, найденное во втором ответе на этот вопрос, с некоторыми небольшими изменениями для ASP.Net MVC 3 для обработки моих 404-х: Как я могу правильно обрабатывать 404-е в ASP.Net MVC?

39
Dave Brace

HttpNotFoundResult не отображает вид. Он просто устанавливает код состояния на 404 и возвращает пустой результат, который полезен для таких вещей, как AJAX, но если вам нужна пользовательская страница с ошибкой 404, вы можете throw new HttpException(404, "Not found"), которая автоматически отобразит сконфигурированное представление в web.config:

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
   <error statusCode="404" redirect="/Http404.html" />
</customErrors>
64
Darin Dimitrov

Это решение объединяет 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 объедините это решение с другими решениями, которые можно найти в stackoverflow.

17
Germán

Полезная информация от @Darin Dimitrov о том, что HttpNotFoundResult фактически возвращает пустой результат.

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

14
CallMeLaNN
protected override void HandleUnknownAction(string actionName)
{
    ViewBag.actionName  = actionName;
    View("Unknown").ExecuteResult(this.ControllerContext);
}
2
Dure_Sameen

Вот верный ответ, который позволяет полностью настроить страницу ошибки в одном месте .. Нет необходимости изменять 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

0
Andrus

Пожалуйста, следуйте этому, если вы хотите httpnotfound Error в вашем контроллере

  public ActionResult Contact()
    {

        return HttpNotFound();
    }
0
user5093161