注意
這不是這篇文章的最新版本。 關於目前版本,請參閱 本文的 .NET 10 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 關於目前版本,請參閱 本文的 .NET 10 版本。
本文說明 Blazor 應用程式請求路由,並指導靜態路由與互動路由、ASP.NET 核心端點路由整合、導航事件,以及元件的 Razor 路由模板與約束。
在 Blazor 中實現路由功能的方法是,使用 @page 指示詞向應用程式中的每個可存取元件提供路由範本。 在編譯具有 Razor 指示詞的 @page 檔案時,所產生的類別會獲得 RouteAttribute 以指定路由範本。 在執行階段,路由器會搜尋具有 RouteAttribute 的元件類別,並轉譯其路由範本符合所要求 URL 的任何元件。
下列 HelloWorld 元件會使用 /hello-world 的路由範本,且元件的轉譯網頁會到達相對 URL /hello-world。
HelloWorld.razor:
@page "/hello-world"
<h1>Hello World!</h1>
不管你是否將該元件加到應用程式的 UI 導覽中作為連結,前一個元件都會在瀏覽器 /hello-world 中載入。
靜態與互動式路由
本章節適用於Blazor Web App。
如果已啟用預先轉譯,Blazor 路由器 (Router 元件,<Router> 中的 Routes.razor) 會在靜態伺服器端轉譯 (靜態 SSR) 期間執行靜態路由至元件。 這種類型的路由稱為 靜態路由。
當互動式渲染模式指派給 Routes 元件時,當伺服器上的靜態 SSR 配合靜態路由完成後,Blazor 路由器會變成互動式。 這種類型的路由稱為 互動式路由。
靜態路由器會使用端點路由和 HTTP 要求路徑來判斷要轉譯的元件。 當路由器變成互動式時,它會使用文件的 URL (瀏覽器網址列中的 URL) 來判斷要轉譯的元件。 這表示,如果文件的 URL 動態變更為另一個有效的內部 URL,互動式路由器可以動態變更轉譯的元件,而且不需要執行 HTTP 要求來擷取新的頁面內容,就可以這麼做。
互動式路由也會防止預先轉譯,因為新的頁面內容不會透過一般頁面的請求從伺服器獲取。 如需詳細資訊,請參閱 ASP.NET Core Blazor 預先呈現的狀態持續性。
ASP.NET Core 端點路由整合
本章節適用於在電路上運作的 Blazor Web App。
Blazor Web App 已整合到 ASP.NET Core 端點路由。 ASP.NET Core 應用程式會在 Program 檔案中使用 MapRazorComponents 來設定可路由元件的端點,並渲染這些端點的根元件。 預設根元件 (第一個載入的元件) 為 App 元件 (App.razor):
app.MapRazorComponents<App>();
本節適用於 Blazor Server 在電路上運作的應用程式。
Blazor Server 已整合到 ASP.NET Core 端點路由。 ASP.NET Core 應用程式已經過設定,以接受 MapBlazorHub 檔案中的 Program 互動元件的連入連線:
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
本節適用於 Blazor Server 在電路上運作的應用程式。
Blazor Server 已整合到 ASP.NET Core 端點路由。 ASP.NET Core 應用程式已設置為接受 MapBlazorHub 中 Startup.Configure 互動元件的傳入連線。
一般設定是將所有要求路由至 Razor 頁面,其會做為 Blazor Server 應用程式伺服器端部分的主機。 依照慣例,主機頁面通常會在應用程式的 _Host.cshtml 資料夾中名為 Pages。
主機檔案中指定的路由稱為後援路由,因為它在路由比對會以低優先順序運作。 當其他路由不符時,會使用後援路由。 這可讓應用程式使用其他控制器和頁面,而不會干擾 Blazor Server 應用程式中的元件路由。
如需有關設定非根 URL 伺服器裝載的資訊 MapFallbackToPage ,請參閱 ASP.NET Core Blazor 應用程式基底路徑。
路由範本
Router 元件可讓您路由至 Razor 元件,且位於應用程式的 Routes 元件 (Components/Routes.razor) 中。
當帶有Razor指令的.razor@page元件()被編譯時,生成的元件類別會提供一個RouteAttribute,以指定該元件的路由範本。
當應用程式啟動時,會掃描指定為路由器 AppAssembly 的組件,以收集具有 RouteAttribute 的應用程式元件的路由資訊。
在執行階段,RouteView 元件:
選擇性地為未使用 DefaultLayout指定配置的元件,提供具有配置類別的 @layout 參數。 架構 Blazor 的專案範本會將 MainLayout 元件 (MainLayout.razor) 指定為應用程式的預設配置。 如需配置的詳細資訊,請參閱 ASP.NET Core Blazor 配置。
元件支援使用多個@page指示的多個路由範本。 下列範例元件會在請求 /blazor-route 和 /different-blazor-route 時載入。
BlazorRoute.razor:
@page "/blazor-route"
@page "/different-blazor-route"
<h1>Routing Example</h1>
<p>
This page is reached at either <code>/blazor-route</code> or
<code>/different-blazor-route</code>.
</p>
重要
若要讓 URL 正確解析,應用程式必須包含 <base> 標記 (<head> 內容的位置) 與在 href 屬性中指定的應用程式基底路徑。 如需詳細資訊,請參閱 ASP.NET Core Blazor 應用程式基底路徑。
除了使用 @page 指示詞將路由範本指定為字串常值,還可以使用 @attribute 指示詞來指定以常數為基礎的路由範本。
在下列範例中,元件中的 @page 指示詞會取代為 @attribute 指示詞,而 Constants.CounterRoute 中的常數型路由範本,其會在應用程式中的其他位置設定為 "/counter":
- @page "/counter"
+ @attribute [Route(Constants.CounterRoute)]
注意
針對 .NET 5.0.1 和任何其他 5.x 版本,Router 元件中包含的一個參數為 PreferExactMatches,此參數已設定為 @true。 如需詳細資訊,請參閱 從 ASP.NET Core 3.1 移轉至 .NET 5。
將某個元素的焦點設置在導覽上
FocusOnNavigate 元件會在從某個頁面導覽到另一個頁面之後,根據 CSS 選取器,將 UI 焦點設定為元素。
<FocusOnNavigate RouteData="routeData" Selector="h1" />
當 Router 元件導覽至新頁面,FocusOnNavigate 元件會將焦點設定為頁面的最上層標頭 (<h1>)。 這是確保使用螢幕助讀程式時宣告頁面導覽的常見策略。
找不到內容時提供自訂內容
對於找不到內容的請求,可以將 Razor 元件指派給 Router 元件的 NotFoundPage 參數。 該參數與 NavigationManager.NotFound開發者程式碼中呼叫的方法 協同運作,該方法會觸發「未找到」回應。
NavigationManager.NotFound 詳見下一篇文章《 ASP.NET 核心 Blazor 導航》。
Blazor專案範本包含一個NotFound.razor頁面。 當 NavigationManager.NotFound 被呼叫時,自動渲染此頁面,使得遺失的路由能以一致的使用者體驗處理。
NotFound.razor:
@page "/not-found"
@layout MainLayout
<h3>Not Found</h3>
<p>Sorry, the content you are looking for does not exist.</p>
元件 NotFound 會被分配給路由器的 NotFoundPage 參數。
NotFoundPage 支援跨狀態代碼頁重新執行中間件使用的路由,包括非Blazor 中間件。
在下列範例中,上述 NotFound 元件會出現在應用程式的 Pages 資料夾中,並傳遞至 NotFoundPage 參數:
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
欲了解更多資訊,請參閱下一篇關於 核心 Blazor 導航 ASP.NET 文章。
如果找不到要求的路由內容,Router 元件可讓應用程式指定自訂內容。
在 Router 元件的 NotFound 參數中設定自訂內容:
<Router ...>
...
<NotFound>
...
</NotFound>
</Router>
支援任意項目做為 NotFound 參數的內容,例如其他互動式元件。 若要將預設配置套用至 NotFound 內容,請參閱 ASP.NET Core Blazor 配置。
Blazor Web App不使用 NotFound 參數 (<NotFound>...</NotFound> 標記),但支援參數†以便在 .NET 8/9 中向後相容,以避免架構中的重大變更。 伺服器端 ASP.NET Core 中介軟體管線會處理伺服器上的要求。 使用伺服器端技術來處理錯誤的請求。
†在此内容中支持 表示放置 <NotFound>...</NotFound> 標記不會產生例外狀況,但使用標記也無效。
如需詳細資訊,請參閱下列資源:
從多個總成導向至元件
本章節適用於Blazor Web App。
使用 Router 元件的 AdditionalAssemblies 參數和端點慣例建立器 AddAdditionalAssemblies 來探索其他組件中的可路由元件。 下列小節說明每個 API 的使用時機和方式。
靜態路由
若要從其他組件中發現可路由的元件以進行靜態伺服器端渲染(static SSR),即使路由器日後會變成互動式用於互動式渲染,這些組件必須向 Blazor 架構公開。 在伺服器專案的 AddAdditionalAssemblies 檔案中,使用鏈結至 MapRazorComponents 的其他組件呼叫 Program 方法。
下列範例會使用專案的 BlazorSample.Client 檔案,在 _Imports.razor 專案的組件中包含可路由的元件:
app.MapRazorComponents<App>()
.AddAdditionalAssemblies(typeof(BlazorSample.Client._Imports).Assembly);
注意
上述指引也適用於 元件類別庫 案例。 如需類別庫和靜態 SSR 的其他重要指導,請參閱 具有靜態伺服器端轉譯 (static SSR) 的 ASP.NET CoreRazor 類別庫 (RCL)。
互動式路由
互動式渲染模式可以指派給 Routes 元件 (Routes.razor),這使得 Blazor 路由器在伺服器上進行靜態 SSR 和靜態路由之後變為互動式。 例如,<Routes @rendermode="InteractiveServer" /> 將互動式伺服器端轉譯 (互動式 SSR) 指派給 Routes 元件。
Router 元件會從 Routes 元件繼承互動式伺服器端轉譯 (互動式 SSR)。 路由器在伺服器上完成靜態路由設定後會進入互動模式。
互動式路由的內部瀏覽並不涉及向伺服器要求新的頁面內容。 因此,內部頁面請求不會發生預渲染。 如需詳細資訊,請參閱 ASP.NET Core Blazor 預先呈現的狀態持續性。
如果伺服器專案中定義了 Routes 元件,AdditionalAssemblies 元件的 Router 參數應該包含 .Client 專案的組件。 這可讓路由器在以互動方式轉譯時正確運作。
在以下範例中,Routes 元件位於伺服器專案中,而 _Imports.razor 專案的 BlazorSample.Client 檔案會指出要搜尋可路由元件的組件:
<Router
AppAssembly="..."
AdditionalAssemblies="[ typeof(BlazorSample.Client._Imports).Assembly ]">
...
</Router>
除了指定給 AppAssembly 的組件之外,會掃描其他組件。
注意
上述指引也適用於 元件類別庫 案例。
或者,可路由的元件只存在於套用全域互動式 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}"
<h1>Route Parameter Example 1</h1>
<p>Blazor is @Text!</p>
@code {
[Parameter]
public string? Text { get; set; }
}
支援選用的參數。 在下列範例中,text 選擇性參數會將路由區段的值指派給元件的 Text 屬性。 如果區段不存在,則 Text 的值會設定為 fantastic。
不支援選用的參數。 在下列範例中,會套用兩個 @page 指令。 第一道指令允許導航到沒有參數的元件。 第二個指示詞會將 {text} 路由參數值指派給元件的 Text 屬性。
RouteParameter2.razor:
@page "/route-parameter-2/{text?}"
<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";
}
當使用 OnInitialized{Async} 生命週期方法 而非 OnParametersSet{Async} 生命週期方法 時,如果使用者在相同的元件中瀏覽,就不會發生將 Text 屬性預設指派給 fantastic 的情況。 例如,當使用者從 /route-parameter-2/amazing 瀏覽至 /route-parameter-2 時,就會發生這種情況。 當元件實例持續存在並接受新參數時,不會再度叫用 OnInitialized 方法。
注意
路由參數無法與查詢字串值一同運作。 要處理查詢字串,請參見「查詢字串」。
路徑限制條件
路由約束會強制執行在路由區段和元件之間的型別匹配。
在下列範例中,與 User 元件的路由只會在下列情況下比對:
- 要求 URL 中有
Id路由區段。 -
Id區段是整數 (int) 型別。
User.razor:
@page "/user/{Id:int}"
<h1>User Example</h1>
<p>User Id: @Id</p>
@code {
[Parameter]
public int Id { get; set; }
}
注意
路由條件約束不適用查詢字串值。 要處理查詢字串,請參見「查詢字串」。
下表所示的路由限制可供使用。 如需查看符合不因文化而變的路由條件限制的資訊,請參閱表格下方的警告以獲得更多詳細資訊。
| 限制 | 範例 | 範例匹配項目 | 不變的 文化特性 (Culture) 比對 |
|---|---|---|---|
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} |
00001111-aaaa-2222-bbbb-3333cccc4444、{00001111-aaaa-2222-bbbb-3333cccc4444} |
否 |
int |
{id:int} |
123456789、-123456789 |
是 |
long |
{ticks:long} |
123456789、-123456789 |
是 |
nonfile |
{parameter:nonfile} |
不是 BlazorSample.styles.css,不是 favicon.ico |
是 |
警告
用來確認 URL 並轉換成 CLR 類型(例如 int 或 DateTime)的路由限制一律使用不變文化。 這些條件假設 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; }
}
避免在路由參數中擷取檔案
下列路由範本無意中在其選擇性路由參數 (Optional) 中擷取靜態資產路徑。 例如,擷取應用程式的樣式表單 (.styles.css) 會破壞應用程式的樣式:
@page "/{optional?}"
...
@code {
[Parameter]
public string? Optional { get; set; }
}
若要將路由參數限制為擷取非檔案路徑,請在路由範本中使用 :nonfile 限制式 :
@page "/{optional:nonfile?}"
處理包含點的 URL 的路由
伺服器端預設路由範本會假設,如果要求 URL 的最後一個區段包含點 (.),則是要求檔案。 例如,相對 URL /example/some.thing 會由路由器解譯為要求名為 some.thing 的檔案。 若未進行額外的設定,如果 是要路由至具有 some.thing 指示詞的元件,且 @page 為路由參數值,則應用程式會傳回 some.thing回應。 若要使用含有點的一個或多個參數的路由,應用程式必須使用自訂範本來設定該路由。
考慮下列 Example 元件,其可以從 URL 的最後一個區段接收路由參數。
Example.razor:
@page "/example/{param?}"
<p>
Param: @Param
</p>
@code {
[Parameter]
public string? Param { get; set; }
}
在 Server 檔案中新增一個包含選用參數的後援檔案路由範本,以允許裝載 Blazor WebAssembly 解決方案的 param 應用程式在路由 Program 參數中處理帶有點的要求。
app.MapFallbackToFile("/example/{param?}", "index.html");
若要設定 Blazor Server 應用程式,以便在 param 檔案中使用包含點的 Program 路由參數來傳送請求,請新增具有選用參數的後援頁面路由範本:
app.MapFallbackToPage("/example/{param?}", "/_Host");
如需詳細資訊,請參閱 ASP.NET Core 中的路線規劃。
若要允許在託管的ServerBlazor WebAssembly中,應用程式對包含點的param路由參數的請求進行路由,請在Startup.Configure中新增具備選用參數的後援檔案路由範本。
Startup.cs:
endpoints.MapFallbackToFile("/example/{param?}", "index.html");
若要配置 Blazor Server 應用程式以處理路由參數 param 中帶有點的請求,請在 Startup.Configure 中新增包含選用參數的後援頁面路由範本。
Startup.cs:
endpoints.MapFallbackToPage("/example/{param?}", "/_Host");
如需詳細資訊,請參閱 ASP.NET Core 中的路線規劃。
全匹配路由參數
組件中支援擷取跨多個資料夾界限路徑的 Catch-all 路由參數。
萬用路由參數為:
- 命名以符合路由段名稱。 名稱不區分大小寫。
- 一個
string型別。 框架不提供自動轉換。 - 在 URL 的結尾。
CatchAll.razor:
@page "/catch-all/{*pageRoute}"
<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; }
}
對於具有 /catch-all/this/is/a/test 路由範本的 URL /catch-all/{*pageRoute},PageRoute 的值會設定為 this/is/a/test。
擷取路徑的斜線和區段會經過解碼。 針對 /catch-all/{*pageRoute} 的路由範本,URL /catch-all/this/is/a%2Ftest%2A 會產生 this/is/a/test*。
使用 OnNavigateAsync 處理非同步導覽事件
Router 元件支援 OnNavigateAsync 功能。 當使用者執行下列動作時會叫用 OnNavigateAsync 處理常式:
- 第一次通過瀏覽器直接訪問路徑。
- 透過連結或 NavigationManager.NavigateTo 呼叫來導航到新路由,此呼叫用於開發者程式碼中,用來導向一個 URI。 [NavigationManager API 將在下一篇文章《 ASP.NET 核心 Blazor 導航》中說明。]
<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 會執行兩次:
- 當要求的端點元件最初靜態渲染時發生一次。
- 第二次是在瀏覽器呈現端點元件時。
若要防止 OnNavigateAsync 中的開發者程式碼被執行兩次,Routes 元件可以儲存 NavigationContext 以便在 OnAfterRender{Async} 生命週期方法中使用,其中可以檢查 firstRender 。 如需詳細資訊,請參閱 使用 JavaScript Interop 預先轉譯一節。
若要防止 OnNavigateAsync 中的開發人員程式碼執行兩次,App 元件可以將 NavigationContext 儲存在 OnAfterRender{Async} 中供使用,在其中可以檢查 firstRender。 如需詳細資訊,請參閱 使用 JavaScript Interop 預先轉譯一節。
在 OnNavigateAsync 中管理取消
傳遞至回撥 NavigationContext 的 OnNavigateAsync 物件中,包含一個在發生新導覽事件時設定的 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 中的取消權杖但未擲回,可能會導致非預期的行為,例如轉譯先前導覽的元件。
使用者與 <Navigating> 內容的互動
如果在瀏覽期間發生重大延遲,例如Blazor WebAssembly應用程式中延遲載入組件或 Blazor 伺服器端應用程式的網路連線速度緩慢時,Router 元件可以向使用者指出目前正在進行頁面轉換。
在指定 Router 元件的組件頂端,為 @using 命名空間新增 Microsoft.AspNetCore.Components.Routing 指示:
@using Microsoft.AspNetCore.Components.Routing
提供內容給 Navigating 參數,以在頁面轉換事件期間顯示。
在路由器元素內容中 (<Router>...</Router>):
<Navigating>
<p>Loading the requested page…</p>
</Navigating>
如需使用 Navigating 屬性的範例,請參閱 ASP.NET Core Blazor WebAssembly 中的延遲載入組件。