檢查傳入的請求 URL 並將它們路由到適當的視圖或頁面是每個單頁應用程序 (SPA) 框架的基本功能。 Blazor Server 和 WebAssembly 應用程序還支持使用一些內置組件和服務進行路由。 在本教程中,我將介紹在 Blazor 應用中實現路由所需的所有內容。
Blazor 應用中的路由配置
在開始為不同的 Blazor 組件/頁面創建路由之前,我們需要了解 Blazor 服務器應用程序如何集成到 ASP.NET Core 端點路由中。 Blazor 服務器應用程序通過 SignalR 連接與客戶端通信,並接受 Blazor 組件的傳入連接,我們在 Startup.cs 文件中調用 MapBlazorHub 方法配置方法,如下所示:
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
默認配置將所有請求路由到 Razor 頁面,該頁面充當 Blazor 服務器應用程序服務器端部分的主機。 按照慣例,此主機頁面是 _Host.cshtml,它位於應用程序的 Pages 文件夾中。 主機文件中指定的路由稱為回退路由,並且在路由匹配中具有非常低的優先級,這意味着當沒有其他路由匹配時,將使用此路由。
Blazor 路由器組件簡介
Router 組件是 Blazor 中的內置組件之一,用於 Blazor 應用程序的 App 組件中。 此組件啟用 Blazor 應用程序中的路由並提供與當前導航狀態相對應的路由數據。 該組件攔截傳入的請求並呈現與請求的 URL 匹配的頁面。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
下表顯示了路由器組件的屬性。
屬性 | 說明 |
---|---|
AdditionalAssemblies | 獲取或設置一組附加程序集,應搜索這些程序集以查找可以匹配 URI 的組件。 |
AppAssembly | 獲取或設置應搜索與 URI 匹配的組件的程序集。 |
Found | 獲取或設置在為請求的路由找到匹配項時顯示的內容。 |
Navigating | 獲取或設置異步導航進行時要顯示的內容。 |
NotFound | 獲取或設置當沒有找到所請求路由的匹配項時顯示的內容。 |
OnNavigateAsync | 獲取或設置在導航到新頁面之前應調用的處理程序。 |
編譯 Blazor 組件 (.razor) 時,它們生成的 C# 類保存在 obj\Debug\net5.0\Razor\Pages 文件夾中
如果您打開任何已編譯的文件,您會注意到在編譯后,所有帶有 @page 指令的組件都生成了一個帶有 RouteAttribute 屬性的類。
應用程序啟動時,會掃描 AppAssembly 屬性指定的程序集,以從所有指定了 RouteAttribute 的類中收集路由信息。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
如果您創建了單獨的組件類庫,並且希望應用程序從這些程序集中掃描和加載路由,那么您可以使用 AdditionalAssemblies 屬性來接受程序集對象的集合。
下面是從組件類庫中定義的兩個可路由組件 Component1 和 Component2 加載路由信息的示例。
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"
AdditionalAssemblies="new[] { typeof(Component1).Assembly, typeof(Component2).Assembly }">
</Router>
在運行時,RouteView 組件從路由器接收 RouteData 以及任何路由參數,並使用組件中定義的布局呈現指定的組件。 如果未定義布局,則使用 DefaultLayout 屬性指定的布局。 默認布局通常是共享文件夾中可用的 MainLayout 組件,但您也可以創建和指定自定義布局。
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
Found 模板用於顯示找到匹配路由的內容,如下例所示,其中找到匹配路由並在瀏覽器中呈現計數器頁面。
NotFound 模板用於在沒有找到匹配的路由時顯示內容。 默認情況下,NotFound 模板僅顯示一條消息,如下面的屏幕截圖所示。
我們還可以創建自定義錯誤布局和頁面並顯示自定義錯誤頁面。 讓我們在 Shared 文件夾中創建一個名為 ErrorLayout.razor 的新自定義布局。
ErrorLayout.razor
@inherits LayoutComponentBase
<main role="main" class="container">
<div class="text-center">
@Body
</div>
</main>
然后把LayoutView組件的Layout屬性改成ErrorLayout,把LayoutView里面的內容改成如下
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(ErrorLayout)">
<h1 class="display-1">404</h1>
<h1 class="display-4">Not Found</h1>
<p class="lead">
Oops! Looks like this page doesn't exist.
</p>
</LayoutView>
</NotFound>
</Router>
如果您將在瀏覽器中運行該應用程序並嘗試訪問未在應用程序中任何位置指定的 URL,那么您將看到一個自定義 404 錯誤頁面,如下所示。
所有 Blazor 應用程序都應將 PreferExactMatches 屬性顯式設置為 @true,以便路由匹配更喜歡精確匹配而不是通配符。 根據 Microsoft 官方文檔,從 .NET 6 開始,此屬性將不可用,並且路由器將始終更喜歡精確匹配。
定義路由、參數和約束
在我們學習如何為 Blazor 組件定義路由之前,我們需要確保我們在每個頁面上都有以下可用的基本標記來正確解析 URL。 如果您正在創建 Blazor 服務器應用程序,則可以在 Pages/_Host.cshtml 文件的 head 部分添加此標記,對於 Blazor WebAssembly 應用程序,可以在 wwwroot/index.html 文件中添加此標記。
<base href="~/" />
要定義路由,我們可以使用 @page 指令,如下面的 Counter 組件示例所示。
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
我們現在可以使用 /counter URL 訪問計數器組件。
我們還可以使用多個@page 指令定義多個路由模板,如下例所示。
@page "/counter"
@page "/mycounter"
這意味着現在也可以使用 /mycounter URL 訪問相同的 Counter 組件:
使用路由參數和 Blazor 路由模板支持參數將數據從一個頁面傳遞到另一個頁面是非常常見的做法。 路由參數名稱不區分大小寫,一旦我們定義了路由參數,路由器就會自動填充具有相同名稱的相應組件屬性。 例如,在下面的代碼片段中,我們定義了一個路由參數 title 並在組件中創建了一個相應的屬性 Title。 此屬性將自動填充路由參數文本的值。 然后,我們將 Title 屬性顯示為 h1 元素內的頁面標題。
@page "/counter/{title}"
<h1>@Title</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public string Title { get; set; }
private void IncrementCount()
{
currentCount++;
}
}
運行應用程序並嘗試在地址欄中指定 /counter/ 之后的任何字符串,您將看到路由參數值顯示為頁面標題。
我們還可以定義可選的路由參數,如下例所示,其中標題是可選參數,因為它在參數名稱后面有問號 (?)。 如果我們不提供此路由參數的值,則該參數將在 OnInitialized 方法中使用 Counter 的默認值進行初始化。
@page "/counter/{title?}"
<h1>@Title</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public string Title { get; set; }
protected override void OnInitialized()
{
Title = Title ?? "Counter";
}
private void IncrementCount()
{
currentCount++;
}
}
Blazor 還支持在路由上強制執行類型匹配的路由約束。 在下面的代碼片段中,我創建了一個以 int 類型開頭的路由參數,這意味着現在我只能為此路由參數提供整數值。 計數器現在將從路由參數中指定的值開始。
@page "/counter/{start:int}"
<h1>Counter</h1>
<p>Current count: @Start</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int Start { get; set; }
private void IncrementCount()
{
Start++;
}
}
在瀏覽器中運行應用程序並在 URL 中指定任何整數值,例如 /counter/4,您將看到計數器將從該起始值開始遞增。
下表顯示了 Blazor 路由約束支持的類型。
Constraint | Example | Example Matches |
---|---|---|
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 |
也可以定義多個路由參數,如下例所示,我們將 start 和 increment 定義為 int 類型參數。
@page "/counter/{start:int}/{increment:int}"
<h1>Counter</h1>
<p>Current count: @Start</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[Parameter]
public int Start { get; set; }
[Parameter]
public int Increment { get; set; }
private void IncrementCount()
{
Start+=Increment;
}
}
運行應用程序並在地址 URL 中指定起始值和增量值,如下所示,您會注意到計數器不僅以值 2 開始,而且每次單擊 [外鏈圖片轉存中...(img-Ax5NtNoK-1626849711493)]
按鈕時它也會增加 3。
運行應用程序並在地址 URL 中指定起始值和增量值,如下所示,您會注意到計數器不僅以值 2 開始,而且每次單擊 Click me 按鈕時它也會增加 3。
Blazor NavigationManager 服務概述
NavigationManager 服務允許我們在 C# 代碼中管理 URI 和導航。 NavigationManager 類具有以下通用屬性、方法和事件。
導航 | 類型 | 描述 |
---|---|---|
BaseUri | 屬性 | 獲取或設置當前的基 URI。 BaseUri 始終表示為帶有尾部斜杠的字符串形式的絕對 URI。 通常,這對應於文檔 元素上的“href”屬性。 |
Uri | 屬性 | 獲取或設置當前 URI。 Uri 始終表示為字符串形式的絕對 URI。 |
NavigateTo | 方法 | 導航到指定的 URI。 |
ToAbsoluteUri | 方法 | 將相對 URI 轉換為絕對 URI。 |
ToBaseRelativePath | 方法 | 給定一個基本 URI(例如,先前由 BaseUri 返回的 URI),將絕對 URI 轉換為相對於基本 URI 前綴的 URI。 |
LocationChanged | 事件 | 當導航位置改變時觸發的事件。 |
讓我們創建一個頁面來查看上面的一些屬性和方法。 創建一個新的 Blazor 組件並使用 @inject 指令注入 NavigationManager 服務。 嘗試在頁面上打印 Uri 和 BaseUri 屬性以查看它們返回的 URI 類型。
@page "/navigationmanager"
@inject NavigationManager nvm
<h3>Navigation Manager</h3>
<br />
<p>@nvm.Uri</p>
<p>@nvm.BaseUri</p>
運行該應用程序,您將在瀏覽器中看到類似於以下內容的輸出。 Uri 屬性將顯示頁面的當前絕對 URI,而 BaseUri 屬性將顯示當前基本 URI。
在頁面上添加兩個按鈕 Home Page 和 Counter Page 並在@code 塊中添加它們的 onclick 事件處理程序方法。 在事件處理程序方法中,我們可以使用 NavigateTo 方法將用戶從 C# 代碼重定向到不同的 Blazor 組件。
@page "/navigationmanager"
@inject NavigationManager nvm
<h3>Navigation Manager</h3>
<br />
<p>@nvm.Uri</p>
<p>@nvm.BaseUri</p>
<button class="btn btn-primary" @onclick="GoToHome">
Home Page
</button>
<button class="btn btn-primary" @onclick="GoToCounter">
Counter Page
</button>
@code {
private void GoToHome()
{
nvm.NavigateTo("/");
}
private void GoToCounter()
{
nvm.NavigateTo("counter");
}
}
運行應用程序並嘗試同時單擊兩個按鈕,您將能夠按預期導航到主頁和計數器頁面。
如果不想以編程方式處理導航並想在 HTML 中生成超鏈接,則可以使用 Blazor NavLink 組件。 NavLink 組件類似於 HTML <a> 元素,具有一些很酷的功能。 如果 href 屬性值與當前 URL 匹配,它會自動切換活動類與元素。 這允許我們在當前選擇的鏈接上應用不同的樣式。 你可以在 Shared/NavMenu.razor 文件中看到這個組件的用法
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
</ul>
</div>
該組件還有一個 Match 屬性,可以設置為以下之一:
- NavLinkMatch.All:當它匹配整個當前 URL 時,NavLink 處於活動狀態。
- NavLinkMatch.Prefix(默認):NavLink 在匹配當前 URL 的任何前綴時處於活動狀態。
總結概括
在本文章中,我嘗試介紹 Blazor 應用程序中可用的許多路由功能,還介紹了開發人員可用的不同路由相關組件和服務。 我希望您現在能夠更有信心地定義路由、參數和約束。 如果您喜歡本教程,請與他人分享以傳播知識。