July 7th, 2023
0 reactions

.NET 8 Preview 5 中的 ASP.NET Core 更新

Partner Technical Advisor

作者:Daniel Roth   原文:ASP.NET Core updates in .NET 8 Preview 5 – .NET Blog (microsoft.com)

.NET 8 Preview 5 现在已经发布,其中包括了对 ASP.NET Core 的许多重要更新。

以下是预览版本中新增功能的摘要:

  • 改进了NET Core调试体验
  • 服务器和中间件
    • IHttpSysRequestTimingFeature
    • ITlsHandshakeFeature 中的 SNI 主机名
    • IExceptionHandler
  • Blazor
    • 新的 Blazor Web App项目模板
    • Blazor 路由与端点路由的集成
    • 使用Blazor Server启用各个组件的交互性
    • 改进了 Webcil 文件的打包
    • Blazor 内容安全策略 (Content Security Policy, CSP)的兼容性
  • API编写
    • 支持泛型属性
  • SignalR
    • SignalR 无缝重连
  • Native AOT
    • 在编译时生成的最小API中支持AsParameters和自动生成元数据
  • 认证与授权
    • NET Core SPA 模板中的身份验证更新
    • 用于推荐 AuthorizationBuilder 用法的新分析器

有关为.NET 8 的 ASP.NET Core  规划的更多详细信息,请参阅 GitHub 上的 .NET 8 的完整ASP.NET Core 路线图。

入门

如果您想要在.NET 8 Preview 5 中使用 ASP.NET Core,请安装 .NET 8 SDK。

如果您在 Windows 上使用 Visual Studio,我们建议您安装最新的Visual Studio 2022 预览版。如果您使用的是 Visual Studio Code,那么可以尝试新的C# 开发工具包。Visual Studio for Mac 目前不支持 .NET 8 预览版。

升级现有项目

若要将现有 ASP.NET Core 应用程序从 .NET 8 Preview 4 升级到 .NET 8 Preview 5,请执行以下操作:

  • 将应用程序的目标框架更新为 .NET 8.0。
  • 将所有AspNetCore.* 包引用更新为8.0.0-preview.5.*。
  • 将所有Extensions.* 包引用更新为8.0.0-preview.5.*。

具体信息请参阅.NET 8 的 ASP.NET Core重要更新的完整列表。

改进了 ASP.NET Core 调试体验

.NET 8 Preview 5 引入了对 ASP.NET Core 调试的重要更新。我们添加了调试自定义属性,以便更轻松地在 IDE 的调试器中查找有关 HttpContext、HttpRequest、HttpResponse 和 ClaimsPrincipal 等类型的重要信息。

.NET 7ASP.NET Core debugging before

.NET 8ASP.NET Core debugging after

我们希望不断改进 ASP.NET Core 调试。如果您对其他常用或难以调试的类型有建议,请在评论区或在aspnetcore GitHub 存储库中告诉我们。

服务器和中间件

IHttpSysRequestTimingFeature

新的IHttpSysRequestTimingFeature接口公开了使用 HTTP.sys 服务器时与请求处理相关的详细时间戳数据。

以前,HTTP.sys 请求信息是通过IHttpSysRequestInfoFeature提供的。

随着IHttpSysRequestTimingFeature 接口的添加,我们的目标是定义更明确的API,这些API可以为请求计时数据提供更好的支持。

namespace Microsoft.AspNetCore.Server.HttpSys
{
    public interface IHttpSysRequestTimingFeature
    {
        ReadOnlySpan<long> Timestamps { get; }
        bool TryGetTimestamp(HttpSysRequestTimingType timestampType, out long timestamp);
        bool TryGetElapsedTime(HttpSysRequestTimingType startingTimestampType, HttpSysRequestTimingType endingTimestampType, out TimeSpan elapsed);
    }
}

Timestamps 属性允许访问所有 HTTP.sys 计时时间戳。 这些时间戳可以通过QueryPerformanceCounter 获得,时间戳频率可以通过 QueryPerformanceFrequency 确定。

TryGetTimestamp方法检索为提供的计时类型检索时间戳,而TryGetElapsedTime方法给出两个指定计时之间经过的时间。

此增强功能为开发人员提供了:

  • 请求处理各个阶段的更为细致的理解。
  • 精确的性能诊断能力。
  • 改进了对sys 请求计时数据的访问和控制。

我们迫不及待地希望看到这种对 HTTP.sys 计时信息的高级访问能够帮助优化您的应用程序并改善您的诊断体验。

ITlsHandshakeFeature 中的 SNI 主机名

服务器名称指示 (SNI) 主机名现在在ITlsHandshakeFeature接口中公开。

/// <summary>
/// Gets the host name from the "server_name" (SNI) extension of the client hello if present.
/// See <see href="https://www.rfc-editor.org/rfc/rfc6066#section-3">RFC 6066</see>.
/// </summary>
string? HostName => null;

SNI 是 TLS 握手过程的一部分,它允许客户端指定他们尝试连接的主机名。这使得托管多个虚拟主机或域的服务器能够在握手过程中提供正确的安全证书。通常,SNI 仅在 TLS 堆栈中处理并用于选择匹配的证书,但通过公开它,应用程序中的其他组件可以使用该信息进行诊断、速率限制、路由、计费等。

公开主机名对于管理数千个 SNI 绑定的大型服务特别有用。此功能允许您在 TLS 握手期间深入了解所选的 SNI,从而显著提高客户升级期间的调试能力。这种透明度的提高,使得问题解决得更迅速并且增强了服务的可靠性。

感谢karimsalem1对此功能的贡献!

IExceptionHandler

异常处理程序中间件是一个现有组件,它用于捕获意外的请求处理异常以及返回用户友好的错误页面,不会泄漏实现细节。IExceptionHandler是一个新的服务接口,可以由异常处理程序中间件解析和调用。它为开发人员提供了一个回调,允许在中央位置处理已知的异常。

IExceptionHandler 通过调用IServiceCollection.AddExceptionHandler<T> 来注册。 可以添加多个 IExceptionHandler ,并且它们将按照注册的顺序被调用。 如果异常处理程序处理了该请求,它可以返回 true 以停止处理。如果任何异常处理程序都没有处理异常,则控制权将退回到中间件的旧行为和选项。对于已处理和未处理的异常,将发出不同的指标和日志。

public interface IExceptionHandler
{
    ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken);
}

感谢Arvin Kahbazi 对此功能的贡献!

SignalR

SignalR 无缝重连

这个预览版包括对 SignalR 中“无缝重新连接”的早期支持。这个功能旨在减少客户端感知到的宕机时间,这些宕机时间可能是由于切换网络连接、通过隧道等原因造成的网络连接暂时中断。它通过在服务器和客户端上临时缓冲数据并确认两者发送的消息,识别当连接返回时以及连接中断时可能已发送的消息来实现此目的。

由于该功能还在设计中,所以还没有任何配置,支持仅限于使用 WebSocket 的 .NET 客户端。

要选择使用该功能,请更新您的 .NET 客户端代码以启用该选项:

var hubConnection = new HubConnectionBuilder()
    .WithUrl("<hub url>",
             options =>
             {
                options.UseAcks = true;
             })
    .Build();

在未来的预览版中,可能会有从服务器端禁用该功能的选项、将发生缓冲的配置以及超时限制。此外,还将增加对其他传输和客户端的支持。

请在 https://github.com/dotnet/aspnetcore/issues/46691 提供反馈并关注该功能的进展。

Blazor

新的 Blazor Web App项目模板

在这个预览版中,我们引入了一个新的 Blazor 项目模板:Blazor Web App 模板。这个新模板为使用 Blazor 组件来构建任何风格的 Web UI(包括服务器端渲染和客户端渲染)提供了单一起点。它将现有 Blazor Server 和 Blazor WebAssembly 托管模型的优势与我们在 .NET 8 中添加的新 Blazor 功能相结合:服务器端渲染、流式渲染、增强的导航和表单处理,以及在每个组件的基础上使用Blazor Server或Blazor WebAssembly添加交互性的能力。在这个预览版中,并非所有功能都已启用,新的Blazor Web App模板提供了一种便捷的方式来尝试这些可用的新功能。

您可以通过运行以下命令行创建一个新的 Blazor Web App:

dotnet new blazor -o BlazorWebApp

新的 Blazor Web App 项目模板可以在 Visual Studio 中使用:

Blazor Web App project template

默认情况下,Blazor Web App模板是为Razor组件的服务器端渲染而设置的。在Program.cs中,调用AddRazorComponents()添加相关的服务,然后MapRazorComponents<App>()发现可用的组件并指定应用程序的根组件。

Program.cs

using BlazorWebApp;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents();

var app = builder.Build();

// ...

app.MapRazorComponents<App>();

app.Run();

Blazor Web App模板中的根App组件定义了根 HTML 内容、设置了 Blazor 路由并添加了blazor.web.js脚本。不再需要IRazorComponentApplication接口。

App.razor

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorWebApp1</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link rel="icon" type="image/png" href="favicon.png" />
    <link href="BlazorWebApp1.styles.css" rel="stylesheet" />
    <HeadOutlet />
</head>

<body>

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

    
</body>
</html>

Blazor 路由与端点路由集成

Blazor Router组件现在集成了端点路由来处理服务器端和客户端路由。这意味着使用服务器端渲染路由到组件的工作方式与使用客户端渲染一样。这也意味着您现在可以使用Router来指定默认布局。

新的 Blazor Web App模板包括几个带有路由的示例页面。Index.razor定义应用程序的主页并只包含一些基本标记。ShowData.razor使用流式渲染来显示一些天气预报数据。Router组件与端点路由集成之后将请求路由到这些页面。

请注意,当前每个页面导航都需要完整的页面加载。默认情况下,应用程序没有设置客户端路由,而且我们还没有添加对增强页面导航的支持。在未来的 .NET 8 预览版本中即将推出增强型导航功能。

使用Blazor Server启用各个组件的交互性

在 .NET 8 Preview 5 中,我们现在可以使用 Blazor Server 渲染模式为各个组件启用交互性。您可以使用 AddServerComponents 扩展方法启用与 Blazor Server 的交互性,然后使用新的 [RenderModeServer] 属性启用特定组件的交互性。

新的 Blazor Web App 模板默认情况下不会设置任何类型(Blazor Server 或 Blazor WebAssembly)的交互性,所以没有Counter组件。但我们可以添加一个Counter组件并自行设置它以实现交互。

添加一个带有Counter组件的页面:

  1. 通过添加包含以下内容的Pages/Counter.razor文件来创建计数器页面:
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        <title>BlazorWebApp1</title>
        <base href="/" />
        <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
        <link href="css/app.css" rel="stylesheet" />
        <link rel="icon" type="image/png" href="favicon.png" />
        <link href="BlazorWebApp1.styles.css" rel="stylesheet" />
        <HeadOutlet />
    </head>
    
    <body>
    
        <Router AppAssembly="@typeof(App).Assembly">
            <Found Context="routeData">
                <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
                <FocusOnNavigate RouteData="@routeData" Selector="h1" />
            </Found>
            <NotFound>
                <PageTitle>Not found</PageTitle>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p role="alert">Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    
        
    </body>
    </html>
  2. 更新Shared/NavMenu.razor为计数器页面添加链接:
    <div class="nav-item px-3">
        <NavLink class="nav-link" href="counter">
            <span class="oi oi-plus" aria-hidden="true"></span> Counter
        </NavLink>
    </div>

您现在可以浏览到计数器页面,但计数器按钮还不能工作,因为该页面只是在服务器端呈现。

使用 Blazor Server 设置Counter组件的交互性:

  1. 在Program.cs中添加 Blazor Server 交互服务:
    builder.Services.AddRazorComponents()
        .AddServerComponents();
  2. 在Pages/Counter.razor中添加[RenderModeServer]属性:
    @attribute [RenderModeServer]

现在,当您浏览到计数器页面时,它应该是交互式的。当您浏览到该页面时,将建立 Blazor Server连接。当您单击Counter按钮时,会触发onclick事件,从而增加计数。

如果你想创建一个已经设置了交互式计数器的Blazor Web App,你可以通过以下命令行实现:

dotnet new blazor --use-server -o BlazorWebApp

在Visual Studio中创建 Blazor Web App时,–use-server选项目前还不可用,但我们预计很快就会添加它。

在这个预览版中,一旦 Blazor Server连接和回路建立起来,即使您离开计数器页面,它也会一直存在。 我们期望在未来的更新中改进回路寿命的管理方式。 此外,虽然此预览中添加了 Blazor WebAssembly 渲染模式的一些 API(例如 [RenderModeWebAssembly] 属性),但对在 WebAssembly 上交互运行的支持尚未完全实现。 这将会添加在未来的更新中。

改进了 Webcil 文件的打包

Webcil 是适用于 .NET 程序集的新型 Web 打包格式,旨在支持在限制性网络环境中使用 Blazor WebAssembly。。在 .NET 8 Preview 5 中,我们通过添加标准 WebAssembly 包装器改进了 Webcil 格式。这意味着 Webcil 文件现在只是具有标准 .wasm 扩展名的 WebAssembly 文件。

现在,当您发布 Blazor WebAssembly 应用程序时,Webcil 是默认打包格式。如果您想禁用 Webcil,您可以在项目文件中设置

<WasmEnableWebcil>false</WasmEnableWebcil>。

Blazor 内容安全策略 (CSP) 兼容性

Blazor WebAssembly在指定内容安全策略 (CSP) 时不再需要启用unsafe-eval脚本源。

API编写

支持泛型属性

这些属性以前需要 System.Type 参数的属性,但是现在以更简洁的通用变体形式提供。 这是通过 C# 11 中对泛型属性的支持实现的。例如,可以按如下方式修改用于注释操作响应类型的语法:

[ApiController]
[Route("api/[controller]")]
public class TodosController : Controller
{
  [HttpGet("/")]
  // Before: [ProducesResponseType(typeof(Todo), StatusCodes.Status200OK)]
  [ProducesResponseType<Todo>(StatusCodes.Status200OK)]
  public Todo Get() => new Todo(1, "Write a sample", DateTime.Now, false);
}

以下属性支持泛型变体:

  • [ProducesResponseType<T>]
  • [Produces<T>]
  • [MiddlewareFilter<T>]
  • [ModelBinder<T>]
  • [ModelMetadataType<T>]
  • [ServiceFilter<T>]
  • [TypeFilter<T>]

Native AOT

在编译时生成的最小API中支持AsParameters和自动生成元数据

在 .NET 8 Preview 3 中,我们为最小API引入了编译时代码生成功能,以支持Native AOT 场景。在这个预览版中,现在在编译时生成的最小 API 包括对使用AsParameters属性修饰的参数的支持,以及支持请求和响应类型的自动元数据推断。

例如,在下面的示例中,生成的代码将:

  • 从查询中绑定一个projectId参数
  • 从 JSON 正文绑定Todo参数
  • 注释端点元数据以指示它接受 JSON 有效负载
  • 注释端点元数据以指示它返回 Todo作为 JSON 负载
var app = WebApplication.Create();

app.MapPost("/todos", ([AsParameters] CreateTodoArgs payload) => 
{
    if (payload.TodoToCreate is not null)
    {
        return payload.TodoToCreate;
    }
    return new Todo(0, "New todo", DateTime.Now, false);
});

app.Run();

record CreateTodoArgs(int ProjectId, Todo? TodoToCreate);
record Todo(int Id, string Name, DateTime CreatedAt, bool IsCompleted);

这种行为与最小 API 的运行时代码生成的行为相匹配。

认证与授权

ASP.NET Core SPA 模板中的身份验证更新

ASP.NET Core React 和 Angular 项目模板不再依赖 Duende IdentityServer。相反,这些模板现在使用默认的 ASP.NET Core Identity UI 和 cookie 身份验证来处理单个用户帐户的身份验证。这使得我们可以创建安全的单页应用程序 (SPA) 项目,而无需配置和管理全功能 OpenID Connect (OIDC) 服务器的复杂性。对于仍需要完整 OIDC 支持的项目,您可以使用 Duende项目模板通过 ASP.NE T Core Identity 设置 Duende IdentityServer。

推荐 AuthorizationBuilder 用法的新分析器

在 .NET 7 中,我们引入了对 AddAuthorizationBuilder API 的支持,它允许用户使用更简洁的调用模式来注册授权服务和配置授权策略。在此预览版中,我们引入了新的 Roslyn 分析器,以支持在适用的情况下使用新的 AddAuthorizationBuilder API 更新到 terser 语法。

感谢社区成员David Acker对新的分析器的贡献!

提供反馈

我们希望您喜欢 .NET 8 中的 ASP.NET Core的预览版。请在GitHub上提交问题来告诉我们您对这些新改进的看法。

感谢您使用 ASP.NET Core!

Author

Mia Wu
Partner Technical Advisor

0 comments