ASP.NET MVC 4

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 = "" })
или RSS канал: Что новенького на smarly.net