2009-04-04 4 views
119

Можно создать дубликат:
How can I properly handle 404 in ASP.NET MVC?ASP.NET MVC 404 Обработка ошибок

Я сделал изменения, описанные в 404 Http error handler in Asp.Net MVC (RC 5) и я все еще получаю стандартную страницу 404 ошибки. Нужно ли мне что-то менять в IIS?

+1

Это хороший прочтение на эту тему @ [Как эффективно справляться с ошибками 404 Not Found с помощью ASP.NET MVC 4] (http://yassershaikh.com/how-to-handle-404-not-found-errors -effectively-with-asp-net-mvc-4 /) – Yasser

ответ

131

Еще одно решение.

Добавить ErrorControllers или статическую страницу с информацией об ошибке 404.

Измените файл web.config (в случае контроллера).

<system.web> 
    <customErrors mode="On" > 
     <error statusCode="404" redirect="~/Errors/Error404" /> 
    </customErrors> 
</system.web> 

Или в случае статической страницы

<system.web> 
    <customErrors mode="On" > 
     <error statusCode="404" redirect="~/Static404.html" /> 
    </customErrors> 
</system.web> 

Это будет обрабатывать как пропущенные маршруты и пропущенное действие.

+2

Ницца! :) ErrorsController может наследовать с той же базы, что и все остальные контроллеры, и, таким образом, иметь доступ к определенной функциональности. Кроме того, представление Error404 может быть обернуто в мастер, предоставляя пользователю общий внешний вид остальной части сайта без дополнительной работы. – Dimskiy

+7

Используйте '', чтобы увидеть фактическую страницу ошибки во время разработки. –

+1

Это правильно. Не вызывайте Response.Clear(); как было предложено Майком Чалием см. http://blogs.msdn.com/b/rickandy/archive/2012/03/01/response-redirect-and-asp-net-mvc-do-not-mix.aspx – RickAndMSFT

0

В IIS вы можете указать перенаправление на «определенную» страницу на основе кода ошибки. В вашем примере вы можете настроить 404 -> вашу страницу с ошибкой 404.

1

Что я могу посоветовать, это посмотреть на FilterAttribute. Например, MVC уже имеет HandleErrorAttribute. Вы можете настроить его, чтобы обрабатывать только 404. Ответ, если вы заинтересованы, я посмотрю пример.

BTW

Solution (с последним маршрутом), что вы приняли в предыдущем вопросе не работают в большинстве ситуаций. Второе решение с HandleUnknownAction будет работать, но для этого необходимо внести изменения в каждый контроллер или иметь один базовый контроллер.

Мой выбор - решение с HandleUnknownAction.

+0

Похоже, проблема заключается в том, что стандартный маршрут по умолчанию "{controller}/{action}/{id}" ловит everythgin, поэтому он не попадает в последний маршрут. Я думал, что если контроллер не сможет найти, что следующий маршрут будет оценен. – Clearly

+0

HandleUnknownAction работает только с действиями, которые не работают. Что делать, если маршрут привязан, но результирующий контроллер не может быть найден? Какой у вас лучший способ справиться с этим? – Clearly

+0

Да, это правильно, только когда действие не найдено. Вы можете попытаться объединить оба решения. HandleUnknownAction для пропущенных действий и маршрута для пропущенных контроллеров. Другим возможным решением является пользовательский RouteHandler. –

340

Я исследовал МНОГО о том, как правильно управлять в MVC 404-(в частности MVC3), и это, ИМХО это лучшее решение, которое я придумал:

В global.asax :

public class MvcApplication : HttpApplication 
{ 
    protected void Application_EndRequest() 
    { 
     if (Context.Response.StatusCode == 404) 
     { 
      Response.Clear(); 

      var rd = new RouteData(); 
      rd.DataTokens["area"] = "AreaName"; // In case controller is in another area 
      rd.Values["controller"] = "Errors"; 
      rd.Values["action"] = "NotFound"; 

      IController c = new ErrorsController(); 
      c.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); 
     } 
    } 
} 

ErrorsController:

public sealed class ErrorsController : Controller 
{ 
    public ActionResult NotFound() 
    { 
     ActionResult result; 

     object model = Request.Url.PathAndQuery; 

     if (!Request.IsAjaxRequest()) 
      result = View(model); 
     else 
      result = PartialView("_NotFound", model); 

     return result; 
    } 
} 

Редактировать :

Если вы используете IoC (например,AutoFac), вы должны создать свой контроллер с помощью:

var rc = new RequestContext(new HttpContextWrapper(Context), rd); 
var c = ControllerBuilder.Current.GetControllerFactory().CreateController(rc, "Errors"); 
c.Execute(rc); 

Вместо того

IController c = new ErrorsController(); 
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd)); 

(необязательно)

Объяснение:

Есть 6 сценариев, которые я могу подумайте, где приложения ASP.NET MVC3 могут генерировать 404s.

Порожденных ASP.NET:

  • Сценарий 1: URL не соответствует маршрут в таблице маршрутизации.

порожденных ASP.NET MVC:

  • Сценарий 2: URL соответствует маршруту, но указывает контроллер, который не существует.

  • Сценарий 3: URL-адрес соответствует маршруту, но указывает действие, которое не существует.

генерируется вручную:

  • Сценарий 4: Действие возвращает HttpNotFoundResult с помощью метода HttpNotFound().

  • Сценарий 5: действие бросает HttpException с кодом состояния 404.

  • Сценарий 6: An действия вручную изменяет свойство Response.StatusCode к 404.

Цели

  • (A) Показать пользовательскую страницу ошибки 404 пользователю.

  • (B) Поддержание кода статуса 404 при ответе клиента (особенно важно для SEO).

  • (C) Отправить ответ напрямую, без привлечения перенаправления 302.

Решение Попытка: Пользовательские ошибки

<system.web> 
    <customErrors mode="On"> 
     <error statusCode="404" redirect="~/Errors/NotFound"/> 
    </customError> 
</system.web> 

Проблемы с этим раствором:

  • не соответствует объективному (A) в сценариях (1), (4), (6).
  • Не соответствует объективу (B) автоматически. Он должен быть запрограммирован вручную.
  • Не соответствует цели (C).

Решение ПОПЫТКА: Ошибки HTTP

<system.webServer> 
    <httpErrors errorMode="Custom"> 
     <remove statusCode="404"/> 
     <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/> 
    </httpErrors> 
</system.webServer> 

Проблемы с этим раствором:

  • только работает на IIS 7+.
  • Не соответствует объективному (А) сценарию (2), (3), (5).
  • Не соответствует объективу (B) автоматически. Он должен быть запрограммирован вручную.

Решение Попытка: Ошибки HTTP с Заменить

<system.webServer> 
    <httpErrors errorMode="Custom" existingResponse="Replace"> 
     <remove statusCode="404"/> 
     <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/> 
    </httpErrors> 
</system.webServer> 

Проблемы с этим решением:

  • работает только на IIS 7+.
  • Не соответствует объективу (B) автоматически. Он должен быть запрограммирован вручную.
  • Он скрывает исключения на уровне приложений HTTP. Например. не может использовать раздел customErrors, System.Web.Mvc.HandleErrorAttribute и т. д. Он может не только показывать общие страницы ошибок.

Решения ПОПЫТКА CustomErrors и Ошибка HTTP

<system.web> 
    <customErrors mode="On"> 
     <error statusCode="404" redirect="~/Errors/NotFound"/> 
    </customError> 
</system.web> 

и

<system.webServer> 
    <httpErrors errorMode="Custom"> 
     <remove statusCode="404"/> 
     <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/> 
    </httpErrors> 
</system.webServer> 

Проблема с этим решением:

  • только работает на IIS 7+.
  • Не соответствует объективу (B) автоматически. Он должен быть запрограммирован вручную.
  • Не соответствует объективному (C) в сценариях (2), (3), (5).

Люди, которые обеспокоены этим, даже пытались создать свои собственные библиотеки (см. http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html). Но предыдущее решение, похоже, охватывает все сценарии без сложности использования внешней библиотеки.

+0

В сочетании с атрибутом HandleErrorAttribute, который шаблон MVC автоматически устанавливает при создании нового проекта, это, безусловно, лучшее и самое простое решение. – s1mm0t

+6

Мне нравится ваш анализ, но не ваше решение :) проблемы с ожиданием до конца запроса, что некоторые из контекста запроса уже были отброшены, такие как состояние сеанса. –

+1

Response.Clear и MVC не смешиваются, не используйте этот подход http://blogs.msdn.com/b/rickandy/archive/2012/03/01/response-redirect-and-asp-net-mvc-do -not-mix.aspx – RickAndMSFT

5

Ответ от Marco - это лучшее решение. Мне нужно было управлять обработкой ошибок, и я имею в виду, что на самом деле CONTROL это. Конечно, я немного расширил решение и создал полную систему управления ошибками, которая управляет всем. Я также читал об этом решении в других блогах, и это кажется очень приемлемым большинством продвинутых разработчиков.

Вот окончательный код, который я использую:

protected void Application_EndRequest() 
    { 
     if (Context.Response.StatusCode == 404) 
     { 
      var exception = Server.GetLastError(); 
      var httpException = exception as HttpException; 
      Response.Clear(); 
      Server.ClearError(); 
      var routeData = new RouteData(); 
      routeData.Values["controller"] = "ErrorManager"; 
      routeData.Values["action"] = "Fire404Error"; 
      routeData.Values["exception"] = exception; 
      Response.StatusCode = 500; 

      if (httpException != null) 
      { 
       Response.StatusCode = httpException.GetHttpCode(); 
       switch (Response.StatusCode) 
       { 
        case 404: 
         routeData.Values["action"] = "Fire404Error"; 
         break; 
       } 
      } 
      // Avoid IIS7 getting in the middle 
      Response.TrySkipIisCustomErrors = true; 
      IController errormanagerController = new ErrorManagerController(); 
      HttpContextWrapper wrapper = new HttpContextWrapper(Context); 
      var rc = new RequestContext(wrapper, routeData); 
      errormanagerController.Execute(rc); 
     } 
    } 

и внутри моего ErrorManagerController:

 public void Fire404Error(HttpException exception) 
    { 
     //you can place any other error handling code here 
     throw new PageNotFoundException("page or resource"); 
    } 

Теперь в моем действии, я бросаю пользовательские исключения, которые я создал. И мой контроллер наследуется от пользовательского класса на основе контроллера, который я создал. Пользовательский базовый контроллер был создан для переопределения обработки ошибок. Вот мой пользовательский класс Базовый контроллер:

public class MyBasePageController : Controller 
{ 
    protected override void OnException(ExceptionContext filterContext) 
    { 
     filterContext.GetType(); 
     filterContext.ExceptionHandled = true; 
     this.View("ErrorManager", filterContext).ExecuteResult(this.ControllerContext); 
     base.OnException(filterContext); 
    } 
} 

«ErrorManager» в приведенном выше коде, это просто вид, что использует модель, основанную на ExceptionContext

Мое решение работает отлично, и я могу справиться с любой ошибка на моем веб-сайте и отображение различных сообщений на основе любого типа исключения.

+1

Я не согласен с вами в том, что это самое лучшее решение. Общей задачей, такой как эта, не должно быть так сложно настроить. Ответ Маркоса велик, но вы действительно не так уж много кода для простых вещей. – PussInBoots

Смежные вопросы