Собственная страница ошибки 404 на ASP.NET MVCИсточник: habrahabrru
При разработке проекта на ASP.NET MVC возникла необходимость сделать собственную страницу ошибки 404. Я рассчитывал, что справлюсь с этой задачей за несколько минут. Через 6 часов работы я определил два варианта ее решения разной степени сложности. Описание - далее. В ASP.NET MVC 3, с которой я работаю, появились глобальные фильтры. В шаблоне нового проекта, уже встроено ее использование для отображения собственной страницы ошибок (через HandleErrorAttribute). Замечательный метод, только он не умеет обрабатывать ошибки с кодом 404 (страница не найдена). Поэтому пришлось искать другой красивый вариант обработки этой ошибки. Обработка ошибки 404 через Web.config Платформа ASP.NET предоставляет возможность произвольно обрабатывать ошибки путем нехитрой настройки файла Web.config. Для это в секцию system.web нужно добавить следующий код: <customErrors mode="On" > При появлении ошибки 404 пользователь будет отправлен на страницу yoursite.ru/Errors/Error404/?aspxerrorpath=/Not/Found/Url. Кроме того, что это не очень красиво и удобно (нельзя отредактировать url) Способ можно несколько улучшить, добавив redirectMode="ResponseRewrite" в customErrors: <customErrors mode="On" redirectMode="ResponseRewrite" > В данном случае должен происходить не редирект на страницу обработки ошибки, а подмена запрошенного ошибочного пути содержимым указанной страницы. Однако, и здесь есть свои сложности. На ASP.NET MVC этот способ в приведенном виде не работает. Достаточно подробное обсуждение (на английском) можно прочитать в топике. Кратко говоря, этот метод основан на методе Server.Transfer, который используется в классическом ASP.NET и, соответственно, работает только со статическими файлами. С динамическими страницами, как в примере, он работать отказывается (так как не видит на диске файл '/Errors/Error404/'). То есть, если заменить '/Errors/Error404/' на, например, '/Errors/Error404.htm', то описанные метод будет работать. Однако в этом случае не получится выполнять дополнительные действия по обработке ошибок, например, логирование. Этот способ работает только с IIS 7 и выше, поэтому проверить этот метод не удалось - используем IIS 6. Поиски пришлось продолжить. Танцы с бубном и Application_Error Если описанный выше метод применить по каким-либо причинам не удается, то придется писать больше строк кода. Частичное решение приведено в статье. Ниже приведены мои требования к решению проблемы отображения ошибки 404 NotFound:
Я думаю, что Application_Error в Global.asax должен быть использован для целей более высокого уровня, например, для обработки необработанных исключений или логирования, а не работы с ошибкой 404. Поэтому я стараюсь вынести весь код, связанный с ошибкой 404, вне файла Global.asax. Шаг 1: Создаем общее место для обработки ошибки 404 Это облегчит поддержку решение. Используем ErrorController, чтобы можно было легче улучшать страницу 404 в дальнейшем. Также нужно убедиться, что контроллер возвращает код 404! public class ErrorController : MyController public ActionResult Http404(string url) // TODO: добавить реализацию ILogger return View("NotFound", model); #endregion Шаг 2: Используем собственный базовый класс для контроллеров, чтобы легче вызывать метод для ошибки 404 и обрабатывать HandleUnknownAction Ошибка 404 в ASP.NET MVC должна быть обработана в нескольких местах. Первое - это HandleUnknownAction. protected override void HandleUnknownAction(string actionName) public ActionResult InvokeHttp404(HttpContextBase httpContext) return new EmptyResult(); #endregion Шаг 3: Используем инъекцию зависимостей в фабрике контроллеров и обрабатываем 404 HttpException Например, так (не обязательно использовать StructureMap): return errorController; return ObjectFactory.GetInstance(controllerType) as Controller; Пример для MVC2.0: return errorController; return ObjectFactory.GetInstance(controllerType) as Controller; Я думаю, что лучше отлавливать ошибки в месте их возникновения. Поэтому я предпочитаю метод, описанный выше, обработке ошибок в Application_Error. Шаг 4: Добавляем маршрут NotFound в Global.asax для путей, которые не удалось определить нашему приложению Этот маршрут должен вызывать действие Http404. Обратите внимание, что параметр url будет относительным адресом, потому что движок маршрутизации отсекает часть с доменным именем. Именно поэтому мы добавили все эти условные операторы на первом шаге. Это третье и последнее место в приложении MVC для отлова ошибок 404, которые Вы не вызываете самостоятельно. Если здесь не удалось сопоставить входящий путь ни какому контроллеру и действию, то MVC передаст обработку этой ошибки дальше платформе ASP.NET (в файл Global.asax). А мы не хотим, чтобы это случилось. Шаг 5: Наконец, вызываем ошибку 404, когда приложению не удается что-либо найти Например, когда нашему контроллеру Loan, унаследованному от MyController, передан неправильный параметр ID: public ActionResult Detail(int ID) Было бы замечательно, если бы можно было все это реализовать меньшим количеством кода. Но я считаю, что это решение легче поддерживать, тестировать, и в целом оно более удобно. Библиотека для второго решения Ну и на последок: уже готова библиотека, позволяющая организовать обработку ошибок описанным выше способом. Найти ее можно здесь - github.com/andrewdavey/NotFoundMvc. Заключение Интереса ради я посмотрел, как эта задача решена в Orchard. Был удивлен и несколько разочарован - разработчики решили вообще не обрабатывать это исключение - собственные страницы ошибок 404, на мой взгляд, давно стали стандартом в веб-разработке. В своем приложении я использовал обработку ошибки через Web.config с использованием роутинга. До окончания разработки приложения, а останавливаться на обработке ошибки 404 довольно опасно - можно вообще приложение тогда никогда не выпустить. Ближе к окончанию, скорее всего, внедрю второе решение. |