.NET 10 中的新增功能系列文章2——ASP.NET Core 中的新增功能
引言
隨著技術(shù)的不斷演進(jìn),.NET 平臺(tái)持續(xù)為開(kāi)發(fā)者帶來(lái)創(chuàng)新和改進(jìn)。作為 .NET 生態(tài)系統(tǒng)中的重要組成部分,ASP.NET Core 在每個(gè)版本中都引入了令人興奮的新功能和優(yōu)化。本文將深入探討 .NET 10 中 ASP.NET Core 的主要更新,特別是 Blazor 框架中的各項(xiàng)增強(qiáng)功能,旨在為開(kāi)發(fā)者提供一個(gè)清晰、全面的概述,幫助您更好地利用這些新特性來(lái)構(gòu)建更強(qiáng)大、更安全的 Web 應(yīng)用程序。我們將重點(diǎn)關(guān)注安全性、性能、開(kāi)發(fā)體驗(yàn)和路由方面的改進(jìn),并提供相關(guān)的代碼示例和詳細(xì)說(shuō)明。
正文
Blazor Web App 安全性增強(qiáng)
在 .NET 10 中,Blazor Web App 的安全性得到了顯著提升。官方新增并更新了多個(gè)安全示例,涵蓋了不同的身份驗(yàn)證和授權(quán)場(chǎng)景。
1. 新的和經(jīng)過(guò)更新的 Blazor Web App 安全示例
Blazor Web App 的安全示例得到了全面的更新,主要包括以下幾個(gè)方面:
- OpenID Connect (OIDC) 保護(hù):提供了如何使用 OIDC 保護(hù) Blazor Web App 的詳細(xì)指南和示例。
- Microsoft Entra ID (原 Azure AD) 保護(hù):更新了使用 Microsoft Entra ID 保護(hù) Blazor Web App 的示例。
- Windows 身份驗(yàn)證保護(hù):新增了使用 Windows 身份驗(yàn)證保護(hù) Blazor Web App 的示例。
所有 OIDC 和 Entra 示例解決方案現(xiàn)在都包含一個(gè)單獨(dú)的 Web API 項(xiàng)目 (MinimalApiJwt),用于演示如何安全地配置和調(diào)用外部 Web API。 調(diào)用 Web API 的方式通過(guò)使用令牌處理程序和具名 HTTP 客戶端,來(lái)對(duì)接 OIDC 身份提供程序,或使用 Microsoft Entra ID 的 Microsoft Identity Web 包/API。
這些示例解決方案在 Program 文件中的 C# 代碼中進(jìn)行配置。此外,還提供了從應(yīng)用程序設(shè)置文件(例如 appsettings.json)配置解決方案的新指南,位于 OIDC 或 Entra 文章的“通過(guò) JSON 配置提供程序(應(yīng)用設(shè)置)提供配置”部分。
Entra 文章和示例應(yīng)用還包含了關(guān)于以下方法的新的指導(dǎo):
- 如何對(duì) Web 場(chǎng)托管方案使用加密的分布式令牌緩存。
- 如何將 Azure Key Vault 與 Azure 托管標(biāo)識(shí) 配合使用來(lái)保護(hù)數(shù)據(jù)。
Blazor UI 組件和性能優(yōu)化
1. QuickGrid RowClass 參數(shù)
為了提供更靈活的 UI 樣式控制,QuickGrid 組件新增了 RowClass 參數(shù)。 開(kāi)發(fā)者現(xiàn)在可以根據(jù)行項(xiàng)的特定條件,動(dòng)態(tài)地將樣式表類應(yīng)用于網(wǎng)格的行。 例如,通過(guò)定義一個(gè)方法,根據(jù) MyGridItem 的 IsArchived 屬性來(lái)應(yīng)用不同的 CSS 類:
<QuickGrid ... RowClass="GetRowCssClass">
...
</QuickGrid>
@code {
private string GetRowCssClass(MyGridItem item) =>
item.IsArchived ? "row-archived" : null;
}
這極大地增強(qiáng)了 QuickGrid 的定制能力,使得數(shù)據(jù)呈現(xiàn)更加直觀和富有表現(xiàn)力。
2. 關(guān)閉 QuickGrid 列選項(xiàng)
現(xiàn)在,可以使用 QuickGrid 的新方法 HideColumnOptionsAsync 關(guān)閉列選項(xiàng)的用戶界面。 這對(duì)于需要在用戶執(zhí)行特定操作(如應(yīng)用篩選器)后自動(dòng)關(guān)閉列選項(xiàng)的場(chǎng)景非常有用,從而提升用戶體驗(yàn)。
<QuickGrid @ref="movieGrid" Items="movies">
<PropertyColumn Property="@(m => m.Title)" Title="Title">
<ColumnOptions>
<input type="search" @bind="titleFilter" placeholder="Filter by title"
@bind:after="@(() => movieGrid.HideColumnOptionsAsync())" />
</ColumnOptions>
</PropertyColumn>
<PropertyColumn Property="@(m => m.Genre)" Title="Genre" />
<PropertyColumn Property="@(m => m.ReleaseYear)" Title="Release Year" />
</QuickGrid>
@code {
private QuickGrid<Movie>? movieGrid;
private string titleFilter = string.Empty;
private IQueryable<Movie> movies = new List<Movie> { ... }.AsQueryable();
private IQueryable<Movie> filteredMovies =>
movies.Where(m => m.Title!.Contains(titleFilter));
}
3. 響應(yīng)流式處理的默認(rèn)啟用和選擇停用
在 .NET 10 之前,HttpClient 請(qǐng)求的響應(yīng)流式處理是可選啟用的,現(xiàn)在默認(rèn)啟用。 這意味著調(diào)用 HttpContent.ReadAsStreamAsync 對(duì)于 HttpResponseMessage.Content(response.Content.ReadAsStreamAsync())返回的是 BrowserHttpReadStream,而不再返回 MemoryStream。 BrowserHttpReadStream 不支持同步操作,例如 Stream.Read(Span<Byte>)。 如果代碼使用了同步操作,開(kāi)發(fā)者可以選擇禁用響應(yīng)流式處理或手動(dòng)將 Stream 復(fù)制到 MemoryStream。
要選擇退出全局響應(yīng)流式處理,可以在項(xiàng)目文件中添加 <WasmEnableStreamingResponse>false</WasmEnableStreamingResponse> 屬性,或者將 DOTNET_WASM_ENABLE_STREAMING_RESPONSE 環(huán)境變量設(shè)置為 false 或 0。
<WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>
要選擇退出單個(gè)請(qǐng)求的響應(yīng)流式處理,可以將 HttpRequestMessage 上的 SetBrowserResponseStreamingEnabled 設(shè)置為 false:
requestMessage.SetBrowserResponseStreamingEnabled(false);
4. Blazor 腳本作為靜態(tài) Web 資產(chǎn)
為了提升性能和優(yōu)化資源加載,在 .NET 10 及更高版本中,Blazor 腳本現(xiàn)在被用作具有自動(dòng)壓縮和指紋的靜態(tài) Web 資產(chǎn),而不再?gòu)?ASP.NET Core 共享框架中的嵌入資源提供。 這有助于更好地利用瀏覽器緩存和 CDN,提高應(yīng)用的加載速度。
5. Blazor WebAssembly 性能分析和診斷計(jì)數(shù)器
新版本為 Blazor WebAssembly 應(yīng)用引入了全面的性能分析和診斷計(jì)數(shù)器。 這些計(jì)數(shù)器提供了組件生命周期、導(dǎo)航、事件處理和線路管理的詳細(xì)可觀測(cè)性,幫助開(kāi)發(fā)者識(shí)別和解決性能瓶頸。
6. 預(yù)加載的 Blazor 框架靜態(tài)資源
在 Blazor Web App 中,框架靜態(tài)資源使用 Link 頭信息自動(dòng)預(yù)加載,這允許瀏覽器在提取和呈現(xiàn)初始頁(yè)面之前預(yù)加載資源。 在獨(dú)立 Blazor WebAssembly 應(yīng)用中,框架資源會(huì)被安排為高優(yōu)先級(jí)下載,并在瀏覽器 index.html 頁(yè)面處理過(guò)程早期進(jìn)行緩存。 這種機(jī)制可以顯著縮短應(yīng)用的首次加載時(shí)間。
7. Blazor WebAssembly 在 Blazor Web App 中靜態(tài)資源預(yù)加載
為了更好地在 Blazor Web App 中預(yù)加載 WebAssembly 資產(chǎn),Blazor 將 <link> 標(biāo)頭替換為 LinkPreload 組件 (<LinkPreload />)。 這使得應(yīng)用程序的基路徑配置 (<base href="..." />) 能夠正確識(shí)別應(yīng)用程序的根目錄。 默認(rèn)情況下,Blazor Web App 模板在 .NET 10 中采用此特性。 升級(jí)到 .NET 10 的應(yīng)用可以通過(guò)在 LinkPreload 組件的頭部?jī)?nèi)容 (<base>) 中,將 App 組件放置在基 URL 標(biāo)簽 (App.razor) 后來(lái)實(shí)現(xiàn)該功能。
<head>
...
<base href="/" />
+ <LinkPreload />
...
</head>
8. 自定義 Blazor 緩存和 BlazorCacheBootResources MSBuild 屬性已刪除
由于所有 Blazor 客戶端文件現(xiàn)在都由瀏覽器進(jìn)行指紋標(biāo)記和緩存,Blazor 的自定義緩存機(jī)制和 BlazorCacheBootResources MSBuild 屬性已從框架中移除。 開(kāi)發(fā)者應(yīng)從客戶端項(xiàng)目的項(xiàng)目文件中刪除此屬性,因?yàn)樗辉儆腥魏斡绊憽?/p>
Blazor 路由和導(dǎo)航改進(jìn)
1. 路由模板要點(diǎn)
[Route] 屬性現(xiàn)在支持路由語(yǔ)法突出顯示,以幫助開(kāi)發(fā)者更好地可視化路由模板的結(jié)構(gòu),從而減少路由配置錯(cuò)誤。
2. MapsTo 不再滾動(dòng)到頂部來(lái)進(jìn)行同一頁(yè)面導(dǎo)航
以前,NavigationManager.NavigateTo 在進(jìn)行同一頁(yè)面導(dǎo)航時(shí)會(huì)滾動(dòng)到頁(yè)面頂部。 在 .NET 10 中,此行為已更改,瀏覽器在導(dǎo)航到同一頁(yè)面時(shí)不再滾動(dòng)到頁(yè)面頂部。 這意味著在更新當(dāng)前頁(yè)面的地址(例如更改查詢字符串或片段)時(shí)不再重置視口,從而提升了用戶體驗(yàn),尤其是在單頁(yè)應(yīng)用中。
3. 已將重新連接 UI 組件添加到 Blazor Web App 項(xiàng)目模板中
Blazor Web App 項(xiàng)目模板現(xiàn)在包含一個(gè) ReconnectModal 組件,它包含了并置的樣式表和 JavaScript 文件,旨在在客戶端失去與服務(wù)器的 WebSocket 連接時(shí),改進(jìn)開(kāi)發(fā)者對(duì)重新連接 UI 的控制。 該組件不會(huì)以編程方式插入樣式,確保符合 style-src 策略更嚴(yán)格的內(nèi)容安全策略 (CSP) 設(shè)置。 新的重新連接 UI 功能包括通過(guò)在重新連接 UI 元素上設(shè)置特定的 CSS 類來(lái)指示重新連接狀態(tài),以及調(diào)度新的 components-reconnect-state-changed 事件以更改重新連接狀態(tài)。 代碼可以使用 CSS 類和新事件所指示的新重新連接狀態(tài)“retrying”更好地區(qū)分重新連接過(guò)程的階段。
4. 使用 NavLinkMatch.All 時(shí)忽略查詢字符串和片段
當(dāng)使用 NavLink 參數(shù)的 NavLinkMatch.All 值時(shí),Match 組件現(xiàn)在將忽略查詢字符串和片段。 這意味著如果 URL 路徑匹配但查詢字符串或片段發(fā)生更改,則鏈接將保留 active 類。 要還原到原始行為,可以將 Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragment AppContext 開(kāi)關(guān)設(shè)置為 true。 開(kāi)發(fā)者也可以通過(guò)重寫 NavLink 的 ShouldMatch 方法來(lái)自定義匹配行為。
public class CustomNavLink : NavLink
{
protected override bool ShouldMatch(string currentUriAbsolute)
{
// Custom matching logic
}
}
5. NavigationManager.NavigateTo 不再拋出異常 NavigationException
以前,在進(jìn)行靜態(tài)服務(wù)器端渲染 (SSR) 時(shí),調(diào)用 NavigationManager.NavigateTo 會(huì)在轉(zhuǎn)換為重定向響應(yīng)前拋出 NavigationException,從而中斷執(zhí)行。 在 .NET 10 中,在靜態(tài) SSR 期間調(diào)用 NavigationManager.NavigateTo 不再引發(fā) NavigationException。 其行為與交互式呈現(xiàn)一致,執(zhí)行導(dǎo)航但不會(huì)拋出異常。 依賴于 NavigationException 被拋出的代碼應(yīng)進(jìn)行更新。 例如,在默認(rèn) Blazor Identity UI 中,IdentityRedirectManager 過(guò)去在調(diào)用 RedirectTo 之后拋出一個(gè) InvalidOperationException,以確保它不會(huì)在交互式呈現(xiàn)期間被調(diào)用。 現(xiàn)在應(yīng)刪除此異常和 [DoesNotReturn] 屬性。
6. Blazor 路由器具有參數(shù) NotFoundPage
Blazor 現(xiàn)在提供了一種改進(jìn)的方法,用于在導(dǎo)航到不存在的頁(yè)面時(shí)顯示“找不到”頁(yè)面。 可以通過(guò)使用 NotFoundPage 參數(shù)將頁(yè)面類型傳遞給組件 Router 來(lái)指定在調(diào)用 NavigationManager.NotFound 時(shí)要呈現(xiàn)的頁(yè)面。 推薦使用此方式替代 NotFound 的呈現(xiàn)片段 (<NotFound>...</NotFound>),因?yàn)樗С致酚伞⑦m配狀態(tài)碼頁(yè)面重執(zhí)行中間件,并兼容非 Blazor 場(chǎng)景。 如果同時(shí)定義了 NotFound 呈現(xiàn)片段和 NotFoundPage,則以 NotFoundPage 指定的頁(yè)面優(yōu)先。
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>This content is ignored because NotFoundPage is defined.</NotFound>
</Router>
項(xiàng)目 Blazor 模板現(xiàn)在默認(rèn)包含一個(gè) NotFound.razor 頁(yè)面。 每當(dāng)在應(yīng)用中調(diào)用 NavigationManager.NotFound 時(shí),此頁(yè)面都會(huì)自動(dòng)呈現(xiàn),從而更輕松地處理缺失路由,并提供一致的用戶體驗(yàn)。
7. 使用 NavigationManager 處理靜態(tài) SSR 和全局交互式呈現(xiàn)中的“未找到”響應(yīng)
NavigationManager 現(xiàn)在包含一個(gè) NotFound 方法,用于處理在靜態(tài)服務(wù)器端渲染(靜態(tài) SSR)或全局交互渲染期間找不到請(qǐng)求資源的情況。
- 靜態(tài)服務(wù)器端呈現(xiàn) (SSR):調(diào)用
NotFound會(huì)將 HTTP 狀態(tài)代碼設(shè)置為 404。 - 交互式呈現(xiàn):通知 Blazor 路由器 (
Router組件) 呈現(xiàn)“未找到”內(nèi)容。 - 流式呈現(xiàn):如果增強(qiáng)導(dǎo)航處于活動(dòng)狀態(tài),流式呈現(xiàn)會(huì)呈現(xiàn)“未找到”內(nèi)容,而無(wú)需重新加載頁(yè)面。 當(dāng)增強(qiáng)的導(dǎo)航被阻止時(shí),框架會(huì)重定向到“找不到”內(nèi)容,并刷新頁(yè)面。
流式 NavigationManager.NotFound 內(nèi)容呈現(xiàn)使用以下順序:
NotFoundPage傳遞給Router組件(如果存在)。- 已配置的狀態(tài)碼頁(yè)面重執(zhí)行中間件頁(yè)面。
- 如果上述兩種方法均未采用,則不采取任何操作。
非流式 NavigationManager.NotFound 內(nèi)容呈現(xiàn)使用以下順序:
NotFoundPage傳遞給Router組件(如果存在)。- 若存在“未找到”呈現(xiàn)片段內(nèi)容,則使用該內(nèi)容。不建議在 .NET 10 或更高版本中使用。
DefaultNotFound404 內(nèi)容(“Not found”純文本)。
UseStatusCodePagesWithReExecute 在處理瀏覽器地址路由問(wèn)題(如 URL 輸入錯(cuò)誤或點(diǎn)擊無(wú)效鏈接)時(shí)優(yōu)先。 當(dāng) NavigationManager.OnNotFound 被調(diào)用時(shí),可以使用 NotFound 事件進(jìn)行通知。
Blazor 開(kāi)發(fā)體驗(yàn)和互操作性
1. 用于保存組件和服務(wù)狀態(tài)的聲明性模型
現(xiàn)在可以通過(guò)聲明方式指定狀態(tài),使其能夠通過(guò) [SupplyParameterFromPersistentComponentState] 特性從組件和服務(wù)中持久化。 在預(yù)呈現(xiàn)期間,具有此屬性的屬性會(huì)通過(guò) PersistentComponentState 服務(wù)自動(dòng)持久化。 當(dāng)組件以交互方式呈現(xiàn)或?qū)嵗?wù)時(shí),將檢索狀態(tài)。
以前,使用 PersistentComponentState 服務(wù)預(yù)呈現(xiàn)期間保留組件狀態(tài)涉及大量代碼。 現(xiàn)在可以使用新的聲明性模型簡(jiǎn)化此代碼:
@page "/movies"
@inject IMovieService MovieService
@if (MoviesList == null)
{
<p><em>Loading...</em></p>
}
else
{
<QuickGrid Items="MoviesList.AsQueryable()">
...
</QuickGrid>
}
@code {
[SupplyParameterFromPersistentComponentState]
public List<Movie>? MoviesList { get; set; }
protected override async Task OnInitializedAsync()
{
MoviesList ??= await MovieService.GetMoviesAsync();
}
}
可以為同一類型的多個(gè)組件序列化狀態(tài),并且可以在服務(wù)中建立聲明性狀態(tài),通過(guò)在 RegisterPersistentService 組件生成器(Razor)上以自定義服務(wù)類型和渲染模式調(diào)用 AddRazorComponents,以便在整個(gè)應(yīng)用中使用。
2. 新的 JavaScript 互作功能
Blazor 添加了對(duì)以下 JS 互操作功能的支持:
- 使用構(gòu)造函數(shù)創(chuàng)建對(duì)象的實(shí)例 JS,并獲取引用實(shí)例的
IJSObjectReference/IJSInProcessObjectReference.NET 句柄。 - 讀取或修改 JS 對(duì)象屬性的值,包括數(shù)據(jù)屬性和訪問(wèn)器屬性。
以下異步方法在 IJSRuntime 和 IJSObjectReference 上可用:
-
InvokeNewAsync(string identifier, object?[]? args):異步調(diào)用指定的 JS 構(gòu)造函數(shù)。var classRef = await JSRuntime.InvokeNewAsync("jsInterop.TestClass", "Blazor!"); var text = await classRef.GetValueAsync<string>("text"); var textLength = await classRef.InvokeAsync<int>("getTextLength"); -
GetValueAsync<TValue>(string identifier):異步讀取指定 JS 屬性的值。var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync<int>( "jsInterop.testObject.num"); -
SetValueAsync<TValue>(string identifier, TValue value):異步更新指定 JS 屬性的值。await JSRuntime.SetValueAsync("jsInterop.testObject.num", 30);
這些方法都有重載版本,可接收 CancellationToken 參數(shù)或 TimeSpan 超時(shí)時(shí)間參數(shù)。
以下同步方法可在 IJSInProcessRuntime 和 IJSInProcessObjectReference 上使用:
-
InvokeNew(string identifier, object?[]? args):同步調(diào)用指定的 JS 構(gòu)造函數(shù)。var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var classRef = inProcRuntime.InvokeNew("jsInterop.TestClass", "Blazor!"); var text = classRef.GetValue<string>("text"); var textLength = classRef.Invoke<int>("getTextLength"); -
GetValue<TValue>(string identifier):同步讀取指定 JS 屬性的值。var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var valueFromDataProperty = inProcRuntime.GetValue<int>( "jsInterop.testObject.num"); -
SetValue<TValue>(string identifier, TValue value):同步更新指定 JS 屬性的值。var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); inProcRuntime.SetValue("jsInterop.testObject.num", 20);
3. JavaScript 捆綁程序支持
Blazor 的構(gòu)建輸出現(xiàn)在可以通過(guò)將 MSBuild 屬性 WasmBundlerFriendlyBootConfig 設(shè)置為 true,在發(fā)布期間生成捆綁程序友好的輸出,從而兼容 JavaScript 捆綁程序(如 Gulp、Webpack 和 Rollup)。 這為開(kāi)發(fā)者在 Blazor 應(yīng)用中使用現(xiàn)有 JavaScript 工具鏈提供了更大的靈活性。
4. 在獨(dú)立 Blazor WebAssembly 應(yīng)用中設(shè)置環(huán)境
從 .NET 10 開(kāi)始,Properties/launchSettings.json 文件不再用于控制獨(dú)立 Blazor WebAssembly 應(yīng)用中的環(huán)境。 現(xiàn)在,開(kāi)發(fā)者應(yīng)在應(yīng)用的項(xiàng)目文件(.csproj)中使用 <WasmApplicationEnvironmentName> 屬性來(lái)設(shè)置環(huán)境。
<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
默認(rèn)環(huán)境為:Development(用于生成)和 Production(用于發(fā)布)。
5. 內(nèi)聯(lián)的啟動(dòng)配置文件
Blazor 的啟動(dòng)配置,在 .NET 10 發(fā)布之前存在于名為 blazor.boot.json 的文件中,現(xiàn)在已內(nèi)聯(lián)到 dotnet.js 腳本中。 這主要影響直接操作 blazor.boot.json 文件的開(kāi)發(fā)者。
6. 改進(jìn)了表單驗(yàn)證
Blazor 現(xiàn)在改進(jìn)了表單驗(yàn)證功能,包括對(duì)驗(yàn)證嵌套對(duì)象和集合項(xiàng)的屬性的支持。 要選擇使用新的驗(yàn)證功能,需要執(zhí)行以下操作:
-
在注冊(cè)服務(wù)的文件
Program中調(diào)用擴(kuò)展方法AddValidation。builder.Services.AddValidation(); -
在 C# 類文件中聲明表單模型類型,而不是在組件 Razor (
.razor) 中。 -
使用
[ValidatableType]特性批注根窗體模型類型。[ValidatableType] public class Order { public Customer Customer { get; set; } = new(); public List<OrderItem> OrderItems { get; set; } = []; } public class Customer { [Required(ErrorMessage = "Name is required.")] public string? FullName { get; set; } [Required(ErrorMessage = "Email is required.")] public string? Email { get; set; } public ShippingAddress ShippingAddress { get; set; } = new(); }
在組件中,繼續(xù)使用 DataAnnotationsValidator 組件內(nèi)部的 EditForm 組件:
<EditForm Model="Model">
<DataAnnotationsValidator />
<h3>Customer Details</h3>
<div class="mb-3">
<label>
Full Name
<InputText @bind-Value="Model!.Customer.FullName" />
</label>
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
</div>
@* ... form continues ... *@
</EditForm>
@code {
public Order? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
// ... code continues ...
}
聲明組件(Razor文件)之外的.razor模型類型的要求是由于新的驗(yàn)證功能和Razor編譯器本身都使用源生成器。 目前,一個(gè)源生成器的輸出不能用作另一個(gè)源生成器的輸入。
ASP.NET Core Identity 的 Web 身份驗(yàn)證 API(密鑰)支持
ASP.NET Core Identity 現(xiàn)在支持基于 WebAuthn 和 FIDO2 標(biāo)準(zhǔn)的密鑰身份驗(yàn)證。 Web 身份驗(yàn)證(WebAuthn)API,廣泛被稱為 passkeys,是一種現(xiàn)代的抗釣魚身份驗(yàn)證方法,通過(guò)利用公鑰加密和基于設(shè)備的身份驗(yàn)證來(lái)提高安全性和用戶體驗(yàn)。 此功能允許用戶使用安全、基于設(shè)備的身份驗(yàn)證方法(例如生物識(shí)別或安全密鑰)在沒(méi)有密碼的情況下登錄。 預(yù)覽版 6 Blazor Web App 項(xiàng)目模板提供現(xiàn)成的密鑰管理和登錄功能。
線路狀態(tài)持久性
在服務(wù)器端呈現(xiàn)期間,即使與服務(wù)器連接長(zhǎng)時(shí)間斷開(kāi)或主動(dòng)暫停,Blazor Web App 也可以保留用戶會(huì)話(電路)狀態(tài),只要沒(méi)有觸發(fā)整頁(yè)刷新。 這樣,用戶就可以在瀏覽器標(biāo)簽頁(yè)節(jié)流、移動(dòng)設(shè)備用戶切換應(yīng)用、網(wǎng)絡(luò)中斷或主動(dòng)資源管理(暫停非活動(dòng)電路)等情況下恢復(fù)會(huì)話,而不會(huì)丟失未保存的工作。
持久化狀態(tài)所需的服務(wù)器資源比持久化線路少:
- 即使斷開(kāi)連接,線路也可能繼續(xù)執(zhí)行工作,并消耗 CPU、內(nèi)存和其他資源。持久化狀態(tài)僅消耗開(kāi)發(fā)人員控制的固定內(nèi)存量。
- 持久化狀態(tài)表示應(yīng)用消耗的內(nèi)存子集,因此服務(wù)器不需要跟蹤應(yīng)用的組件和其他服務(wù)器端對(duì)象。
以下兩種情況會(huì)保留狀態(tài):
- 組件狀態(tài):組件用于交互式服務(wù)器呈現(xiàn)的狀態(tài),例如,從數(shù)據(jù)庫(kù)檢索到的項(xiàng)目列表或用戶正在填寫的表單。
- 作用域服務(wù):如當(dāng)前用戶這類保存在服務(wù)器端服務(wù)中的狀態(tài)。
默認(rèn)情況下,當(dāng)在 AddInteractiveServerComponents 文件中調(diào)用 AddRazorComponents 時(shí),會(huì)啟用狀態(tài)持久性。 MemoryCache 是單個(gè)應(yīng)用實(shí)例的默認(rèn)存儲(chǔ)實(shí)現(xiàn),存儲(chǔ)最多 1,000 條持久化線路兩小時(shí),這是可配置的。 開(kāi)發(fā)者可以使用以下選項(xiàng)更改內(nèi)存提供程序的默認(rèn)值:
PersistedCircuitInMemoryMaxRetained:要保留的最大線路數(shù)。默認(rèn)值為 1,000 條線路。PersistedCircuitInMemoryRetentionPeriod:最長(zhǎng)保留期是TimeSpan。默認(rèn)為 2 小時(shí)。
services.Configure<CircuitOptions>(options => {
options.PersistedCircuitInMemoryMaxRetained = {CIRCUIT COUNT};
options.PersistedCircuitInMemoryRetentionPeriod = {RETENTION PERIOD};
});
批注組件屬性 [SupplyFromPersistentComponentState] 以啟用線路狀態(tài)持久性。
@foreach (var item in Items) {
<ItemDisplay @key="@($"unique-prefix-{item.Id}")" Item="item" />
}
@code {
[SupplyFromPersistentComponentState]
public List<Item> Items { get; set; }
protected override async Task OnInitializedAsync()
{
Items ??= await LoadItemsAsync();
}
}
若要為作用域服務(wù)保留狀態(tài),請(qǐng)用 [SupplyFromPersistentComponentState] 注解服務(wù)屬性,將服務(wù)添加到服務(wù)集合,并調(diào)用 RegisterPersistentService 擴(kuò)展方法:
public class CustomUserService {
[SupplyFromPersistentComponentState]
public string UserData { get; set; }
}
services.AddScoped<CustomUserService>();
services.AddRazorComponents()
.AddInteractiveServerComponents()
.RegisterPersistentService<CustomUserService>(RenderMode.InteractiveAuto);
Client-side fingerprinting
在 .NET 10 中,開(kāi)發(fā)者可以選擇啟用獨(dú)立 Blazor WebAssembly 應(yīng)用的 JavaScript 模塊的客戶端指紋識(shí)別功能。 在生成/發(fā)布期間的獨(dú)立 Blazor WebAssembly 應(yīng)用中,框架使用生成期間計(jì)算的值來(lái)替代 index.html 中的占位符,以對(duì)靜態(tài)資產(chǎn)進(jìn)行指紋識(shí)別。 指紋會(huì)植入到 blazor.webassembly.js 腳本文件名中。
文件中必須存在 wwwroot/index.html 以下標(biāo)記才能采用指紋功能:
<head>
...
+ <script type="importmap"></script>
</head>
<body>
...
- <script src="_framework/blazor.webassembly.js"></script>
+ <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>
</html>
在項(xiàng)目文件中(.csproj),將 <OverrideHtmlAssetPlaceholders> 屬性集添加到 true:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
+ <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
</Project>
任何帶有指紋標(biāo)記的 index.html 中的腳本都會(huì)被框架打上指紋。 例如,名為 scripts.js 的腳本文件位于應(yīng)用的 wwwroot/js 文件夾中,通過(guò)在文件擴(kuò)展名之前添加 #[.{fingerprint}] 進(jìn)行指紋處理(.js):
<script src="js/scripts#[.{fingerprint}].js"></script>
若要對(duì)獨(dú)立應(yīng)用中的其他 JS 模塊進(jìn)行指紋識(shí)別,請(qǐng)使用 Blazor WebAssembly 應(yīng)用的項(xiàng)目文件(<StaticWebAssetFingerprintPattern>)中的 .csproj 屬性。
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs"
Expression="#[.{fingerprint}]!" />
文件自動(dòng)放置在導(dǎo)入映射中,并在解析 JavaScript 互操作的導(dǎo)入時(shí),瀏覽器會(huì)使用導(dǎo)入映射來(lái)解析指紋文件。
結(jié)論
.NET 10 中的 ASP.NET Core 帶來(lái)了諸多重要更新,特別是 Blazor 框架,它在安全性、性能、用戶體驗(yàn)和開(kāi)發(fā)效率方面均有顯著提升。 從增強(qiáng)的 Blazor Web App 安全示例、QuickGrid 組件的樣式和操作控制,到響應(yīng)流式處理的默認(rèn)啟用和客戶端指紋識(shí)別,這些功能都旨在幫助開(kāi)發(fā)者構(gòu)建更安全、更快速、更易于維護(hù)的現(xiàn)代 Web 應(yīng)用。 新的 JavaScript 互操作功能和聲明式狀態(tài)管理模型也極大地簡(jiǎn)化了開(kāi)發(fā)流程。 此外,路由和導(dǎo)航的改進(jìn),特別是 NavigationManager.NavigateTo 行為的優(yōu)化以及 NotFoundPage 參數(shù)的引入,使得 Blazor 應(yīng)用的用戶體驗(yàn)更加流暢和可控。 密鑰身份驗(yàn)證的支持則進(jìn)一步加強(qiáng)了應(yīng)用的安全性。 總體而言,.NET 10 的 ASP.NET Core 持續(xù)致力于提升開(kāi)發(fā)者的生產(chǎn)力,并為構(gòu)建高性能的 Web 應(yīng)用程序提供了更堅(jiān)實(shí)的基礎(chǔ)。 開(kāi)發(fā)者應(yīng)積極探索和利用這些新功能,以充分發(fā)揮 .NET 10 的潛力。
系列文章
.NET 10 中的新增功能系列文章1——運(yùn)行時(shí)中的新增功能
.NET 10 中的新增功能系列文章3——.NET MAUI 中的新增功能
.NET 10 中的新增功能系列文章4——.NET SDK中的新增功能
.NET 10 新增功能系列文章(5)——C# 14 中的新增功能
浙公網(wǎng)安備 33010602011771號(hào)