Текст книги "Asp.net mvc framework"
Автор книги: Гайдар Магдануров
сообщить о нарушении
Текущая страница: 5 (всего у книги 17 страниц)
□ располагаться в папке Controllers корня проекта;
□ иметь суффикс Controller, например HomeController.cs, AccountController.cs;
□ класс контроллера должен иметь то же название, что и файл:
HomeController, AccountController.
Каждый контроллер содержит набор методов, которые в терминах MVC Framework называются действиями (actions). Действия реализуют бизнес-логику ответа и изменение модели данных в зависимости от действия пользователя или запроса клиента. Действия всегда возвращают результат, реализующий класс, наследующий ActionResult: такими классами являются следующие стандартные типы: ViewResult, JsonResult, FileResult, RedirectResult, RedirectToRouteResult, ContentResult, EmptyResult. В зависимости от возвращаемого типа клиент получит тот или иной тип набора данных: HTML-страницу, JSON-данные, бинарный файл и др. Кроме того, MVC Framework позволяет определять вам свои собственные определения типа возвращаемых значений.
Для примера рассмотрим проект ASP.NET MVC, который создается по умолчанию:
□ в файле AccountController.cs определен класс-контроллер AccountController, который содержит набор методов, возвращающих результат типа ActionResult;
□ класс AccountController, кроме всего прочего, содержит методы LogOn, LogOff, Register, ChangePassword и ChangePasswordSuccess, которые являются действиями, возвращающими результат типа ActionResult;
□ действия этого класса возвращают разнообразные типы значений. Так, большинство действий возвращают результат типа ViewResult, вызывая стандартный метод View. Некоторые методы могут вернуть результат в виде RedirectResult с помощью вызова метода Redirect или RedirectToRouteResult с помощью RedirectToAction;
□ в зависимости от типа возвращаемого значения пользователь получит определенный результат. Если метод вернул ViewResult, то пользователь получит HTML-код и увидит страницу. В случае когда результатом вызова действия будут данные типа RedirectResult, то браузер пользователя перенаправит вызов на другую страницу. В случае же когда тип возвращаемого значения – это RedirectToRouteResult, MVC Framework перенаправит вызов на другое действие текущего или иного контроллера.
Рассмотрим более конкретный пример, метод LogOn класса AccountController:
public ActionResult LogOn(string userName,
string password,
bool rememberMe,
string returnUrl)
{
if (!ValidateLogOn(userName, password))
{
return View () ;
}
FormsAuth.SignIn(userName, rememberMe) ;
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
Этот метод представляет действие, которое в зависимости от полученных от пользователя данных производит авторизацию пользователя либо сообщает об ошибке авторизации. В данном случае выполняется проверка данных пользователя и, если они неверны, возвращается стандартное представление, сопоставленное данному действию. Если данные верны, происходит авторизация, и пользователь перенаправляется либо на главную страницу, либо на URL, указанный в параметре returnUrl. Перенаправление на главную страницу происходит через вызов метода RedirectToAction, который возвращает результат типа RedirectToRouteResult, перенаправление на другой URL происходит через вызов Redirect, который возвращает результат типа RedirectResult.
Простой пример реализации контроллера
Чтобы показать на примере реализацию контроллера в MVC Framework, создадим новый контроллер для проекта, который формируется по умолчанию. Этот контроллер должен будет реализовывать простейшие административные действия, например, выводить список пользователей, создавать их, блокировать, менять пользователям пароль. Заметим, что наша первая реализация будет весьма простой, имеющей недостатки, которые по ходу этой главы будут исправляться при описывании очередной темы.
Перед созданием нашего примера заполним базу данных пользователей данными:
□ добавим две роли пользователей: Administrators и Users;
□ добавим пользователей Admin с ролью Administrators и User с ролью Users.
Для добавления этих данных необходимо воспользоваться встроенным в Visual Studio средством для управления пользователями, которое можно вызвать, нажав последнюю иконку на панели Solution Explorer, которая появляется во время того, когда активен проект, реализующий поддержку стандартных провайдеров базы данных пользователей (рис. 4.1).

Рис. 4.1. Панель кнопок Solution Explorer
Для того чтобы создать контроллер в Visual Studio 2008, необходимо проделать следующие действия: в контекстном меню папки Controllers выбрать пункт Add, затем Controller (рис. 4.2).

Рис. 4.2. Пункт меню, позволяющий добавить в проект новый контроллер
В появившемся окне необходимо ввести название класса нового контроллера, в нашем случае AdminController (рис. 4.3).
После этого Visual Studio сгенерирует в пространстве имен по умолчанию необходимый нам класс:
public class AdminController : Controller {
//
// GET: /Admin/
public ActionResult Index() {
return View();
}
}

Рис. 4.3. Окно ввода имени контроллера
Visual Studio только облегчает вам жизнь, упрощая рутинные операции, но при желании вы могли бы создать этот класс самостоятельно.
Мастер автоматически создал в контроллере метод Index, который представляет собой действие, вызываемое при обращении к контроллеру по умолчанию, согласно правилам маршрутизации, которые создаются в проекте (о маршрутизации подробнее будет рассказано позднее). Добавим в действие Index логику по созданию списка пользователей:
public ActionResult Index()
{
if (User.IsInRole("Administrators"))
{
MembershipProvider mp = Membership.Provider;
int userCount;
var users = mp.GetAllUsers(0, Int32.MaxValue, out userCount);
ViewData.Model = users;
return View ();
}
else
{
return RedirectToAction("Index", "Home");
}
}
Здесь, после проверки на принадлежность текущего пользователя к группе Administrators, создается список зарегистрированных пользователей, после чего он передается в специальный объект viewData, который в MVC Framework содержит модель данных, необходимых для представления. Если пользователь прошел проверку на принадлежность к группе Administrators, то после создания набора действие завершится вызовом метода viewData, который сформирует представление, сопоставленное данному действию. В случае же, когда пользователь, не имеющий права на доступ к этому действию, вызовет его, действие перенаправит вызов на другое действие Index контроллера Home, что, по сути, означает перенаправление на главную страницу сайта.
Для отображения наших данных нам необходимо создать представление. В MVC Framework представление для контроллера должно создаваться по определенным правилам:
□ все представления для определенного контроллера должны находиться в папке, название которой повторяет название контроллера, например: Home, Account;
□ все такие папки должны находиться в папке Views, которая располагается в корне проекта или веб-сайта;
□ каждый отдельный файл представления должен иметь название, совпадающее с именем действия контроллера, которому оно соответствует, например LogOn.aspx, Register.aspx.
Согласно этим правилам создадим папку Admin в папке Views, в которую через контекстное меню добавим представление Index (рис. 4.4).

Рис. 4.4. Пункт меню, позволяющий добавить новое представление

Рис. 4.5. Окно создания нового представления
В окне Add View (рис. 4.5) необходимо указать название представления (без расширения файла), остальные параметры пока оставьте без изменения. Добавим разметку и код для формирования представления в файле Index.aspx:
ContentPlaceHolderID="MainContent" runat="server"> Список пользователей <% foreach (MembershipUser user in (MembershipUserCollection)ViewData.Model) { %> "Select", new {userid = (Guid)user.ProviderUserKey}) %> <% } %>
Имя
Email
Последняя активность
Подтвержден
Заблокирован
<%= Html.ActionLink("Выбрать",
<%= Html.Encode(user.UserName) %>
<%= Html.Encode(user.Email) %>
<%= user.LastActivityDate %>
<%= user.IsApproved %>
<%= user.IsLockedOut %>
Теперь мы можем запустить проект и посмотреть на результат работы. Для этого нам необходимо войти в систему под пользователем Admin и перейти по адресу http://"наш сайт"/Admin. Если вы все сделали правильно, то результатом будет примерно такой список пользователей – рис. 4.6.

Рис. 4.6. Список пользователей
Для завершения создания контроллера реализуем все остальные необходимые действия так, как показано в листинге 4.1.
Листинг 4.1. Действия контроллера AdminController
public ActionResult Select(Guid? userId)
{
if (!userId.HasValue)
throw new HttpException(404, "Пользователь не найден");
if (User.IsInRole(«Administrators»))
{
MembershipProvider mp = Membership.Provider;
MembershipUser user = mp.GetUser(userId, false);
ViewData.Model = user;
return View () ;
}
else
{
return RedirectToAction("Index", "Home");
}
}
public ActionResult Update(Guid? userId,
string email,
bool isApproved,
bool isLockedOut)
{
if (!userId.HasValue)
throw new HttpException(404, "Пользователь не найден");
if (User.IsInRole(«Administrators»))
{
MembershipProvider mp = Membership.Provider;
MembershipUser user = mp.GetUser(userId, false);
user.Email = email; user.IsApproved = isApproved;
if (user.IsLockedOut && !isLockedOut)
user.UnlockUser();
mp.UpdateUser(user);
return RedirectToAction("Index");
}
else
{
return RedirectToAction("Index", "Home");
}
}
public ActionResult Delete(Guid? userId)
{
if (!userId.HasValue)
throw new HttpException(404, "Пользователь не найден");
if (User.IsInRole(«Administrators»))
{
MembershipProvider mp = Membership.Provider;
MembershipUser user = mp.GetUser(userId, false);
mp.DeleteUser(user.UserName, true);
return RedirectToAction("Index");
}
else
{
return RedirectToAction("Index", "Home");
}
}
В листинге 4.1 определено три действия для отображения данных конкретного пользователя, удаления данных и сохранения новых данных. Обратите внимание, что действия Update и Delete не возвращают сопоставленного представления, а только перенаправляют запрос на вызов других действий с имеющимися представлениями. Только действие Select возвращает сопоставленное представление. Давайте создадим соответствующее представление для контроллера Admin, назвав его Select, и определим его следующим образом:
runat="server"> <%=Html.ActionLink("Вернуться в список", "Index")%> <% MembershipUser user = (MembershipUser) ViewData.Model; %> Пользователь <%= Html.Encode(user.UserName) %> <% using (Html.BeginForm(«Update», «Admin»)) { %> <%= Html.Hidden("userId", (Guid)user.ProviderUserKey) %>
<%=Html.TextBox("email", user.Email)%>
user.IsApproved)%>подтвержден
user.IsLockedOut)%>заблокирован
<%=Html.ActionLink("Удалить", "Delete", new {userId = (Guid)
user.ProviderUserKey})%>
<% } %>
Когда вы запустите проект, то после входа под логином Admin и перехода на страницу администрирования, вы сможете выбирать пользователей путем перехода по ссылкам Выбрать. После такого перехода для пользователя User вы должны увидеть следующую форму (рис. 4.7).

Рис. 4.7. Форма редактирования данных пользователя
C помощью этой формы и действий, реализованных в нашем контроллере, вы сможете модифицировать данные пользователя либо удалить его из системы.
Архитектура контроллеров
Мы рассмотрели простейший вариант создания контроллеров и их действий, а также сопоставленных с ними представлений. Но архитектура MVC Framework помимо рассмотренных механизмов содержит массу больших и малых механизмов, которые позволяют регулировать контроллеры, управлять вызовами кода, защищать контроллеры и действия от нежелательных вызовов, формировать базовые архитектуры для набора действий или даже группы контроллеров.
Далее мы более подробно рассмотрим архитектуру реализации контроллеров в MVC Framework. По мере рассмотрения, наш простейший код будет видоизменяться согласно новым техникам, о которых будет идти рассказ.
Порядок вызова архитектурных механизмов
ASP.NET MVC реализует следующий порядок выполнения внутренних механизмов:
1. При первом обращении к приложению в Global.asax производится регистрация маршрутов механизма маршрутизации ASP.NET в специальном объекте RouteTable.
2. С помощью urlRoutingModule производится маршрутизация запроса с использованием созданных маршрутов, при этом выбирается первый подходящий маршрут, после чего создается объект типа RequestContext.
3. Обработчик MvcHandler, с помощью данных полученного объекта RequestContext, инстанцирует объект фабрики контроллеров типа IControllerFactory. Если он не был задан специально, по умолчанию инстанцируется объект класса DafaultControllerFactory.
4. В фабрике контроллеров вызывается метод GetControllerInstance, который принимает параметр в виде типа необходимого контроллера. Этот метод возвращает инстанцированный объект необходимого контроллера.
5. У полученного объекта контроллера вызывается метод Execute, которому передается объект RequestContext.
6. С помощью ActionInvoker, свойства класса Controller типа ControllerActionInvoker, определяется необходимое действие и выполняется его код. Если ActionInvoker специально не сопоставлен, то используется свойство ActionInvoker по умолчанию. Для инициализации параметров действия используется механизм Model Binding, который может быть переопределен.
7. Определение действия может быть изменено в случае, когда пользователь определил для действий атрибуты типа ActionMethodSelectorAttribute. Для каждого такого атрибута вызывается метод IsValidForRequest, которому передается набор параметров типа ControllerContext и MethodInfo. Метод IsValidForRequest возвращает результат в виде bool, который определяет, подходит ли выбранное действие для исполнения в контексте запроса.
8. Выполнение действия может быть отменено, изменено или пропущено, если пользователь определил для действия атрибуты типа FilterAttibute. Такие атрибуты могут использоваться для определения прав доступа, кэширования результатов, обработки исключений.
9. В случае когда для действия определены атрибуты типа ActionFilterAttribute, то выполнение действия может быть изменено таким атрибутом на следующих этапах: перед выполнением действия, после выполнения действия, перед исполнением результата или после исполнения результата действия.
10. В случае когда Actioninvoker был специально переопределен, то после вызова действия результат действия передается методу CreateActionResult свойства Actioninvoker, который волен переопределить возвращаемый результат действия и вернуть результат типа ActionResult.
11. В случае, когда Actioninvoker не был переопределен, действие должно вернуть результат выполнения в виде ActionResult.
12. Для результата типа ActionResult вызывается метод ExecuteResult, который осуществляет отображение результата для контекста запроса. Создав свой вариант класса, наследующий ActionResult и реализующий ExecuteResult, можно переопределить действие и возвращать результат в нужном виде, например, формировать RSS-ленту.
Рассмотрим все шаги, относящиеся к контроллерам и действиям, более подробно. Для этого выделим основные темы:
□ фабрика контроллеров;
□ действия, фильтры и атрибуты;
□ механизм Model Binding.
Фабрика контроллеров
После того как внутренний механизм MVC получит запрос, обработает таблицу маршрутизации и подберет необходимый маршрут, в дело вступает фабрика контроллеров. По умолчанию создается и используется экземпляр фабрики контроллеров типа DefaultFactoryController. Для реализации своего варианта поведения фабрики контроллеров разработчик может реализовать свой класс, наследующий класс DefaultFactoryController.
Фабрика контроллеров – это механизм, созданный с одной целью – создавать экземпляры контроллеров. В ASP.NET MVC фабрика контроллеров – это один из механизмов, который позволяет настроить поведение механизма MVC под себя, расширить функционал, предоставить разработчику возможность гибкой настройки действия MVC.
Фабрика контроллеров в ASP.NET реализует интерфейс IControllerFactory, который содержит всего два метода:
public interface IControllerFactory {
IController CreateController(RequestContext requestContext,
string controllerName);
void ReleaseController(IController controller);
}
Здесь CreateController должен создавать экземпляр контроллера, а ReleaseController – разрушать его или проводить какие-то другие действия после завершения работы контроллера. Вы можете создать свою фабрику контроллеров, реализовав этот интерфейс. Но более простым способом расширения фабрики является ее реализация с помощью наследования от класса DefaultFactoryController.
Для примера рассмотрим такую, вполне возможную задачу, решить которую позволит фабрика контроллеров:
□ необходимо создать механизм, который позволит ограничивать создание определенных контроллеров на основе "черного списка";
□ решение на базе маршрутов таблицы маршрутизации не может нас удовлетворить, поскольку создание маршрутов производится через исполняемый код, который по каким-то причинам не может быть модифицирован для загрузки маршрутов из внешнего источника;
□ решение на базе маршрутов не может нас удовлетворить в связи с тем, что у нас много универсальных маршрутов, которые затрагивают сразу много контроллеров;
□ требуется возможность работать с "черным списком" в виде редактирования определенного файла для возможности разделения и предоставления прав к нему.
Согласно этим требованиям создадим фабрику контроллеров, которая позволит нам ограничивать выполнение контроллеров по их именам через XML-файл "черного списка". Вот пример такого файла:
Как можно увидеть, данным файлом мы хотели бы заблокировать выполнение контроллеров 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 в таком виде, в котором и производительность будет на высоте, и необходимый функционал не будет потерян.
Для примера рассмотрим листинг 4.2 с реализацией такого класса ActionInvoker, который максимально быстро вызывает действие контроллера без поддержки каких-либо атрибутов и накладных расходов на такую поддержку.
Листинг 4.2
public class FastControllerActionInvoker : ControllerActionInvoker
{
public override bool InvokeAction(
ControllerContext controllerContext, string actionName)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
if (String.IsNullOrEmpty(actionName))
throw new ArgumentException("actionName");
ControllerDescriptor controllerDescriptor =
GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext,
controllerDescriptor, actionName);
if (actionDescriptor != null)
{
IDictionary
GetParameterValues(controllerContext, actionDescriptor);
var actionResult = InvokeActionMethod(controllerContext,
actionDescriptor, parameters);
InvokeActionResult(controllerContext, actionResult);
return true;
}
return false;
}
}
Существенное достоинство этого класса в том, что уменьшение затрат на вызов действия позволило ускорить вызов в среднем на величину от 5 до 30 % по разным оценкам проведенного нами тестирования. С другой стороны, данный вариант не поддерживает многие стандартные механизмы MVC: фильтры, обработку ошибок, проверку авторизации.
Для использования нового класса FastControllerActionInvoker нужно присвоить его экземпляр свойству ActionInvoker необходимого контроллера. Например, используем наш новый класс для контроллера AccountController стандартного проекта MVC:
public AccountController()
: this(null, null)
{
ActionInvoker = new FastControllerActionInvoker();
}
Атрибуты ActionMethodSelectorAttribute
Мы рассмотрели работу механизма ControllerActionInvoker, который призван найти и выполнить необходимое действие контроллера. Одной из особенностей этого поиска является поиск установленных для действий атрибутов типа ActionMethodSelectorAttribute. Эти атрибуты имеют одно-единственное предназначение – определение того, может ли быть вызвано это действие в данном контексте запроса или нет. Рассмотрим определение класса ActionMethodSelectorAttribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false,
Inherited = true)]
public abstract class ActionMethodSelectorAttribute : Attribute {
public abstract bool IsValidForRequest(
ControllerContext controllerContext,
MethodInfo methodInfo);
}
Как вы видите, атрибут содержит только один метод IsValidForRequest, который возвращает булево значение, определяющее, может ли быть вызвано помеченное действие. Этот атрибут очень полезен для обеспечения безопасности, т. к. позволяет «спрятать» часть методов от любой возможности неправомерного использования.
Для удобства разработчиков MVC Framework уже реализует два атрибута, наследующих от ActionMethodSelectorAttribute:
□ AcceptVerbsAttribute – позволяет задать для действия допустимые типы HTTP-запросов из следующего списка: GET, POST, PUT, DELETE, HEAD. Запросы, отличные от указанных, будут игнорироваться;
□ NonActionAttribute – позволяет пометить метод, не являющийся действием. Такой метод невозможно будет вызвать никаким внешним запросом.
Используем эти атрибуты для нашего контроллера AdminController. Так как действия Index, Select и Delete могут быть вызваны только GET-запросами, пометим их соответствующим атрибутом, как показано в следующем фрагменте:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Delete(Guid? userId)
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Select(Guid? userId)
Наоборот, действие Update вызывается только POST-запросами, поэтому пометим их следующим образом:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update(Guid? userId, string email,
bool isApproved, bool isLockedOut);
Теперь, если мы попытаемся вызвать действие Update из строки запроса браузера, набрав URL вроде такого http://localhost:5434/Admin/Update, то получим ошибку с сообщением о том, что страница не была найдена. Без атрибута AcceptVerbs метод был бы вызван.
Для демонстрации действия атрибута NonActionAttribute проведем некоторые изменения в нашем коде. Обратите внимание на то, что в методах класса ActionController повторяется следующий код:
User.IsInRole(«Administrators»)
Вынесем его в отдельный метод UserlsAdmin:
[NonAction]
private bool UserIsAdmin()
{
return User.IsInRole(''Administrators");
}
Обратите внимание, что мы пометили этот метод атрибутом NonAction, который указывает на то, что данный метод не является действием и не может быть выбран механизмом MVC при поиске в контроллере необходимого действия.
Атрибуты, производные от FilterAttribute
Когда механизм MVC находит необходимое для вызова действие, производится поиск и выполнение ряда атрибутов, которые являются производными от атрибута FilterAttribute. Такие атрибуты называются фильтрами и в основном предназначены для проверки прав вызова и безопасности контекста запроса. MVC Framework содержит ряд таких атрибутов, которые вы можете использовать в своих проектах:
□ AuthorizeAttribute – позволяет указывать ограничения для имен пользователей и ролей, которые могут вызвать данное действие;
□ HandleErrorAttribute – позволяет определять действия для обработки необработанных исключений;
□ ValidateAntiForgeryTokenAttribute – проверяет контекст запроса на соответствие указанному маркеру безопасности при получении данных из форм;
□ ValidateInputAttribute – управляет механизмом проверки запроса на небезопасные значения. Позволяет отключить такого рода проверку для случаев, когда требуется получить данные разного вида, в том числе потенциально опасные.
AuthorizeAttribute
AuthorizeAttribute – весьма полезный атрибут, который позволяет задавать группы и пользователей, имеющих доступ к заданному действию контроллера или ко всему контроллеру сразу. В нашем примере для проверки прав на доступ к действию контроллера AdminController мы создали следующий метод:
[NonAction]
private bool UserIsAdmin()
{
return User.IsInRole("Administrators");
}
С использованием атрибута AuthorizeAttribute нужда в этом методе пропадает. Чтобы продемонстрировать действие AuthorizeAttribute, перепишем контроллер AdminController по-новому, так, как показано в листинге 4.3.
Листинг 4.3
public class AdminController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
[Authorize(Roles = "Administrators")]
public ActionResult Index()
{
MembershipProvider mp = Membership.Provider;
int userCount;
var users = mp.GetAllUsers(0, Int32.MaxValue, out userCount);
ViewData.Model = users;




























