+7 (495) 229-0436   shopadmin@itshop.ru 119334, г. Москва, ул. Бардина, д. 4, корп. 3
 
 
Вход
 
 
Каталог
 
 
Подписка на новости
Новости ITShop
Windows 7 и Office: Новости и советы
Обучение и сертификация Microsoft
Вопросы и ответы по MSSQLServer
Delphi - проблемы и решения
Adobe Photoshop: алхимия дизайна
 
Ваш отзыв
Оцените качество магазина ITShop.ru на Яндекс.Маркете. Если вам нравится наш магазин - скажите об этом Google!
 
 
Способы оплаты
 
Курс расчета
 
 1 у.е. = 91.78 руб.
 
 Цены показывать:
 
 
 
 
  
Новости, статьи, акции
 

Собственная страница ошибки 404 на ASP.NET MVC

01.11.2011 18:08

При разработке проекта на ASP.NET MVC возникла необходимость сделать собственную страницу ошибки 404. Я рассчитывал, что справлюсь с этой задачей за несколько минут. Через 6 часов работы я определил два варианта ее решения разной степени сложности. Описание - далее.

 В ASP.NET MVC 3, с которой я работаю, появились глобальные фильтры. В шаблоне нового проекта, уже встроено ее использование для отображения собственной страницы ошибок (через HandleErrorAttribute). Замечательный метод, только он не умеет обрабатывать ошибки с кодом 404 (страница не найдена). Поэтому пришлось искать другой красивый вариант обработки этой ошибки.

Обработка ошибки 404 через Web.config

 Платформа ASP.NET предоставляет возможность произвольно обрабатывать ошибки путем нехитрой настройки файла Web.config. Для это в секцию system.web нужно добавить следующий код:

<customErrors mode="On" >
       <error statusCode="404" redirect="/Errors/Error404/" />
</customErrors>

 При появлении ошибки 404 пользователь будет отправлен на страницу yoursite.ru/Errors/Error404/?aspxerrorpath=/Not/Found/Url. Кроме того, что это не очень красиво и удобно (нельзя отредактировать url)

Способ можно несколько улучшить, добавив redirectMode="ResponseRewrite" в customErrors:

<customErrors mode="On" redirectMode="ResponseRewrite" >
       <error statusCode="404" redirect="/Errors/Error404/" />
</customErrors>

 В данном случае должен происходить не редирект на страницу обработки ошибки, а подмена запрошенного ошибочного пути содержимым указанной страницы. Однако, и здесь есть свои сложности. На ASP.NET MVC этот способ в приведенном виде не работает. Достаточно подробное обсуждение (на английском) можно прочитать в топике. Кратко говоря, этот метод основан на методе Server.Transfer, который используется в классическом ASP.NET и, соответственно, работает только со статическими файлами. С динамическими страницами, как в примере, он работать отказывается (так как не видит на диске файл  '/Errors/Error404/'). То есть, если заменить '/Errors/Error404/' на, например, '/Errors/Error404.htm', то описанные метод будет работать. Однако в этом случае не получится выполнять дополнительные действия по обработке ошибок, например, логирование.
 В указанном топике было предложено добавить в каждую страницу следующий код:
Response.TrySkipIisCustomErrors = true;

 Этот способ работает только с IIS 7 и выше, поэтому проверить этот метод не удалось - используем IIS 6. Поиски пришлось продолжить.

Танцы с бубном и Application_Error

 Если описанный выше метод применить по каким-либо причинам не удается, то придется писать больше строк кода. Частичное решение приведено в статье.
 Наиболее полное решение "с бубном" я нашел в топике. Обсуждение ведется на английском, поэтому переведу текст решения на русский.

Ниже приведены мои требования к решению проблемы отображения ошибки 404 NotFound:

  • Я хочу обрабатывать пути, для которых не определено действие.
  • Я хочу обрабатывать пути, для которых не определен контроллер.
  • Я хочу обрабатывать пути, которые не удалось разобрать моему приложению. Я не хочу, чтобы эти ошибки обрабатывались в Global.asax или IIS, потому что потом я не смогу сделать редирект обратно на мое приложение.
  • Я хочу обрабатывать собственные (например, когда требуемый товар не найден по ID) ошибки 404 в едином стиле.
  • Я хочу, чтобы все ошибки 404 возвращали MVC View, а не статическую страницу, чтобы потом иметь возможность получить больше данных об ошибках. И они должны возвращать код статуса 404.

 Я думаю, что Application_Error в Global.asax должен быть использован для целей более высокого уровня, например, для обработки необработанных исключений или логирования, а не работы с ошибкой 404. Поэтому я стараюсь вынести весь код, связанный с ошибкой 404, вне файла Global.asax.

Шаг 1: Создаем общее место для обработки ошибки 404

 Это облегчит поддержку решение. Используем ErrorController, чтобы можно было легче улучшать страницу 404 в дальнейшем. Также нужно убедиться, что контроллер возвращает код 404!

public class ErrorController : MyController
{
    #region Http404

    public ActionResult Http404(string url)
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        var model = new NotFoundViewModel();
        // Если путь относительный ('NotFound' route), тогда его нужно заменить на запрошенный путь
        model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
            Request.Url.OriginalString : url;
        // Предотвращаем зацикливание при равенстве Referrer и Request
        model.ReferrerUrl = Request.UrlReferrer != null &&
            Request.UrlReferrer.OriginalString != model.RequestedUrl ?
            Request.UrlReferrer.OriginalString : null;

        // TODO: добавить реализацию ILogger

        return View("NotFound", model);
    }
    public class NotFoundViewModel
    {
        public string RequestedUrl { get; set; }
        public string ReferrerUrl { get; set; }
    }

    #endregion
}

Шаг 2: Используем собственный базовый класс для контроллеров, чтобы легче вызывать метод для ошибки 404 и обрабатывать HandleUnknownAction

 Ошибка 404 в ASP.NET MVC должна быть обработана в нескольких местах. Первое - это HandleUnknownAction.
 Метод InvokeHttp404 является единым местом для перенаправления к ErrorController и нашему вновь созданному действию Http404. Используйте методологию DRY!
public abstract class MyController : Controller
{
    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    {
        // Если контроллер - ErrorController, то не нужно снова вызывать исключение
        if (this.GetType() != typeof(ErrorController))
            this.InvokeHttp404(HttpContext);
    }

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    {
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    }

    #endregion
}

Шаг 3: Используем инъекцию зависимостей в фабрике контроллеров и обрабатываем 404 HttpException

 Например, так (не обязательно использовать StructureMap):
Пример для MVC1.0:
public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }
}

Пример для MVC2.0:
 protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    try
    {
        if (controllerType == null)
            return base.GetControllerInstance(requestContext, controllerType);
    }
    catch (HttpException ex)
    {
        if (ex.GetHttpCode() == 404)
        {
            IController errorController = ObjectFactory.GetInstance<ErrorController>();
            ((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);

            return errorController;
        }
        else
            throw ex;
    }

    return ObjectFactory.GetInstance(controllerType) as Controller;
}

 Я думаю, что лучше отлавливать ошибки в месте их возникновения. Поэтому я предпочитаю метод, описанный выше, обработке ошибок в Application_Error.
 Это второе место для отлова ошибок 404.

Шаг 4: Добавляем маршрут NotFound в Global.asax для путей, которые не удалось определить нашему приложению

 Этот маршрут должен вызывать действие Http404. Обратите внимание, что параметр url будет относительным адресом, потому что движок маршрутизации отсекает часть с доменным именем. Именно поэтому мы добавили все эти условные операторы на первом шаге.
        routes.MapRoute("NotFound", "{*url}",
            new { controller = "Error", action = "Http404" });

 Это третье и последнее место в приложении MVC для отлова ошибок 404, которые Вы не вызываете самостоятельно. Если здесь не удалось сопоставить входящий путь ни какому контроллеру и действию, то MVC передаст обработку этой ошибки дальше платформе ASP.NET (в файл Global.asax). А мы не хотим, чтобы это случилось.

Шаг 5: Наконец, вызываем ошибку 404, когда приложению не удается что-либо найти

 Например, когда нашему контроллеру Loan, унаследованному от MyController, передан неправильный параметр ID:
//
// GET: /Detail/ID

public ActionResult Detail(int ID)
{
    Loan loan = this._svc.GetLoans().WithID(ID);
    if (loan == null)
        return this.InvokeHttp404(HttpContext);
    else
        return View(loan);
}

 Было бы замечательно, если бы можно было все это реализовать меньшим количеством кода. Но я считаю, что это решение легче поддерживать, тестировать, и в целом оно более удобно.

Библиотека для второго решения

 Ну и на последок: уже готова библиотека, позволяющая организовать обработку ошибок описанным выше способом. Найти ее можно здесь - github.com/andrewdavey/NotFoundMvc.

Заключение

 Интереса ради я посмотрел, как эта задача решена в Orchard. Был удивлен и несколько разочарован - разработчики решили вообще не обрабатывать это исключение - собственные страницы ошибок 404, на мой взгляд, давно стали стандартом в веб-разработке.

 В своем приложении я использовал обработку ошибки через Web.config с использованием роутинга. До окончания разработки приложения, а останавливаться на обработке ошибки 404 довольно опасно - можно вообще приложение тогда никогда не выпустить. Ближе к окончанию, скорее всего, внедрю второе решение.

Ссылки по теме

  
Помощь
Задать вопрос
 программы
 обучение
 экзамены
 компьютеры
Бесплатный звонок
ICQ-консультанты
Skype-консультанты

Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта
 
Бестселлеры
Курсы обучения "Atlassian JIRA - система управления проектами и задачами на предприятии"
Microsoft Windows 10 Профессиональная 32-bit/64-bit. Все языки. Электронный ключ
Microsoft Office для Дома и Учебы 2019. Все языки. Электронный ключ
Курс "Oracle. Программирование на SQL и PL/SQL"
Курс "Основы TOGAF® 9"
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год. Электронный ключ
Курс "Нотация BPMN 2.0. Ее использование для моделирования бизнес-процессов и их регламентации"
 

О нас
Интернет-магазин ITShop.ru предлагает широкий спектр услуг информационных технологий и ПО.

На протяжении многих лет интернет-магазин предлагает товары и услуги, ориентированные на бизнес-пользователей и специалистов по информационным технологиям.

Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.

В нашем магазине вы можете приобрести лицензионное ПО выбрав необходимое из широкого спектра и ассортимента по самым доступным ценам. Наши менеджеры любезно помогут определиться с выбором ПО, которое необходимо именно вам. Также мы проводим учебные курсы. Мы приглашаем к сотрудничеству учебные центры, организаторов семинаров и бизнес-тренингов, преподавателей. Сфера сотрудничества - продвижение бизнес-тренингов и курсов обучения по информационным технологиям.



 

О нас

 
Главная
Каталог
Новинки
Акции
Вакансии
 

Помощь

 
Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта
 

Способы оплаты

 

Проекты Interface Ltd.

 
Interface.ru   ITShop.ru   Interface.ru/training   Olap.ru   ITnews.ru  
 

119334, г. Москва, ул. Бардина, д. 4, корп. 3
+7 (495) 229-0436   shopadmin@itshop.ru
Проверить аттестат
© ООО "Interface Ltd."
Продаем программное обеспечение с 1990 года