Asp.net mvc framework
Шрифт:
? необходимо создать механизм, который позволит ограничивать создание определенных контроллеров на основе "черного списка";
? решение на базе маршрутов таблицы маршрутизации не может нас удовлетворить, поскольку создание маршрутов производится через исполняемый код, который по каким-то причинам не может быть модифицирован для загрузки маршрутов из внешнего источника;
? решение на базе маршрутов не может нас удовлетворить в связи с тем, что у нас много универсальных маршрутов, которые затрагивают сразу много контроллеров;
? требуется возможность работать с "черным списком"
Согласно этим требованиям создадим фабрику контроллеров, которая позволит нам ограничивать выполнение контроллеров по их именам через XML-файл "черного списка". Вот пример такого файла:
<?xml version="1.0" encoding="utf-8" ?>
<blacklist>
<item typeName="AccountController" />
<item typeName="AdminController" />
</blacklist>
Как можно увидеть, данным файлом мы хотели бы заблокировать выполнение контроллеров AccountController и AdminController.
Реализуем фабрику контроллеров, наследуя класс DefaultFactoryController:
public class ControllerFactory : DefaultControllerFactory {
protected override IController
GetControllerInstance(Type controllerType)
{
if (controllerType == null)
return base.GetControllerInstance(controllerType);
XmlDocument xdoc = new XmlDocument;
string blacklistPath =
HttpContext.Current.Server.MapPath("~/blacklist.xml");
xdoc.Load(blacklistPath);
XmlNodeList nodes = xdoc.GetElementsByTagName("blacklist");
foreach (XmlNode node in nodes[0].ChildNodes)
{
if (node.Attributes["typeName"].Value == controllerType.Name)
throw new HttpException(404, "Страница не найдена");
}
return base.GetControllerInstance(controllerType);
}
}
Как вы можете видеть, единственным методом нашего класса является реализация перегруженного метода GetControllerInstance класса DefaultControllerFactory. В нем мы реализуем следующую последовательность действий:
1. Проверяем, не передается ли в фабрику контроллера типа контроллера в виде null, если это так, то завершаем выполнение вызовом базового метода для выполнения действия по умолчанию.
2. Загружаем наш XML-файл с "черным списком" и ищем в нем имя типа контроллера, который запрошен для создания.
3. Если в "черном списке" существует запись о блокировании данного контроллера, то возвращаем ответ на запрос в виде 404 ошибки HTTP "Страница не найдена".
4. Если контроллер отсутствует в "черном списке", то мы выполняем базовое действие по умолчанию для поиска и создания необходимого контроллера.
Для того чтобы наш код заработал, мы должны зарегистрировать нашу фабрику контроллеров. Функцию регистрации выполняет метод SetControllerFactory класса controllerBuilder. Добавим его вызов в файл Global.asax:
protected void Application_Start
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory( new ControllerFactory);
}
После
этого попробуем запустить наше приложение. Главная страница запустится нормально, т. к. она создается с помощью контроллера HomeController. Но если мы попробуем перейти на страницу входа, регистрации или административную страницу, то увидим стандартное сообщение браузера о том, что страница не была найдена.Другим, пожалуй, самым распространенным вариантом использования фабрики контроллеров является реализация архитектурного паттерна Инверсия контроля (Inversion of Control), который в данном применении позволяет в приложении уменьшить зависимость и ослабить связи между контроллерами. Для реализации такого механизма используются сторонние библиотеки, вроде Unity Application Blocks от Microsoft, Spring.NET или Ninject.
Действия, фильтры и атрибуты
Переопределение свойства Actionlnvoker
После того как фабрика контроллеров создала контроллер, производится вызов его метода Execute, который с помощью специального свойства ActionInvoker определяет необходимый метод для выполнения действия и вызывает его. По умолчанию ActionInvoker создается как экземпляр класса ControllerActionInvoker, но разработчик волен переопределить его. Переопределение ActionInvoker — это еще одна точка расширения ASP.NET MVC, которой вы можете воспользоваться для самых разнообразных целей. Например, т. к. именно ActionInvoker исполняет все необходимые фильтры типа ActionFilter, то вы вольны изменить этот механизм для того, чтобы часть фильтров не могла быть использована для ваших контроллеров в некотором гипотетическом случае.
По умолчанию метод InvokeAction класса ActionInvoker вместе с самим вызовом действия реализует механизм обработки заданных через атрибуты фильтров для действия.
Всего InvokeAction формирует четыре группы фильтров:
? ActionFilters — вызываются во время исполнения действия;
? ResultFilters — вызываются после исполнения действия при обработке результата;
? AuthorizationFilters — вызываются до исполнения действия, чтобы произвести проверку доступа;
? ExceptionFilters — вызываются во время обработки возникшего исключения.
Существуют такие действия, скорость вызова которых критически важна. В связи с этим лишние операции по поиску и исполнению фильтров типа ActionFilter могут отрицательно сказаться на производительности, даже когда никаких фильтров не используется. Поэтому создание своего варианта класса ActionInvoker и метода InvokeAction имеет смысл и может быть полезно. Вы можете реализовать InvokeAction в таком виде, в котором и производительность будет на высоте, и необходимый функционал не будет потерян.