ASP.NET Core の Blazor ルーティングとナビゲーション

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の ASP.NET Core 8.0 バージョンを参照してください。

この記事では、Blazor アプリの要求ルーティングを管理する方法と、NavLink コンポーネントを使用してナビゲーション リンクを作成する方法について説明します。

重要

この記事全体のコード例では、Navigation で呼び出されるメソッドを示します。これはクラスとコンポーネントに挿入される NavigationManager です。

静的ルーティングと対話型ルーティング

"このセクションは Blazor WebAssembly に適用されます。"

プリレンダリングが無効になっていない場合、Blazor ルーター (Router コンポーネント、Routes.razor 内の <Router>) は、静的サーバー側レンダリング (静的 SSR) 中にコンポーネントへの静的ルーティングを実行します。 この種類のルーティングは、"静的ルーティング" と呼ばれます。

対話型レンダリング モードが Routes コンポーネントに割り当てられると、Blazor ルーターは、サーバー上での静的ルーティングを使用する静的 SSR の後に対話形式になります。 この種類のルーティングは、"対話型ルーティング" と呼ばれます。

静的ルーターはエンドポイント ルーティングと HTTP 要求パスを使用して、レンダリングするコンポーネントを決定します。 ルーターは対話形式になると、ドキュメントの URL (ブラウザーのアドレス バー内の URL) を使用して、レンダリングするコンポーネントを決定します。 つまり対話型ルーターは、ドキュメントの URL が別の有効な内部 URL に動的に変更された場合、どのコンポーネントをレンダリングするかを動的に変更できます。しかもそれを、新しいページ コンテンツをフェッチする HTTP 要求を実行せずに行うことができます。

また、対話型ルーティングでは、新しいページ コンテンツが通常のページ要求でサーバーから要求されないため、プリレンダリングも防止されます。 詳細については、「ASP.NET Core Razor のプリレンダリングとコンポーネント」をご覧ください。

ルート テンプレート

Router コンポーネントは、Razor コンポーネントへのルーティングを可能にし、アプリの Routes コンポーネント (Components/Routes.razor) に配置されています。

Router コンポーネントを使用すると、Razor コンポーネントにルーティングできます。 Router コンポーネントは、App コンポーネント (App.razor) で使用されます。

@page ディレクティブ を含む Razor コンポーネント (.razor) がコンパイルされると、生成されたコンポーネント クラスに、コンポーネントのルート テンプレートを指定する RouteAttribute が指定されます。

アプリが起動すると、Router の AppAssembly として指定されたアセンブリがスキャンされ、RouteAttribute を持つアプリのコンポーネントのルート情報が収集されます。

実行時に、RouteView コンポーネントは、

  • Router から、ルート パラメーターと共に RouteData を受け取ります。
  • さらに入れ子になったレイアウトを含め、指定されたコンポーネントをそのレイアウトでレンダリングします。

必要に応じて、@layout ディレクティブ を使用してレイアウトを指定しないコンポーネントに対して、レイアウト クラスで DefaultLayout パラメーターを指定します。 フレームワークの Blazor プロジェクト テンプレートによって、MainLayout コンポーネント (MainLayout.razor) が、アプリの既定のレイアウトとして指定されます。 レイアウトについて詳しくは、「ASP.NET Core Blazor のレイアウト」をご覧ください。

コンポーネントにより、複数の @page ディレクティブ を使用する複数のルート テンプレートがサポートされます。 次のコンポーネントの例では、/blazor-route/different-blazor-route に対する要求を読み込みます。

BlazorRoute.razor:

@page "/blazor-route"
@page "/different-blazor-route"

<PageTitle>Routing</PageTitle>

<h1>Routing Example</h1>

<p>
    This page is reached at either <code>/blazor-route</code> or 
    <code>/different-blazor-route</code>.
</p>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>
@page "/blazor-route"
@page "/different-blazor-route"

<h1>Blazor routing</h1>

重要

URL が正しく解決されるように、アプリには <base> タグを含め (<head> コンテンツの場所)、href 属性でアプリのベース パスを指定する必要があります。 詳細については、「ASP.NET Core Blazor のホストと展開」を参照してください。

Router は、クエリ文字列値では使用できません。 クエリ文字列の使用方法については、「クエリ文字列」セクションをご覧ください。

ルート テンプレートを @page ディレクティブを使用して文字列リテラルとして指定する代わりに、定数ベースのルート テンプレートを @attribute ディレクティブと共に指定できます。

次の例では、コンポーネント内の @page ディレクティブが @attribute ディレクティブと Constants.CounterRoute の定数ベースのルート テンプレートに置き換えられます。これは、アプリ内の別の場所で "/counter" に設定されています。

- @page "/counter"
+ @attribute [Route(Constants.CounterRoute)]

メモ

ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router コンポーネントに @trueに設定された PreferExactMatches パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。

ナビゲーション時に要素にフォーカスを合わせる

FocusOnNavigate コンポーネントでは、あるページから別のページに移動した後、CSS セレクターに基づいて要素に UI フォーカスを設定します。

<FocusOnNavigate RouteData="routeData" Selector="h1" />

Router コンポーネントが新しいページに移動すると、FocusOnNavigate コンポーネントによってページの最上位ヘッダー (<h1>) にフォーカスが設定されます。 これは、スクリーン リーダーを使用する際にページ ナビゲーションが読み上げられるようにするための一般的な方法です。

コンテンツが見つからないときにカスタム コンテンツを提供する

Router コンポーネントを使用すると、要求されたルートでコンテンツが見つからない場合に、アプリでカスタム コンテンツを指定できます。

Router コンポーネントの NotFound パラメーターのカスタム コンテンツを設定します。

<Router ...>
    ...
    <NotFound>
        ...
    </NotFound>
</Router>

他の対話型コンポーネントなど、NotFound パラメータのコンテンツとして任意の項目がサポートされます。 NotFound コンテンツに既定のレイアウトを適用するには、「ASP.NET Core Blazor のレイアウト」をご覧ください。

重要

Blazor Web アプリでは NotFound パラメータ (<NotFound>...</NotFound> マークアップ) は使用しませんが、フレームワークの破壊的変更を回避するため、下位互換性の目的でこのパラメータがサポートされています。 サーバー側の ASP.NET Core ミドルウェア パイプラインでは、サーバー上の要求が処理されます。 サーバー側の手法を使用して、不適切な要求を処理します。 詳細については、「ASP.NET Core Blazor レンダリング モード」を参照してください。

複数のアセンブリからコンポーネントにルーティングする

"このセクションは Blazor WebAssembly に適用されます。"

Router コンポーネントのAdditionalAssemblies パラメーターとエンドポイント規則ビルダー AddAdditionalAssemblies を使用して、追加のアセンブリ内のルーティング可能なコンポーネントを検出します。 次の各サブセクションでは、各 API をいつどのように使用するかについて説明します。

静的ルーティング

静的サーバー側レンダリング (静的 SSR) 用の追加アセンブリからルーティング可能なコンポーネントを検出するには、後でルーターが対話型レンダリング用に対話形式になった場合でも、アセンブリを Blazor フレームワークに公開する必要があります。 サーバー プロジェクトの Program ファイルで MapRazorComponents にチェーン化された追加のアセンブリを使用して、AddAdditionalAssemblies メソッドを呼び出します。

次の例では、プロジェクトの _Imports.razor ファイルを使用して、BlazorSample.Client プロジェクトのアセンブリ内のルーティング可能なコンポーネントを含めています。

app.MapRazorComponents<App>()
    .AddAdditionalAssemblies(typeof(BlazorSample.Client._Imports).Assembly);

Note

上記のガイダンスは、コンポーネント クラス ライブラリのシナリオにも適用されます。 クラス ライブラリと静的 SSR に関するその他の重要なガイダンスについては、「ASP.NET Core の Razor クラス ライブラリ (RCL) と静的サーバー側レンダリング (静的 SSR)」を参照してください。

対話型ルーティング

対話型レンダリング モードを Routes コンポーネント (Routes.razor) に割り当てることで、サーバー上の静的 SSR と静的ルーティングの後に Blazor ルーターを対話形式にすることができます。 たとえば、<Routes @rendermode="InteractiveServer" /> は対話型サーバー側レンダリング (対話型 SSR) を Routes コンポーネントに割り当てます。 Router コンポーネントは、Routes コンポーネントから対話型サーバー側レンダリング (対話型 SSR) を継承します。 ルーターはサーバー上の静的ルーティングの後で対話形式になります。

対話型ルーティングの内部ナビゲーションでは、サーバーに新しいページ コンテンツを要求する必要はありません。 そのため、内部ページ要求ではプリレンダリングは行われません。 詳細については、「ASP.NET Core Razor のプリレンダリングとコンポーネント」をご覧ください。

Routes コンポーネントがサーバー プロジェクトで定義されている場合は、Router コンポーネントの AdditionalAssemblies パラメーターに .Client プロジェクトのアセンブリが含まれている必要があります。 これにより、ルーターは対話形式でレンダリングされたときに正しく動作します。

次の例では、Routes コンポーネントがサーバー プロジェクト内にあり、BlazorSample.Client プロジェクトの _Imports.razor ファイルはルーティング可能なコンポーネントを検索するアセンブリを示しています。

<Router
    AppAssembly="..."
    AdditionalAssemblies="new[] { typeof(BlazorSample.Client._Imports).Assembly }">
    ...
</Router>

AppAssembly に指定されたアセンブリに加え、追加のアセンブリがスキャンされます。

Note

上記のガイダンスは、コンポーネント クラス ライブラリのシナリオにも適用されます。

または、ルーティング可能なコンポーネントは、グローバルな対話型 WebAssembly または自動レンダリングが適用された .Client プロジェクトにのみ存在し、Routes コンポーネントはサーバー プロジェクトではなく .Client プロジェクトで定義されます。 この場合、ルーティング可能なコンポーネントを含む外部アセンブリがないため、AdditionalAssemblies の値を指定する必要はありません。

このセクションは Blazor Server アプリに適用されます。

Router コンポーネントのAdditionalAssemblies パラメーターとエンドポイント規則ビルダー AddAdditionalAssemblies を使用して、追加のアセンブリ内のルーティング可能なコンポーネントを検出します。

次の例では、Component1 は、参照されている ComponentLibrary という名前のコンポーネント クラス ライブラリに定義されたルーティング可能なコンポーネントです。

<Router
    AppAssembly="..."
    AdditionalAssemblies="new[] { typeof(ComponentLibrary.Component1).Assembly }">
    ...
</Router>

AppAssembly に指定されたアセンブリに加え、追加のアセンブリがスキャンされます。

ルート パラメーター

ルーターでルート パラメーターを使用すれば、同じ名前の対応するコンポーネント パラメーターを設定できます。 ルート パラメーター名では大文字と小文字は区別されません。 次の例では、text パラメーターを使用して、ルート セグメントの値をコンポーネントの Text プロパティに割り当てます。 /route-parameter-1/amazing に対して要求が行われると、コンテンツは Blazor is amazing! としてレンダリングされます。

RouteParameter1.razor:

@page "/route-parameter-1/{text}"

<PageTitle>Route Parameter 1</PageTitle>

<h1>Route Parameter Example 1</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}
@page "/route-parameter-1/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }
}

省略可能なパラメーターがサポートされています。 次の例では、省略可能なパラメーター text を使用して、ルート セグメントの値をコンポーネントの Text プロパティに割り当てます。 セグメントが存在しない場合、Text の値は fantastic に設定されます。

省略可能なパラメーターはサポートされていません。 以下の例では、2 つの @page ディレクティブが適用されています。 1 つ目のディレクティブでは、パラメーターを指定せずにコンポーネントへの移動を許可します。 2 つ目のディレクティブでは、{text} ルート パラメーターの値をコンポーネントの Text プロパティに割り当てます。

RouteParameter2.razor:

@page "/route-parameter-2/{text?}"

<PageTitle>Route Parameter 2</PageTitle>

<h1>Route Parameter Example 2</h1>

<p>Blazor is @Text!</p>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}
@page "/route-parameter-2/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}
@page "/route-parameter-2/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}
@page "/route-parameter-2/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}
@page "/route-parameter-2"
@page "/route-parameter-2/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnParametersSet()
    {
        Text = Text ?? "fantastic";
    }
}

OnParametersSet の代わりに OnInitialized{Async} メソッドが使用されている場合、ユーザーが同じコンポーネント内を移動すると、fantastic への Text プロパティの既定の割り当ては行われません。 たとえば、この状況は、ユーザーが /route-parameter-2/amazing から /route-parameter-2 に移動するときに発生します。 コンポーネント インスタンスは保持され、新しいパラメーターを受け入れるので、OnInitialized メソッドが再度呼び出されることはありません。

Note

ルート パラメーターは、クエリ文字列値では使用できません。 クエリ文字列の使用方法については、「クエリ文字列」セクションをご覧ください。

ルート制約

ルート制約は、コンポーネントへのルート セグメントに型の一致を適用します。

次の例で、User コンポーネントへのルートは、次の場合にのみ一致します。

  • 要求 URL に Id ルート セグメントが存在する。
  • Id セグメントが整数 (int) 型である。

User.razor:

@page "/user/{Id:int}"

<PageTitle>User</PageTitle>

<h1>User Example</h1>

<p>User Id: @Id</p>

@code {
    [Parameter]
    public int Id { get; set; }
}

注意

ルート制約は、クエリ文字列値では使用できません。 クエリ文字列の使用方法については、「クエリ文字列」セクションをご覧ください。

次の表に示すルート制約を使用できます。 インバリアント カルチャと一致するルート制約については、表の下の警告で詳細をご確認ください。

制約 一致の例 インバリアント
カルチャ
一致
bool {active:bool} trueFALSE いいえ
datetime {dob:datetime} 2016-12-312016-12-31 7:32pm はい
decimal {price:decimal} 49.99-1,000.01 はい
double {weight:double} 1.234-1,001.01e8 はい
float {weight:float} 1.234-1,001.01e8 はい
guid {id:guid} CD2C1638-1638-72D5-1638-DEADBEEF1638{CD2C1638-1638-72D5-1638-DEADBEEF1638} いいえ
int {id:int} 123456789-123456789 はい
long {ticks:long} 123456789-123456789 はい

警告

URL の妥当性を検証し、CLR 型 (intDateTime など) に変換されるルート制約では、常にインバリアント カルチャが使用されます。 これらの制約では、URL がローカライズ不可であることが前提となります。

ルート制約は、省略可能なパラメーターでも使用できます。 次の例で、Id は必須ですが、Option は、省略可能なブール型のルート パラメーターです。

User.razor:

@page "/user/{id:int}/{option:bool?}"

<p>
    Id: @Id
</p>

<p>
    Option: @Option
</p>

@code {
    [Parameter]
    public int Id { get; set; }

    [Parameter]
    public bool Option { get; set; }
}

ドットを含む URL によるルーティング

"サーバー側" の既定のルート テンプレートでは、ファイルが要求されているドット (.) が要求 URL の最後のセグメントに含まれていると想定されます。 たとえば、相対 URL /example/some.thing は、ルーターによって some.thing という名前のファイルに対する要求として解釈されます。 追加の構成がないと、some.thing@page ディレクティブ を含むコンポーネントにルーティングすることを意図しており、some.thing がルート パラメーター値である場合に、アプリによって "404 - 見つかりません" という応答が返されます。 ドットが含まれる 1 つまたは複数のパラメーターを指定してルートを使用するには、アプリでカスタム テンプレートを使ってルートを構成する必要があります。

URL の最後のセグメントからルート パラメーターを受け取ることができる次の Example コンポーネントについて考えてみます。

Example.razor:

@page "/example/{param?}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string? Param { get; set; }
}
@page "/example/{param?}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string? Param { get; set; }
}
@page "/example/{param?}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string Param { get; set; }
}
@page "/example"
@page "/example/{param}"

<p>
    Param: @Param
</p>

@code {
    [Parameter]
    public string Param { get; set; }
}

ホストされている Blazor WebAssembly ソリューションServer アプリで、param ルート パラメーターのドットを使って要求をルーティングできるようにするには、Program ファイルで省略可能なパラメーターを指定してフォールバック ファイル ルート テンプレートを追加します。

app.MapFallbackToFile("/example/{param?}", "index.html");

param ルート パラメーターのドットを使って要求をルーティングするように Blazor Server アプリを構成するには、Program ファイルで省略可能なパラメーターを指定してフォールバック ページ ルート テンプレートを追加します。

app.MapFallbackToPage("/example/{param?}", "/_Host");

詳細については、「ASP.NET Core のルーティング」を参照してください。

ホストされている Blazor WebAssembly ソリューションServer アプリで、param ルート パラメーターのドットを使って要求をルーティングできるようにするには、Startup.Configure で省略可能なパラメーターを指定してフォールバック ファイル ルート テンプレートを追加します。

Startup.cs:

endpoints.MapFallbackToFile("/example/{param?}", "index.html");

param ルート パラメーターのドットを使って要求をルーティングするように Blazor Server アプリを構成するには、Startup.Configure で省略可能なパラメーターを指定してフォールバック ページ ルート テンプレートを追加します。

Startup.cs:

endpoints.MapFallbackToPage("/example/{param?}", "/_Host");

詳細については、「ASP.NET Core のルーティング」を参照してください。

キャッチオールのルート パラメーター

複数のフォルダー境界にまたがるパスをキャプチャするキャッチオール ルート パラメーターが、コンポーネントでサポートされます。

キャッチオールのルート パラメーターは次のとおりです。

  • ルート セグメント名と一致する名前が付けられている。 名前付けで大文字と小文字は区別されない。
  • string 型。 フレームワークによって自動キャストは提供されません。
  • URL の末尾。

CatchAll.razor:

@page "/catch-all/{*pageRoute}"

<PageTitle>Catch All</PageTitle>

<h1>Catch All Parameters Example</h1>

<p>Add some URI segments to the route and request the page again.</p>

<p>
    PageRoute: @PageRoute
</p>

@code {
    [Parameter]
    public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"

@code {
    [Parameter]
    public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"

@code {
    [Parameter]
    public string? PageRoute { get; set; }
}
@page "/catch-all/{*pageRoute}"

@code {
    [Parameter]
    public string PageRoute { get; set; }
}

ルート テンプレートが /catch-all/{*pageRoute} の URL /catch-all/this/is/a/test の場合、PageRoute の値は this/is/a/test に設定されます。

キャプチャされたパスのスラッシュとセグメントはデコードされます。 /catch-all/{*pageRoute} のルート テンプレートでは、URL /catch-all/this/is/a%2Ftest%2A から this/is/a/test* が生成されます。

URI およびナビゲーション状態ヘルパー

C# コード内の URI とナビゲーションを管理するには、NavigationManager を使用します。 NavigationManager には、次の表に示すイベントとメソッドがあります。

メンバー 説明
Uri 現在の絶対 URI を取得します。
BaseUri 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。
NavigateTo 指定された URI に移動します。 forceLoadfalse の場合:
  • また、拡張ナビゲーションは現在の URL で使用でき、Blazor の拡張ナビゲーションがアクティブにされます。
  • そうでない場合、Blazor は要求された URL のページ全体の再読み込みを実行します。
forceLoadtrue の場合:
  • クライアント側のルーティングはバイパスされます。
  • URI が通常クライアント側の対話型ルーターによって処理されるかどうかにかかわらず、ブラウザーはサーバーからの新しいページの読み込みを強制されます。

詳しくは、「ナビゲーションとフォーム処理の強化」セクションをご覧ください。

replacetrue の場合、履歴スタックに新しい URI をプッシュする代わりに、ブラウザー履歴の現在の URI が置き換えられます。

LocationChanged ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。
ToAbsoluteUri 相対 URI を絶対 URI に変換します。
ToBaseRelativePath アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。
RegisterLocationChangingHandler 受信ナビゲーション イベントを処理するハンドラーを登録します。 NavigateTo を呼び出すと、常にハンドラーが呼び出されます。
GetUriWithQueryParameter 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。
メンバー 説明
Uri 現在の絶対 URI を取得します。
BaseUri 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。
NavigateTo 指定された URI に移動します。 forceLoadtrue の場合:
  • クライアント側のルーティングはバイパスされます。
  • URI が通常クライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーでは、強制的にサーバーから新しいページが読み込まれます。
replacetrue の場合、履歴スタックに新しい URI をプッシュする代わりに、ブラウザー履歴の現在の URI が置き換えられます。
LocationChanged ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。
ToAbsoluteUri 相対 URI を絶対 URI に変換します。
ToBaseRelativePath アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。
RegisterLocationChangingHandler 受信ナビゲーション イベントを処理するハンドラーを登録します。 NavigateTo を呼び出すと、常にハンドラーが呼び出されます。
GetUriWithQueryParameter 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。
メンバー 説明
Uri 現在の絶対 URI を取得します。
BaseUri 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。
NavigateTo 指定された URI に移動します。 forceLoadtrue の場合:
  • クライアント側のルーティングはバイパスされます。
  • URI が通常クライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーでは、強制的にサーバーから新しいページが読み込まれます。
replacetrue の場合、履歴スタックに新しい URI をプッシュする代わりに、ブラウザー履歴の現在の URI が置き換えられます。
LocationChanged ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。
ToAbsoluteUri 相対 URI を絶対 URI に変換します。
ToBaseRelativePath アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。
GetUriWithQueryParameter 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。
メンバー 説明
Uri 現在の絶対 URI を取得します。
BaseUri 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUri はドキュメントの <base> 要素の href 属性に対応します (<head> コンテンツの場所)。
NavigateTo 指定された URI に移動します。 forceLoadtrue の場合:
  • クライアント側のルーティングはバイパスされます。
  • URI が通常クライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーでは、強制的にサーバーから新しいページが読み込まれます。
LocationChanged ナビゲーションの場所が変更されたときに発生するイベントです。
ToAbsoluteUri 相対 URI を絶対 URI に変換します。
ToBaseRelativePath アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。 例については、「ベース URI プレフィックスに対する相対 URI を生成する」セクションを参照してください。

場所の変更

LocationChanged イベントの場合、LocationChangedEventArgs でナビゲーション イベントに関する次の情報が提供されます。

次のコンポーネント:

  • NavigateTo を使用してボタンが選択されたときに、アプリの Counter コンポーネント (Counter.razor) に移動します。
  • NavigationManager.LocationChanged をサブスクライブすることで、場所の変更イベントを処理します。
    • HandleLocationChanged メソッドは、Dispose がフレームワークによって呼び出されると、アンフックになります。 このメソッドをアンフックすることで、コンポーネントのガベージ コレクションが許可されます。

    • ロガーの実装では、ボタンが選択されたときに次の情報をログに記録します。

      BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:{PORT}/counter

Navigate.razor:

@page "/navigate"
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<PageTitle>Navigate</PageTitle>

<h1>Navigate Example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager Navigation

<h1>Navigate in component code example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        Navigation.NavigateTo("counter");
    }

    protected override void OnInitialized()
    {
        Navigation.LocationChanged += HandleLocationChanged;
    }

    private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
    {
        Logger.LogInformation("URL of new location: {Location}", e.Location);
    }

    public void Dispose()
    {
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}

コンポーネントの破棄について詳しくは、「ASP.NET Core Razor コンポーネントのライフサイクル」をご覧ください。

ナビゲーションとフォーム処理の強化

"このセクションは Blazor WebAssembly に適用されます。"

Blazor Web Apps は、ページ ナビゲーションとフォーム処理の要求に対して 2 種類のルーティングを実行できます。

  • 通常のナビゲーション (ドキュメント間のナビゲーション): 要求 URL に対してページ全体の再読み込みがトリガーされます。
  • 拡張ナビゲーション (同じドキュメントのナビゲーション)†: Blazor は要求をインターセプトし、代わりに fetch 要求を実行します。 その後、Blazor は応答の内容をページの DOM にパッチします。 Blazor の強化されたナビゲーションとフォーム処理により、ページ全体の再読み込みが不要になり、ページの状態が維持されるため、ページの読み込みが速くなり、通常はページ上のユーザーのスクロール位置が失われません。

†拡張ナビゲーションは、次の場合に使用できます。

  • Blazor Server スクリプト (blazor.server.js) または Blazor WebAssembly スクリプト (blazor.webassembly.js) ではなく、Blazor Web App スクリプト (blazor.web.js) が使われます。
  • この機能が明示的に無効にされることはありません。
  • 宛先 URL は、内部ベース URI 空間 (アプリのベース パス) 内にあります。

サーバー側のルーティングと拡張ナビゲーションが有効になっている場合、場所変更ハンドラーは、対話型ランタイムから開始したプログラム ナビゲーションに対してのみ呼び出されます。 今後のリリースでは、リンクをたどるなど、ナビゲーションの種類が追加されると、場所変更ハンドラーが呼び出される可能性もあります。

拡張ナビゲーションが発生すると、通常は、対話型サーバーと WebAssembly ランタイムに登録されている LocationChanged イベント ハンドラーが呼び出されます。 場所変更ハンドラーが拡張ナビゲーションをインターセプトしない場合があります。 たとえば、対話型ランタイムが使用可能になる前に、ユーザーが別のページに切り替えることがあります。 そのため、場所変更ハンドラーが実行される保証がないため、アプリ ロジックがこのハンドラーの呼び出しに依存しないことが重要です。

NavigateTo を呼び出すとき:

  • forceLoadfalse の場合、これが既定値です。
    • また、拡張ナビゲーションは現在の URL で使用でき、Blazor の拡張ナビゲーションがアクティブにされます。
    • そうでない場合、Blazor は要求された URL のページ全体の再読み込みを実行します。
  • forceLoadtrueの場合: 拡張ナビゲーションが使用可能かどうかに関係なく、Blazor は要求された URL のページ全体の再読み込みを実行します。

NavigationManager.Refresh(bool forceLoad = false) を呼び出すことで、現在のページを更新できます。その場合、常に拡張ナビゲーションが実行されます (使用可能な場合)。 拡張ナビゲーションを使用できない場合、Blazor はページ全体の再読み込みを実行します。

Navigation.Refresh();

拡張ナビゲーションが使用可能な場合でも、trueforceLoad パラメーターに渡して、ページ全体の再読み込みが常に実行されるようにします。

Navigation.Refresh(true);

拡張ナビゲーションは既定で有効になっていますが、data-enhance-nav HTML 属性を使用して階層的およびリンクごとに制御できます。

次の例では、拡張ナビゲーションを無効にします。

<a href="redirect" data-enhance-nav="false">
    GET without enhanced navigation
</a>
<ul data-enhance-nav="false">
    <li>
        <a href="redirect">GET without enhanced navigation</a>
    </li>
    <li>
        <a href="redirect-2">GET without enhanced navigation</a>
    </li>
</ul>

宛先がBlazor 以外のエンドポイントの場合、拡張ナビゲーションは適用されません。クライアント側の JavaScript は、ページ全体の読み込みとして再試行します。 これにより、既存のページに修正プログラムを適用すべきではない外部ページについて、フレームワークに混乱を招くことはありません。

拡張フォーム処理を有効にするには、EditForm フォームに Enhance パラメーターを追加するか、HTML フォーム (<form>) に data-enhance 属性を追加します。

<EditForm ... Enhance ...>
    ...
</EditForm>
<form ... data-enhance ...>
    ...
</form>

拡張フォーム処理は階層的ではなく、次の子フォームにはフローしません。

サポート外: フォームの先祖要素に拡張ナビゲーションを設定して、フォームの拡張ナビゲーションを有効にすることはできません。

<div ... data-enhance ...>
    <form ...>
        <!-- NOT enhanced -->
    </form>
</div>

拡張フォームの投稿は、Blazor エンドポイントでのみ機能します。 拡張フォームを Blazor 以外のエンドポイントに投稿すると、エラーが発生します。

拡張ナビゲーションを無効にするには:

  • EditForm の場合は、フォーム要素から Enhance パラメーターを削除します (または、次に false を設定します: Enhance="false")。
  • HTML <form> の場合は、フォーム要素から data-enhance 属性を削除します (または、それを false に設定します: data-enhance="false")。

更新された内容がサーバー レンダリングの一部でない場合、Blazor の拡張ナビゲーションとフォーム処理は DOM に対する動的な変更を元に戻す可能性があります。 要素の内容を保持するには、data-permanent 属性を使用します。

次の例では、ページが読み込まれた場合に、<div> 要素の内容がスクリプトによって動的に更新されます。

<div data-permanent>
    ...
</div>

クライアントで Blazor を開始したら、enhancedload イベントを使用して拡張ページの更新をリッスンできます。 これにより、強化されたページ更新によって元に戻された可能性がある変更を DOM に再適用できます。

Blazor.addEventListener('enhancedload', () => console.log('Enhanced update!'));

拡張ナビゲーションとグローバルなフォーム処理を無効にするには、「ASP.NET Core Blazor の起動」をご覧ください。

静的サーバー側レンダリング (静的 SSR) による拡張ナビゲーションでは、JavaScript を読み込むときに特別な注意が必要です。 詳細については、「静的サーバー側レンダリング (静的 SSR) を使用した ASP.NET Core Blazor JavaScript」を参照してください。

ベース URI プレフィックスに対する相対 URI を生成する

ToBaseRelativePath は、アプリのベース URI に基づいて、絶対 URI をベース URI プレフィックスに対する相対 URI に変換します。

次に例を示します。

try
{
    baseRelativePath = Navigation.ToBaseRelativePath(inputURI);
}
catch (ArgumentException ex)
{
    ...
}

アプリのベース URI が https://localhost:8000 の場合、次の結果が得られます。

  • inputURIhttps://localhost:8000/segment を渡すと、segmentbaseRelativePath になります。
  • inputURIhttps://localhost:8000/segment1/segment2 を渡すと、segment1/segment2baseRelativePath になります。

アプリのベース URI が inputURI のベース URI と一致しない場合は、ArgumentException がスローされます。

inputURIhttps://localhost:8001/segment を渡すと、次の例外が発生します。

System.ArgumentException: 'The URI 'https://localhost:8001/segment' is not contained by the base URI 'https://localhost:8000/'.'

NavigationManager では、ブラウザーの History API を使用して、アプリによって行われた各場所の変更に関連付けられているナビゲーション履歴の状態を維持します。 履歴状態の維持は、外部 ID プロバイダーを使用してユーザーを認証する場合など、外部リダイレクト シナリオで特に役立ちます。 詳細については、「ナビゲーション オプション」セクションを参照してください。

NavigationOptionsNavigateTo に渡して、次の動作を制御します。

  • ForceLoad: クライアント側のルーティングをバイパスし、URI がクライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーにサーバーから新しいページを読み込むよう強制します。 既定値は false です。
  • ReplaceHistoryEntry: 履歴スタックの現在のエントリを置き換えます。 false の場合は、履歴スタックに新しいエントリを追加します。 既定値は false です。
  • HistoryEntryState: 履歴エントリに追加する状態を取得または設定します。
Navigation.NavigateTo("/path", new NavigationOptions
{
    HistoryEntryState = "Navigation state"
});

場所の変更の処理中にターゲット履歴エントリに関連付けられている状態を取得する方法の詳細については、「場所の変更を処理/防止する」セクション参照してください。

クエリ文字列

[SupplyParameterFromQuery]属性を使用し、コンポーネント パラメータはクエリ文字列からのものであることを指定します。

[SupplyParameterFromQuery]属性と共に[Parameter]属性を使用して、ルーティング可能なコンポーネントのコンポーネント パラメーターは、クエリ文字列からのものであることを指定します。

メモ

コンポーネント パラメーターでは、@page ディレクティブを使用したルーティング可能なコンポーネントのクエリ パラメーター値のみを受け取ることができます。

トップダウンの情報フローが妨害されないようにし、フレームワークとアプリの両方でパラメーターの処理順序を明確にするために、クエリ パラメーターを直接受け取るのはルーティング可能なコンポーネントのみです。 この設計により、特定のパラメーターの処理順序を想定して記述されたアプリ コードの特定しにくいバグを回避できます。 クエリ パラメーターの値をルーティング可能でないコンポーネントに渡すためには、自由にカスタム カスケード パラメーターを定義したり、通常のコンポーネント パラメーターに直接割り当てたりできます。

クエリ文字列から提供されるコンポーネント パラメーターでは、次の型がサポートされます。

  • boolDateTimedecimaldoublefloatGuidintlongstring
  • 上記の型の Null 許容バリアント。
  • 上記の型の配列 (Null 許容、Null 非許容どちらでも)。

指定された型に対して、適切なカルチャに依存しない書式設定が適用されます (CultureInfo.InvariantCulture)。

コンポーネント パラメーター名とは異なるクエリ パラメーター名を使用するには、[SupplyParameterFromQuery] 属性の Name プロパティを指定します。 次の例では、コンポーネント パラメーターの C# 名は {COMPONENT PARAMETER NAME} です。 {QUERY PARAMETER NAME} プレースホルダーに対して異なるクエリ パラメーター名が指定されています。

[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
public string? {COMPONENT PARAMETER NAME} { get; set; }
[Parameter]
[SupplyParameterFromQuery(Name = "{QUERY PARAMETER NAME}")]
public string? {COMPONENT PARAMETER NAME} { get; set; }

URL が /search?filter=scifi%20stars&page=3&star=LeVar%20Burton&star=Gary%20Oldman である次の例の場合:

  • Filter プロパティは scifi stars に解決されます。
  • Page プロパティは 3 に解決されます。
  • 配列 Starsstar という名前 (Name = "star") のクエリ パラメーターから入力され、LeVar BurtonGary Oldman に解決されます。

メモ

次のルーティング可能なページ コンポーネントのクエリ文字列パラメーターは、@page ディレクティブの必要なく、ルーティング不可能なコンポーネントでも機能します (たとえば、他のコンポーネントで使用されている、共有 Search コンポーネント用の Search.razor の場合)。

Search.razor:

@page "/search"

<h1>Search Example</h1>

<p>Filter: @Filter</p>

<p>Page: @Page</p>

@if (Stars is not null)
{
    <p>Stars:</p>

    <ul>
        @foreach (var name in Stars)
        {
            <li>@name</li>
        }
    </ul>
}

@code {
    [SupplyParameterFromQuery]
    public string? Filter { get; set; }

    [SupplyParameterFromQuery]
    public int? Page { get; set; }

    [SupplyParameterFromQuery(Name = "star")]
    public string[]? Stars { get; set; }
}

Search.razor:

@page "/search"

<h1>Search Example</h1>

<p>Filter: @Filter</p>

<p>Page: @Page</p>

@if (Stars is not null)
{
    <p>Stars:</p>

    <ul>
        @foreach (var name in Stars)
        {
            <li>@name</li>
        }
    </ul>
}

@code {
    [Parameter]
    [SupplyParameterFromQuery]
    public string? Filter { get; set; }

    [Parameter]
    [SupplyParameterFromQuery]
    public int? Page { get; set; }

    [Parameter]
    [SupplyParameterFromQuery(Name = "star")]
    public string[]? Stars { get; set; }
}

NavigationManager.GetUriWithQueryParameter を使用して、現在の URL の 1 つ以上のクエリ パラメーターを追加、変更、または削除します。

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameter("{NAME}", {VALUE})

前の例の場合:

  • {NAME} プレースホルダーでは、クエリ パラメーター名が指定されます。 {VALUE} プレースホルダーでは、サポートされている型として値が指定されます。 サポートされている型の一覧については、このセクションで後述します。
  • 1 つのパラメーターを含む現在の URL と等しい文字列が返されます。
    • クエリ パラメーター名が現在の URL に存在しない場合に追加されます。
    • クエリ パラメーターが現在の URL に存在する場合、指定された値に更新されます。
    • 指定された値の型が Null 許容で、値が null の場合は、削除されます。
  • 指定された型に対して、適切なカルチャに依存しない書式設定が適用されます (CultureInfo.InvariantCulture)。
  • クエリ パラメーターの名前と値は URL エンコードされます。
  • 型のインスタンスが複数ある場合、一致するクエリ パラメーター名を持つすべての値が置き換えられます。

NavigationManager.GetUriWithQueryParameters を呼び出して、Uri から構築され、複数のパラメーターが追加、更新、または削除された URI を作成します。 各値について、フレームワークでは value?.GetType() を使用して各クエリ パラメーターのランタイム型が決定され、適切なカルチャに依存しない書式設定が選択されます。 サポートされていない型に対しては、フレームワークによってエラーがスローされます。

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters({PARAMETERS})

{PARAMETERS} プレースホルダーは IReadOnlyDictionary<string, object> です。

URI 文字列を GetUriWithQueryParameters に渡して、指定された URI から、複数のパラメーターを追加、更新、または削除して、新しい URI を生成します。 各値について、フレームワークでは value?.GetType() を使用して各クエリ パラメーターのランタイム型が決定され、適切なカルチャに依存しない書式設定が選択されます。 サポートされていない型に対しては、フレームワークによってエラーがスローされます。 サポートされている型の一覧については、このセクションで後述します。

@inject NavigationManager Navigation

...

Navigation.GetUriWithQueryParameters("{URI}", {PARAMETERS})
  • {URI} プレースホルダーは、クエリ文字列を含む URI、または含まない URI です。
  • {PARAMETERS} プレースホルダーは IReadOnlyDictionary<string, object> です。

サポートされている型は、ルート制約でサポートされている型と同じです。

  • bool
  • DateTime
  • decimal
  • double
  • float
  • Guid
  • int
  • long
  • string

サポートされる型には次のようなものがあります。

  • 上記の型の Null 許容バリアント。
  • 上記の型の配列 (Null 許容、Null 非許容どちらでも)。

パラメーターが存在する場合にクエリ パラメーター値を置き換える

Navigation.GetUriWithQueryParameter("full name", "Morena Baccarin")
現在の URL 生成された URL
scheme://host/?full%20name=David%20Krumholtz&age=42 scheme://host/?full%20name=Morena%20Baccarin&age=42
scheme://host/?fUlL%20nAmE=David%20Krumholtz&AgE=42 scheme://host/?full%20name=Morena%20Baccarin&AgE=42
scheme://host/?full%20name=Jewel%20Staite&age=42&full%20name=Summer%20Glau scheme://host/?full%20name=Morena%20Baccarin&age=42&full%20name=Morena%20Baccarin
scheme://host/?full%20name=&age=42 scheme://host/?full%20name=Morena%20Baccarin&age=42
scheme://host/?full%20name= scheme://host/?full%20name=Morena%20Baccarin

パラメーターが存在しない場合にクエリ パラメーターと値を追加する

Navigation.GetUriWithQueryParameter("name", "Morena Baccarin")
現在の URL 生成された URL
scheme://host/?age=42 scheme://host/?age=42&name=Morena%20Baccarin
scheme://host/ scheme://host/?name=Morena%20Baccarin
scheme://host/? scheme://host/?name=Morena%20Baccarin

パラメーター値が null の場合にクエリ パラメーターを削除する

Navigation.GetUriWithQueryParameter("full name", (string)null)
現在の URL 生成された URL
scheme://host/?full%20name=David%20Krumholtz&age=42 scheme://host/?age=42
scheme://host/?full%20name=Sally%20Smith&age=42&full%20name=Summer%20Glau scheme://host/?age=42
scheme://host/?full%20name=Sally%20Smith&age=42&FuLl%20NaMe=Summer%20Glau scheme://host/?age=42
scheme://host/?full%20name=&age=42 scheme://host/?age=42
scheme://host/?full%20name= scheme://host/

クエリ パラメーターの追加、更新、削除

次に例を示します。

  • name が削除されます (存在する場合)。
  • age が値 25 (int) で追加されます (存在しない場合)。 存在する場合、age は値 25 に更新されます。
  • eye color が値 green として追加または更新されます。
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["name"] = null,
        ["age"] = (int?)25,
        ["eye color"] = "green"
    })
現在の URL 生成された URL
scheme://host/?name=David%20Krumholtz&age=42 scheme://host/?age=25&eye%20color=green
scheme://host/?NaMe=David%20Krumholtz&AgE=42 scheme://host/?age=25&eye%20color=green
scheme://host/?name=David%20Krumholtz&age=42&keepme=true scheme://host/?age=25&keepme=true&eye%20color=green
scheme://host/?age=42&eye%20color=87 scheme://host/?age=25&eye%20color=green
scheme://host/? scheme://host/?age=25&eye%20color=green
scheme://host/ scheme://host/?age=25&eye%20color=green

列挙可能な値のサポート

次に例を示します。

  • full name が単一の値 Morena Baccarin として追加または更新されます。
  • ping パラメーターが 351687240 として追加されるか置き換えられます。
Navigation.GetUriWithQueryParameters(
    new Dictionary<string, object?>
    {
        ["full name"] = "Morena Baccarin",
        ["ping"] = new int?[] { 35, 16, null, 87, 240 }
    })
現在の URL 生成された URL
scheme://host/?full%20name=David%20Krumholtz&ping=8&ping=300 scheme://host/?full%20name=Morena%20Baccarin&ping=35&ping=16&ping=87&ping=240
scheme://host/?ping=8&full%20name=David%20Krumholtz&ping=300 scheme://host/?ping=35&full%20name=Morena%20Baccarin&ping=16&ping=87&ping=240
scheme://host/?ping=8&ping=300&ping=50&ping=68&ping=42 scheme://host/?ping=35&ping=16&ping=87&ping=240&full%20name=Morena%20Baccarin

追加または変更されたクエリ文字列を使用して移動するには、生成された URL を NavigateTo に渡します。

次の例では以下を呼び出します。

  • GetUriWithQueryParameter を呼び出し、値 Morena Baccarin を使用して name クエリ パラメーターを追加または置き換えます。
  • NavigateTo を呼び出して、新しい URL へのナビゲーションをトリガーします。
Navigation.NavigateTo(
    Navigation.GetUriWithQueryParameter("name", "Morena Baccarin"));

要求のクエリ文字列は、NavigationManager.Uri プロパティから取得されます。

@inject NavigationManager Navigation

...

var query = new Uri(Navigation.Uri).Query;

クエリ文字列のパラメーターを解析するための 1 つの方法は、JavaScript (JS) 相互運用URLSearchParams を使用することです。

export createQueryString = (string queryString) => new URLSearchParams(queryString);

JavaScript モジュールを使用した JavaScript の分離について詳しくは、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」をご覧ください。

名前付き要素へのハッシュされたルーティング

要素へのハッシュされた (#) 参照による次の方法を使って、名前付き要素に移動します。 コンポーネント内の要素へのルートと、外部コンポーネント内の要素へのルートでは、ルート (root) 相対パスが使われます。 先頭のスラッシュ (/) は省略できます。

次の各方法の例では、Counter コンポーネント内の idtargetElement の要素への移動を示します。

  • アンカー要素 (<a>) と href:

    <a href="/counter#targetElement">
    
  • NavLink コンポーネントと href:

    <NavLink href="/counter#targetElement">
    
  • NavigationManager.NavigateTo に相対 URL を渡す:

    Navigation.NavigateTo("/counter#targetElement");
    

次の例では、コンポーネント内の名前付きの H2 見出しと外部コンポーネントへの、ハッシュされたルーティングを示します。

Home (Home.razor) と Counter (Counter.razor) コンポーネントでは、ナビゲーション ターゲットとして機能するように、次のマークアップを既存のコンポーネント マークアップの末尾に配置します。 <div> では、ブラウザーのスクロール動作を示すために、わざと縦方向のスペースを作成しています。

<div class="border border-info rounded bg-info" style="height:500px"></div>

<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>

次の HashedRouting コンポーネントをアプリに追加します。

HashedRouting.razor:

@page "/hashed-routing"
@inject NavigationManager Navigation

<PageTitle>Hashed routing</PageTitle>

<h1>Hashed routing to named elements</h1>

<ul>
    <li>
        <a href="/hashed-routing#targetElement">
            Anchor in this component
        </a>
    </li>
    <li>
        <a href="/#targetElement">
            Anchor to the <code>Home</code> component
        </a>
    </li>
    <li>
        <a href="/counter#targetElement">
            Anchor to the <code>Counter</code> component
        </a>
    </li>
    <li>
        <NavLink href="/hashed-routing#targetElement">
            Use a `NavLink` component in this component
        </NavLink>
    </li>
    <li>
        <button @onclick="NavigateToElement">
            Navigate with <code>NavigationManager</code> to the 
            <code>Counter</code> component
        </button>
    </li>
</ul>

<div class="border border-info rounded bg-info" style="height:500px"></div>

<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>

@code {
    private void NavigateToElement()
    {
        Navigation.NavigateTo("/counter#targetElement");
    }
}

<Navigating> コンテンツとのユーザー操作

Blazor WebAssembly アプリでのアセンブリの遅延読み込み中や、Blazor サーバー側アプリへの低速なネットワーク接続など、ナビゲーション中に大幅な遅延が発生する場合、Router コンポーネントはページ切り替えが発生していることをユーザーに示すことができます。

Router コンポーネントを指定するコンポーネントの上部に、Microsoft.AspNetCore.Components.Routing 名前空間の @using ディレクティブを追加します。

@using Microsoft.AspNetCore.Components.Routing

ページ切り替えイベント中に表示するコンテンツを Navigating パラメータに指定します。

ルーター要素コンテンツ (<Router>...</Router>) は、次のとおりです。

<Navigating>
    <p>Loading the requested page&hellip;</p>
</Navigating>

Navigating プロパティを使う例については、「ASP.NET Core Blazor WebAssembly でのアセンブリの遅延読み込み」をご覧ください。

OnNavigateAsync で非同期ナビゲーション イベントを処理する

Router コンポーネントは、OnNavigateAsync 機能をサポートしています。 OnNavigateAsync ハンドラーは、ユーザーが次のことを行った場合に呼び出されます。

  • ブラウザー内でルートに直接移動して、初めてアクセスする。
  • リンクまたは NavigationManager.NavigateTo 呼び出しを使用して新しいルートに移動する。
<Router AppAssembly="typeof(App).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext args)
    {
        ...
    }
}
<Router AppAssembly="typeof(Program).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext args)
    {
        ...
    }
}

OnNavigateAsync を使う例については、「ASP.NET Core Blazor WebAssembly でのアセンブリの遅延読み込み」をご覧ください。

サーバー上でプリレンダリングする場合、OnNavigateAsync が "2 回" 実行されます。

  • 1 回目は、要求されたエンドポイント コンポーネントが、最初に静的にレンダリングされるとき。
  • 2 回目は、エンドポイント コンポーネントがブラウザーによってレンダリングされるとき。

OnNavigateAsync で開発者コードが 2 回実行されないようにするには、Routes コンポーネントに、OnAfterRender{Async} で使用する NavigationContext を格納します (firstRender がチェックされている場合)。 詳細については、「Blazor のライフサイクル」の記事にある「JavaScript 相互運用を使用したプリレンダリング」を参照してください。

OnNavigateAsync で開発者コードが 2 回実行されないようにするには、App コンポーネントに、OnAfterRender{Async} で使用する NavigationContext を格納します (firstRender がチェックされている場合)。 詳細については、「Blazor のライフサイクル」の記事にある「JavaScript 相互運用を使用したプリレンダリング」を参照してください。

OnNavigateAsync でキャンセルを処理する

OnNavigateAsync コールバックに渡される NavigationContext オブジェクトには、新しいナビゲーション イベントが発生したときに設定される CancellationToken が含まれています。 このキャンセル トークンが、古いナビゲーションに対して OnNavigateAsync コールバックを継続して実行しないように設定されている場合は、OnNavigateAsync コールバックをスローする必要があります。

ユーザーがエンドポイントに移動したものの、その後すぐに新しいエンドポイントに移動する場合、アプリで最初のエンドポイントの OnNavigateAsync コールバックを実行し続けるべきではありません。

次に例を示します。

  • キャンセル トークンは、PostAsJsonAsync への呼び出しで渡されます。ユーザーが /about エンドポイントから離れた場合は、POST が取り消されます。
  • ユーザーが /store エンドポイントから移動した場合は、製品のプリフェッチ操作中にキャンセル トークンが設定されます。
@inject HttpClient Http
@inject ProductCatalog Products

<Router AppAssembly="typeof(App).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext context)
    {
        if (context.Path == "/about") 
        {
            var stats = new Stats { Page = "/about" };
            await Http.PostAsJsonAsync("api/visited", stats, 
                context.CancellationToken);
        }
        else if (context.Path == "/store")
        {
            var productIds = new[] { 345, 789, 135, 689 };

            foreach (var productId in productIds) 
            {
                context.CancellationToken.ThrowIfCancellationRequested();
                Products.Prefetch(productId);
            }
        }
    }
}
@inject HttpClient Http
@inject ProductCatalog Products

<Router AppAssembly="typeof(Program).Assembly" 
    OnNavigateAsync="OnNavigateAsync">
    ...
</Router>

@code {
    private async Task OnNavigateAsync(NavigationContext context)
    {
        if (context.Path == "/about") 
        {
            var stats = new Stats { Page = "/about" };
            await Http.PostAsJsonAsync("api/visited", stats, 
                context.CancellationToken);
        }
        else if (context.Path == "/store")
        {
            var productIds = new[] { 345, 789, 135, 689 };

            foreach (var productId in productIds) 
            {
                context.CancellationToken.ThrowIfCancellationRequested();
                Products.Prefetch(productId);
            }
        }
    }
}

メモ

NavigationContext 内のキャンセル トークンが取り消された場合にスローしないと、前のナビゲーションからのコンポーネントをレンダリングするなど、意図しない動作が発生する可能性があります。

場所の変更を処理/防止する

RegisterLocationChangingHandler では、受信ナビゲーション イベントを処理するハンドラーを登録します。 LocationChangingContext によって提供されるハンドラーのコンテキストには、次のプロパティが含まれます。

  • TargetLocation: ターゲットの場所を取得します。
  • HistoryEntryState: ターゲット履歴エントリに関連付けられている状態を取得します。
  • IsNavigationIntercepted: ナビゲーションがリンクからインターセプトされたかどうかを取得します。
  • CancellationToken: ナビゲーションが取り消されたかどうかを判断する CancellationToken を取得します。たとえば、ユーザーが別のナビゲーションをトリガーしたかどうかを判断します。
  • PreventNavigation: ナビゲーションが続行されないようにするために呼び出されます。

コンポーネントでは、OnAfterRender または OnAfterRenderAsync メソッドに複数の場所変更ハンドラーを登録できます。 ナビゲーションでは、(複数のコンポーネントにまたがって) アプリ全体に登録されているすべての場所変更ハンドラーを呼び出し、すべての内部ナビゲーションでそれらをすべて並列で実行します。 NavigateTo に加え、次の場合にハンドラーが呼び出されます。

  • 内部リンクを選択する場合。これは、アプリのベース パスの下にある URL を指すリンクです。
  • ブラウザーの [進む] ボタンと [戻る] ボタンを使用して移動する場合。

ハンドラーは、アプリ内の内部ナビゲーションに対してのみ実行されます。 ユーザーが別のサイトに移動するリンクを選択した場合、またはアドレス バーを別のサイトに手動で変更した場合、場所変更ハンドラーは実行されません。

IDisposable を実装し、登録済みハンドラーを破棄して登録を解除します。 詳しくは、「ASP.NET Core Razor コンポーネントのライフサイクル」をご覧ください。

重要

場所の変更を処理するときは、JavaScript (JS) 相互運用機能を使って DOM のクリーンアップ タスクを実行しないでください。 クライアントの JS で MutationObserver パターンを使います。 詳しくは、「ASP.NET Core Blazor JavaScript の相互運用性 (JS 相互運用)」をご覧ください。

次の例では、ナビゲーション イベントの場所変更ハンドラーが登録されています。

NavHandler.razor:

@page "/nav-handler"
@implements IDisposable
@inject NavigationManager Navigation

<p>
    <button @onclick="@(() => Navigation.NavigateTo("/"))">
        Home (Allowed)
    </button>
    <button @onclick="@(() => Navigation.NavigateTo("/counter"))">
        Counter (Prevented)
    </button>
</p>

@code {
    private IDisposable? registration;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            registration = 
                Navigation.RegisterLocationChangingHandler(OnLocationChanging);
        }
    }

    private ValueTask OnLocationChanging(LocationChangingContext context)
    {
        if (context.TargetLocation == "/counter")
        {
            context.PreventNavigation();
        }

        return ValueTask.CompletedTask;
    }

    public void Dispose() => registration?.Dispose();
}

内部ナビゲーションは非同期的に取り消すことができるため、登録されたハンドラーに対して複数の重複する呼び出しが発生する可能性があります。 たとえば、ユーザーがすぐにページの [戻る] ボタンを選択したり、ナビゲーションが実行される前に複数のリンクを選択したりすると、複数のハンドラー呼び出しが発生することがあります。 非同期ナビゲーション ロジックの概要を以下に示します。

  • 場所変更ハンドラーが登録されている場合は、最初にすべてのナビゲーションが元に戻されてから、ナビゲーションが取り消されていない場合は再生されます。
  • 重複するナビゲーション要求が行われた場合、最新の要求では常に以前の要求を取り消します。これは、次のことを意味します。
    • アプリで、複数の [戻る] と [進む] ボタンの選択が 1 つの選択として扱われる場合があります。
    • ナビゲーションが完了する前にユーザーが複数のリンクを選択した場合、最後に選択したリンクによってナビゲーションが決まります。

ナビゲーション履歴スタックのエントリと状態を制御するために NavigateToNavigationOptions を渡す方法の詳細については、「ナビゲーション オプション」セクションを参照してください。

その他のコード例については、「BasicTestApp」(dotnet/aspnetcore 参照ソース) の NavigationManagerComponent に関するページを参照してください。

注意

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

表示されている限り NavigationLock コンポーネントはナビゲーション イベントをインターセプトし、続行またはキャンセルの決定が行われるまで、特定のナビゲーションを効果的に "ロック" します。 ナビゲーション インターセプトのスコープをコンポーネントの有効期間に設定できる場合、NavigationLock を使用します。

NavigationLock パラメーター:

  • ConfirmExternalNavigation では、外部ナビゲーションの確認またはキャンセルをユーザーに求めるブラウザー ダイアログを設定します。 既定値は false です。 確認ダイアログを表示するには、ブラウザーのアドレス バーの URL を使用して外部ナビゲーションをトリガーする前に、最初にユーザーがページを操作する必要があります。 操作の要件について詳しくは、「Window: beforeunload イベント」(MDN ドキュメント) を参照してください。
  • OnBeforeInternalNavigation では、内部ナビゲーション イベントのコールバックを設定します。

次の NavLock コンポーネントでは、以下のことを行います。

  • https://www.microsoft.com へのナビゲーションを成功させるには、Microsoft の Web サイトに従う試みをユーザーが確認する必要があります。
  • PreventNavigation は、JSconfirm ダイアログを生成する JavaScript (JS) 相互運用機能呼び出しによるナビゲーションの確認をユーザーが拒否した場合に、ナビゲーションが発生しないようにするために呼び出されます。

NavLock.razor:

@page "/nav-lock"
@inject IJSRuntime JSRuntime
@inject NavigationManager Navigation

<NavigationLock ConfirmExternalNavigation="true" 
    OnBeforeInternalNavigation="OnBeforeInternalNavigation" />

<p>
    <button @onclick="Navigate">Navigate</button>
</p>

<p>
    <a href="https://www.microsoft.com">Microsoft homepage</a>
</p>

@code {
    private void Navigate()
    {
        Navigation.NavigateTo("/");
    }

    private async Task OnBeforeInternalNavigation(LocationChangingContext context)
    {
        var isConfirmed = await JSRuntime.InvokeAsync<bool>("confirm", 
            "Are you sure you want to navigate to the root page?");

        if (!isConfirmed)
        {
            context.PreventNavigation();
        }
    }
}

その他のコード例については、「BasicTestApp」(dotnet/aspnetcore 参照ソース) の ConfigurableNavigationLock コンポーネントに関するページを参照してください。

ナビゲーション リンクを作成するときは、HTML ハイパーリンク要素 (<a>) の代わりに NavLink コンポーネントを使用します。 NavLink コンポーネントは <a> 要素のように動作しますが、href が現在の URL と一致するかどうかに基づいて active CSS クラスを切り替える点が異なります。 active クラスは、表示されているナビゲーション リンクの中でどのページがアクティブ ページであるかをユーザーが理解するのに役立ちます。 必要に応じて、CSS クラス名を NavLink.ActiveClass に割り当てて、現在のルートが href と一致したときに、レンダリングされるリンクにカスタム CSS クラスを適用します。

<NavLink> 要素の Match 属性に割り当てられる 2 つの NavLinkMatch オプションがあります。

  • NavLinkMatch.All:NavLink は、現在の URL 全体に一致する場合にアクティブになります。
  • NavLinkMatch.Prefix (既定値):NavLink は、現在の URL の任意のプレフィックスに一致する場合にアクティブになります。

前の例では、HomeNavLinkhref="" はホーム URL と一致し、アプリの既定のベース パス (/) でのみ active CSS クラスを受け取ります。 2 番目の NavLink は、ユーザーが component プレフィックスを含む任意の URL (/component/component/another-segment など) にアクセスしたときに、active クラスを受け取ります。

追加の NavLink コンポーネント属性は、レンダリングされるアンカー タグに渡されます。 次の例では、NavLink コンポーネントに target 属性が含まれています。

<NavLink href="example-page" target="_blank">Example page</NavLink>

次の HTML マークアップがレンダリングされます。

<a href="example-page" target="_blank">Example page</a>

警告

Blazor による子コンテンツのレンダリング方法により、for ループ内の NavLink コンポーネントのレンダリングでは、インクリメントするループ変数が NavLink (子) コンポーネントのコンテンツ内で使用されている場合、ローカル インデックス変数が必要になります。

@for (int c = 0; c < 10; c++)
{
    var current = c;
    <li ...>
        <NavLink ... href="product-number/@c">
            <span ...></span> Product #@current
        </NavLink>
    </li>
}

このシナリオでのインデックス変数の使用は、NavLink コンポーネントだけでなく、子コンテンツでループ変数を使用するすべての子コンポーネントで必須です。

または、Enumerable.Range と共に foreach ループを使用します。

@foreach (var c in Enumerable.Range(0,10))
{
    <li ...>
        <NavLink ... href="product-number/@c">
            <span ...></span> Product #@c
        </NavLink>
    </li>
}

ASP.NET Core エンドポイントのルーティングの統合

このセクションは、回線上で動作する Blazor Web アプリに適用されます。

このセクションは Blazor Server アプリに適用されます。

Blazor Web アプリは ASP.NET Core エンドポイントのルーティングに統合されています。 ASP.NET Core アプリは、Program ファイルで MapRazorComponents を使用して、対話型コンポーネントの着信接続を受け入れるように構成されています。 既定のルート コンポーネント (最初に読み込まれるコンポーネント) は、App コンポーネント (App.razor) です。

app.MapRazorComponents<App>();

Blazor Server は ASP.NET Core エンドポイントのルーティングに統合されています。 ASP.NET Core アプリは、Program ファイルで MapBlazorHub を使用して、対話型コンポーネントの着信接続を受け入れるように構成されています。

app.UseRouting();

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

Blazor Server は ASP.NET Core エンドポイントのルーティングに統合されています。 ASP.NET Core アプリは、Startup.ConfigureMapBlazorHub を使用して、対話型コンポーネントの着信接続を受け入れるように構成されています。

一般的な構成は、すべての要求を Razor ページにルーティングすることです。これは、Blazor Server アプリのサーバー側部分のホストとして機能します。 慣例により、"ホスト" のページは通常、アプリの Pages フォルダーでは _Host.cshtml という名前になります。

ホスト ファイルに指定されるルートは、ルート照合で低い優先順位で動作するため、フォールバック ルートと呼ばれます。 フォールバック ルートは、他のルートが一致しない場合に使用されます。 これにより、Blazor Server アプリのコンポーネント ルーティングに干渉することなく、他のコントローラーやページをアプリで使用できるようになります。

ルート以外の URL のサーバー ホスト用に MapFallbackToPage を構成する方法については、「ASP.NET Core Blazor のホストと展開」をご覧ください。