ASP.NET Core 中的区域Areas in ASP.NET Core

作者:Dhananjay KumarRick AndersonBy Dhananjay Kumar and Rick Anderson

区域是 ASP.NET 功能,用于将相关功能以单独的名称空间(用于路由)和文件夹结构(用于视图)的形式组织到一个组中。Areas are an ASP.NET feature used to organize related functionality into a group as a separate namespace (for routing) and folder structure (for views). 使用区域通过为 controlleraction 或 Razor Page page 添加其他路由参数 area,创建用于路由目的的层次结构。Using areas creates a hierarchy for the purpose of routing by adding another route parameter, area, to controller and action or a Razor Page page.

区域提供了一种将 ASP.NET Core Web 应用划分为更小的功能组的方法,每个功能组都有自己的一组 Razor Pages、控制器、视图和模型。Areas provide a way to partition an ASP.NET Core Web app into smaller functional groups, each with its own set of Razor Pages, controllers, views, and models. 区域实际上是应用内的结构。An area is effectively a structure inside an app. 在 ASP.NET Core Web 项目中,Pages、模型、控制器和视图等逻辑组件保存在不同的文件夹中。In an ASP.NET Core web project, logical components like Pages, Model, Controller, and View are kept in different folders. ASP.NET Core 运行时使用命名约定来创建这些组件之间的关系。The ASP.NET Core runtime uses naming conventions to create the relationship between these components. 对于大型应用,将应用分区为独立的高级功能区域可能更有利。For a large app, it may be advantageous to partition the app into separate high level areas of functionality. 例如,具有多个业务单位(如结账、计费、搜索等)的电子商务应用。For instance, an e-commerce app with multiple business units, such as checkout, billing, and search. 每个单位都有自己的区域,以包含视图、控制器、Razor Pages 和模型。Each of these units have their own area to contain views, controllers, Razor Pages, and models.

如果发生以下情况,请考虑在项目中使用区域:Consider using Areas in a project when:

  • 应用由可以进行逻辑分隔的多个高级功能组件组成。The app is made of multiple high-level functional components that can be logically separated.
  • 想对应用进行分区,以便可以独立处理每个功能区域。You want to partition the app so that each functional area can be worked on independently.

查看或下载示例代码如何下载)。View or download sample code (how to download). 下载示例提供了用于测试区域的基本应用。The download sample provides a basic app for testing areas.

如果使用 Razor Pages,请参阅本文档中的使用 Razor Pages 的区域If you're using Razor Pages, see Areas with Razor Pages in this document.

带视图的控制器区域Areas for controllers with views

使用区域、控制器和视图的典型 ASP.NET Core Web 应用包含以下内容:A typical ASP.NET Core web app using areas, controllers, and views contains the following:

  • 区域文件夹结构An Area folder structure.

  • 使用 [Area] 属性(该属性将控制器与区域关联)进行修饰的控制器:Controllers decorated with the [Area] attribute to associate the controller with the area:

    [Area("Products")]
    public class ManageController : Controller
    {
    
  • 添加到启动的区域路由The area route added to startup:

    app.UseMvc(routes =>
    {
        routes.MapRoute(
          name: "MyArea",
          template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
    
        routes.MapRoute(
           name: "default",
           template: "{controller=Home}/{action=Index}/{id?}");
    });
    

区域文件夹结构Area folder structure

请考虑具有两个逻辑组(产品和服务)的应用。Consider an app that has two logical groups, Products and Services. 使用区域,文件夹结构类似于以下内容:Using areas, the folder structure would be similar to the following:

  • 项目名称Project name
    • 区域Areas
      • 产品Products
        • ControllersControllers
          • HomeController.csHomeController.cs
          • ManageController.csManageController.cs
        • 视图Views
          • 主页Home
            • Index.cshtmlIndex.cshtml
          • 管理Manage
            • Index.cshtmlIndex.cshtml
            • About.cshtmlAbout.cshtml
      • 服务Services
        • ControllersControllers
          • HomeController.csHomeController.cs
        • 视图Views
          • 主页Home
            • Index.cshtmlIndex.cshtml

虽然前面的布局是使用区域时的典型布局,但只需要视图文件即可使用此文件夹结构。While the preceding layout is typical when using Areas, only the view files are required to use this folder structure. 视图发现按以下顺序搜索匹配的区域视图文件:View discovery searches for a matching area view file in the following order:

/Areas/<Area-Name>/Views/<Controller-Name>/<Action-Name>.cshtml
/Areas/<Area-Name>/Views/Shared/<Action-Name>.cshtml
/Views/Shared/<Action-Name>.cshtml
/Pages/Shared/<Action-Name>.cshtml

控制器和模型等非视图文件夹的位置无关紧要。The location of non-view folders like Controllers and Models does not matter. 例如,控制器和模型文件夹不是必需的。For example, the Controllers and Models folder are not required. 控制器和模型的内容是编译成 .dll 文件的代码。The content of Controllers and Models is code which gets compiled into a .dll. 在对该视图发出请求之前,不会编译视图的内容。The content of the Views isn't compiled until a request to that view has been made.

将控制器与区域关联Associate the controller with an Area

使用[区域]属性指定区域控制器:Area controllers are designated with the [Area] attribute:

using Microsoft.AspNetCore.Mvc;

namespace MVCareas.Areas.Products.Controllers
{
    [Area("Products")]
    public class ManageController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult About()
        {
            return View();
        }
    }
}

添加区域路由Add Area route

区域路由通常使用传统路由,而不使用属性路由。Area routes typically use conventional routing rather than attribute routing. 传统路由依赖于顺序。Conventional routing is order-dependent. 一般情况下,具有区域的路由应放在路由表中靠前的位置,因为它们比没有区域的路由更特定。In general, routes with areas should be placed earlier in the route table as they're more specific than routes without an area.

如果所有区域的 url 空间一致,则 {area:...} 可用作路由模板中的令牌:{area:...} can be used as a token in route templates if url space is uniform across all areas:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
          name: "MyArea",
          template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

        routes.MapRoute(
           name: "default",
           template: "{controller=Home}/{action=Index}/{id?}");
    });
}

在前面的代码中,exists 应用了路由必须与区域匹配的约束。In the preceding code, exists applies a constraint that the route must match an area. 使用 {area:...} 是将路由添加到区域的最简单的机制。Using {area:...} is the least complicated mechanism to adding routing to areas.

以下代码使用 MapAreaRoute 创建两个命名区域路由:The following code uses MapAreaRoute to create two named area routes:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapAreaRoute(
            name: "MyAreaProducts",
            areaName:"Products",
            template: "Products/{controller=Home}/{action=Index}/{id?}");

        routes.MapAreaRoute(
            name: "MyAreaServices",
            areaName: "Services",
            template: "Services/{controller=Home}/{action=Index}/{id?}");

        routes.MapRoute(
           name: "default",
           template: "{controller=Home}/{action=Index}/{id?}");
    });
}

MapAreaRoute 与 ASP.NET Core 2.2 配合使用时,请参阅此 GitHub 问题When using MapAreaRoute with ASP.NET Core 2.2, see this GitHub issue.

有关详细信息,请参阅区域路由For more information, see Area routing.

示例下载中的以下代码显示指定区域的链接生成:The following code from the sample download shows link generation with the area specified:

<li>Anchor Tag Helper links</li>
<ul>
    <li>
        <a asp-area="Products" asp-controller="Home" asp-action="About">
            Products/Home/About
        </a>
    </li>
    <li>
        <a asp-area="Services" asp-controller="Home" asp-action="About">
            Services About
        </a>
    </li>
    <li>
        <a asp-area="" asp-controller="Home" asp-action="About">
            /Home/About
        </a>
    </li>
</ul>
<li>Html.ActionLink generated links</li>
<ul>
    <li>
        @Html.ActionLink("Product/Manage/About", "About", "Manage", 
                                                new { area = "Products" })
    </li>
</ul>
<li>Url.Action generated links</li>
<ul>
    <li>
        <a href='@Url.Action("About", "Manage", new { area = "Products" })'>
            Products/Manage/About
        </a>
    </li>
</ul>

使用上述代码生成的链接在应用的任何位置都有效。The links generated with the preceding code are valid anywhere in the app.

示例下载包含部分视图,该视图包含以前的链接和相同的链接(未指定区域)。The sample download includes a partial view that contains the preceding links and the same links without specifying the area. 布局文件中引用部分视图,因此应用中的每个页面都显示生成的链接。The partial view is referenced in the layout file, so every page in the app displays the generated links. 在未指定区域的情况下生成的链接仅在从同一区域和控制器中的页面引用时才有效。The links generated without specifying the area are only valid when referenced from a page in the same area and controller.

如果未指定区域或控制器,路由取决于环境值。When the area or controller is not specified, routing depends on the ambient values. 当前请求的当前路由值被视为链接生成的环境值。The current route values of the current request are considered ambient values for link generation. 在许多情况下,对于示例应用,使用环境值会生成错误的链接。In many cases for the sample app, using the ambient values generates incorrect links.

有关详细信息,请参阅路由到控制器操作For more information, see Routing to controller actions.

使用 _ViewStart.cshtml 文件的共享区域布局Shared layout for Areas using the _ViewStart.cshtml file

要共享整个应用的常用布局,请将 _ViewStart.cshtml 移动到应用程序根文件夹。To share a common layout for the entire app, move the _ViewStart.cshtml to the application root folder.

更改存储视图的默认区域文件夹Change default area folder where views are stored

以下代码将默认的区域文件夹从 "Areas" 改为"MyAreas"The following code changes the default area folder from "Areas" to "MyAreas":

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.AreaViewLocationFormats.Clear();
        options.AreaViewLocationFormats.Add("/MyAreas/{2}/Views/{1}/{0}.cshtml");
        options.AreaViewLocationFormats.Add("/MyAreas/{2}/Views/Shared/{0}.cshtml");
        options.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml");
    });

    services.AddMvc();
}

使用 Razor Pages 的区域Areas with Razor Pages

使用 Razor Pages 的区域需要在应用根目录中有一个“Areas/<area name>/Pages”文件夹。Areas with Razor Pages require and Areas/<area name>/Pages folder in the root of the app. 示例下载中使用以下文件夹结构The following folder structure is used with the sample download

  • 项目名称Project name
    • 区域Areas
      • 产品Products
        • PagesPages
          • _ViewImports_ViewImports
          • 关于About
          • 索引Index
      • 服务Services
        • PagesPages
          • 管理Manage
            • 关于About
            • 索引Index

示例下载中的以下代码显示指定区域(例如 asp-area="Products")的链接生成:The following code from the sample download shows link generation with the area specified (for example, asp-area="Products"):

<li>Anchor Tag Helper links</li>
<ul>
    <li>
        <a asp-area="Products" asp-page="/About">
            Products/About
        </a>
    </li>
    <li>
        <a asp-area="Services" asp-page="/Manage/About">
            Services/Manage/About
        </a>
    </li>
    <li>
        <a asp-area="" asp-page="/About">
            /About
        </a>
    </li>
</ul>
<li>Url.Page generated links</li>
<ul>
    <li>
        <a href='@Url.Page("/Manage/About", new { area = "Services" })'>
            Services/Manage/About
        </a>
    </li>
    <li>
        <a href='@Url.Page("/About", new { area = "Products" })'>
            Products/About
        </a>
    </li>
</ul>

使用上述代码生成的链接在应用的任何位置都有效。The links generated with the preceding code are valid anywhere in the app.

示例下载包含部分视图,该视图包含以前的链接和相同的链接(未指定区域)。The sample download includes a partial view that contains the preceding links and the same links without specifying the area. 布局文件中引用部分视图,因此应用中的每个页面都显示生成的链接。The partial view is referenced in the layout file, so every page in the app displays the generated links. 在未指定区域的情况下生成的链接仅在从同一区域中的页引用时才有效。The links generated without specifying the area are only valid when referenced from a page in the same area.

如果未指定区域,路由取决于环境值。When the area is not specified, routing depends on the ambient values. 当前请求的当前路由值被视为链接生成的环境值。The current route values of the current request are considered ambient values for link generation. 在许多情况下,对于示例应用,使用环境值会生成错误的链接。In many cases for the sample app, using the ambient values generates incorrect links. 例如,考虑从下面的代码生成的链接:For example, consider the links generated from the following code:

<li>
    <a asp-page="/Manage/About">
        Services/Manage/About
    </a>
</li>
<li>
    <a asp-page="/About">
        /About
    </a>
</li>

对于上述代码:For the preceding code:

  • 只有当最后一个请求是针对 Services 区域的页时,从 <a asp-page="/Manage/About"> 生成的链接才是正确的。The link generated from <a asp-page="/Manage/About"> is correct only when the last request was for a page in Services area. 例如 /Services/Manage//Services/Manage/Index/Services/Manage/AboutFor example, /Services/Manage/, /Services/Manage/Index, or /Services/Manage/About.
  • 只有当最后一个请求是针对 /Home 中的页时,从 <a asp-page="/About"> 生成的链接才是正确的。The link generated from <a asp-page="/About"> is correct only when the last request was for a page in /Home.
  • 代码摘自示例下载The code is from the sample download.

使用 _ViewImports 文件导入命名空间和标记帮助程序Import namespace and Tag Helpers with _ViewImports file

可向每个区域“页面”文件夹添加一个 _ViewImports.cshtml 文件,以将命名空间和标记帮助器导入到该文件夹的每个 Razor 页面中。A _ViewImports.cshtml file can be added to each area Pages folder to import the namespace and Tag Helpers to each Razor Page in the folder.

请考虑使用示例代码的“服务”区域,它不包含 _ViewImports.cshtml 文件。Consider the Services area of the sample code, which doesn't contain a _ViewImports.cshtml file. 以下标记显示 /Services/Manage/About Razor Page:The following markup shows the /Services/Manage/About Razor Page:

@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model RPareas.Areas.Services.Pages.Manage.AboutModel
@{
    ViewData["Title"] = "Srv Mng About";
}

<h2>/Services/Manage/About</h2>

<a asp-area="Products" asp-page="/Index">
    Products/Index
</a>

在前面的标记中:In the preceding markup:

  • 必须使用完全限定的域名来指定模型 (@model RPareas.Areas.Services.Pages.Manage.AboutModel)。The fully qualified domain name must be used to specify the model (@model RPareas.Areas.Services.Pages.Manage.AboutModel).
  • 标记帮助程序@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 启动Tag Helpers are enabled by @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

在示例下载中,“产品”区域包含下列 _ViewImports.cshtml 文件:In the sample download, the Products area contains the following _ViewImports.cshtml file:

@namespace RPareas.Areas.Products.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

以下标记显示 /Products/About Razor Page:The following markup shows the /Products/About Razor Page:

@page
@model AboutModel
@{
    ViewData["Title"] = "Prod About";
}

<h2>Products/About</h2>

<a asp-area="Services" asp-page="/Manage/About">
    Services/Manage/About
</a>

在前面的文件中,命名空间和 @addTagHelper 指令通过 Areas/Products/Pages/_ViewImports.cshtml 文件导入到文件中。In the preceding file, the namespace and @addTagHelper directive is imported to the file by the Areas/Products/Pages/_ViewImports.cshtml file.

有关详细信息,请参阅管理标记帮助程序范围导入共享指令For more information, see Managing Tag Helper scope and Importing Shared Directives.

Razor Pages 区域的共享布局Shared layout for Razor Pages Areas

要共享整个应用的常用布局,请将 _ViewStart.cshtml 移动到应用程序根文件夹。To share a common layout for the entire app, move the _ViewStart.cshtml to the application root folder.

发布区域Publishing Areas

当 *.csproj 文件中包含 <Project Sdk="Microsoft.NET.Sdk.Web"> 时,所有 *.cshtml 文件以及 wwwroot 目录中的文件都将发布到输出中。All *.cshtml files and files within the wwwroot directory are published to output when <Project Sdk="Microsoft.NET.Sdk.Web"> is included in the *.csproj file.