.NET 10 中的新增功能系列文章2——ASP.NET Core 中的新增功能

引言

随着技术的不断演进,.NET 平台持续为开发者带来创新和改进。作为 .NET 生态系统中的重要组成部分,ASP.NET Core 在每个版本中都引入了令人兴奋的新功能和优化。本文将深入探讨 .NET 10 中 ASP.NET Core 的主要更新,特别是 Blazor 框架中的各项增强功能,旨在为开发者提供一个清晰、全面的概述,帮助您更好地利用这些新特性来构建更强大、更安全的 Web 应用程序。我们将重点关注安全性、性能、开发体验和路由方面的改进,并提供相关的代码示例和详细说明。

正文

Blazor Web App 安全性增强

在 .NET 10 中,Blazor Web App 的安全性得到了显著提升。官方新增并更新了多个安全示例,涵盖了不同的身份验证和授权场景。

1. 新的和经过更新的 Blazor Web App 安全示例

Blazor Web App 的安全示例得到了全面的更新,主要包括以下几个方面:

  • OpenID Connect (OIDC) 保护:提供了如何使用 OIDC 保护 Blazor Web App 的详细指南和示例。
  • Microsoft Entra ID (原 Azure AD) 保护:更新了使用 Microsoft Entra ID 保护 Blazor Web App 的示例。
  • Windows 身份验证保护:新增了使用 Windows 身份验证保护 Blazor Web App 的示例。

所有 OIDC 和 Entra 示例解决方案现在都包含一个单独的 Web API 项目 (MinimalApiJwt),用于演示如何安全地配置和调用外部 Web API。 调用 Web API 的方式通过使用令牌处理程序和具名 HTTP 客户端,来对接 OIDC 身份提供程序,或使用 Microsoft Entra ID 的 Microsoft Identity Web 包/API。

这些示例解决方案在 Program 文件中的 C# 代码中进行配置。此外,还提供了从应用程序设置文件(例如 appsettings.json)配置解决方案的新指南,位于 OIDC 或 Entra 文章的“通过 JSON 配置提供程序(应用设置)提供配置”部分。

Entra 文章和示例应用还包含了关于以下方法的新的指导:

  • 如何对 Web 场托管方案使用加密的分布式令牌缓存。
  • 如何将 Azure Key Vault 与 Azure 托管标识 配合使用来保护数据。

Blazor UI 组件和性能优化

1. QuickGrid RowClass 参数

为了提供更灵活的 UI 样式控制,QuickGrid 组件新增了 RowClass 参数。 开发者现在可以根据行项的特定条件,动态地将样式表类应用于网格的行。 例如,通过定义一个方法,根据 MyGridItemIsArchived 属性来应用不同的 CSS 类:

/* by 01022.hk - online tools website : 01022.hk/zh/favicon.html */
<QuickGrid ... RowClass="GetRowCssClass">
    ...
</QuickGrid>

@code {
    private string GetRowCssClass(MyGridItem item) =>
        item.IsArchived ? "row-archived" : null;
}

这极大地增强了 QuickGrid 的定制能力,使得数据呈现更加直观和富有表现力。

2. 关闭 QuickGrid 列选项

现在,可以使用 QuickGrid 的新方法 HideColumnOptionsAsync 关闭列选项的用户界面。 这对于需要在用户执行特定操作(如应用筛选器)后自动关闭列选项的场景非常有用,从而提升用户体验。

/* by 01022.hk - online tools website : 01022.hk/zh/favicon.html */
<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. 响应流式处理的默认启用和选择停用

在 .NET 10 之前,HttpClient 请求的响应流式处理是可选启用的,现在默认启用。 这意味着调用 HttpContent.ReadAsStreamAsync 对于 HttpResponseMessage.Contentresponse.Content.ReadAsStreamAsync())返回的是 BrowserHttpReadStream,而不再返回 MemoryStreamBrowserHttpReadStream 不支持同步操作,例如 Stream.Read(Span<Byte>)。 如果代码使用了同步操作,开发者可以选择禁用响应流式处理或手动将 Stream 复制到 MemoryStream

要选择退出全局响应流式处理,可以在项目文件中添加 <WasmEnableStreamingResponse>false</WasmEnableStreamingResponse> 属性,或者将 DOTNET_WASM_ENABLE_STREAMING_RESPONSE 环境变量设置为 false0

<WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>

要选择退出单个请求的响应流式处理,可以将 HttpRequestMessage 上的 SetBrowserResponseStreamingEnabled 设置为 false

requestMessage.SetBrowserResponseStreamingEnabled(false);
4. Blazor 脚本作为静态 Web 资产

为了提升性能和优化资源加载,在 .NET 10 及更高版本中,Blazor 脚本现在被用作具有自动压缩和指纹的静态 Web 资产,而不再从 ASP.NET Core 共享框架中的嵌入资源提供。 这有助于更好地利用浏览器缓存和 CDN,提高应用的加载速度。

5. Blazor WebAssembly 性能分析和诊断计数器

新版本为 Blazor WebAssembly 应用引入了全面的性能分析和诊断计数器。 这些计数器提供了组件生命周期、导航、事件处理和线路管理的详细可观测性,帮助开发者识别和解决性能瓶颈。

6. 预加载的 Blazor 框架静态资源

在 Blazor Web App 中,框架静态资源使用 Link 头信息自动预加载,这允许浏览器在提取和呈现初始页面之前预加载资源。 在独立 Blazor WebAssembly 应用中,框架资源会被安排为高优先级下载,并在浏览器 index.html 页面处理过程早期进行缓存。 这种机制可以显著缩短应用的首次加载时间。

7. Blazor WebAssembly 在 Blazor Web App 中静态资源预加载

为了更好地在 Blazor Web App 中预加载 WebAssembly 资产,Blazor 将 <link> 标头替换为 LinkPreload 组件 (<LinkPreload />)。 这使得应用程序的基路径配置 (<base href="..." />) 能够正确识别应用程序的根目录。 默认情况下,Blazor Web App 模板在 .NET 10 中采用此特性。 升级到 .NET 10 的应用可以通过在 LinkPreload 组件的头部内容 (<base>) 中,将 App 组件放置在基 URL 标签 (App.razor) 后来实现该功能。

<head>
    ...
    <base href="/" />
+   <LinkPreload />
    ...
</head>
8. 自定义 Blazor 缓存和 BlazorCacheBootResources MSBuild 属性已删除

由于所有 Blazor 客户端文件现在都由浏览器进行指纹标记和缓存,Blazor 的自定义缓存机制和 BlazorCacheBootResources MSBuild 属性已从框架中移除。 开发者应从客户端项目的项目文件中删除此属性,因为它不再有任何影响。

Blazor 路由和导航改进

1. 路由模板要点

[Route] 属性现在支持路由语法突出显示,以帮助开发者更好地可视化路由模板的结构,从而减少路由配置错误。

2. MapsTo 不再滚动到顶部来进行同一页面导航

以前,NavigationManager.NavigateTo 在进行同一页面导航时会滚动到页面顶部。 在 .NET 10 中,此行为已更改,浏览器在导航到同一页面时不再滚动到页面顶部。 这意味着在更新当前页面的地址(例如更改查询字符串或片段)时不再重置视口,从而提升了用户体验,尤其是在单页应用中。

3. 已将重新连接 UI 组件添加到 Blazor Web App 项目模板中

Blazor Web App 项目模板现在包含一个 ReconnectModal 组件,它包含了并置的样式表和 JavaScript 文件,旨在在客户端失去与服务器的 WebSocket 连接时,改进开发者对重新连接 UI 的控制。 该组件不会以编程方式插入样式,确保符合 style-src 策略更严格的内容安全策略 (CSP) 设置。 新的重新连接 UI 功能包括通过在重新连接 UI 元素上设置特定的 CSS 类来指示重新连接状态,以及调度新的 components-reconnect-state-changed 事件以更改重新连接状态。 代码可以使用 CSS 类和新事件所指示的新重新连接状态“retrying”更好地区分重新连接过程的阶段。

4. 使用 NavLinkMatch.All 时忽略查询字符串和片段

当使用 NavLink 参数的 NavLinkMatch.All 值时,Match 组件现在将忽略查询字符串和片段。 这意味着如果 URL 路径匹配但查询字符串或片段发生更改,则链接将保留 active 类。 要还原到原始行为,可以将 Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragment AppContext 开关设置为 true。 开发者也可以通过重写 NavLinkShouldMatch 方法来自定义匹配行为。

public class CustomNavLink : NavLink
{
    protected override bool ShouldMatch(string currentUriAbsolute)
    {
        // Custom matching logic
    }
}
5. NavigationManager.NavigateTo 不再抛出异常 NavigationException

以前,在进行静态服务器端渲染 (SSR) 时,调用 NavigationManager.NavigateTo 会在转换为重定向响应前抛出 NavigationException,从而中断执行。 在 .NET 10 中,在静态 SSR 期间调用 NavigationManager.NavigateTo 不再引发 NavigationException。 其行为与交互式呈现一致,执行导航但不会抛出异常。 依赖于 NavigationException 被抛出的代码应进行更新。 例如,在默认 Blazor Identity UI 中,IdentityRedirectManager 过去在调用 RedirectTo 之后抛出一个 InvalidOperationException,以确保它不会在交互式呈现期间被调用。 现在应删除此异常和 [DoesNotReturn] 属性。

6. Blazor 路由器具有参数 NotFoundPage

Blazor 现在提供了一种改进的方法,用于在导航到不存在的页面时显示“找不到”页面。 可以通过使用 NotFoundPage 参数将页面类型传递给组件 Router 来指定在调用 NavigationManager.NotFound 时要呈现的页面。 推荐使用此方式替代 NotFound 的呈现片段 (<NotFound>...</NotFound>),因为它支持路由、适配状态码页面重执行中间件,并兼容非 Blazor 场景。 如果同时定义了 NotFound 呈现片段和 NotFoundPage,则以 NotFoundPage 指定的页面优先。

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

项目 Blazor 模板现在默认包含一个 NotFound.razor 页面。 每当在应用中调用 NavigationManager.NotFound 时,此页面都会自动呈现,从而更轻松地处理缺失路由,并提供一致的用户体验。

7. 使用 NavigationManager 处理静态 SSR 和全局交互式呈现中的“未找到”响应

NavigationManager 现在包含一个 NotFound 方法,用于处理在静态服务器端渲染(静态 SSR)或全局交互渲染期间找不到请求资源的情况。

  • 静态服务器端呈现 (SSR):调用 NotFound 会将 HTTP 状态代码设置为 404。
  • 交互式呈现:通知 Blazor 路由器 (Router 组件) 呈现“未找到”内容。
  • 流式呈现:如果增强导航处于活动状态,流式呈现会呈现“未找到”内容,而无需重新加载页面。 当增强的导航被阻止时,框架会重定向到“找不到”内容,并刷新页面。

流式 NavigationManager.NotFound 内容呈现使用以下顺序:

  1. NotFoundPage 传递给 Router 组件(如果存在)。
  2. 已配置的状态码页面重执行中间件页面。
  3. 如果上述两种方法均未采用,则不采取任何操作。

非流式 NavigationManager.NotFound 内容呈现使用以下顺序:

  1. NotFoundPage 传递给 Router 组件(如果存在)。
  2. 若存在“未找到”呈现片段内容,则使用该内容。不建议在 .NET 10 或更高版本中使用。
  3. DefaultNotFound 404 内容(“Not found”纯文本)。

UseStatusCodePagesWithReExecute 在处理浏览器地址路由问题(如 URL 输入错误或点击无效链接)时优先。 当 NavigationManager.OnNotFound 被调用时,可以使用 NotFound 事件进行通知。

Blazor 开发体验和互操作性

1. 用于保存组件和服务状态的声明性模型

现在可以通过声明方式指定状态,使其能够通过 [SupplyParameterFromPersistentComponentState] 特性从组件和服务中持久化。 在预呈现期间,具有此属性的属性会通过 PersistentComponentState 服务自动持久化。 当组件以交互方式呈现或实例化服务时,将检索状态。

以前,使用 PersistentComponentState 服务预呈现期间保留组件状态涉及大量代码。 现在可以使用新的声明性模型简化此代码:

@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();
    }
}

可以为同一类型的多个组件序列化状态,并且可以在服务中建立声明性状态,通过在 RegisterPersistentService 组件生成器(Razor)上以自定义服务类型和渲染模式调用 AddRazorComponents,以便在整个应用中使用。

2. 新的 JavaScript 互作功能

Blazor 添加了对以下 JS 互操作功能的支持:

  • 使用构造函数创建对象的实例 JS,并获取引用实例的 IJSObjectReference/IJSInProcessObjectReference .NET 句柄。
  • 读取或修改 JS 对象属性的值,包括数据属性和访问器属性。

以下异步方法在 IJSRuntimeIJSObjectReference 上可用:

  • InvokeNewAsync(string identifier, object?[]? args):异步调用指定的 JS 构造函数。

    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 参数或 TimeSpan 超时时间参数。

以下同步方法可在 IJSInProcessRuntimeIJSInProcessObjectReference 上使用:

  • InvokeNew(string identifier, object?[]? args):同步调用指定的 JS 构造函数。

    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 的构建输出现在可以通过将 MSBuild 属性 WasmBundlerFriendlyBootConfig 设置为 true,在发布期间生成捆绑程序友好的输出,从而兼容 JavaScript 捆绑程序(如 Gulp、Webpack 和 Rollup)。 这为开发者在 Blazor 应用中使用现有 JavaScript 工具链提供了更大的灵活性。

4. 在独立 Blazor WebAssembly 应用中设置环境

从 .NET 10 开始,Properties/launchSettings.json 文件不再用于控制独立 Blazor WebAssembly 应用中的环境。 现在,开发者应在应用的项目文件(.csproj)中使用 <WasmApplicationEnvironmentName> 属性来设置环境。

<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>

默认环境为:Development(用于生成)和 Production(用于发布)。

5. 内联的启动配置文件

Blazor 的启动配置,在 .NET 10 发布之前存在于名为 blazor.boot.json 的文件中,现在已内联到 dotnet.js 脚本中。 这主要影响直接操作 blazor.boot.json 文件的开发者。

6. 改进了表单验证

Blazor 现在改进了表单验证功能,包括对验证嵌套对象和集合项的属性的支持。 要选择使用新的验证功能,需要执行以下操作:

  1. 在注册服务的文件 Program 中调用扩展方法 AddValidation

    builder.Services.AddValidation();
    
  2. 在 C# 类文件中声明表单模型类型,而不是在组件 Razor (.razor) 中。

  3. 使用 [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();
    }
    

在组件中,继续使用 DataAnnotationsValidator 组件内部的 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模型类型的要求是由于新的验证功能和Razor编译器本身都使用源生成器。 目前,一个源生成器的输出不能用作另一个源生成器的输入。

ASP.NET Core Identity 的 Web 身份验证 API(密钥)支持

ASP.NET Core Identity 现在支持基于 WebAuthn 和 FIDO2 标准的密钥身份验证。 Web 身份验证(WebAuthn)API,广泛被称为 passkeys,是一种现代的抗钓鱼身份验证方法,通过利用公钥加密和基于设备的身份验证来提高安全性和用户体验。 此功能允许用户使用安全、基于设备的身份验证方法(例如生物识别或安全密钥)在没有密码的情况下登录。 预览版 6 Blazor Web App 项目模板提供现成的密钥管理和登录功能。

线路状态持久性

在服务器端呈现期间,即使与服务器连接长时间断开或主动暂停,Blazor Web App 也可以保留用户会话(电路)状态,只要没有触发整页刷新。 这样,用户就可以在浏览器标签页节流、移动设备用户切换应用、网络中断或主动资源管理(暂停非活动电路)等情况下恢复会话,而不会丢失未保存的工作。

持久化状态所需的服务器资源比持久化线路少:

  • 即使断开连接,线路也可能继续执行工作,并消耗 CPU、内存和其他资源。持久化状态仅消耗开发人员控制的固定内存量。
  • 持久化状态表示应用消耗的内存子集,因此服务器不需要跟踪应用的组件和其他服务器端对象。

以下两种情况会保留状态:

  • 组件状态:组件用于交互式服务器呈现的状态,例如,从数据库检索到的项目列表或用户正在填写的表单。
  • 作用域服务:如当前用户这类保存在服务器端服务中的状态。

默认情况下,当在 AddInteractiveServerComponents 文件中调用 AddRazorComponents 时,会启用状态持久性。 MemoryCache 是单个应用实例的默认存储实现,存储最多 1,000 条持久化线路两小时,这是可配置的。 开发者可以使用以下选项更改内存提供程序的默认值:

  • PersistedCircuitInMemoryMaxRetained:要保留的最大线路数。默认值为 1,000 条线路。
  • PersistedCircuitInMemoryRetentionPeriod:最长保留期是 TimeSpan。默认为 2 小时。
services.Configure<CircuitOptions>(options => {
    options.PersistedCircuitInMemoryMaxRetained = {CIRCUIT COUNT};
    options.PersistedCircuitInMemoryRetentionPeriod = {RETENTION PERIOD};
});

批注组件属性 [SupplyFromPersistentComponentState] 以启用线路状态持久性。

@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();
    }
}

若要为作用域服务保留状态,请用 [SupplyFromPersistentComponentState] 注解服务属性,将服务添加到服务集合,并调用 RegisterPersistentService 扩展方法:

public class CustomUserService {
    [SupplyFromPersistentComponentState]
    public string UserData { get; set; }
}
services.AddScoped<CustomUserService>();
services.AddRazorComponents()
    .AddInteractiveServerComponents()
    .RegisterPersistentService<CustomUserService>(RenderMode.InteractiveAuto);

Client-side fingerprinting

在 .NET 10 中,开发者可以选择启用独立 Blazor WebAssembly 应用的 JavaScript 模块的客户端指纹识别功能。 在生成/发布期间的独立 Blazor WebAssembly 应用中,框架使用生成期间计算的值来替代 index.html 中的占位符,以对静态资产进行指纹识别。 指纹会植入到 blazor.webassembly.js 脚本文件名中。

文件中必须存在 wwwroot/index.html 以下标记才能采用指纹功能:

<head>
    ...
+   <script type="importmap"></script>
</head>

<body>
    ...
-   <script src="_framework/blazor.webassembly.js"></script>
+   <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>

</html>

在项目文件中(.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>

任何带有指纹标记的 index.html 中的脚本都会被框架打上指纹。 例如,名为 scripts.js 的脚本文件位于应用的 wwwroot/js 文件夹中,通过在文件扩展名之前添加 #[.{fingerprint}] 进行指纹处理(.js):

<script src="js/scripts#[.{fingerprint}].js"></script>

若要对独立应用中的其他 JS 模块进行指纹识别,请使用 Blazor WebAssembly 应用的项目文件(<StaticWebAssetFingerprintPattern>)中的 .csproj 属性。

<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs" 
  Expression="#[.{fingerprint}]!" />

文件自动放置在导入映射中,并在解析 JavaScript 互操作的导入时,浏览器会使用导入映射来解析指纹文件。

结论

.NET 10 中的 ASP.NET Core 带来了诸多重要更新,特别是 Blazor 框架,它在安全性、性能、用户体验和开发效率方面均有显著提升。 从增强的 Blazor Web App 安全示例、QuickGrid 组件的样式和操作控制,到响应流式处理的默认启用和客户端指纹识别,这些功能都旨在帮助开发者构建更安全、更快速、更易于维护的现代 Web 应用。 新的 JavaScript 互操作功能和声明式状态管理模型也极大地简化了开发流程。 此外,路由和导航的改进,特别是 NavigationManager.NavigateTo 行为的优化以及 NotFoundPage 参数的引入,使得 Blazor 应用的用户体验更加流畅和可控。 密钥身份验证的支持则进一步加强了应用的安全性。 总体而言,.NET 10 的 ASP.NET Core 持续致力于提升开发者的生产力,并为构建高性能的 Web 应用程序提供了更坚实的基础。 开发者应积极探索和利用这些新功能,以充分发挥 .NET 10 的潜力。

系列文章

.NET 10 中的新增功能系列文章1——运行时中的新增功能



本文是由葡萄城技术开发团队发布,转载请注明出处:葡萄城官网


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值