Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
ASP.NET Core MVC позволяет организовать приложение в виде областей, где каждая область представляет какой-то свой сегмент приложения. Например, одна область может отвечать за функции администрирования, другая - за управление учетными записями и т.д. И каждая область представляет собой мини-проект, имеет свою собственную структуру каталогов. Это позволяет разрабатывать и тестировать код для каждой области отдельно независимо от других областей.
Возьмем простой проект ASP.NET Core с MVC и добавим в него области. Как правило, области помещаются в отдельную папку. Поэтому добавим в проект новую папку, которую назовем Areas. Затем в эту папку добавим каталог под названием Store. То есть у нас будет область под названием Store.
И далее в папке Areas/Store создадим подкаталоги Controllers и Views. В итоге проект будет выглядеть так:
Затем в папку Controllers добавим новый контроллер HomeController:
using Microsoft.AspNetCore.Mvc; namespace AreasApp.Areas.Store.Controllers { [Area("Store")] public class HomeController : Controller { public IActionResult Index() { return View(); } } }
Для ассоциации контроллера с определенной областью к этому контроллеру применяется атрибут Area, в который передается название области. Без этого атрибута контроллер не будет принадлежать области Store.
Далее в папку Areas/Store/Views добавим подкаталог Home - для представлений нового контроллера. И затем в этот подкаталог поместим представление Index.cshtml с одной строкой:
<h2>HomeController из области Store</h2>
Вообще поиск представления для контроллера из области ведется по следующим путям:
/Areas/Store/Views/Home/
/Areas/Store/Views/Shared/
/Views/Shared/
Соответственно все три выше указанные папки мы могли бы использовать для хранения представления Index.cshtml для контроллера из области Store.
Таким образом, проект теперь выглядит так:
Но пока эта область совершенно неактивна. Мы никак не можем к ней обратиться. И теперь добавим в приложение поддержку этой области. Для этого изменим класс Startup:
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace AreasApp { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); } public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "MyArea", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }
Для сопоставления с областью в первом маршруте определен сегмент area
. Ограничение exists
используется для того, чтобы маршрут сопоставлялся только с теми областями, которые определены в приложении.
И теперь мы сможем обращаться к области с запросом http://localhost:xxxx/Store:
В данном случае определяется маршрут для всех областей. С помощью метода MapAreaControllerRoute() можно указать маршрут для конкретной области:
public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { // маршрут для области store endpoints.MapAreaControllerRoute( name: "store_area", areaName:"store", pattern: "store/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
Метод MapAreaControllerRoute()
принимает три обязательных параметра: имя маршрута, имя области (areaName) и шаблон маршрута.
Причем шаблон маршрута начинается с названия области: "store/{controller=Home}/{action=Index}/{id?}"
Подобным образом можно определить маршруты для нескольких областей:
app.UseEndpoints(endpoints => { // маршрут для области store endpoints.MapAreaControllerRoute( name: "store_area", areaName:"store", pattern: "store/{controller=Home}/{action=Index}/{id?}"); // маршрут для области service endpoints.MapAreaControllerRoute( name: "service_area", areaName:"service", pattern: "service/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
Несмотря на то, что в примере выше шаблон маршрута начинается с названия области и при обращении к методу контроллеру мы вводим в начале название области, в принципе это необязательно. Например, определим следующий маршрут:
app.UseEndpoints(endpoints => { // маршрут для области store endpoints.MapAreaControllerRoute( name: "store_area", areaName:"store", pattern: "products/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
Теперь шаблон маршрута для области Store начинается с токена "products", однако параметр areaName по прежнему позволяет связать данный маршрут с областью Store. Только теперь в адресной строке нам придется вводить вначале "Products", а не "Store":
Подобным образом мы можем использовать области при маршрутизациии через RouterMiddleware и метод UseMvc:
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace AreasApp { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(op => op.EnableEndpointRouting = false); } public void Configure(IApplicationBuilder app) { app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "areas", template: "{area:exists}/{controller=Home}/{action=Index}"); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
Определение маршрутов для конкретных областей:
app.UseMvc(routes => { routes.MapAreaRoute( name: "store_area", areaName:"Store", template: "Products/{controller=Home}/{action=Index}/{id?}"); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
Также мы могли бы задать маршрут через атрибут маршрутизации, в котором параметр area
представлял бы область:
[Area("Store")] public class HomeController : Controller { [Route("[area]")] [Route("[area]/[controller]")] [Route("[area]/[controller]/[action]")] public IActionResult Index() { return View(); } }
Для обращения к такому методу Index мы можем использовать три адреса: http://localhost:xxxx/Store, http://localhost:xxxx/Store/Home и http://localhost:xxxx/Store/Home/Index
В этом случае в классе Startup можно не определять маршрут для области:
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
Также можно использовать префиксы:
[Area("Store")] public class HomeController : Controller { [Route("Products")] [Route("Products/[controller]")] [Route("Products/[controller]/[action]")] public IActionResult Index() { return View(); } }
Для обращения к такому методу Index мы можем использовать следующие адреса: http://localhost:xxxx/Products, http://localhost:xxxx/Products/Home и http://localhost:xxxx/Products/Home/Index
При работе с областями следует учитывать ряд моментов.
Если в контроллеры области используют представления, то система будет искать эти представления по следующим путям:
/Areas/Название_области/Views/Название_контроллера/Имя_представления.cshtml /Areas/Название_области/Views/Shared/Имя_представления.cshtml /Views/Shared/Имя_представления.cshtml
Поэтому все представления, которые находятся вне описанных выше путей, будут недоступны для области. К примеру, в главном проекте функциональность tag-хелперов добавляется с помощью файла _ViewImports.cshtml. В области он будет недоступен. И чтобы задействовать tag-хелперы в представлениях в области, нам надо добавить аналогичный файл в каталог Views, который находится в области. Либо же добавлять в представления области эту строку, где надо использовать tag-хелперы.
Другой важный момент - это генерация ссылок. При генерации ссылок в представлениях отдельных областей используются все те хелперы, например, Html.ActionLink или AncorTagHelper, однако тут есть и некоторые особенности.
Чтобы сгенерировать ссылку на какое-либо действие контроллера, которые находятся внутри области, то мы указываем действие и контроллер (если действие находится в другом контроллере):
@Html.ActionLink("Онлайн-магазин", "Index", "Home") // или <a asp-action="Index" asp-controller="Home">Онлайн-магазин</a>
В итоге будет сгенерирована ссылка: <a href="Store/Home/Index/">Онлайн-магазин</a>
(если текущее представление расположено в области Store).
Если же требуемое действие и контроллер находятся в другой области, то мы указываем область в параметре хелпера:
@Html.ActionLink("Все книги", "List", new { area = "Store", controller = "Book" }) // или <a asp-action="List" asp-controller="Book" asp-route-area="Store">Все книги</a>
Сгенерированная ссылка будет выглядеть так: <a href="Store/Book/List/">Все книги</a>
Если же метод и контроллер, на которые мы хотим сделать ссылку, находятся в основной части приложения, то для параметра area определяем пустую строку:
@Html.ActionLink("Главная", "Index", new { area = "", controller = "Home" }) // или так <a asp-action="Index" asp-controller="Home" asp-route-area="">Главная</a>