Возможный дубликат:
Как я могу правильно обрабатывать 404 в ASP.NET MVC?
Я внес изменения, описанные в 404 обработчике ошибок Http в Asp.Net MVC (RC 5), и я все еще получаю стандартную страницу ошибок 404. Нужно ли что-то менять в IIS?
Возможный дубликат:
Как я могу правильно обрабатывать 404 в ASP.NET MVC?
Я внес изменения, описанные в 404 обработчике ошибок Http в Asp.Net MVC (RC 5), и я все еще получаю стандартную страницу ошибок 404. Нужно ли что-то менять в IIS?
Еще одно решение.
Добавьте 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>
Это будет обрабатывать как пропущенные маршруты, так и пропущенные действия.
Я исследовал много того, как правильно управлять 404-ми в MVC (в частности, MVC3), и это, IMHO, лучшее решение, которое я придумал:
В 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;
}
}
Edit:
Если вы используете 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 могут генерировать 404.
Создано в ASP.NET:
Сгенерировано ASP.NET MVC:
Сценарий 2. URL-адрес соответствует маршруту, но указывает контроллер, который не существует.
Сценарий 3: URL соответствует маршруту, но указывает на несуществующее действие.
Сгенерировано вручную:
Сценарий 4: Действие возвращает HttpNotFoundResult с помощью метода HttpNotFound().
Сценарий 5: Действие выдает HttpException с кодом состояния 404.
Сценарий 6: Действия вручную изменяют свойство Response.StatusCode на 404.
Цели
(A) Показать пользовательскую страницу ошибки 404 для пользователя.
(B) Сохраните код статуса 404 в ответе клиента (особенно важно для SEO).
(C) Отправьте ответ напрямую, без перенаправления 302.
Попытка решения: пользовательские ошибки
<system.web>
<customErrors mode="On">
<error statusCode="404" redirect="~/Errors/NotFound"/>
</customErrors>
</system.web>
Проблемы с этим решением:
Попытка решения: ошибки HTTP
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
Проблемы с этим решением:
Попытка решения: ошибки HTTP с заменой
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
Проблемы с этим решением:
Решение Попытка пользовательских ошибок и ошибок 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>
Проблемы с этим решением:
Люди, которые раньше сталкивались с этим, даже пытались создать свои собственные библиотеки (см. http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html). Но предыдущее решение, похоже, охватывает все сценарии без сложности использования внешней библиотеки.
Ответ от 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");
}
Теперь, в моем Action, я бросаю пользовательское исключение, которое я создал. И мой контроллер наследуется от пользовательского класса на основе контроллера, который я создал. Пользовательский базовый контроллер был создан для переопределения обработки ошибок. Вот мой пользовательский класс Base Controller:
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
Мое решение отлично работает, и я могу обрабатывать ЛЮБЫЕ ошибки на моем веб-сайте и отображать разные сообщения на основе любого типа исключения.
Похоже, это лучший способ поймать все.
В IIS вы можете указать перенаправление на "определенную" страницу на основе кода ошибки. В этом примере вы можете настроить 404 → вашу страницу с ошибкой 404.
Что я могу посоветовать, это посмотреть на FilterAttribute. Например, MVC уже имеет HandleErrorAttribute. Вы можете настроить его, чтобы обрабатывать только 404. Ответ, если вы заинтересованы, я посмотрю пример.
BTW
Решение (с последним маршрутом), которое вы приняли в предыдущем вопросе, не работает в большинстве ситуаций. Второе решение с HandleUnknownAction будет работать, но для этого потребуется внести изменения в каждый контроллер или иметь один базовый контроллер.
Мой выбор - это решение с HandleUnknownAction.