ASP.NET Core の区分

作成者: Dhananjay Kumar および Rick Anderson

区分は、関連する機能を個別のグループとしてまとめるために使用される ASP.NET の機能です。

  • ルーティング用の名前空間。
  • ビューおよび Razor Pages のフォルダー構造。

ルーティングを行うために、区分を使用して、別のルート パラメーター areacontrolleraction または Razor Page の page に追加して階層を作成します。

区分は、ASP.NET Core Web アプリをより小さな機能グループにパーティション分割する方法であり、分割した各グループにそれぞれの Razor Pages、コントローラー、ビュー、モデルがあります。 区分は、実質的にはアプリ内の構造体となります。 ASP.NET Core Web プロジェクトでは、ページ、モデル、コントローラー、ビューなどの論理コンポーネントが別々のフォルダーに保存されます。 ASP.NET Core ランタイムでは、名前付け規則を使用し、これらのコンポーネント間のリレーションシップを作成します。 大きなアプリでは、アプリを機能の個別の高レベル区分に分割すると便利な場合があります。 チェックアウト、請求、検索などの複数のビジネス ユニットがある eコマース アプリの場合です。 これらのユニットにはそれぞれ、ビュー、コントローラー、Razor Pages、モデルを格納する区分があります。

次のような場合は、プロジェクトで区分を使用することを検討してください。

  • 論理的に大まかに区切れる複数の機能コンポーネントでアプリが構成されている。
  • 各機能区分を個別に使用できるようにアプリを分割したい。

Razor Pages を使用している場合は、このドキュメントの「Razor Pages がある区分」をご覧ください。

ビューを伴うコントローラーの区分

区分、コントローラー、ビューを使用する一般的な ASP.NET Core Web アプリに含まれる内容:

  • 区分フォルダーの構造

  • コントローラーと区分を関連付けるための [Area] 属性を持つコントローラー:

    [Area("Products")]
    public class ManageController : Controller
    {
    
  • スタートアップに追加された区分ルートProgram.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddControllersWithViews();
    
    var app = builder.Build();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapControllerRoute(
        name: "MyArea",
        pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
    
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.Run();
    

区分フォルダーの構造

あるアプリに ProductsServices という 2 つの論理グループが与えられているとします。 区分を利用すると、フォルダーの構造は次のようになります。

  • Project name
    • Areas
      • Products
        • Controllers
          • HomeController.cs
          • ManageController.cs
        • ビュー
          • Home
            • Index.cshtml
          • Manage
            • Index.cshtml
            • About.cshtml
      • サービス
        • Controllers
          • HomeController.cs
        • ビュー
          • Home
            • Index.cshtml

区分を使用するとき、前述のレイアウトが一般的ですが、このフォルダー構造を使用するにはビュー ファイルのみが求められます。 ビューの検出では、一致する区分ビュー ファイルを次の順序で検索します。

/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

コントローラーを区分に関連付ける

区分コントローラーは [Area] 属性で指名されます:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Docs.Samples;

namespace MVCareas.Areas.Products.Controllers;

[Area("Products")]
public class ManageController : Controller
{
    public IActionResult Index()
    {
        ViewData["routeInfo"] = ControllerContext.MyDisplayRouteInfo();
        return View();
    }

    public IActionResult About()
    {
        ViewData["routeInfo"] = ControllerContext.MyDisplayRouteInfo();
        return View();
    }
}

区分ルートを追加する

区分ルートでは通常、属性ルーティングではなく、従来のルーティングが使用されます。 規則ルーティングは順序に依存します。 一般に、区分のあるルートは区分を持たないルートより具体的なので、区分のあるルートはルート テーブルの前の方に配置する必要があります。

URL スペースがすべての区分で統一されている場合、ルート テンプレートでトークンとして {area:...} を使用できます。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

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

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "MyArea",
    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

上記のコードでは、ルートは 1 つの区分に一致しなければならないという制約が exists によって適用されます。 {area:...}MapControllerRoute を使用することは、次のことを意味します。

  • これは、ルーティングを区分に追加するメカニズムとして最も単純である。
  • [Area("Area name")] 属性を持つすべてのコントローラーに一致する。

次のコードでは、MapAreaControllerRoute を使用し、名前の付いた区分ルートが 2 つ作成されます。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

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

app.UseRouting();

app.UseAuthorization();

app.MapAreaControllerRoute(
    name: "MyAreaProducts",
    areaName: "Products",
    pattern: "Products/{controller=Home}/{action=Index}/{id?}");

app.MapAreaControllerRoute(
    name: "MyAreaServices",
    areaName: "Services",
    pattern: "Services/{controller=Home}/{action=Index}/{id?}");

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

詳細については、区分のルーティングに関するページを参照してください。

サンプル ダウンロードに含まれる次のコードでは、区分が指定された上でリンクが生成されます。

<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>

サンプル ダウンロードには 部分ビュー が含まれており、その内容は次のとおりです。

  • 前のリンク。
  • area が指定されていないこと以外は前のものに似ているリンク。

部分ビューはレイアウト ファイルで参照されます。そのため、生成されたリンクがアプリのすべてのページに表示されます。 区分が指定されずに生成されたリンクは、同じ区分やコントローラーのページから参照されるときにのみ有効です。

区分またはコントローラーが指定されていないとき、ルーティングはアンビエント値に依存します。 現在の要求の現在のルート値は、リンク生成の場合、アンビエント値として見なされます。 サンプル アプリでは、アンビエント値を使用すると、区分を指定しないマークアップで不正なリンクが生成される場合が多くあります。

詳細については、「コントローラー アクションへのルーティング」を参照してください。

_ViewStart.cshtml ファイルを使用した区分の共有レイアウト

アプリ全体で共通レイアウトを共有するには、アプリケーションのルート フォルダー_ViewStart.cshtml を格納します。 詳細については、「ASP.NET Core でのレイアウト」をご覧ください

アプリケーションのルート フォルダー

アプリケーションのルート フォルダーは、ASP.NET Core テンプレートを使用して作成された Web アプリの Program.cs ファイルを含むフォルダーです。

_ViewImports.cshtml

MVC の /Views/_ViewImports.cshtml と、Razor Pages の /Pages/_ViewImports.cshtml は区分のビューにインポートされません。 次のいずれかの方法を使用して、ビューをすべてのビューにインポートします。

  • アプリケーションのルート フォルダー_ViewImports.cshtml を追加します。 アプリケーションのルート フォルダー内の _ViewImports.cshtml は、アプリ内のすべてのビューに適用されます。
  • 区分の下にある適切なビュー フォルダーに _ViewImports.cshtml ファイルをコピーします。 たとえば、個々のユーザー アカウントで作成された Razor Pages アプリには、次のフォルダーに _ViewImports.cshtml ファイルがあります:
    • /Areas/Identity/Pages/_ViewImports.cshtml
    • /Pages/_ViewImports.cshtml

_ViewImports.cshtml ファイルには、通常、タグ ヘルパー のインポート、@using@inject ステートメントが含まれます。 詳細については、「共有ディレクティブのインポート」を参照してください。

ビューが保存されている既定の区分フォルダーを変更する

次のコードでは、既定の区分フォルダーが "Areas" から "MyAreas" に変更されます。

using Microsoft.AspNetCore.Mvc.Razor;

var builder = WebApplication.CreateBuilder(args);

builder.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");
});

builder.Services.AddControllersWithViews();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

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

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "MyArea",
    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Razor Pages がある区分

Razor Pages がある区分には、アプリのルートに Areas/<area name>/Pages フォルダーが必要です。 サンプル アプリでは次のフォルダー構造が使われます。

  • Project name
    • Areas
      • Products
        • Pages
          • _ViewImports
          • リソース センターについて
          • Index
      • サービス
        • Pages
          • Manage
            • リソース センターについて
            • Index

サンプル ダウンロードの次のコードでは、区分を指定したリンクの生成を示しています (例: 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>

サンプル ダウンロードには、部分ビューが含まれます。部分ビューには、上記のリンクと区分が指定されていない同じリンクが含まれます。 部分ビューはレイアウト ファイルで参照されます。そのため、生成されたリンクがアプリのすべてのページに表示されます。 区分が指定されずに生成されたリンクは、同じ区分のページから参照されるときにのみ有効です。

区分が指定されていないとき、ルーティングは "アンビエント" 値に依存します。 現在の要求の現在のルート値は、リンク生成の場合、アンビエント値として見なされます。 サンプル アプリでは多くの場合、アンビエント値を使用すると、間違ったリンクが生成されます。 たとえば、次のコードから生成されるリンクを考えてみます。

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

上のコードの場合:

  • <a asp-page="/Manage/About"> から生成されるリンクは、前回の要求が Services 区分内のページに向けられていた場合にのみ正しくなります。 たとえば、「/Services/Manage/」、「/Services/Manage/Index」、「/Services/Manage/About」のように指定します。
  • <a asp-page="/About"> から生成されるリンクは、前回の要求が /Home 内のページに向けられていた場合にのみ正しくなります。
  • コードは、サンプル ダウンロードからのものです。

_ViewImports ファイルを使って名前空間とタグ ヘルパーをインポートする

_ViewImports.cshtml ファイルを各区分の Pages フォルダーに追加して、フォルダー内の各 Razor ページに名前空間とタグ ヘルパーをインポートすることができます。

サンプル コードの Services 区分について検討します。これには _ViewImports.cshtml ファイルが含まれていません。 次のマークアップは /Services/Manage/AboutRazor Page を示します:

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

<div>
  ViewData["routeInfo"]:  @ViewData["routeInfo"]
</div>

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

上のマークアップについて:

  • 完全修飾クラス名を使ってモデルを指定する必要があります (@model RPareas.Areas.Services.Pages.Manage.AboutModel)。
  • タグ ヘルパー@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers によって有効になります

サンプル ダウンロードでは、Products 区分に次の _ViewImports.cshtml ファイルが含まれています。

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

次のマークアップは /Products/AboutRazor Page を示します:

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

上のファイルでは、Areas/Products/Pages/_ViewImports.cshtml ファイルによって、名前空間と @addTagHelper ディレクティブがファイルにインポートされています。

詳細については、「タグ ヘルパーのスコープの管理」と「共有ディレクティブのインポート」をご覧ください。

Razor Pages 区分の共有レイアウト

アプリ全体で共通レイアウトを共有するには、アプリケーションのルート フォルダーに _ViewStart.cshtml を移動します。

区分の発行

*.csproj ファイルに <Project Sdk="Microsoft.NET.Sdk.Web"> が含まれているときは、すべての *.cshtml ファイル、および wwwroot ディレクトリ内のファイルが出力に発行されます。

Visual Studio を使用して MVC 区分を追加する

ソリューション エクスプローラーでプロジェクトを右クリックし、[> 個の新しいスキャフォールディング項目を追加する] の順に選択し、[MVC 区分] を選択します。

その他のリソース

区分は、関連する機能を個別のグループとしてまとめるために使用される ASP.NET の機能です。

  • ルーティング用の名前空間。
  • ビューおよび Razor Pages のフォルダー構造。

ルーティングを行うために、区分を使用して、別のルート パラメーター areacontrolleraction または Razor Page の page に追加して階層を作成します。

区分は、ASP.NET Core Web アプリをより小さな機能グループにパーティション分割する方法であり、分割した各グループにそれぞれの Razor Pages、コントローラー、ビュー、モデルがあります。 区分は、実質的にはアプリ内の構造体となります。 ASP.NET Core Web プロジェクトでは、ページ、モデル、コントローラー、ビューなどの論理コンポーネントが別々のフォルダーに保存されます。 ASP.NET Core ランタイムでは、名前付け規則を使用し、これらのコンポーネント間のリレーションシップを作成します。 大きなアプリでは、アプリを機能の個別の高レベル区分に分割すると便利な場合があります。 チェックアウト、請求、検索などの複数のビジネス ユニットがある eコマース アプリの場合です。 これらのユニットにはそれぞれ、ビュー、コントローラー、Razor Pages、モデルを格納する区分があります。

次のような場合は、プロジェクトで区分を使用することを検討してください。

  • 論理的に大まかに区切れる複数の機能コンポーネントでアプリが構成されている。
  • 各機能区分を個別に使用できるようにアプリを分割したい。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。 ダウンロード サンプルからは、区分をテストするための基本的なアプリが与えられます。

Razor Pages を使用している場合は、このドキュメントの「Razor Pages がある区分」をご覧ください。

ビューを伴うコントローラーの区分

区分、コントローラー、ビューを使用する一般的な ASP.NET Core Web アプリに含まれる内容:

  • 区分フォルダーの構造

  • コントローラーと区分を関連付けるための [Area] 属性を持つコントローラー:

    [Area("Products")]
    public class ManageController : Controller
    {
    
  • スタートアップに追加された区分ルート:

    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?}");
    });
    

区分フォルダーの構造

あるアプリに ProductsServices という 2 つの論理グループが与えられているとします。 区分を利用すると、フォルダーの構造は次のようになります。

  • Project name
    • Areas
      • Products
        • Controllers
          • HomeController.cs
          • ManageController.cs
        • ビュー
          • Home
            • Index.cshtml
          • Manage
            • Index.cshtml
            • About.cshtml
      • サービス
        • Controllers
          • HomeController.cs
        • ビュー
          • Home
            • Index.cshtml

区分を使用するとき、前述のレイアウトが一般的ですが、このフォルダー構造を使用するにはビュー ファイルのみが求められます。 ビューの検出では、一致する区分ビュー ファイルを次の順序で検索します。

/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

コントローラーを区分に関連付ける

区分コントローラーは [区分] 属性で指名されます:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Docs.Samples;

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

        public IActionResult About()
        {
            ViewData["routeInfo"] = ControllerContext.MyDisplayRouteInfo();
            return View();
        }
    }
}

区分ルートを追加する

区分ルートでは通常、属性ルーティングではなく、従来のルーティングが使用されます。 規則ルーティングは順序に依存します。 一般に、区分のあるルートは区分を持たないルートより具体的なので、区分のあるルートはルート テーブルの前の方に配置する必要があります。

URL スペースがすべての区分で統一されている場合、ルート テンプレートでトークンとして {area:...} を使用できます。

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

    app.UseRouting();

    app.UseAuthorization();

    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?}");
    });
}

上記のコードでは、ルートは 1 つの区分に一致しなければならないという制約が exists によって適用されます。 {area:...}MapControllerRoute を使用することは、次のことを意味します。

  • これは、ルーティングを区分に追加するメカニズムとして最も単純である。
  • [Area("Area name")] 属性を持つすべてのコントローラーに一致する。

次のコードでは、MapAreaControllerRoute を使用し、名前の付いた区分ルートが 2 つ作成されます。

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapAreaControllerRoute(
            name: "MyAreaProducts",
            areaName: "Products",
            pattern: "Products/{controller=Home}/{action=Index}/{id?}");

        endpoints.MapAreaControllerRoute(
            name: "MyAreaServices",
            areaName: "Services",
            pattern: "Services/{controller=Home}/{action=Index}/{id?}");

        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

詳細については、区分のルーティングに関するページを参照してください。

サンプル ダウンロードに含まれる次のコードでは、区分が指定された上でリンクが生成されます。

<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>

サンプル ダウンロードには 部分ビュー が含まれており、その内容は次のとおりです。

  • 前のリンク。
  • area が指定されていないこと以外は前のものに似ているリンク。

部分ビューはレイアウト ファイルで参照されます。そのため、生成されたリンクがアプリのすべてのページに表示されます。 区分が指定されずに生成されたリンクは、同じ区分やコントローラーのページから参照されるときにのみ有効です。

区分またはコントローラーが指定されていないとき、ルーティングはアンビエント値に依存します。 現在の要求の現在のルート値は、リンク生成の場合、アンビエント値として見なされます。 サンプル アプリでは、アンビエント値を使用すると、区分を指定しないマークアップで不正なリンクが生成される場合が多くあります。

詳細については、「コントローラー アクションへのルーティング」を参照してください。

_ViewStart.cshtml ファイルを使用した区分の共有レイアウト

アプリ全体で共通レイアウトを共有するには、アプリケーションのルート フォルダー_ViewStart.cshtml を格納します。 詳細については、「ASP.NET Core でのレイアウト」をご覧ください

アプリケーションのルート フォルダー

アプリケーションのルート フォルダーは、ASP.NET Core テンプレートを使用して作成された Web アプリの Startup.cs を含むフォルダーです。

_ViewImports.cshtml

MVC の /Views/_ViewImports.cshtml、および Razor Pages の /Pages/_ViewImports.cshtml は、区分内のビューにはインポートされません。 次のいずれかの方法を使用して、ビューをすべてのビューにインポートします。

  • _ViewImports.cshtmlアプリケーションのルート フォルダーに追加します。 アプリケーションのルート フォルダーに追加された _ViewImports.cshtml は、アプリ内のすべてのビューに適用されます。
  • _ViewImports.cshtml ファイルを区分内の適切なビュー フォルダーに コピーします。

通常、_ViewImports.cshtml ファイルには、タグ ヘルパーのインポート、@using、および @inject ステートメントが含まれます。 詳細については、「共有ディレクティブのインポート」を参照してください。

ビューが保存されている既定の区分フォルダーを変更する

次のコードでは、既定の区分フォルダーが "Areas" から "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.AddControllersWithViews();
}

Razor Pages がある区分

Razor Pages がある区分には、アプリのルートに Areas/<area name>/Pages フォルダーが必要です。 サンプル アプリでは次のフォルダー構造が使われます。

  • Project name
    • Areas
      • Products
        • Pages
          • _ViewImports
          • リソース センターについて
          • Index
      • サービス
        • Pages
          • Manage
            • リソース センターについて
            • Index

サンプル ダウンロードの次のコードでは、区分を指定したリンクの生成を示しています (例: 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>

サンプル ダウンロードには、部分ビューが含まれます。部分ビューには、上記のリンクと区分が指定されていない同じリンクが含まれます。 部分ビューはレイアウト ファイルで参照されます。そのため、生成されたリンクがアプリのすべてのページに表示されます。 区分が指定されずに生成されたリンクは、同じ区分のページから参照されるときにのみ有効です。

区分が指定されていないとき、ルーティングは "アンビエント" 値に依存します。 現在の要求の現在のルート値は、リンク生成の場合、アンビエント値として見なされます。 サンプル アプリでは多くの場合、アンビエント値を使用すると、間違ったリンクが生成されます。 たとえば、次のコードから生成されるリンクを考えてみます。

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

上のコードの場合:

  • <a asp-page="/Manage/About"> から生成されるリンクは、前回の要求が Services 区分内のページに向けられていた場合にのみ正しくなります。 たとえば、「/Services/Manage/」、「/Services/Manage/Index」、「/Services/Manage/About」のように指定します。
  • <a asp-page="/About"> から生成されるリンクは、前回の要求が /Home 内のページに向けられていた場合にのみ正しくなります。
  • コードは、サンプル ダウンロードからのものです。

_ViewImports ファイルを使って名前空間とタグ ヘルパーをインポートする

_ViewImports.cshtml ファイルを各区分の Pages フォルダーに追加して、名前空間とタグ ヘルパーをフォルダー内の各 Razor Page にインポートできます。

サンプル コードの Services 区分について検討します。これには _ViewImports.cshtml ファイルは含まれません。 次のマークアップは /Services/Manage/AboutRazor Page を示します:

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

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

上のマークアップについて:

  • 完全修飾クラス名を使ってモデルを指定する必要があります (@model RPareas.Areas.Services.Pages.Manage.AboutModel)。
  • タグ ヘルパー@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers によって有効になります

サンプル ダウンロードでは、Products 区分に次の _ViewImports.cshtml ファイルが含まれています。

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

次のマークアップは /Products/AboutRazor Page を示します:

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

上のファイルでは、Areas/Products/Pages/_ViewImports.cshtml ファイルによって名前空間と @addTagHelper ディレクティブがファイルにインポートされています。

詳細については、「タグ ヘルパーのスコープの管理」と「共有ディレクティブのインポート」をご覧ください。

Razor Pages 区分の共有レイアウト

アプリ全体で共通レイアウトを共有するには、アプリケーションのルート フォルダーに _ViewStart.cshtml を移動します。

区分の発行

*.csproj ファイルに <Project Sdk="Microsoft.NET.Sdk.Web"> が含まれているときは、すべての *.cshtml ファイル、および wwwroot ディレクトリ内のファイルが出力に発行されます。

Visual Studio を使用して MVC 区分を追加する

ソリューション エクスプローラーでプロジェクトを右クリックし、[> 個の新しいスキャフォールディング項目を追加する] の順に選択し、[MVC 区分] を選択します。

区分は ASP.NET の機能であり、関連する機能を別の名前空間 (ルーティングの場合) およびフォルダー構造 (ビューの場合) としてグループにまとめるために使用されます。 ルーティングを行うために、区分を使用して、別のルート パラメーター areacontrolleraction または Razor Page の page に追加して階層を作成します。

区分は、ASP.NET Core Web アプリをより小さな機能グループにパーティション分割する方法であり、分割した各グループにそれぞれの Razor Pages、コントローラー、ビュー、モデルがあります。 区分は、実質的にはアプリ内の構造体となります。 ASP.NET Core Web プロジェクトでは、ページ、モデル、コントローラー、ビューなどの論理コンポーネントが別々のフォルダーに保存されます。 ASP.NET Core ランタイムでは、名前付け規則を使用し、これらのコンポーネント間のリレーションシップを作成します。 大きなアプリでは、アプリを機能の個別の高レベル区分に分割すると便利な場合があります。 チェックアウト、請求、検索などの複数のビジネス ユニットがある eコマース アプリの場合です。 これらのユニットにはそれぞれ、ビュー、コントローラー、Razor Pages、モデルを格納する区分があります。

次のような場合は、プロジェクトで区分を使用することを検討してください。

  • 論理的に大まかに区切れる複数の機能コンポーネントでアプリが構成されている。
  • 各機能区分を個別に使用できるようにアプリを分割したい。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。 ダウンロード サンプルからは、区分をテストするための基本的なアプリが与えられます。

Razor Pages を使用している場合は、このドキュメントの「Razor Pages がある区分」をご覧ください。

ビューを伴うコントローラーの区分

区分、コントローラー、ビューを使用する一般的な ASP.NET Core Web アプリに含まれる内容:

  • 区分フォルダーの構造

  • コントローラーと区分を関連付けるための [Area] 属性を持つコントローラー:

    [Area("Products")]
    public class ManageController : Controller
    {
    
  • スタートアップに追加された区分ルート:

    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?}");
    });
    

区分フォルダーの構造

あるアプリに ProductsServices という 2 つの論理グループが与えられているとします。 区分を利用すると、フォルダーの構造は次のようになります。

  • Project name
    • Areas
      • Products
        • Controllers
          • HomeController.cs
          • ManageController.cs
        • ビュー
          • Home
            • Index.cshtml
          • Manage
            • Index.cshtml
            • About.cshtml
      • サービス
        • Controllers
          • HomeController.cs
        • ビュー
          • Home
            • Index.cshtml

区分を使用するとき、前述のレイアウトが一般的ですが、このフォルダー構造を使用するにはビュー ファイルのみが求められます。 ビューの検出では、一致する区分ビュー ファイルを次の順序で検索します。

/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

コントローラーを区分に関連付ける

区分コントローラーは [区分] 属性で指名されます:

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();
        }
    }
}

区分ルートを追加する

区分ルートでは通常、属性ルーティングではなく、従来のルーティングが使用されます。 規則ルーティングは順序に依存します。 一般に、区分のあるルートは区分を持たないルートより具体的なので、区分のあるルートはルート テーブルの前の方に配置する必要があります。

URL スペースがすべての区分で統一されている場合、ルート テンプレートでトークンとして {area:...} を使用できます。

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?}");
    });
}

上記のコードでは、ルートは 1 つの区分に一致しなければならないという制約が exists によって適用されます。 {area:...} の使用は、ルーティングを区分に追加するメカニズムとして最も単純です。

次のコードでは、MapAreaRoute を使用し、名前の付いた区分ルートが 2 つ作成されます。

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?}");
    });
}

ASP.NET Core 2.2 で MapAreaRoute を使用するときは、この GitHub 問題を確認してください。

詳細については、区分のルーティングに関するページを参照してください。

サンプル ダウンロードに含まれる次のコードでは、区分が指定された上でリンクが生成されます。

<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>

上記のコードで生成されたリンクは、アプリ内のあらゆる場所で有効となります。

サンプル ダウンロードには、部分ビューが含まれます。部分ビューには、上記のリンクと区分が指定されていない同じリンクが含まれます。 部分ビューはレイアウト ファイルで参照されます。そのため、生成されたリンクがアプリのすべてのページに表示されます。 区分が指定されずに生成されたリンクは、同じ区分やコントローラーのページから参照されるときにのみ有効です。

区分またはコントローラーが指定されていないとき、ルーティングはアンビエント値に依存します。 現在の要求の現在のルート値は、リンク生成の場合、アンビエント値として見なされます。 サンプル アプリでは多くの場合、アンビエント値を使用すると、間違ったリンクが生成されます。

詳細については、「コントローラー アクションへのルーティング」を参照してください。

_ViewStart.cshtml ファイルを使用した区分の共有レイアウト

アプリ全体で共通レイアウトを共有するには、アプリケーションのルート フォルダーに _ViewStart.cshtml を移動します。

_ViewImports.cshtml

標準の場所では、/Views/_ViewImports.cshtml は区分に適用されません。 お使いの区分で共通のタグ ヘルパー@using、または @inject を使用するには、適切な _ViewImports.cshtml ファイル 区分ビューに確実に適用するようにします。 すべてのビューで同じ動作が必要な場合は、/Views/_ViewImports.cshtml をアプリケーション ルートに移動します。

ビューが保存されている既定の区分フォルダーを変更する

次のコードでは、既定の区分フォルダーが "Areas" から "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 がある区分

Razor Pages がある区分には、アプリのルートに Areas/<area name>/Pages フォルダーが必要です。 サンプル アプリでは次のフォルダー構造が使われます。

  • Project name
    • Areas
      • Products
        • Pages
          • _ViewImports
          • リソース センターについて
          • Index
      • サービス
        • Pages
          • Manage
            • リソース センターについて
            • Index

サンプル ダウンロードの次のコードでは、区分を指定したリンクの生成を示しています (例: 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>

上記のコードで生成されたリンクは、アプリ内のあらゆる場所で有効となります。

サンプル ダウンロードには、部分ビューが含まれます。部分ビューには、上記のリンクと区分が指定されていない同じリンクが含まれます。 部分ビューはレイアウト ファイルで参照されます。そのため、生成されたリンクがアプリのすべてのページに表示されます。 区分が指定されずに生成されたリンクは、同じ区分のページから参照されるときにのみ有効です。

区分が指定されていないとき、ルーティングは "アンビエント" 値に依存します。 現在の要求の現在のルート値は、リンク生成の場合、アンビエント値として見なされます。 サンプル アプリでは多くの場合、アンビエント値を使用すると、間違ったリンクが生成されます。 たとえば、次のコードから生成されるリンクを考えてみます。

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

上のコードの場合:

  • <a asp-page="/Manage/About"> から生成されるリンクは、前回の要求が Services 区分内のページに向けられていた場合にのみ正しくなります。 たとえば、「/Services/Manage/」、「/Services/Manage/Index」、「/Services/Manage/About」のように指定します。
  • <a asp-page="/About"> から生成されるリンクは、前回の要求が /Home 内のページに向けられていた場合にのみ正しくなります。
  • コードは、サンプル ダウンロードからのものです。

_ViewImports ファイルを使って名前空間とタグ ヘルパーをインポートする

_ViewImports.cshtml ファイルを各区分の Pages フォルダーに追加して、名前空間とタグ ヘルパーをフォルダー内の各 Razor Page にインポートできます。

サンプル コードの Services 区分について検討します。これには _ViewImports.cshtml ファイルは含まれません。 次のマークアップは /Services/Manage/AboutRazor 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>

上のマークアップについて:

  • 完全修飾ドメイン名を使ってモデルを指定する必要があります (@model RPareas.Areas.Services.Pages.Manage.AboutModel)。
  • タグ ヘルパー@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers によって有効になります

サンプル ダウンロードでは、Products 区分に次の _ViewImports.cshtml ファイルが含まれています。

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

次のマークアップは /Products/AboutRazor Page を示します:

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

<h2>Products/About</h2>

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

上のファイルでは、Areas/Products/Pages/_ViewImports.cshtml ファイルによって名前空間と @addTagHelper ディレクティブがファイルにインポートされています。

詳細については、「タグ ヘルパーのスコープの管理」と「共有ディレクティブのインポート」をご覧ください。

Razor Pages 区分の共有レイアウト

アプリ全体で共通レイアウトを共有するには、アプリケーションのルート フォルダーに _ViewStart.cshtml を移動します。

区分の発行

*.csproj ファイルに <Project Sdk="Microsoft.NET.Sdk.Web"> が含まれているときは、すべての *.cshtml ファイル、および wwwroot ディレクトリ内のファイルが出力に発行されます。