ASP.NET Core での Razor ページのルートとアプリの規則

ページ ルートとアプリ モデル プロバイダーの規則を使用して、Razor ページ アプリでページのルーティング、検出、および処理を制御する方法について説明します。

ページ ルートの指定、ルート セグメントの追加、ルートへのパラメーターの追加を行うには、ページの @page ディレクティブを使用します。 詳しくは、「カスタム ルート」をご覧ください。

ルート セグメントやパラメーター名として使用できない予約語がいくつかあります。 詳しくは、ルーティング: ルーティングの予約名に関するページをご覧ください。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

シナリオ サンプルによるデモの内容
モデルの規則

Conventions.Add
ルート テンプレートとヘッダーをアプリのページに追加します。
ページ ルート アクション規則 ルート テンプレートをフォルダー内のページおよび単一ページに追加します。
ページ モデル アクション規則 ヘッダーをフォルダー内のページに追加し、ヘッダーを単一ページに追加し、ヘッダーをアプリのページに追加するようにフィルター ファクトリを構成します。

Razor Pages の規則は、RazorPagesOptions を構成する AddRazorPages のオーバーロードを使用して構成されます。 次の規則の例は、このトピックで後述されます。


var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages(options =>
    {
        options.Conventions.Add( ... );
        options.Conventions.AddFolderRouteModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageRouteModelConvention(
            "/About", model => { ... });
        options.Conventions.AddPageRoute(
            "/Contact", "TheContactPage/{text?}");
        options.Conventions.AddFolderApplicationModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageApplicationModelConvention(
            "/About", model => { ... });
        options.Conventions.ConfigureFilter(model => { ... });
        options.Conventions.ConfigureFilter( ... );
    });
}

ルートの順番

ルートは、処理 (ルートの照合) に使われる順番 (Order) を指定します。

ルートの順番 動作
-1 ルートは、他のルートが処理される前に処理されます。
0 順番が指定されていません (既定値)。 Order を割り当てない (Order = null) 場合、処理に使われるルートの Order は既定で 0 (ゼロ) になります。
1、2、… n ルートの処理順序を指定します。

ルートの処理は、次の規則で定められています。

  • ルートは並んだ順番に処理されます (-1, 0, 1, 2, … n)。
  • ルートの Order が同じ場合は、最初に最も明確なルートが照合され、続けて次に明確なルートが照合されます。
  • Order とパラメーター数が同じルートが 1 つの要求 URL と一致した場合、ルートは PageConventionCollection に追加された順番で処理されます。

可能であれば、定められたルートの処理順序に依存しないようにしてください。 通常は、ルーティングによって、URL が一致する正しいルートが選択されます。 要求が正しくルーティングされるようにルートの Order プロパティを設定する必要がある場合、アプリのルーティング方式がクライアントにとって紛らわしいものとなり、保守が脆弱になる可能性があります。 アプリのルーティング方式を簡略化するよう努めてください。 サンプル アプリでは、1 つのアプリで複数のルーティング シナリオを示すために、ルートの明示的な処理順序が必要です。 ただし、運用環境のアプリでルートの Order を設定するやり方は避けるようにしてください。

Razor Pages ルーティングと MVC コントローラー ルーティングは、実装を共有します。 MVC のトピックに記載されているルートの順番については、コントローラー アクションへのルーティング: 属性ルートの順序の指定に関するページをご覧ください。

モデルの規則

IPageConvention の委任を追加すると、Razor Pages に適用されるモデルの規則を追加できます。

すべてのページにルート モデル規則を追加する

Conventions を使用すると、IPageRouteModelConvention を作成して、ページ ルート モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

サンプル アプリには、{globalTemplate?} ルート テンプレートをアプリ内のすべてのページに追加するために GlobalTemplatePageRouteModelConvention クラスが含まれます。

using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace SampleApp.Conventions;

public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel!.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

上のコードでは以下の操作が行われます。

Razor Pages のオプション (Conventions の追加など) は、Razor Pages がサービス コレクションに追加されると追加されます。 例については、サンプル アプリを参照してください。

using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.EntityFrameworkCore;
using SampleApp.Conventions;
using SampleApp.Data;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
                                   options.UseInMemoryDatabase("InMemoryDb"));

builder.Services.AddRazorPages(options =>
   {
       options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

       options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
       {
           var selectorCount = model.Selectors.Count;
           for (var i = 0; i < selectorCount; i++)
           {
               var selector = model.Selectors[i];
               model.Selectors.Add(new SelectorModel
               {
                   AttributeRouteModel = new AttributeRouteModel
                   {
                       Order = 2,
                       Template = AttributeRouteModel.CombineTemplates(
                           selector.AttributeRouteModel!.Template,
                           "{otherPagesTemplate?}"),
                   }
               });
           }
       });

       options.Conventions.AddPageRouteModelConvention("/About", model =>
       {
           var selectorCount = model.Selectors.Count;
           for (var i = 0; i < selectorCount; i++)
           {
               var selector = model.Selectors[i];
               model.Selectors.Add(new SelectorModel
               {
                   AttributeRouteModel = new AttributeRouteModel
                   {
                       Order = 2,
                       Template = AttributeRouteModel.CombineTemplates(
                           selector.AttributeRouteModel!.Template,
                           "{aboutTemplate?}"),
                   }
               });
           }
       });

   });

var app = builder.Build();

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

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.MapRazorPages();
app.Run();

GlobalTemplatePageRouteModelConvention クラスについて考えてみましょう。

using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace SampleApp.Conventions;

public class GlobalTemplatePageRouteModelConvention : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel!.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

AttributeRouteModelOrder プロパティに 1 が設定されます。 これにより、サンプル アプリで次のルートの照合動作が保証されます。

  • TheContactPage/{text?} のルート テンプレートは、このトピックの後半で追加されます。 Contact Page ルートには、null の既定の順序 (Order = 0) が設定されているため、Order = 1 を含む {globalTemplate?} ルート テンプレートの前に一致します。

  • {aboutTemplate?} ルート テンプレートは、前のコードに示されています。 {aboutTemplate?} テンプレートの Order には 2 が指定されます。 [About] ページが /About/RouteDataValue で要求されると、"RouteDataValue" は RouteData.Values["globalTemplate"] (Order = 1) に読み込まれ、Order プロパティが設定されるため RouteData.Values["aboutTemplate"] (Order = 2) には読み込まれません。

  • {otherPagesTemplate?} ルート テンプレートは、前のコードに示されています。 {otherPagesTemplate?} テンプレートの Order には 2 が指定されます。 ルート パラメーターを使用して Pages/OtherPages フォルダー内の任意のページが要求されると、次のようになります。

  • たとえば、/OtherPages/Page1/xyz のように指定します。

  • ルート データ値 "xyz"RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

  • RouteData.Values["otherPagesTemplate"] (Order = 2) は、Order プロパティ 2 の値が大きいため、読み込まれません。

可能な場合は、Order を設定しないでください。 Order が設定されていなければ、既定で Order = 0 となります。 ルーティングに依存して、Order プロパティではなく、正しいルートを選択します。

localhost:{port}/About/GlobalRouteValue でサンプルの About ページを要求し、結果を調べます。

The About page is requested with a route segment of GlobalRouteValue. The rendered page shows that the route data value is captured in the OnGet method of the page.

サンプル アプリでは、ログ出力にルーティング情報を表示するために、Rick.Docs.Samples.RouteInfo NuGet パッケージが使用されます。 ロガーで localhost:{port}/About/GlobalRouteValue を使用すると、使用した要求、Order、テンプレートが表示されます。

info: SampleApp.Pages.AboutModel[0]
       /About/GlobalRouteValue   Order = 1 Template = About/{globalTemplate?}

すべてのページにアプリ モデル規則を追加する

Conventions を使用すると、IPageApplicationModelConvention を作成して、ページ アプリ モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

この規則やこのトピックで後述されるその他の規則のデモを実行するには、サンプル アプリに AddHeaderAttribute クラスを含めます。 クラス コンストラクターは、name 文字列と values 文字列配列を受け入れます。 これらの値は、応答ヘッダーを設定するために、その OnResultExecuting メソッド内で使用されます。 完全クラスは、このトピックで後述される「ページ モデル アクション規則」セクションで示されます。

サンプル アプリでは、AddHeaderAttribute クラスを使用して、ヘッダー (GlobalHeader) をアプリ内のすべてのページに追加します。

public class GlobalHeaderPageApplicationModelConvention 
    : IPageApplicationModelConvention
{
    public void Apply(PageApplicationModel model)
    {
        model.Filters.Add(new AddHeaderAttribute(
            "GlobalHeader", new string[] { "Global Header Value" }));
    }
}

Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseInMemoryDatabase("InMemoryDb"));

builder.Services.AddRazorPages(options =>
   {
       options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

       options.Conventions.Add(new GlobalHeaderPageApplicationModelConvention());

localhost:{port}/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that the GlobalHeader has been added.

すべてのページにハンドラー モデル規則を追加する

Conventions を使用すると、IPageHandlerModelConvention を作成して、ページ ハンドラー モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

public class GlobalPageHandlerModelConvention
    : IPageHandlerModelConvention
{
    public void Apply(PageHandlerModel model)
    {
        // Access the PageHandlerModel
    }
}

ページ ルート アクション規則

IPageRouteModelProvider から派生する既定のルート モデル プロバイダーは、ページ ルートを構成するための拡張ポイントを提供するようにデザインされた規則を呼び出します。

フォルダー ルート モデル規則

AddFolderRouteModelConvention を使用すると、指定したフォルダーの下にあるすべてのページを対象に PageRouteModel へのアクションを呼び出す IPageRouteModelConvention を作成して追加できます。

サンプル アプリでは AddFolderRouteModelConvention を使用して、{otherPagesTemplate?} ルート テンプレートを OtherPages フォルダーのページに追加します。

options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel!.Template,
                    "{otherPagesTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder プロパティに 2 が設定されます。 この設定により、1 つのルート値が指定されたときに、(このトピックの前半で 1 に設定した) {globalTemplate?} のテンプレートが優先的に最初のルート データ値の位置に指定されます。 ルート パラメーター値 (/OtherPages/Page1/RouteDataValue など) を使用して Pages/OtherPages フォルダー内のページが要求されると、Order プロパティが設定されているため、"RouteDataValue" が RouteData.Values["otherPagesTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue でサンプルの Page1 ページを要求し、その結果を調べます。

Page1 in the OtherPages folder is requested with a route segment of GlobalRouteValue and OtherPagesRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

ページ ルート モデル規則

AddPageRouteModelConvention を使用すると、指定した名前のページを対象に PageRouteModel へのアクションを呼び出す IPageRouteModelConvention を作成して追加できます。

サンプル アプリでは AddPageRouteModelConvention を使用して、{aboutTemplate?} ルート テンプレートを [About] ページに追加します。

options.Conventions.AddPageRouteModelConvention("/About", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel!.Template,
                    "{aboutTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder プロパティに 2 が設定されます。 この設定により、1 つのルート値が指定されたときに、(このトピックの前半で 1 に設定した) {globalTemplate?} のテンプレートが優先的に最初のルート データ値の位置に指定されます。 [About] ページが /About/RouteDataValue にあるルート パラメーター値で要求されると、Order プロパティが設定されているため、"RouteDataValue" は RouteData.Values["aboutTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

localhost:{port}/About/GlobalRouteValue/AboutRouteValue でサンプルの [About] ページを要求し、その結果を調べます。

About page is requested with route segments for GlobalRouteValue and AboutRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

ロガーの出力は、次のように表示されます。

info: SampleApp.Pages.AboutModel[0]
       /About/GlobalRouteValue/AboutRouteValue   Order = 2 Template = About/{globalTemplate?}/{aboutTemplate?}

パラメーター トランスフォーマーを使用してページ ルートをカスタマイズする

パラメーター トランスフォーマー」を参照してください。

ページ ルートの構成

AddPageRoute を使用すると、指定したページ パスにあるページへのルートを構成できます。 そのページに対して生成されたリンクでは、指定したルートを使用します。 AddPageRoute では、AddPageRouteModelConvention を使用してルートを確立します。

サンプル アプリを使用すると、ContactRazor Page の /TheContactPage へのルートが作成されます。

options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");

Contact ページには、既定のルート経由の /Contact1` でアクセスすることもできます。

サンプル アプリの Contact ページへのカスタム ルートを使用すると、省略可能な text ルート セグメント ({text?}) が許可されます。 また、訪問者が /Contact ルートでページにアクセスする場合、ページの @page ディレクティブにはこの省略可能なセグメントも含まれます。

@page "{text?}"
@model ContactModel
@{
    ViewData["Title"] = "Contact";
}

<h1>@ViewData["Title"]</h1>
<h2>@Model.Message</h2>

<address>
    One Microsoft Way<br>
    Redmond, WA 98052-6399<br>
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

<address>
    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

<p>@Model.RouteDataTextTemplateValue</p>

レンダリングされたページの Contact リンク用に生成された URL には、更新されたルートが反映されることに注意してください。

Sample app Contact link in the navigation bar

Inspecting the Contact link in the rendered HTML indicates that the href is set to '/TheContactPage'

通常のルート (/Contact) またはカスタム ルート (/TheContactPage) のいずれかで、Contact ページにアクセスします。 追加の text ルート セグメントを指定した場合、ページには指定した HTML エンコードのセグメントが示されます。

Edge browser example of supplying an optional 'text' route segment of 'TextValue' in the URL. The rendered page shows the 'text' segment value.

ページ モデル アクション規則

IPageApplicationModelProvider を実装する既定のページ モデル プロバイダーは、ページ モデルを構成するための拡張ポイントを提供するようにデザインされた規則を呼び出します。 これらの規則は、ページ検出をビルドおよび変更したり、シナリオを処理したりするときに便利です。

このセクションの例の場合、サンプル アプリでは、応答ヘッダーを適用する ResultFilterAttribute である、AddHeaderAttribute クラスを使用します。

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string[] _values;

    public AddHeaderAttribute(string name, string[] values)
    {
        _name = name;
        _values = values;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _values);
        base.OnResultExecuting(context);
    }
}

規則を使用して、このサンプルでは、フォルダー内のすべてのページおよび単一ページに属性を適用する方法のデモを実行します。

フォルダー アプリ モデル規則

AddFolderApplicationModelConvention を使用すると、指定したフォルダーの下にあるすべてのページを対象に PageApplicationModel インスタンスへのアクションを呼び出す IPageApplicationModelConvention を作成して追加できます。

このサンプルでは、ヘッダー (OtherPagesHeader) をアプリの OtherPages フォルダー内にあるページに追加して、AddFolderApplicationModelConvention を使用するデモを実行します。

options.Conventions.AddFolderApplicationModelConvention("/OtherPages", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "OtherPagesHeader", new string[] { "OtherPages Header Value" }));
});

localhost:5000/OtherPages/Page1 でサンプルの Page1 ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the OtherPages/Page1 page show that the OtherPagesHeader has been added.

ページ アプリ モデル規則

AddPageApplicationModelConvention を使用すると、指定した名前のページを対象に PageApplicationModel へのアクションを呼び出す IPageApplicationModelConvention を作成して追加できます。

サンプルでは、ヘッダー (AboutHeader) を [About] ページに追加して、AddPageApplicationModelConvention を使用するデモを実行します。

options.Conventions.AddPageApplicationModelConvention("/About", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "AboutHeader", new string[] { "About Header Value" }));
});

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that the AboutHeader has been added.

フィルターの構成

ConfigureFilter では、指定したフィルターを適用するように構成します。 フィルター クラスを実装できますが、サンプル アプリでは、ラムダ式でフィルターを実装する方法を示しています。これは、フィルターを返すファクトリとしてバックグラウンドで実装されます。

options.Conventions.ConfigureFilter(model =>
{
    if (model.RelativePath.Contains("OtherPages/Page2"))
    {
        return new AddHeaderAttribute(
            "OtherPagesPage2Header",
            new string[] { "OtherPages/Page2 Header Value" });
    }
    return new EmptyFilter();
});

ページ アプリ モデルは、OtherPages フォルダーの Page2 ページにつながるセグメントの相対パスを確認するために使用されます。 条件を満たすと、ヘッダーが追加されます。 満たさない場合は、EmptyFilter が適用されます。

EmptyFilterアクション フィルターです。 アクション フィルターは Razor Pages によって無視されるため、パスに OtherPages/Page2 が含まれない場合、EmptyFilter には意図されたような効果はありません。

localhost:5000/OtherPages/Page2 でサンプルの Page2 ページを要求し、そのヘッダーを調べて結果を確認します。

The OtherPagesPage2Header is added to the response for Page2.

フィルター ファクトリの構成

ConfigureFilter では、すべての Razor Pages にフィルターを適用するように、指定したファクトリを構成します。

サンプル アプリでは、アプリのページに対する 2 つの値と共にヘッダー (FilterFactoryHeader) を追加して、フィルター ファクトリを使用する例を提供します。

options.Conventions.ConfigureFilter(new AddHeaderWithFactory());

AddHeaderWithFactory.cs:

public class AddHeaderWithFactory : IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new AddHeaderFilter();
    }

    private class AddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "FilterFactoryHeader", 
                new string[] 
                { 
                    "Filter Factory Header Value 1",
                    "Filter Factory Header Value 2"
                });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that two FilterFactoryHeader headers have been added.

MVC フィルターとページ フィルター (IPageFilter)

Razor Pages はハンドラー メソッドを使用するため、MVC アクション フィルターは Razor Pages によって無視されます。 使用できるその他の種類の MVC フィルターは次のとおりです。承認例外リソース、および結果。 詳細については、「フィルター」トピックを参照してください。

ページ フィルター (IPageFilter) は、Razor Pages に適用されるフィルターの 1 つです。 詳細については、Razor Pages のフィルター メソッドに関するページを参照してください。

その他の技術情報

ページ ルートとアプリ モデル プロバイダーの規則を使用して、Razor ページ アプリでページのルーティング、検出、および処理を制御する方法について説明します。

個別のページにカスタムのページ ルートを構成する必要がある場合、このトピックで後述される「AddPageRoute convention」で、ページへのルーティングを構成します。

ページ ルートの指定、ルート セグメントの追加、ルートへのパラメーターの追加を行うには、ページの @page ディレクティブを使用します。 詳しくは、「カスタム ルート」をご覧ください。

ルート セグメントやパラメーター名として使用できない予約語がいくつかあります。 詳しくは、ルーティング: ルーティングの予約名に関するページをご覧ください。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

シナリオ このサンプルでは、次のデモを実行します。
モデルの規則

Conventions.Add
  • IPageRouteModelConvention
  • IPageApplicationModelConvention
  • IPageHandlerModelConvention
ルート テンプレートとヘッダーをアプリのページに追加します。
ページ ルート アクション規則
  • AddFolderRouteModelConvention
  • AddPageRouteModelConvention
  • AddPageRoute
ルート テンプレートをフォルダー内のページおよび単一ページに追加します。
ページ モデル アクション規則
  • AddFolderApplicationModelConvention
  • AddPageApplicationModelConvention
  • ConfigureFilter (フィルター クラス、ラムダ式、またはフィルター ファクトリ)
ヘッダーをフォルダー内のページに追加し、ヘッダーを単一ページに追加し、ヘッダーをアプリのページに追加するようにフィルター ファクトリを構成します。

Razor Pages の規則は、Startup.ConfigureServicesRazorPagesOptions を構成する AddRazorPages のオーバーロードを使用して構成されます。 次の規則の例は、このトピックで後述されます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages(options =>
    {
        options.Conventions.Add( ... );
        options.Conventions.AddFolderRouteModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageRouteModelConvention(
            "/About", model => { ... });
        options.Conventions.AddPageRoute(
            "/Contact", "TheContactPage/{text?}");
        options.Conventions.AddFolderApplicationModelConvention(
            "/OtherPages", model => { ... });
        options.Conventions.AddPageApplicationModelConvention(
            "/About", model => { ... });
        options.Conventions.ConfigureFilter(model => { ... });
        options.Conventions.ConfigureFilter( ... );
    });
}

ルートの順番

ルートは、処理 (ルートの照合) に使われる順番 (Order) を指定します。

順番 動作
-1 ルートは、他のルートが処理される前に処理されます。
0 順番が指定されていません (既定値)。 Order を割り当てない (Order = null) 場合、処理に使われるルートの Order は既定で 0 (ゼロ) になります。
1、2、… n ルートの処理順序を指定します。

ルートの処理は、次の規則で定められています。

  • ルートは並んだ順番に処理されます (-1, 0, 1, 2, … n)。
  • ルートの Order が同じ場合は、最初に最も明確なルートが照合され、続けて次に明確なルートが照合されます。
  • Order とパラメーター数が同じルートが 1 つの要求 URL と一致した場合、ルートは PageConventionCollection に追加された順番で処理されます。

可能であれば、定められたルートの処理順序に依存しないようにしてください。 通常は、ルーティングによって、URL が一致する正しいルートが選択されます。 要求が正しくルーティングされるようにルートの Order プロパティを設定する必要がある場合、アプリのルーティング方式がクライアントにとって紛らわしいものとなり、保守が脆弱になる可能性があります。 アプリのルーティング方式を簡略化するよう努めてください。 サンプル アプリでは、1 つのアプリで複数のルーティング シナリオを示すために、ルートの明示的な処理順序が必要です。 ただし、運用環境のアプリでルートの Order を設定するやり方は避けるようにしてください。

Razor Pages ルーティングと MVC コントローラー ルーティングは、実装を共有します。 MVC のトピックに記載されているルートの順番については、コントローラー アクションへのルーティング: 属性ルートの順序の指定に関するページをご覧ください。

モデルの規則

IPageConvention の委任を追加すると、Razor Pages に適用されるモデルの規則を追加できます。

すべてのページにルート モデル規則を追加する

Conventions を使用すると、IPageRouteModelConvention を作成して、ページ ルート モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

サンプル アプリでは、{globalTemplate?} ルート テンプレートをアプリ内のすべてのページに追加します。

public class GlobalTemplatePageRouteModelConvention 
    : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

AttributeRouteModelOrder プロパティに 1 が設定されます。 これにより、サンプル アプリで次のルートの照合動作が保証されます。

  • TheContactPage/{text?} のルート テンプレートは、このトピックの後半で追加されます。 連絡先ページのルートには既定の順番 null (Order = 0) が設定されているため、{globalTemplate?} ルート テンプレートの前に一致します。
  • {aboutTemplate?} ルート テンプレートは、このトピックの後半で追加されます。 {aboutTemplate?} テンプレートの Order には 2 が指定されます。 [About] ページが /About/RouteDataValue で要求されると、"RouteDataValue" は RouteData.Values["globalTemplate"] (Order = 1) に読み込まれ、Order プロパティが設定されるため RouteData.Values["aboutTemplate"] (Order = 2) には読み込まれません。
  • {otherPagesTemplate?} ルート テンプレートは、このトピックの後半で追加されます。 {otherPagesTemplate?} テンプレートの Order には 2 が指定されます。 ルート パラメーター (/OtherPages/Page1/RouteDataValue など) を使用して Pages/OtherPages フォルダー内のいずれかのページが要求されると、Order プロパティが設定されているため、"RouteDataValue" が RouteData.Values["otherPagesTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

Razor Pages のオプション (Conventions の追加など) は、Razor Pages が Startup.ConfigureServices のサービス コレクションに追加されたときに追加されます。 例については、サンプル アプリを参照してください。

options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

localhost:5000/About/GlobalRouteValue でサンプルの [About] ページを要求し、その結果を調べます。

The About page is requested with a route segment of GlobalRouteValue. The rendered page shows that the route data value is captured in the OnGet method of the page.

すべてのページにアプリ モデル規則を追加する

Conventions を使用すると、IPageApplicationModelConvention を作成して、ページ アプリ モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

この規則やこのトピックで後述されるその他の規則のデモを実行するには、サンプル アプリに AddHeaderAttribute クラスを含めます。 クラス コンストラクターは、name 文字列と values 文字列配列を受け入れます。 これらの値は、応答ヘッダーを設定するために、その OnResultExecuting メソッド内で使用されます。 完全クラスは、このトピックで後述される「ページ モデル アクション規則」セクションで示されます。

サンプル アプリでは、AddHeaderAttribute クラスを使用して、ヘッダー (GlobalHeader) をアプリ内のすべてのページに追加します。

public class GlobalHeaderPageApplicationModelConvention 
    : IPageApplicationModelConvention
{
    public void Apply(PageApplicationModel model)
    {
        model.Filters.Add(new AddHeaderAttribute(
            "GlobalHeader", new string[] { "Global Header Value" }));
    }
}

Startup.cs:

options.Conventions.Add(new GlobalHeaderPageApplicationModelConvention());

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that the GlobalHeader has been added.

すべてのページにハンドラー モデル規則を追加する

Conventions を使用すると、IPageHandlerModelConvention を作成して、ページ ハンドラー モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

public class GlobalPageHandlerModelConvention
    : IPageHandlerModelConvention
{
    public void Apply(PageHandlerModel model)
    {
        // Access the PageHandlerModel
    }
}

Startup.cs:

options.Conventions.Add(new GlobalPageHandlerModelConvention());

ページ ルート アクション規則

IPageRouteModelProvider から派生する既定のルート モデル プロバイダーは、ページ ルートを構成するための拡張ポイントを提供するようにデザインされた規則を呼び出します。

フォルダー ルート モデル規則

AddFolderRouteModelConvention を使用すると、指定したフォルダーの下にあるすべてのページを対象に PageRouteModel へのアクションを呼び出す IPageRouteModelConvention を作成して追加できます。

サンプル アプリでは AddFolderRouteModelConvention を使用して、{otherPagesTemplate?} ルート テンプレートを OtherPages フォルダーのページに追加します。

options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{otherPagesTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder プロパティに 2 が設定されます。 この設定により、1 つのルート値が指定されたときに、(このトピックの前半で 1 に設定した) {globalTemplate?} のテンプレートが優先的に最初のルート データ値の位置に指定されます。 ルート パラメーター値 (/OtherPages/Page1/RouteDataValue など) を使用して Pages/OtherPages フォルダー内のページが要求されると、Order プロパティが設定されているため、"RouteDataValue" が RouteData.Values["otherPagesTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue でサンプルの Page1 ページを要求し、その結果を調べます。

Page1 in the OtherPages folder is requested with a route segment of GlobalRouteValue and OtherPagesRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

ページ ルート モデル規則

AddPageRouteModelConvention を使用すると、指定した名前のページを対象に PageRouteModel へのアクションを呼び出す IPageRouteModelConvention を作成して追加できます。

サンプル アプリでは AddPageRouteModelConvention を使用して、{aboutTemplate?} ルート テンプレートを [About] ページに追加します。

options.Conventions.AddPageRouteModelConvention("/About", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{aboutTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder プロパティに 2 が設定されます。 この設定により、1 つのルート値が指定されたときに、(このトピックの前半で 1 に設定した) {globalTemplate?} のテンプレートが優先的に最初のルート データ値の位置に指定されます。 [About] ページが /About/RouteDataValue にあるルート パラメーター値で要求されると、Order プロパティが設定されているため、"RouteDataValue" は RouteData.Values["aboutTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

localhost:5000/About/GlobalRouteValue/AboutRouteValue でサンプルの [About] ページを要求し、その結果を調べます。

About page is requested with route segments for GlobalRouteValue and AboutRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

パラメーター トランスフォーマーを使用してページ ルートをカスタマイズする

ASP.NET Core によって生成されたページ ルートは、パラメーター トランスフォーマーを使用してカスタマイズできます。 パラメーター トランスフォーマーは IOutboundParameterTransformer を実装し、パラメーターの値を変換します。 たとえば、SlugifyParameterTransformer パラメーター トランスフォーマーでは、SubscriptionManagement のルート値が subscription-management に変更されます。

PageRouteTransformerConvention ページ ルート モデル規則では、アプリで自動的に生成されたページ ルートのフォルダー名とファイル名のセグメントにパラメーター トランスフォーマーを適用します。 たとえば、/Pages/SubscriptionManagement/ViewAll.cshtml の Razor ページ ファイルには、/SubscriptionManagement/ViewAll から /subscription-management/view-all に書き換えられたルートが含まれます。

PageRouteTransformerConvention では、Razor ページのフォルダー名とファイル名に由来する、自動的に生成されたページ ルートのセグメントのみを変換します。 @page ディレクティブを使用して追加したルート セグメントは変換されません。 この規則では、AddPageRoute で追加したルートも変換されません。

PageRouteTransformerConvention は、Startup.ConfigureServices でオプションとして登録されます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages(options =>
    {
        options.Conventions.Add(
            new PageRouteTransformerConvention(
                new SlugifyParameterTransformer()));
    });
}
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        return Regex.Replace(value.ToString(),
                             "([a-z])([A-Z])",
                             "$1-$2",
                             RegexOptions.CultureInvariant,
                             TimeSpan.FromMilliseconds(100)).ToLowerInvariant();
    }
}

警告

System.Text.RegularExpressions を使用して信頼できない入力を処理するときは、タイムアウトを渡します。 悪意のあるユーザーが RegularExpressions に入力を提供して、サービス拒否攻撃を行う可能性があります。 RegularExpressions を使用する ASP.NET Core フレームワーク API は、タイムアウトを渡します。

ページ ルートの構成

AddPageRoute を使用すると、指定したページ パスにあるページへのルートを構成できます。 そのページに対して生成されたリンクでは、指定したルートを使用します。 AddPageRoute では、AddPageRouteModelConvention を使用してルートを確立します。

サンプル アプリを使用すると、Contact.cshtml/TheContactPage へのルートが作成されます。

options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");

[Contact] ページには、既定のルート経由の /Contact でアクセスすることもできます。

サンプル アプリの [Contact] ページに対するカスタム ルートでは、省略可能な text ルート セグメント ({text?}) を許可します。 また、訪問者が /Contact ルートでページにアクセスする場合、ページの @page ディレクティブにはこの省略可能なセグメントも含まれます。

@page "{text?}"
@model ContactModel
@{
    ViewData["Title"] = "Contact";
}

<h1>@ViewData["Title"]</h1>
<h2>@Model.Message</h2>

<address>
    One Microsoft Way<br>
    Redmond, WA 98052-6399<br>
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

<address>
    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

<p>@Model.RouteDataTextTemplateValue</p>

レンダリングされたページの Contact リンク用に生成された URL には、更新されたルートが反映されることに注意してください。

Sample app Contact link in the navigation bar

Inspecting the Contact link in the rendered HTML indicates that the href is set to '/TheContactPage'

通常のルート (/Contact) またはカスタム ルート (/TheContactPage) のいずれかで、[Contact] ページにアクセスします。 追加の text ルート セグメントを指定した場合、ページには指定した HTML エンコードのセグメントが示されます。

Edge browser example of supplying an optional 'text' route segment of 'TextValue' in the URL. The rendered page shows the 'text' segment value.

ページ モデル アクション規則

IPageApplicationModelProvider を実装する既定のページ モデル プロバイダーは、ページ モデルを構成するための拡張ポイントを提供するようにデザインされた規則を呼び出します。 これらの規則は、ページ検出をビルドおよび変更したり、シナリオを処理したりするときに便利です。

このセクションの例の場合、サンプル アプリでは、応答ヘッダーを適用する ResultFilterAttribute である、AddHeaderAttribute クラスを使用します。

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string[] _values;

    public AddHeaderAttribute(string name, string[] values)
    {
        _name = name;
        _values = values;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _values);
        base.OnResultExecuting(context);
    }
}

規則を使用して、このサンプルでは、フォルダー内のすべてのページおよび単一ページに属性を適用する方法のデモを実行します。

フォルダー アプリ モデル規則

AddFolderApplicationModelConvention を使用すると、指定したフォルダーの下にあるすべてのページを対象に PageApplicationModel インスタンスへのアクションを呼び出す IPageApplicationModelConvention を作成して追加できます。

このサンプルでは、ヘッダー (OtherPagesHeader) をアプリの OtherPages フォルダー内にあるページに追加して、AddFolderApplicationModelConvention を使用するデモを実行します。

options.Conventions.AddFolderApplicationModelConvention("/OtherPages", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "OtherPagesHeader", new string[] { "OtherPages Header Value" }));
});

localhost:5000/OtherPages/Page1 でサンプルの Page1 ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the OtherPages/Page1 page show that the OtherPagesHeader has been added.

ページ アプリ モデル規則

AddPageApplicationModelConvention を使用すると、指定した名前のページを対象に PageApplicationModel へのアクションを呼び出す IPageApplicationModelConvention を作成して追加できます。

サンプルでは、ヘッダー (AboutHeader) を [About] ページに追加して、AddPageApplicationModelConvention を使用するデモを実行します。

options.Conventions.AddPageApplicationModelConvention("/About", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "AboutHeader", new string[] { "About Header Value" }));
});

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that the AboutHeader has been added.

フィルターの構成

ConfigureFilter では、指定したフィルターを適用するように構成します。 フィルター クラスを実装できますが、サンプル アプリでは、ラムダ式でフィルターを実装する方法を示しています。これは、フィルターを返すファクトリとしてバックグラウンドで実装されます。

options.Conventions.ConfigureFilter(model =>
{
    if (model.RelativePath.Contains("OtherPages/Page2"))
    {
        return new AddHeaderAttribute(
            "OtherPagesPage2Header", 
            new string[] { "OtherPages/Page2 Header Value" });
    }
    return new EmptyFilter();
});

ページ アプリ モデルは、OtherPages フォルダーの Page2 ページにつながるセグメントの相対パスを確認するために使用されます。 条件を満たすと、ヘッダーが追加されます。 満たさない場合は、EmptyFilter が適用されます。

EmptyFilterアクション フィルターです。 アクション フィルターは Razor Pages によって無視されるため、パスに OtherPages/Page2 が含まれない場合、EmptyFilter には意図されたような効果はありません。

localhost:5000/OtherPages/Page2 でサンプルの Page2 ページを要求し、そのヘッダーを調べて結果を確認します。

The OtherPagesPage2Header is added to the response for Page2.

フィルター ファクトリの構成

ConfigureFilter では、すべての Razor Pages にフィルターを適用するように、指定したファクトリを構成します。

サンプル アプリでは、アプリのページに対する 2 つの値と共にヘッダー (FilterFactoryHeader) を追加して、フィルター ファクトリを使用する例を提供します。

options.Conventions.ConfigureFilter(new AddHeaderWithFactory());

AddHeaderWithFactory.cs:

public class AddHeaderWithFactory : IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new AddHeaderFilter();
    }

    private class AddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "FilterFactoryHeader", 
                new string[] 
                { 
                    "Filter Factory Header Value 1",
                    "Filter Factory Header Value 2"
                });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that two FilterFactoryHeader headers have been added.

MVC フィルターとページ フィルター (IPageFilter)

Razor Pages はハンドラー メソッドを使用するため、MVC アクション フィルターは Razor Pages によって無視されます。 使用できるその他の種類の MVC フィルターは次のとおりです。承認例外リソース、および結果。 詳細については、「フィルター」トピックを参照してください。

ページ フィルター (IPageFilter) は、Razor Pages に適用されるフィルターの 1 つです。 詳細については、Razor Pages のフィルター メソッドに関するページを参照してください。

その他の技術情報

ページ ルートとアプリ モデル プロバイダーの規則を使用して、Razor ページ アプリでページのルーティング、検出、および処理を制御する方法について説明します。

個別のページにカスタムのページ ルートを構成する必要がある場合、このトピックで後述される「AddPageRoute convention」で、ページへのルーティングを構成します。

ページ ルートの指定、ルート セグメントの追加、ルートへのパラメーターの追加を行うには、ページの @page ディレクティブを使用します。 詳しくは、「カスタム ルート」をご覧ください。

ルート セグメントやパラメーター名として使用できない予約語がいくつかあります。 詳しくは、ルーティング: ルーティングの予約名に関するページをご覧ください。

サンプル コードを表示またはダウンロードします (ダウンロード方法)。

シナリオ このサンプルでは、次のデモを実行します。
モデルの規則

Conventions.Add
  • IPageRouteModelConvention
  • IPageApplicationModelConvention
  • IPageHandlerModelConvention
ルート テンプレートとヘッダーをアプリのページに追加します。
ページ ルート アクション規則
  • AddFolderRouteModelConvention
  • AddPageRouteModelConvention
  • AddPageRoute
ルート テンプレートをフォルダー内のページおよび単一ページに追加します。
ページ モデル アクション規則
  • AddFolderApplicationModelConvention
  • AddPageApplicationModelConvention
  • ConfigureFilter (フィルター クラス、ラムダ式、またはフィルター ファクトリ)
ヘッダーをフォルダー内のページに追加し、ヘッダーを単一ページに追加し、ヘッダーをアプリのページに追加するようにフィルター ファクトリを構成します。

Razor ページの規則を追加および構成するには、Startup クラス内のサービス コレクションの AddMvcAddRazorPagesOptions 拡張メソッドを使用します。 次の規則の例は、このトピックで後述されます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            options.Conventions.Add( ... );
            options.Conventions.AddFolderRouteModelConvention(
                "/OtherPages", model => { ... });
            options.Conventions.AddPageRouteModelConvention(
                "/About", model => { ... });
            options.Conventions.AddPageRoute(
                "/Contact", "TheContactPage/{text?}");
            options.Conventions.AddFolderApplicationModelConvention(
                "/OtherPages", model => { ... });
            options.Conventions.AddPageApplicationModelConvention(
                "/About", model => { ... });
            options.Conventions.ConfigureFilter(model => { ... });
            options.Conventions.ConfigureFilter( ... );
        });
}

ルートの順番

ルートは、処理 (ルートの照合) に使われる順番 (Order) を指定します。

順番 動作
-1 ルートは、他のルートが処理される前に処理されます。
0 順番が指定されていません (既定値)。 Order を割り当てない (Order = null) 場合、処理に使われるルートの Order は既定で 0 (ゼロ) になります。
1、2、… n ルートの処理順序を指定します。

ルートの処理は、次の規則で定められています。

  • ルートは並んだ順番に処理されます (-1, 0, 1, 2, … n)。
  • ルートの Order が同じ場合は、最初に最も明確なルートが照合され、続けて次に明確なルートが照合されます。
  • Order とパラメーター数が同じルートが 1 つの要求 URL と一致した場合、ルートは PageConventionCollection に追加された順番で処理されます。

可能であれば、定められたルートの処理順序に依存しないようにしてください。 通常は、ルーティングによって、URL が一致する正しいルートが選択されます。 要求が正しくルーティングされるようにルートの Order プロパティを設定する必要がある場合、アプリのルーティング方式がクライアントにとって紛らわしいものとなり、保守が脆弱になる可能性があります。 アプリのルーティング方式を簡略化するよう努めてください。 サンプル アプリでは、1 つのアプリで複数のルーティング シナリオを示すために、ルートの明示的な処理順序が必要です。 ただし、運用環境のアプリでルートの Order を設定するやり方は避けるようにしてください。

Razor Pages ルーティングと MVC コントローラー ルーティングは、実装を共有します。 MVC のトピックに記載されているルートの順番については、コントローラー アクションへのルーティング: 属性ルートの順序の指定に関するページをご覧ください。

モデルの規則

IPageConvention の委任を追加すると、Razor Pages に適用されるモデルの規則を追加できます。

すべてのページにルート モデル規則を追加する

Conventions を使用すると、IPageRouteModelConvention を作成して、ページ ルート モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

サンプル アプリでは、{globalTemplate?} ルート テンプレートをアプリ内のすべてのページに追加します。

public class GlobalTemplatePageRouteModelConvention 
    : IPageRouteModelConvention
{
    public void Apply(PageRouteModel model)
    {
        var selectorCount = model.Selectors.Count;
        for (var i = 0; i < selectorCount; i++)
        {
            var selector = model.Selectors[i];
            model.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel
                {
                    Order = 1,
                    Template = AttributeRouteModel.CombineTemplates(
                        selector.AttributeRouteModel.Template, 
                        "{globalTemplate?}"),
                }
            });
        }
    }
}

AttributeRouteModelOrder プロパティに 1 が設定されます。 これにより、サンプル アプリで次のルートの照合動作が保証されます。

  • TheContactPage/{text?} のルート テンプレートは、このトピックの後半で追加されます。 連絡先ページのルートには既定の順番 null (Order = 0) が設定されているため、{globalTemplate?} ルート テンプレートの前に一致します。
  • {aboutTemplate?} ルート テンプレートは、このトピックの後半で追加されます。 {aboutTemplate?} テンプレートの Order には 2 が指定されます。 [About] ページが /About/RouteDataValue で要求されると、"RouteDataValue" は RouteData.Values["globalTemplate"] (Order = 1) に読み込まれ、Order プロパティが設定されるため RouteData.Values["aboutTemplate"] (Order = 2) には読み込まれません。
  • {otherPagesTemplate?} ルート テンプレートは、このトピックの後半で追加されます。 {otherPagesTemplate?} テンプレートの Order には 2 が指定されます。 ルート パラメーター (/OtherPages/Page1/RouteDataValue など) を使用して Pages/OtherPages フォルダー内のいずれかのページが要求されると、Order プロパティが設定されているため、"RouteDataValue" が RouteData.Values["otherPagesTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

Razor Pages のオプション (Conventions の追加など) は、MVC が Startup.ConfigureServices のサービス コレクションに追加されたときに追加されます。 例については、サンプル アプリを参照してください。

options.Conventions.Add(new GlobalTemplatePageRouteModelConvention());

localhost:5000/About/GlobalRouteValue でサンプルの [About] ページを要求し、その結果を調べます。

The About page is requested with a route segment of GlobalRouteValue. The rendered page shows that the route data value is captured in the OnGet method of the page.

すべてのページにアプリ モデル規則を追加する

Conventions を使用すると、IPageApplicationModelConvention を作成して、ページ アプリ モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

この規則やこのトピックで後述されるその他の規則のデモを実行するには、サンプル アプリに AddHeaderAttribute クラスを含めます。 クラス コンストラクターは、name 文字列と values 文字列配列を受け入れます。 これらの値は、応答ヘッダーを設定するために、その OnResultExecuting メソッド内で使用されます。 完全クラスは、このトピックで後述される「ページ モデル アクション規則」セクションで示されます。

サンプル アプリでは、AddHeaderAttribute クラスを使用して、ヘッダー (GlobalHeader) をアプリ内のすべてのページに追加します。

public class GlobalHeaderPageApplicationModelConvention 
    : IPageApplicationModelConvention
{
    public void Apply(PageApplicationModel model)
    {
        model.Filters.Add(new AddHeaderAttribute(
            "GlobalHeader", new string[] { "Global Header Value" }));
    }
}

Startup.cs:

options.Conventions.Add(new GlobalHeaderPageApplicationModelConvention());

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that the GlobalHeader has been added.

すべてのページにハンドラー モデル規則を追加する

Conventions を使用すると、IPageHandlerModelConvention を作成して、ページ ハンドラー モデルの構築中に適用される IPageConvention インスタンスのコレクションに追加できます。

public class GlobalPageHandlerModelConvention
    : IPageHandlerModelConvention
{
    public void Apply(PageHandlerModel model)
    {
        // Access the PageHandlerModel
    }
}

Startup.cs:

options.Conventions.Add(new GlobalPageHandlerModelConvention());

ページ ルート アクション規則

IPageRouteModelProvider から派生する既定のルート モデル プロバイダーは、ページ ルートを構成するための拡張ポイントを提供するようにデザインされた規則を呼び出します。

フォルダー ルート モデル規則

AddFolderRouteModelConvention を使用すると、指定したフォルダーの下にあるすべてのページを対象に PageRouteModel へのアクションを呼び出す IPageRouteModelConvention を作成して追加できます。

サンプル アプリでは AddFolderRouteModelConvention を使用して、{otherPagesTemplate?} ルート テンプレートを OtherPages フォルダーのページに追加します。

options.Conventions.AddFolderRouteModelConvention("/OtherPages", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{otherPagesTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder プロパティに 2 が設定されます。 この設定により、1 つのルート値が指定されたときに、(このトピックの前半で 1 に設定した) {globalTemplate?} のテンプレートが優先的に最初のルート データ値の位置に指定されます。 ルート パラメーター値 (/OtherPages/Page1/RouteDataValue など) を使用して Pages/OtherPages フォルダー内のページが要求されると、Order プロパティが設定されているため、"RouteDataValue" が RouteData.Values["otherPagesTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

localhost:5000/OtherPages/Page1/GlobalRouteValue/OtherPagesRouteValue でサンプルの Page1 ページを要求し、その結果を調べます。

Page1 in the OtherPages folder is requested with a route segment of GlobalRouteValue and OtherPagesRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

ページ ルート モデル規則

AddPageRouteModelConvention を使用すると、指定した名前のページを対象に PageRouteModel へのアクションを呼び出す IPageRouteModelConvention を作成して追加できます。

サンプル アプリでは AddPageRouteModelConvention を使用して、{aboutTemplate?} ルート テンプレートを [About] ページに追加します。

options.Conventions.AddPageRouteModelConvention("/About", model =>
{
    var selectorCount = model.Selectors.Count;
    for (var i = 0; i < selectorCount; i++)
    {
        var selector = model.Selectors[i];
        model.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel
            {
                Order = 2,
                Template = AttributeRouteModel.CombineTemplates(
                    selector.AttributeRouteModel.Template, 
                    "{aboutTemplate?}"),
            }
        });
    }
});

AttributeRouteModelOrder プロパティに 2 が設定されます。 この設定により、1 つのルート値が指定されたときに、(このトピックの前半で 1 に設定した) {globalTemplate?} のテンプレートが優先的に最初のルート データ値の位置に指定されます。 [About] ページが /About/RouteDataValue にあるルート パラメーター値で要求されると、Order プロパティが設定されているため、"RouteDataValue" は RouteData.Values["aboutTemplate"] (Order = 2) ではなく RouteData.Values["globalTemplate"] (Order = 1) に読み込まれます。

可能な限り、Order を設定しないでください。これにより、Order = 0 が生じます。 正しいルートの選択には、ルーティングを使用してください。

localhost:5000/About/GlobalRouteValue/AboutRouteValue でサンプルの [About] ページを要求し、その結果を調べます。

About page is requested with route segments for GlobalRouteValue and AboutRouteValue. The rendered page shows that the route data values are captured in the OnGet method of the page.

ページ ルートの構成

AddPageRoute を使用すると、指定したページ パスにあるページへのルートを構成できます。 そのページに対して生成されたリンクでは、指定したルートを使用します。 AddPageRoute では、AddPageRouteModelConvention を使用してルートを確立します。

サンプル アプリを使用すると、Contact.cshtml/TheContactPage へのルートが作成されます。

options.Conventions.AddPageRoute("/Contact", "TheContactPage/{text?}");

[Contact] ページには、既定のルート経由の /Contact でアクセスすることもできます。

サンプル アプリの [Contact] ページに対するカスタム ルートでは、省略可能な text ルート セグメント ({text?}) を許可します。 また、訪問者が /Contact ルートでページにアクセスする場合、ページの @page ディレクティブにはこの省略可能なセグメントも含まれます。

@page "{text?}"
@model ContactModel
@{
    ViewData["Title"] = "Contact";
}

<h1>@ViewData["Title"]</h1>
<h2>@Model.Message</h2>

<address>
    One Microsoft Way<br>
    Redmond, WA 98052-6399<br>
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

<address>
    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

<p>@Model.RouteDataTextTemplateValue</p>

レンダリングされたページの Contact リンク用に生成された URL には、更新されたルートが反映されることに注意してください。

Sample app Contact link in the navigation bar

Inspecting the Contact link in the rendered HTML indicates that the href is set to '/TheContactPage'

通常のルート (/Contact) またはカスタム ルート (/TheContactPage) のいずれかで、[Contact] ページにアクセスします。 追加の text ルート セグメントを指定した場合、ページには指定した HTML エンコードのセグメントが示されます。

Edge browser example of supplying an optional 'text' route segment of 'TextValue' in the URL. The rendered page shows the 'text' segment value.

ページ モデル アクション規則

IPageApplicationModelProvider を実装する既定のページ モデル プロバイダーは、ページ モデルを構成するための拡張ポイントを提供するようにデザインされた規則を呼び出します。 これらの規則は、ページ検出をビルドおよび変更したり、シナリオを処理したりするときに便利です。

このセクションの例の場合、サンプル アプリでは、応答ヘッダーを適用する ResultFilterAttribute である、AddHeaderAttribute クラスを使用します。

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string[] _values;

    public AddHeaderAttribute(string name, string[] values)
    {
        _name = name;
        _values = values;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _values);
        base.OnResultExecuting(context);
    }
}

規則を使用して、このサンプルでは、フォルダー内のすべてのページおよび単一ページに属性を適用する方法のデモを実行します。

フォルダー アプリ モデル規則

AddFolderApplicationModelConvention を使用すると、指定したフォルダーの下にあるすべてのページを対象に PageApplicationModel インスタンスへのアクションを呼び出す IPageApplicationModelConvention を作成して追加できます。

このサンプルでは、ヘッダー (OtherPagesHeader) をアプリの OtherPages フォルダー内にあるページに追加して、AddFolderApplicationModelConvention を使用するデモを実行します。

options.Conventions.AddFolderApplicationModelConvention("/OtherPages", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "OtherPagesHeader", new string[] { "OtherPages Header Value" }));
});

localhost:5000/OtherPages/Page1 でサンプルの Page1 ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the OtherPages/Page1 page show that the OtherPagesHeader has been added.

ページ アプリ モデル規則

AddPageApplicationModelConvention を使用すると、指定した名前のページを対象に PageApplicationModel へのアクションを呼び出す IPageApplicationModelConvention を作成して追加できます。

サンプルでは、ヘッダー (AboutHeader) を [About] ページに追加して、AddPageApplicationModelConvention を使用するデモを実行します。

options.Conventions.AddPageApplicationModelConvention("/About", model =>
{
    model.Filters.Add(new AddHeaderAttribute(
        "AboutHeader", new string[] { "About Header Value" }));
});

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that the AboutHeader has been added.

フィルターの構成

ConfigureFilter では、指定したフィルターを適用するように構成します。 フィルター クラスを実装できますが、サンプル アプリでは、ラムダ式でフィルターを実装する方法を示しています。これは、フィルターを返すファクトリとしてバックグラウンドで実装されます。

options.Conventions.ConfigureFilter(model =>
{
    if (model.RelativePath.Contains("OtherPages/Page2"))
    {
        return new AddHeaderAttribute(
            "OtherPagesPage2Header", 
            new string[] { "OtherPages/Page2 Header Value" });
    }
    return new EmptyFilter();
});

ページ アプリ モデルは、OtherPages フォルダーの Page2 ページにつながるセグメントの相対パスを確認するために使用されます。 条件を満たすと、ヘッダーが追加されます。 満たさない場合は、EmptyFilter が適用されます。

EmptyFilterアクション フィルターです。 アクション フィルターは Razor Pages によって無視されるため、パスに OtherPages/Page2 が含まれない場合、EmptyFilter には意図されたような効果はありません。

localhost:5000/OtherPages/Page2 でサンプルの Page2 ページを要求し、そのヘッダーを調べて結果を確認します。

The OtherPagesPage2Header is added to the response for Page2.

フィルター ファクトリの構成

ConfigureFilter では、すべての Razor Pages にフィルターを適用するように、指定したファクトリを構成します。

サンプル アプリでは、アプリのページに対する 2 つの値と共にヘッダー (FilterFactoryHeader) を追加して、フィルター ファクトリを使用する例を提供します。

options.Conventions.ConfigureFilter(new AddHeaderWithFactory());

AddHeaderWithFactory.cs:

public class AddHeaderWithFactory : IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new AddHeaderFilter();
    }

    private class AddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "FilterFactoryHeader", 
                new string[] 
                { 
                    "Filter Factory Header Value 1",
                    "Filter Factory Header Value 2"
                });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

localhost:5000/About でサンプルの [About] ページを要求し、そのヘッダーを調べて結果を確認します。

Response headers of the About page show that two FilterFactoryHeader headers have been added.

MVC フィルターとページ フィルター (IPageFilter)

Razor Pages はハンドラー メソッドを使用するため、MVC アクション フィルターは Razor Pages によって無視されます。 使用できるその他の種類の MVC フィルターは次のとおりです。承認例外リソース、および結果。 詳細については、「フィルター」トピックを参照してください。

ページ フィルター (IPageFilter) は、Razor Pages に適用されるフィルターの 1 つです。 詳細については、Razor Pages のフィルター メソッドに関するページを参照してください。

その他の技術情報