.NET 7 Preview 7中的ASP.NET Core更新——亮点展示

410 阅读8分钟

.NET 7预览版现已发布,其中包括对ASP.NET Core的许多伟大的新改进。

下面是这个预览版中的新内容摘要:

  • 新的Blazor WebAssembly加载页面
  • Blazor数据绑定的get/set/after修改器
  • Blazor虚拟化改进
  • 使用传递状态NavigationManager
  • 对WebAssembly的额外System.Security.Cryptography 支持
  • 更新Angular和React模板
  • gRPC JSON转码的性能
  • 认证将使用单一方案作为DefaultScheme
  • IFormFile/IFormFileCollection 在最小的API中对认证请求的支持
  • 新的问题细节服务
  • 诊断中间件更新
  • 新的HttpResults 接口

关于为.NET 7计划的ASP.NET Core工作的更多细节,请参阅GitHub上完整的.NET 7的ASP.NET Core路线图

开始使用

要开始使用.NET 7 Preview 7中的ASP.NET Core,请安装.NET 7 SDK

如果你在Windows上使用Visual Studio,我们建议安装最新的Visual Studio 2022预览版。如果你在macOS上,我们建议安装最新的Visual Studio 2022 for Mac预览版

要安装最新的.NET WebAssembly构建工具,请在高位命令提示符下运行以下命令:

dotnet workload install wasm-tools

注意:用.NET 7 SDK和.NET 7 WebAssembly构建工具构建.NET 6 Blazor项目,目前还不支持。这将在未来的.NET 7更新中解决:dotnet/runtime#65211

升级一个现有的项目

要将现有的ASP.NET Core应用程序从.NET 7 Preview 6升级到.NET 7 Preview 7:

  • 将所有Microsoft.AspNetCore.*包的引用更新为7.0.0-preview.7.*
  • 将所有Microsoft.Extensions.*包的引用更新到7.0.0-preview.7.*

另请参见ASP.NET Core for .NET 7中的全部中断变化列表。

新的Blazor加载页面

Blazor WebAssembly项目模板有一个新的加载UI,显示应用程序的加载进度。

Blazor WebAssembly loading screen

新的加载界面是在Blazor WebAssembly模板中用HTML和CSS实现的,使用Blazor WebAssembly提供的两个新的CSS自定义属性(变量)。

  • --blazor-load-percentage:已加载的应用程序文件的百分比。
  • --blazor-load-percentage-text:已加载的应用程序文件的百分比,四舍五入到最近的整数。

使用这些新的CSS变量,你可以创建一个自定义的加载UI,与你自己的Blazor WebAssembly应用程序的风格相匹配。

Blazor数据绑定get/set/after修改器

Blazor提供了一个强大的数据绑定功能,用于在UI元素或组件参数与.NET对象之间建立双向绑定关系。在.NET 7中,您现在可以使用新的@bind:after 修改器在绑定事件完成后轻松运行异步逻辑:

<input @bind="searchText" @bind:after="PerformSearch" />

@code {
    string searchText;

    async Task PerformSearch()
    {
        // ... do something asynchronously with 'searchText' ...
    }
}

在这个例子中,PerformSearch async方法将在检测到搜索文本的任何变化后自动运行。

现在为组件参数设置绑定也更容易了。组件可以通过为值和回调定义一对参数来支持双向数据绑定,当值发生变化时回调会被调用。新的@bind:get@bind:set 修改器现在使创建一个与底层UI元素绑定的组件参数变得简单:

<input @bind:get="Value" @bind:set="ValueChanged" />

@code {
    [Parameter] public TValue Value { get; set; }
    [Parameter] public EventCallback<TValue> ValueChanged { get; set; }
}

@bind:get@bind:set 修改器总是一起使用。@bind:get 修饰符指定了要绑定的值,而@bind:set 修饰符则指定了一个回调,当该值发生变化时就会调用。

Blazor虚拟化的改进

Blazor的Virtualize 组件渲染了一个间隔元素来定义滚动区域的垂直高度。默认情况下,它使用这样的一个div 元素:

<div style="height: 12345px"></div>

然而,在某些情况下,父元素可能不允许子div 元素。例如,父元素可能是一个tbody ,它只允许子元素tr 。对于这些情况,你现在可以使用新的SpacerElement 参数来配置Virtualize 使用的间隔元素:

<tbody>
  <Virtualize SpacerElement="tr">...</Virtualize>
</tbody>

传递状态使用NavigationManager

现在你可以在Blazor应用程序中使用NavigationManager ,在导航时传递状态。

navigationManager.NavigateTo("/orders", new NavigationOptions { HistoryEntryState = "My state" });

这种机制允许不同页面之间进行简单的通信。指定的状态会被推送到浏览器的历史堆栈中,以便以后在监听位置改变事件时,可以使用NavigationManager.HistoryEntryState 属性或LocationChangedEventArgs.HistoryEntryState 属性来访问它。

在WebAssembly上的额外System.Security.Cryptography 支持

在WebAssembly上运行时,.NET 6支持SHA系列的散列算法。.NET 7通过在可能的情况下利用SubtleCrypto,并在无法使用SubtleCrypto时退回到.NET的实现,实现更多的加密算法。在.NET 7预览版中,以下算法现在在WebAssembly上得到支持:

  • sha1, sha256, sha384, sha512
  • hmacsha1, hmacsha256, hmacsha384, hmacsha512
  • Aes (只支持CBC模式)
  • Rfc2898DeriveBytes (PBKDF2)
  • 香港DF

更新了Angular和React模板

我们将Angular项目模板更新为Angular 14,React项目模板更新为React 18.2。

gRPC JSON转码的性能

gRPC JSON转码是.NET 7中的一项新功能,用于将gRPC APIs变成RESTful APIs。

.NET 7预览版改善了序列化消息时的性能和内存使用。gRPC JSON转码将gRPC消息序列化为标准化的JSON格式。在Preview 7之前,转码需要一个自定义的JsonConverter ,以定制JSON序列化。这个版本用System.Text.Json的新合约定制功能取代了JsonConverter

下面的基准结果比较了使用合同定制之前和之后的gRPC消息序列化。

方法平均值比率所分配的
序列化消息_转换器386.7 ns1.00160 B
序列化信息_合同213.3 ns0.5580 B
反序列化消息_转换器296.0 ns1.00304 B
反序列化消息_合同167.6 ns0.57224 B

一个自定义的契约和System.Text.Json的高性能序列化器极大地提高了性能和分配。

认证将使用单一方案作为DefaultScheme

作为简化认证工作的一部分,当只注册了一个单一的认证方案时,它将自动作为DefaultScheme ,在这种情况下,就不需要在AddAuthentication() 中指定DefaultScheme 。这种行为可以通过以下方式禁用AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme")

IFormFile/IFormFileCollection 支持最小API中的认证请求

在.NET 7预览版1中,我们引入了对使用IFormFileIFormFileCollection在最小的API中处理文件上传的支持。.NET预览版7增加了对使用Authorization 头、客户端证书或cookie头的最小API的认证文件上传请求的支持。

在最小的API中没有对防伪的内置支持。然而,它可以使用IAntiforgery 服务来实现

新的问题细节服务

.NET 7 Preview 7引入了一个基于IProblemDetailsService 接口的新的问题详情服务,用于在你的应用程序中生成一致的问题详情响应。

要添加问题细节服务,请使用IServiceCollection 上的AddProblemDetails 扩展方法:

builder.Services.AddProblemDetails();

然后,你可以通过调用IProblemDetailsService.WriteAsync ,从你的应用程序中的任何一层写出问题细节响应。例如,这里是你如何从中间件生成一个问题细节响应。

httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;

if (context.RequestServices.GetService<IProblemDetailsService>() is { } problemDetailsService)
{
    return problemDetailsService.WriteAsync(new { HttpContext = httpContext });
}

return ValueTask.CompletedTask;

你可以使用ProblemDetailsOptions ,定制由服务生成的问题细节响应(包括为API控制器自动生成的响应):

builder.Services.AddProblemDetails(options =>
{
    options.CustomizeProblemDetails = (context) => 
    {
       context.ProblemDetails.Extensions.Add("my-extension", new { Property = "value" });
    };
});

此外,你可以创建你自己的IProblemDetailsWriter 实现来进行高级定制:

public class CustomWriter : IProblemDetailsWriter
{
    // Indicates that only responses with StatusCode == 400
    // will be handled by this writer. All others will be
    // handled by different registered writers if available.
    public bool CanWrite(ProblemDetailsContext context) 
        => context.HttpContext.Response.StatusCode == 400;

    public Task WriteAsync(ProblemDetailsContext context)
    {
        //Additional customizations

        // Write to the response
        context.HttpResponse.Response.WriteAsJsonAsync(context.ProblemDetails);
    }        
}

在调用AddProblemDetails 方法之前,注册任何IProblemDetailsWriter 实现:

builder.Services.AddSingleton<IProblemDetailsWriter, CustomWriter>();
builder.Services.AddProblemDetails();

诊断中间件的更新

当新的问题细节服务(IProblemDetailsService)被注册时,以下中间件被更新以生成问题细节的HTTP响应:

  • ExceptionHandlerMiddleware:当没有定义自定义处理程序时,生成问题细节响应,除非不被客户端接受。
  • StatusCodePagesMiddleware:默认生成问题详情响应,除非不被客户端接受。
  • DeveloperExceptionPageMiddleware:当text/html ,除非不被客户端接受,否则生成一个问题细节响应。

下面的例子配置了应用程序,以便为所有尚未有正文内容的HTTP客户端和服务器错误响应生成一个问题细节响应:

var builder = WebApplication.CreateBuilder(args);

// Add services to the containers
builder.Services.AddControllers();
builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();
app.UseStatusCodePages();

//Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.MapControllers();
app.Run();

新的HttpResults 接口

.NET 7预览版3中,ASP.NET Core中实现IResult 的类型被公开了。 现在,在这个预览中,我们在Microsoft.AspNetCore.Http.Http 名称空间中引入了新的接口来描述IResult 类型:

  • Microsoft.AspNetCore.Http.IContentTypeHttpResult
  • Microsoft.AspNetCore.Http.IFileHttpResult
  • Microsoft.AspNetCore.Http.INestedHttpResult
  • Microsoft.AspNetCore.Http.IStatusCodeHttpResult
  • Microsoft.AspNetCore.Http.IValueHttpResult
  • Microsoft.AspNetCore.Http.IValueHttpResult<TValue>

有了这些接口,你现在有了一种更通用的方法,可以在运行时检测IResult 类型,这是过滤器实现中的一种常见模式:

app.MapGet("/weatherforecast", (int days) =>
{
    if (days <= 0)
    {
        return Results.BadRequest();
    }

    var forecast = Enumerable.Range(1, days.Value).Select(index =>
       new WeatherForecast (DateTime.Now.AddDays(index), Random.Shared.Next(-20, 55), "Cool"))
        .ToArray();
    return Results.Ok(forecast);
}).
AddEndpointFilter(async (context,next) =>
{
    var result = await next(context);

    return result switch
    {
        IValueHttpResult<WeatherForecast[]> weatherForecastResult => new WeatherHttpResult(weatherForecastResult.Value),
        _ => result
    };
});

internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)

给予反馈

我们希望你喜欢这个.NET 7中的ASP.NET Core预览版。通过在GitHub上提交问题,让我们知道你对这些新的改进有什么看法。

感谢你尝试使用ASP.NET Core!