ASP.NET MVC 4
Адам Фриман
Работа с областями
MVC фреймворк поддерживает организацию веб приложения по областям (areas), где каждая область представляет собой функциональный сегмент приложения, такой как административная часть, биллинг, поддержка клиентов и так далее. Это полезно для больших проектов, где из-за наличия единого набора папок для всех контроллеров, представлений и моделей приложение может стать трудно управляемым.
Каждая MVC область имеет свою собственную структуру папок, что позволяет все корректно разделять. И ведь становится более очевидным, какие элементы проекта с какой функциональной областью приложения связаны. Это позволяет нескольким разработчикам работать над проектом, не мешая друг другу. Области в значительной степени поддерживаются системой маршрутизации, и именно поэтому мы решили рассказать об этой возможности в данной главе, где мы описываем URL и роуты. В этом разделе мы покажем вам, как настроить и использовать области в ваших MVC проектах.
Создание области
Чтобы добавить область в MVC приложение, щелкните правой кнопкой мыши в окне Solution Explorer
по пункту проекта и выберите Add
-> Area
. Visual Studio предложит вам ввести название области, как показано на рисунке 14-6. В данном случае мы создали область Admin
. Это довольно распространенная область, потому что во многих веб приложениях необходимо разделять функционал, касающийся работы с клиентами, и функционал для администрирования. Нажмите кнопку Add
, чтобы создать область.
Рисунок 14-6: Добавление области в MVC приложение
После нажатия кнопки Add
вы увидите некоторые изменения, внесенные в проект. Прежде всего, проект содержит новую папку верхнего уровня Areas
. В ней содержится папка Admin
, которая представляет область, только что созданную нами. Если бы нам нужно было создать дополнительные области, другие папки появились бы здесь.
Вы видите, что внутри папки Areas/Admin
у нас есть мини-MVC проект. Есть папки Controllers
, Models
и Views
. Первые две из них пустые, но папка Views
содержит папку Shared
(и файл Web.config
, который настраивает движок представления, но нам не интересен движок представления до главы 18).
Другое изменение заключается в том, что здесь есть файл AdminAreaRegistration.cs
, который определяет класс AdminAreaRegistration
, как показано в листинге 14-22.
Листинг 14-22: Класс AdminAreaRegistration
using System.Web.Mvc;
namespace UrlsAndRoutes.Areas.Admin
{
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 { action = "Index", id = UrlParameter.Optional }
);
}
}
}
Интересной частью этого класса является метод RegisterArea
. Как вы видите в листинге, этот метод регистрирует роут с URL паттерном Admin/{controller}/{action}/{id}
. Мы можем определить дополнительные роуты в этом методе, которые будут уникальны для этой области.
Внимание
Если вы присваиваете имена вашим роутам, вы должны убедиться, что они являются уникальными во всем приложении, а не только в области, для которой они предназначены.
Нам не нужно предпринимать никаких действий, чтобы убедиться, что этот метод регистрации будет вызван. Он обрабатывается для нас автоматически методом Application_Start
в Global.asax
, что вы можете увидеть в листинге 14-23.
Листинг 14-23: Регистрация области при помощи Global.asax
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Вызов статического метода AreaRegistration.RegisterAllAreas
заставляет MVC фреймворк пройти через все классы в нашем приложении, найти те, которые наследуются от класса AreaRegistration
, и вызвать метод RegisterArea
для каждого из них.
Внимание
Не меняйте порядок выражений, связанных с маршрутизацией, в методе
Application_Start
. Если вы вызоветеRegisterRoutes
передAreaRegistration.RegisterAllAreas
, то ваши роуты будут определены до роутов области. Учитывая то, что роуты оцениваются по порядку, это будет обозначать, что запросы для контроллеров областей, вероятно, будут сопоставляться с неправильными роутами.
Класс AreaRegistrationContext
, который передается каждому методу области RegisterArea
, предоставляет набор методов MapRoute
, которые область может использовать для регистрации роутов таким же способом, как это делает основное приложение в методе RegisterRoutes
в Global.asax
.
Примечание
Методы
MapRoute
в классеAreaRegistrationContext
автоматически ограничивают роуты, которые вы регистрируете, пространствами имен, содержащими контроллеры для данной области. Это означает, что при создании контроллера в области вы должны оставить его в пространстве имен по умолчанию, в противном случае система маршрутизации не сможет его найти.
Заполнение области
Вы можете создавать контроллеры, представления и модели в области так же, как вы делали в предыдущих примерах. Для создания контроллера щелкните правой кнопкой мыши по папке Controllers
внутри области Admin
и выберите Add
-> Controller
из всплывающего меню. Откроется диалоговое окно Add Controller
, в котором можно ввести имя для нового класса контроллера, как показано на рисунке 14-7.
Рисунок 14-7: Добавление в область контроллера
Нажатие Add
создает пустой контроллер, как показано в листинге 14-24. В этом примере мы вызвали наш класс HomeController
, чтобы продемонстрировать разделение между областями в приложении.
Листинг 14-24: Контроллер, созданный внутри MVC области
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace UrlsAndRoutes.Areas.Admin.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
Для завершения этого простого примера мы можем создать представление, щелкнув правой кнопкой мыши внутри метода действия Index
и выбрав Add View
из всплывающего меню. Мы приняли имя по умолчанию для нашего представления (Index
). Когда вы создадите представление, вы увидите, что оно появилось в папке Areas/Admin/Views/Home
. Представление, которое мы создали, показано в листинге 14-25.
Листинг 14-25: Простое представление для контроллера области
@{
ViewBag.Title = "Index";
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<h2>Admin Area Index</h2>
</div>
</body>
</html>
Смысл всего этого заключается в том, чтобы показать, что работа внутри области в значительной степени такая же, как и работа в основной части MVC проекта. Вы видели, что процесс создания элементов такой же самый. Мы создали контроллер и представление, которые называются так же, как и их «коллеги» в основной части проекта. Если вы запустите приложение и перейдите на /Admin/Home/Index
, вы увидите представление, которое мы создали, как показано на рисунке 14-8.
Рисунок 14-8: Рендеринг представления области
Решение проблемы с контроллером
Хорошо, мы немного солгали: области не столь автономны и самодостаточны, какими они могли бы быть. Если вы перейдете по URL /Home/Index
, вы увидите сообщение об ошибке, как показано на рисунке 14-9.
Рисунок 14-9: Ошибка контроллера
Когда область зарегистрирована, любой роут, который мы определяем, ограничен пространством имен, связанным с областью. Поэтому мы смогли запросить /Admin/Home/Index
и получить класс HomeController
пространства имен WorkingWithAreas.Areas.Admin.Controllers
.
Тем не менее, роуты, определенные в методе RegisterRoutes
в RouteConfig.cs
, не ограничиваются аналогичным образом. В листинге 14-26 в качестве напоминания мы описали текущую роутинговую конфигурацию нашего приложения.
Листинг 14-26: Роутинговая конфигурация MVC приложения
public static void RegisterRoutes(RouteCollection routes) {
routes.Add(new Route("SayHello", new CustomRouteHandler()));
routes.Add(new LegacyRoute(
"~/articles/Windows_3.1_Overview.html",
"~/old/.NET_1.0_Class_Library"));
routes.MapRoute("MyRoute", "{controller}/{action}");
routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" });
}
Роут MyRoute
переводит входящие URL от браузера на метод действия Index
контроллера Home
. В этот момент мы получаем ошибку, потому что для этого роута нет ограничений по пространствам имен, и MVC фреймворк видит два класса HomeController
. Чтобы решить эту проблему, мы должны определить приоритет для основного пространства имен контроллера во всех роутах, которые могут привести к конфликту, как показано в листинге 14-27.
Листинг 14-27: Решение конфликта по пространствам имен областей
public static void RegisterRoutes(RouteCollection routes) {
routes.Add(new Route("SayHello", new CustomRouteHandler()));
routes.Add(new LegacyRoute(
"~/articles/Windows_3.1_Overview.html",
"~/old/.NET_1.0_Class_Library"));
routes.MapRoute("MyRoute", "{controller}/{action}", null,
new[] {"UrlsAndRoutes.Controllers"});
routes.MapRoute("MyOtherRoute", "App/{action}",
new { controller = "Home" }, new[] { "UrlsAndRoutes.Controllers" });
}
Это изменение гарантирует, что контроллеры в основном проекте имеют более высокий приоритет в обработке запросов. Конечно, если вы хотите отдать предпочтение контроллерам в области, вы можете это сделать.
Создание ссылок к методам действий в областях
Вам не нужно предпринимать никаких специальных шагов для создания ссылок, которые относятся к действиям в той же области MVC, в которой находится пользователь. MVC фреймворк обнаруживает, что текущий запрос относится к конкретной области, а затем механизм создания исходящего URL будет находить соответствие только среди роутов, определенных для этой области. Например, это дополнение в представление для области Admin
:
@Html.ActionLink("Click me", "About")
генерирует следующий HTML:
<a href="/Admin/Home/About">Click me</a>
Чтобы создать ссылку на действие в другой области (или вообще не в области), необходимо создать переменную area
и использовать ее, чтобы указать имя нужной области:
@Html.ActionLink("Click me to go to another area", "Index", new { area = "Support" })
Именно по этой причине слово area
зарезервировано от использования в качестве имени переменной сегмента. HTML, сгенерированный этим вызовом, выглядит следующим образом (при условии, что вы создали область Support
, для которой определен стандартный роут):
<a href="/Support/Home">Click me to go to another area</a>
Если вы хотите ссылаться на действие одного из контроллеров высшего уровня (контроллера в папке /Controllers
), то вы должны указать area
как пустую строку, например:
@Html.ActionLink("Click me to go to another area", "Index", new { area = "" })