ASP.NET Core Blazor のルーティング

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

ルート テンプレート

Router コンポーネントを使用すると、Blazor アプリで Razor コンポーネントにルーティングできます。 Router コンポーネントは Blazor アプリの App コンポーネントで使用されます。

App.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <p>Sorry, there's nothing at this address.</p>
    </NotFound>
</Router>

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

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

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

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

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

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

Pages/BlazorRoute.razor:

@page "/BlazorRoute"
@page "/DifferentBlazorRoute"

<h1>Blazor routing</h1>

重要

URL が正しく解決されるように、アプリでは、href 属性に指定されているアプリのベース パスを使用して、その wwwroot/index.html ファイル (Blazor WebAssembly) または Pages/_Layout.cshtml ファイル (Blazor Server) に <base> タグを含める必要があります。 詳細については、「ASP.NET Core Blazor のホストと展開」を参照してください。

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

FocusOnNavigate コンポーネントを使用して、あるページから別のページに移動した後、CSS セレクターに基づいて要素に UI フォーカスを設定します。 Blazor プロジェクト テンプレートから生成されたアプリの App コンポーネントで、FocusOnNavigate コンポーネントの使用を確認できます。

App.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

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

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

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

App コンポーネントで、Router コンポーネントの NotFound テンプレートにカスタム コンテンツを設定します。

App.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <h1>Sorry</h1>
        <p>Sorry, there's nothing at this address.</p> b
    </NotFound>
</Router>

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

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

AdditionalAssemblies パラメーターを使用して、Router コンポーネントで、ルーティング可能なコンポーネントを検索するときに考慮する追加のアセンブリを指定します。 AppAssembly に指定されたアセンブリに加え、追加のアセンブリがスキャンされます。 次の例では、Component1 は、参照されているコンポーネント クラス ライブラリに定義されているルーティング可能なコンポーネントです。 次の AdditionalAssemblies の例は、Component1 のルーティング サポートの結果を示しています。

App.razor:

<Router
    AppAssembly="@typeof(Program).Assembly"
    AdditionalAssemblies="new[] { typeof(Component1).Assembly }">
    @* ... Router component elements ... *@
</Router>

ルート パラメーター

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

Pages/RouteParameter.razor:

@page "/RouteParameter/{text}"

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

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

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

Pages/RouteParameter.razor:

@page "/RouteParameter/{text?}"

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

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

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

オプションのパラメーター値が異なる同じコンポーネントへのアプリの移動を許可するには、OnInitialized{Async} ではなく、OnParametersSet を使用します。 前の例に基づいて、ユーザーが /RouteParameter から /RouteParameter/amazing に、または /RouteParameter/amazing から /RouteParameter に移動できる必要がある場合は、OnParametersSet を使用してください。

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

ルート制約

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

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

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

Pages/User.razor:

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

<h1>User Id: @Id</h1>

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

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

制約 一致の例 インバリアント
カルチャ
一致
bool {active:bool} true, FALSE いいえ
datetime {dob:datetime} 2016-12-31, 2016-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 は、省略可能なブール型のルート パラメーターです。

Pages/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 によるルーティング

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

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

Pages/Example.razor:

@page "/example/{param?}"

<p>
    Param: @Param
</p>

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

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

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

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

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

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

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

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

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

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

Pages/CatchAll.razor:

@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 (末尾のスラッシュを含む) を取得します。 通常、BaseUriwwwroot/index.html (Blazor WebAssembly) または Pages/_Layout.cshtml (Blazor Server) 内のドキュメントの <base> 要素の href 属性に対応します。
NavigateTo 指定された URI に移動します。 forceLoadtrue の場合:
  • クライアント側のルーティングはバイパスされます。
  • URI が通常クライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーでは、強制的にサーバーから新しいページが読み込まれます。
replacetrue の場合、履歴スタックに新しい URI をプッシュする代わりに、ブラウザー履歴の現在の URI が置き換えられます。
LocationChanged ナビゲーションの場所が変更されたときに発生するイベントです。 詳細については、「場所の変更」セクションを参照してください。
ToAbsoluteUri 相対 URI を絶対 URI に変換します。
ToBaseRelativePath ベース URI (たとえば、BaseUri によって以前に返された URI) が与えられると、絶対 URI を、ベース URI プレフィックスに相対的な URI に変換します。
GetUriWithQueryParameter 1 つのパラメーターを追加、更新、または削除して NavigationManager.Uri を更新することで構築された URI を返します。 詳細については、「クエリ文字列」セクションを参照してください。

場所の変更

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

次のコンポーネント:

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

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

      BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:5001/counter

Pages/Navigate.razor:

@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager NavigationManager

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

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

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

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

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

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

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

クエリ文字列

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

注意

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

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

  • bool, DateTime, decimal, double, float, Guid, int, long, string.
  • 上記の型の Null 許容のバリアント (Null 許容のバリアントがない string を除く)。
  • 上記の型の配列 (Null 許容、Null 非許容どちらでも)。

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

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

[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 に解決されます。

Pages/Search.razor:

@page "/search"

<h1>Search Example</h1>

<p>Filter: @Filter</p>

<p>Page: @Page</p>

<p>Assignees:</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 NavigationManager

...

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

前の例の場合:

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

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

@inject NavigationManager NavigationManager

...

NavigationManager.GetUriWithQueryParameters({PARAMETERS})

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

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

@inject NavigationManager NavigationManager

...

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

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

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

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

  • 上記の型の Null 許容のバリアント (Null 許容のバリアントがない string を除く)。
  • 上記の型の配列 (Null 許容、Null 非許容どちらでも)。

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

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

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

NavigationManager.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 の場合にクエリ パラメーターを削除する

NavigationManager.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 として追加または更新されます。
NavigationManager.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 として追加されるか置き換えられます。
NavigationManager.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 へのナビゲーションをトリガーします。
NavigationManager.NavigateTo(
    NavigationManager.GetUriWithQueryParameter("name", "Morena Baccarin"));

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

Router コンポーネントによって、ページ切り替えの発生をユーザーに示すことができます。

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

@using Microsoft.AspNetCore.Components.Routing

ページ切り替えイベント中に表示するマークアップを含むコンポーネントに <Navigating> タグを追加します。 詳細については、Navigating (API ドキュメント) を参照してください。

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

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

Navigating プロパティを使用する例については、ASP.NET Core でのアセンブリの遅延読み込みBlazor WebAssembly を参照してください。

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

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

  • ブラウザー内でルートに直接移動して、初めてアクセスする。
  • リンクまたは NavigationManager.NavigateTo 呼び出しを使用して新しいルートに移動する。

App コンポーネント (App.razor) 内は、次のようになっています。

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

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

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

Blazor Server アプリ、またはホストされている Blazor WebAssembly アプリのサーバー上でプリレンダリングする場合、OnNavigateAsync が "2 回" 実行されます。

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

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

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

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

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

App コンポーネントの例を次に示します。

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

App.razor:

@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 = [345, 789, 135, 689];

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

注意

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

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

注意

NavMenu コンポーネント (NavMenu.razor) は、Blazor プロジェクト テンプレートから生成されたアプリの Shared フォルダーにあります。

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

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

前の例では、HomeNavLink href="" はホーム URL と一致し、アプリの既定のベース パス URL (https://localhost:5001/ など) でのみ active CSS クラスを受け取ります。 2 番目の NavLink は、ユーザーが component プレフィックスを含む任意の URL (https://localhost:5001/componenthttps://localhost:5001/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="@c">
            <span ...></span> @current
        </NavLink>
    </li>
}

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

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

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

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

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

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

app.UseRouting();

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

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

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

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

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

ルート テンプレート

Router コンポーネントを使用すると、Blazor アプリで Razor コンポーネントにルーティングできます。 Router コンポーネントは Blazor アプリの App コンポーネントで使用されます。

App.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <p>Sorry, there's nothing at this address.</p>
    </NotFound>
</Router>

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

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

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

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

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

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

Pages/BlazorRoute.razor:

@page "/BlazorRoute"
@page "/DifferentBlazorRoute"

<h1>Blazor routing</h1>

重要

URL が正しく解決されるように、アプリでは、href 属性に指定されているアプリのベース パスを使用して、その wwwroot/index.html ファイル (Blazor WebAssembly) または Pages/_Host.cshtml ファイル (Blazor Server) に <base> タグを含める必要があります。 詳細については、「ASP.NET Core Blazor のホストと展開」を参照してください。

Router は、クエリ文字列値では使用できません。 クエリ文字列を操作する方法については、「クエリ文字列とパラメーターの解析」セクションを参照してください。

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

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

App コンポーネントで、Router コンポーネントの NotFound テンプレートにカスタム コンテンツを設定します。

App.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <h1>Sorry</h1>
        <p>Sorry, there's nothing at this address.</p> b
    </NotFound>
</Router>

注意

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

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

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

AdditionalAssemblies パラメーターを使用して、Router コンポーネントで、ルーティング可能なコンポーネントを検索するときに考慮する追加のアセンブリを指定します。 AppAssembly に指定されたアセンブリに加え、追加のアセンブリがスキャンされます。 次の例では、Component1 は、参照されているコンポーネント クラス ライブラリに定義されているルーティング可能なコンポーネントです。 次の AdditionalAssemblies の例は、Component1 のルーティング サポートの結果を示しています。

App.razor:

<Router
    AppAssembly="@typeof(Program).Assembly"
    AdditionalAssemblies="new[] { typeof(Component1).Assembly }">
    @* ... Router component elements ... *@
</Router>

注意

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

ルート パラメーター

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

Pages/RouteParameter.razor:

@page "/RouteParameter/{text}"

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

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

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

Pages/RouteParameter.razor:

@page "/RouteParameter/{text?}"

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

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

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

オプションのパラメーター値が異なる同じコンポーネントへのアプリの移動を許可するには、OnInitialized{Async} ではなく、OnParametersSet を使用します。 前の例に基づいて、ユーザーが /RouteParameter から /RouteParameter/amazing に、または /RouteParameter/amazing から /RouteParameter に移動できる必要がある場合は、OnParametersSet を使用してください。

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

注意

ルート パラメーターは、クエリ文字列値では使用できません。 クエリ文字列を操作する方法については、「クエリ文字列とパラメーターの解析」セクションを参照してください。

ルート制約

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

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

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

Pages/User.razor:

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

<h1>User Id: @Id</h1>

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

注意

ルート制約は、クエリ文字列値では使用できません。 クエリ文字列を操作する方法については、「クエリ文字列とパラメーターの解析」セクションを参照してください。

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

制約 一致の例 インバリアント
カルチャ
一致
bool {active:bool} true, FALSE いいえ
datetime {dob:datetime} 2016-12-31, 2016-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 は、省略可能なブール型のルート パラメーターです。

Pages/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 によるルーティング

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

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

Pages/Example.razor:

@page "/example/{param?}"

<p>
    Param: @Param
</p>

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

ホストされている 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 の末尾。

Pages/CatchAll.razor:

@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 (末尾のスラッシュを含む) を取得します。 通常、BaseUriwwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) 内のドキュメントの <base> 要素の href 属性に対応します。
NavigateTo 指定された URI に移動します。 forceLoadtrue の場合:
  • クライアント側のルーティングはバイパスされます。
  • URI が通常クライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーでは、強制的にサーバーから新しいページが読み込まれます。
LocationChanged ナビゲーションの場所が変更されたときに発生するイベントです。
ToAbsoluteUri 相対 URI を絶対 URI に変換します。
ToBaseRelativePath ベース URI (たとえば、BaseUri によって以前に返された URI) が与えられると、絶対 URI を、ベース URI プレフィックスに相対的な URI に変換します。

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

次のコンポーネント:

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

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

      BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:5001/counter

Pages/Navigate.razor:

@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager NavigationManager

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

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

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

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

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

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

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

クエリ文字列とパラメーターの解析

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

@inject NavigationManager NavigationManager

...

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

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

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

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

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

Router コンポーネントによって、ページ切り替えの発生をユーザーに示すことができます。

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

@using Microsoft.AspNetCore.Components.Routing

ページ切り替えイベント中に表示するマークアップを含むコンポーネントに <Navigating> タグを追加します。 詳細については、Navigating (API ドキュメント) を参照してください。

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

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

Navigating プロパティを使用する例については、ASP.NET Core でのアセンブリの遅延読み込みBlazor WebAssembly を参照してください。

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

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

  • ブラウザー内でルートに直接移動して、初めてアクセスする。
  • リンクまたは NavigationManager.NavigateTo 呼び出しを使用して新しいルートに移動する。

App コンポーネント (App.razor) 内は、次のようになっています。

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

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

注意

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

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

Blazor Server アプリ、またはホストされている Blazor WebAssembly アプリのサーバー上でプリレンダリングする場合、OnNavigateAsync が "2 回" 実行されます。

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

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

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

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

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

App コンポーネントの例を次に示します。

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

App.razor:

@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 = [345, 789, 135, 689];

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

注意

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

注意

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

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

注意

NavMenu コンポーネント (NavMenu.razor) は、Blazor プロジェクト テンプレートから生成されたアプリの Shared フォルダーにあります。

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

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

前の例では、HomeNavLink href="" はホーム URL と一致し、アプリの既定のベース パス URL (https://localhost:5001/ など) でのみ active CSS クラスを受け取ります。 2 番目の NavLink は、ユーザーが component プレフィックスを含む任意の URL (https://localhost:5001/componenthttps://localhost:5001/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="@c">
            <span ...></span> @current
        </NavLink>
    </li>
}

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

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

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

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

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

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

Startup.cs:

using Microsoft.AspNetCore.Builder;

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }
}

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

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

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

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

ルート テンプレート

Router コンポーネントを使用すると、Blazor アプリで Razor コンポーネントにルーティングできます。 Router コンポーネントは Blazor アプリの App コンポーネントで使用されます。

App.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <p>Sorry, there's nothing at this address.</p>
    </NotFound>
</Router>

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

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

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

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

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

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

Pages/BlazorRoute.razor:

@page "/BlazorRoute"
@page "/DifferentBlazorRoute"

<h1>Blazor routing</h1>

重要

URL が正しく解決されるように、アプリでは、href 属性に指定されているアプリのベース パスを使用して、その wwwroot/index.html ファイル (Blazor WebAssembly) または Pages/_Host.cshtml ファイル (Blazor Server) に <base> タグを含める必要があります。 詳細については、「ASP.NET Core Blazor のホストと展開」を参照してください。

Router は、クエリ文字列値では使用できません。 クエリ文字列を操作する方法については、「クエリ文字列とパラメーターの解析」セクションを参照してください。

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

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

App コンポーネントで、Router コンポーネントの NotFound テンプレートにカスタム コンテンツを設定します。

App.razor:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <h1>Sorry</h1>
        <p>Sorry, there's nothing at this address.</p> b
    </NotFound>
</Router>

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

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

AdditionalAssemblies パラメーターを使用して、Router コンポーネントで、ルーティング可能なコンポーネントを検索するときに考慮する追加のアセンブリを指定します。 AppAssembly に指定されたアセンブリに加え、追加のアセンブリがスキャンされます。 次の例では、Component1 は、参照されているコンポーネント クラス ライブラリに定義されているルーティング可能なコンポーネントです。 次の AdditionalAssemblies の例は、Component1 のルーティング サポートの結果を示しています。

App.razor:

<Router
    AppAssembly="@typeof(Program).Assembly"
    AdditionalAssemblies="new[] { typeof(Component1).Assembly }">
    @* ... Router component elements ... *@
</Router>

ルート パラメーター

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

Pages/RouteParameter.razor:

@page "/RouteParameter/{text}"

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

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

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

Pages/RouteParameter.razor:

@page "/RouteParameter"
@page "/RouteParameter/{text}"

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

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

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

オプションのパラメーター値が異なる同じコンポーネントへのアプリの移動を許可するには、OnInitialized{Async} ではなく、OnParametersSet を使用します。 前の例に基づいて、ユーザーが /RouteParameter から /RouteParameter/amazing に、または /RouteParameter/amazing から /RouteParameter に移動できる必要がある場合は、OnParametersSet を使用してください。

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

注意

ルート パラメーターは、クエリ文字列値では使用できません。 クエリ文字列を操作する方法については、「クエリ文字列とパラメーターの解析」セクションを参照してください。

ルート制約

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

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

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

Pages/User.razor:

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

<h1>User Id: @Id</h1>

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

注意

ルート制約は、クエリ文字列値では使用できません。 クエリ文字列を操作する方法については、「クエリ文字列とパラメーターの解析」セクションを参照してください。

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

制約 一致の例 インバリアント
カルチャ
一致
bool {active:bool} true, FALSE いいえ
datetime {dob:datetime} 2016-12-31, 2016-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 がローカライズ不可であることが前提となります。

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

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

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

Pages/Example.razor:

@page "/example"
@page "/example/{param}"

<p>
    Param: @Param
</p>

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

ホストされている 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 のルーティング」を参照してください。

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

キャッチオール ルート パラメーターは、ASP.NET Core 5.0 以降でサポートされます。 詳細については、この記事の 5.0 バージョンを選択してください。

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

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

メンバー 説明
Uri 現在の絶対 URI を取得します。
BaseUri 絶対 URI を生成するために、相対 URI パスの前に付加できるベース URI (末尾のスラッシュを含む) を取得します。 通常、BaseUriwwwroot/index.html (Blazor WebAssembly) または Pages/_Host.cshtml (Blazor Server) 内のドキュメントの <base> 要素の href 属性に対応します。
NavigateTo 指定された URI に移動します。 forceLoadtrue の場合:
  • クライアント側のルーティングはバイパスされます。
  • URI が通常クライアント側ルーターによって処理されるかどうかにかかわらず、ブラウザーでは、強制的にサーバーから新しいページが読み込まれます。
LocationChanged ナビゲーションの場所が変更されたときに発生するイベントです。
ToAbsoluteUri 相対 URI を絶対 URI に変換します。
ToBaseRelativePath ベース URI (たとえば、BaseUri によって以前に返された URI) が与えられると、絶対 URI を、ベース URI プレフィックスに相対的な URI に変換します。

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

次のコンポーネント:

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

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

      BlazorSample.Pages.Navigate: Information: URL of new location: https://localhost:5001/counter

Pages/Navigate.razor:

@page "/navigate"
@using Microsoft.Extensions.Logging 
@implements IDisposable
@inject ILogger<Navigate> Logger
@inject NavigationManager NavigationManager

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

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

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

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

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

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

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

クエリ文字列とパラメーターの解析

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

@inject NavigationManager NavigationManager

...

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

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

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

注意

NavMenu コンポーネント (NavMenu.razor) は、Blazor プロジェクト テンプレートから生成されたアプリの Shared フォルダーにあります。

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

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

前の例では、HomeNavLink href="" はホーム URL と一致し、アプリの既定のベース パス URL (https://localhost:5001/ など) でのみ active CSS クラスを受け取ります。 2 番目の NavLink は、ユーザーが component プレフィックスを含む任意の URL (https://localhost:5001/componenthttps://localhost:5001/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="@c">
            <span ...></span> @current
        </NavLink>
    </li>
}

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

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

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

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

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

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

Startup.cs:

using Microsoft.AspNetCore.Builder;

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }
}

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

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

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