В течении прошлых частей было создано простое, но уже работоспособное веб-приложение. Оно, хоть и с некоторыми оговорками, позволяет просматривать, добавлять и редактировать информацию о книгах. Давайте сделаем его более на реалистичным и выделим административную и пользовательскую части.
Как правило блок администрирования располагается в каталоге Admin или Administrator, куда вход закрыт паролем. На текущем этапе не будем вдаваться в подробности авторизации в ASP.NET MVC. Поэтому пока просто сделаем так, чтобы управления каталогом была доступно по адресу http://<имя сервера>/Admin/.
Решение "в лоб" и почему так делать не стоит
В качестве решения можно создать Контроллер Admin и переложить все функции на него. Вроде бы логично, но к чему это приведёт? Давайте представим, сколько может быть задачи у системы управления веб-приложением? Легко понять, что в реальном проекте такой Контроллер будет содержать очень большое число Действий. В результате, такой подход усложнит дальнейшее сопровождение кода.
Также необходимо отметить, что созданный в этом случае класс AdminController объединит в себе функции многих Контроллеров, у которых были четко определенные задачи. По сути, получится "мастер на все руки", что является плохой практикой при проектировании и разработке приложений. При этом от слова Admin в тексте ссылки необходима только группировка функциональности по её общему смыслу.
Поэтому, для решения подобных задач в ASP.NET MVC была введена поддержка областей (Area).
Использование областей (Area) в ASP.NET MVC 3
Идея областей очень простая – позволить разделить большой проект на несколько логических частей, которые могут взаимодействовать друг с другом. При этом они вправе содержать собственные Контроллеры и Представления, а также расширять Модель своими классами.
Кроме того, таким образом можно разделить большой проект между несколькими разработчиками или группами. Например, на сайте могут быть разделы форум и блог со ссылками http://<имя сервера>/Forum/ и http://<имя сервера>/Blog/ соответственно. При этом они могут использовать общую Модель для идентификации посетителя сайта, но при этом иметь и собственные элементы бизнес-логики.
Рассмотрим создание области на конкретном примере.
Создание области
Для создания новой области выберем в контекстном меню проекта, открывающемся в Solution Explorer / Navigation Explorer, пункт "Add > Area ..." и в появившемся диалоге "Add Area" введем имя Admin.
Помощник ASP.NET MVC 3 добавит в проект папку Areas, в которой будут размещены все дополнительные области. При этом главной или областью по умолчанию будем называть корневой каталог веб-приложения.
В папке Areas размещена подпапка только что созданной области Admin. Внутри последней создана структура, аналогичная структуре самого проекта: папки Controllers, Models и Views. Таким образом, область может использовать как классы проекта, так и добавлять свои специфические объекты.
Создание Контроллеров и Представлений осуществляется также с использованием помощника ASP.NET MVC или заготовок, для которых область указывается в качестве значения параметра -Area. И как еще один вариант, можно добавлять классы и файлы вручную.
Стоит обратить внимание на файл <имя области>AreaRegistration.cs (в данном случае это AdminAreaRegistration.cs). В нем расположена информация для регистрации области в ядре ASP.NET MVC 3. Взглянем на него чуть подробнее:
namespace BookCatalog.Areas.Admin
{
using System.Web.Mvc;
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "Admin"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new {
controller = "Catalog",
action = "Index",
id = UrlParameter.Optional
}
);
}
}
}
Класс AdminAreaRegistration является наследником AreaRegistration. Его свойство AreaName содержит имя области, которое будет использоваться внутри ASP.NET MVC. Метод RegisterArea() предназначен для выполнения необходимых действий при её регистрации. В данном случае определяется маршрут, по которому будет доступна данная область. Выделенная в исходном коде строка добавлена для указания Контроллера по умолчанию.
Все области проекта регистрируется в Application_Start() при помощи AreaRegistration.RegisterAllAreas().
Рассмотрим необходимые действия для переноса Контроллеров и Представлений из главной в только что созданную область. При этом Модель затронута не будет.
Переносим функциональность
Для переноса функциональности в область Admin просто скопируем в неё все Контроллеры и их Представления. Сделать это можно просто перетащив их мышкой в Solution Explorer / Navigation Explorer. Затем исправим пространства имен с BookCatalog.Controllers на соответствующее новому месту расположения – BookCatalog.Areas.Admin.Controllers.
Создадим новый шаблон разметки _Layout.cshtml в папке Areas/Admin/Views/Shared. Его содержимое можно скопировать с существующего варианта в главной области. Исключение составит только новая ссылка для возврата к главной странице:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
<div class="menu">
@Html.ActionLink("Catalog", "Index", "Catalog") |
@Html.ActionLink("Publishers", "Index", "Publishers") |
@Html.ActionLink("Languages", "Index", "Languages") |
@Html.ActionLink("Tags", "Index", "Tags") |
@Html.ActionLink("Back", string.Empty, string.Empty, new { area = "" }, null)
</div>
@RenderBody()
</body>
</html>
Ссылки, создаваемые с помощью ActionLink(), осуществляют навигацию в границах текущей области. Чтобы выйти за её пределы необходимо явно указать это в параметрах метода. В выделенной строке осуществляется переход к главной странице веб-приложения. При этом будут задействованы Контроллер и Действие по умолчанию, определенные для главной области.
Следующим шагом является создание файла _ViewStart.cshtml с указанием Представлениям в Areas/Admin/Views использовать новый шаблон разметки:
@{
Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml";
}
Добавляем функции в главную область
Новый Контроллер
Поскольку в главной области не осталось Контроллеров, добавим в неё один для отображения списка книг. С этой целью создадим класс CatalogController в папке Controllers со следующим кодом:
namespace BookCatalog.Controllers
{
using System.Web.Mvc;
using BookCatalog.Models.Repositories;
public class CatalogController : Controller
{
private readonly IBookDetailsRepository _bookdetailsRepository;
// If you are using Dependency Injection, you can delete the following constructor
public CatalogController()
: this(new BookDetailsRepository())
{
}
public CatalogController(IBookDetailsRepository bookdetailsRepository)
{
this._bookdetailsRepository = bookdetailsRepository;
}
// GET: /Catalog/
public ViewResult Index()
{
return this.View(
this._bookdetailsRepository.AllIncluding(
bookdetails => bookdetails.Language,
bookdetails => bookdetails.Publisher,
bookdetails => bookdetails.Tags));
}
}
}
Здесь определено только одно Действие Index, ответственное за вывод списка. Обратите внимание, что разные области могут содержать Контроллеры с одинаковыми именами.
Представление для отображения каталога книг
Теперь добавим Представление для нового Контроллера: в папке View создадим подпапку Catalog и разместим в ней файл Index.cshtml:
@model IEnumerable<BookCatalog.Models.BookDetails>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th>PublishedAt</th>
<th>Url</th>
<th>Description</th>
<th>Tags</th>
<th>Rating</th>
<th>IsFree</th>
<th>Language</th>
<th>Publisher</th>
</tr>
@foreach (var item in Model) {
<tr>
<td>@item.Title</td>
<td>@item.Author</td>
<td>@String.Format("{0:D}", item.PublishedAt)</td>
<td>@item.Url</td>
<td>@item.Description</td>
<td>@string.Join(", ", item.Tags.Select(tag => tag.Text).ToArray())</td>
<td>@item.Rating</td>
<td>@item.IsFree</td>
<td>@(item.Language == null ? "None" : item.Language.Name)</td>
<td>@(item.Publisher == null ? "None" : item.Publisher.Title)</td>
</tr>
}
</table>
В отличие от Представления из области Admin, здесь удалён вывод ссылок на создание, редактирование и удавление записей о книгах. Кроме того, отсутствует столбец IsVisible, который в дальнейшем и будет отвечать за отображение записи в данном списке.
Изменяем файл разметки
Последнее изменение в главной области – файл разметки. Там также больше не нужны ссылки на различные таблицы. Вместо них поставим только одну ссылку – на область администрирования:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
<div class="menu">
@Html.ActionLink("Adminisrtrator", string.Empty, string.Empty, new { area = "Admin" }, null)
</div>
@RenderBody()
</body>
</html>
Здесь также явно указана область, к которой осуществляется переход. При этом будут использованы Контроллер и Действие по умолчанию.
Одинаковые имена Контроллеров
Если сейчас запустить веб-приложение на выполнение, то результатом будет сообщение об ошибке. Дело в том, что ядро ASP.NET MVC 3 видит два класса с одинаковыми именами CatalogController. Оба могут быть Контроллером Catalog, указанным как вариант по умолчанию.
Чтобы разрешить эту ситуацию, необходимо явно указать пространство имен при настройке маршрутов. Для этого в файл Global.asax внесем соответствующее изменение:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new {
controller = "Catalog",
action = "Index",
id = UrlParameter.Optional
}, // Parameter defaults
new string[] { "BookCatalog.Controllers" }
);
}
Теперь можно запустить веб-приложение и убедиться в его работоспособности. В следующей части обратим внимание на текстовые строки, содержащиеся в его исходном коде.
Исходный код проекта (C#, Visual Studio 2010):
mvc3-in-depth-basics-08.zip