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

ページ ルートとアプリ モデル プロバイダーの規則を使用して、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] ページを要求し、その結果を調べます。

[About] ページは、GlobalRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

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

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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、GlobalHeader が追加されたことを示しています。

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

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 ページを要求し、その結果を調べます。

OtherPages フォルダーの Page1 は、GlobalRouteValue と OtherPagesRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

ページ ルート モデル規則

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] ページは、GlobalRouteValue と AboutRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

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

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 には、更新されたルートが反映されることに注意してください。

ナビゲーション バーのサンプル アプリの [Contact] リンク

レンダリングされた HTML の [Contact] リンクを調べると、href に '/TheContactPage' が設定されています

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

URL の 'TextValue' の省略可能な 'text' ルート セグメントを適用する Microsoft Edge ブラウザーの例。 レンダリングされたページには、'text' セグメントの値が示されています。

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

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 ページを要求し、そのヘッダーを調べて結果を確認します。

OtherPages/Page1 ページの応答ヘッダーは、OtherPagesHeader が追加されていることを示しています。

ページ アプリ モデル規則

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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、AboutHeader が追加されたことを示しています。

フィルターの構成

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 ページを要求し、そのヘッダーを調べて結果を確認します。

OtherPagesPage2Header は、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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、FilterFactoryHeader ヘッダーが 2 つ追加されたことを示しています。

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] ページを要求し、その結果を調べます。

[About] ページは、GlobalRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

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

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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、GlobalHeader が追加されたことを示しています。

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

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 ページを要求し、その結果を調べます。

OtherPages フォルダーの Page1 は、GlobalRouteValue と OtherPagesRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

ページ ルート モデル規則

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] ページは、GlobalRouteValue と AboutRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

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

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.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            options.Conventions.Add(
                new PageRouteTransformerConvention(
                    new SlugifyParameterTransformer()));
        });
}

public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
    {
        if (value == null) { return null; }

        // Slugify value
        return Regex.Replace(value.ToString(), "([a-z])([A-Z])", "$1-$2").ToLower();
    }
}

ページ ルートの構成

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 には、更新されたルートが反映されることに注意してください。

ナビゲーション バーのサンプル アプリの [Contact] リンク

レンダリングされた HTML の [Contact] リンクを調べると、href に '/TheContactPage' が設定されています

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

URL の 'TextValue' の省略可能な 'text' ルート セグメントを適用する Microsoft Edge ブラウザーの例。 レンダリングされたページには、'text' セグメントの値が示されています。

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

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 ページを要求し、そのヘッダーを調べて結果を確認します。

OtherPages/Page1 ページの応答ヘッダーは、OtherPagesHeader が追加されていることを示しています。

ページ アプリ モデル規則

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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、AboutHeader が追加されたことを示しています。

フィルターの構成

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 ページを要求し、そのヘッダーを調べて結果を確認します。

OtherPagesPage2Header は、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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、FilterFactoryHeader ヘッダーが 2 つ追加されたことを示しています。

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] ページを要求し、その結果を調べます。

[About] ページは、GlobalRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

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

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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、GlobalHeader が追加されたことを示しています。

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

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 ページを要求し、その結果を調べます。

OtherPages フォルダーの Page1 は、GlobalRouteValue と OtherPagesRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

ページ ルート モデル規則

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] ページは、GlobalRouteValue と AboutRouteValue のルート セグメントで要求されます。 レンダリングされたページは、ルート データの値がページの OnGet メソッドでキャプチャされたことを示しています。

ページ ルートの構成

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 には、更新されたルートが反映されることに注意してください。

ナビゲーション バーのサンプル アプリの [Contact] リンク

レンダリングされた HTML の [Contact] リンクを調べると、href に '/TheContactPage' が設定されています

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

URL の 'TextValue' の省略可能な 'text' ルート セグメントを適用する Microsoft Edge ブラウザーの例。 レンダリングされたページには、'text' セグメントの値が示されています。

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

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 ページを要求し、そのヘッダーを調べて結果を確認します。

OtherPages/Page1 ページの応答ヘッダーは、OtherPagesHeader が追加されていることを示しています。

ページ アプリ モデル規則

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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、AboutHeader が追加されたことを示しています。

フィルターの構成

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 ページを要求し、そのヘッダーを調べて結果を確認します。

OtherPagesPage2Header は、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] ページを要求し、そのヘッダーを調べて結果を確認します。

[About] ページの応答ヘッダーは、FilterFactoryHeader ヘッダーが 2 つ追加されたことを示しています。

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

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

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

その他の技術情報