.NET 7预览版6现已发布,其中包括对ASP.NET Core的许多伟大的新改进。
下面是这个预览版中的新内容摘要:
- 请求解压中间件
- 输出缓存中间件
- 对速率限制中间件的更新
- Kestrel对HTTP/2上的WebSockets的支持
- Kestrel在高核心机器上的性能改进
- 支持在W3CLogger中记录额外的请求头信息
- 空的Blazor项目模板
- WebAssembly上的System.Security.Cryptography支持
- Blazor自定义元素不再是实验性的
- 用于Blazor的实验性
QuickGrid组件 - gRPC JSON转码多段式参数
MapGroup支持更多的扩展方法
关于为.NET 7计划的ASP.NET Core工作的更多细节,请参见GitHub上完整的.NET 7的ASP.NET Core 路线图。
开始使用
要开始使用.NET 7 Preview 6中的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 5升级到.NET 7 Preview 6:
- 将所有Microsoft.AspNetCore.*包的引用更新为
7.0.0-preview.6.*。 - 将所有Microsoft.Extensions.*包的引用更新到
7.0.0-preview.6.*。
另请参见ASP.NET Core for .NET 7中的全部中断变化列表。
请求解压中间件
请求解压中间件是一个新的中间件,它使用Content-Encoding HTTP头来自动识别和解压具有压缩内容的请求,这样服务器的开发者就不需要自己处理。
请求解压中间件是使用IApplicationBuilder 上的UseRequestDecompression 扩展方法和IServiceCollection 的AddRequestDecompression 扩展方法添加的。
对Brotli (br)、Deflate (deflate)和GZip (gzip)的支持是开箱即有的。其他的内容编码可以通过注册一个自定义的解压提供者类来添加,该类实现了IDecompressionProvider 接口,同时在RequestDecompressionOptions 中添加了Content-Encoding 头值。
输出缓存中间件
输出缓存是一个新的中间件,它可以帮助你存储你的Web应用的结果,并从缓存中提供它们,而不是每次都计算它们,这可以提高性能并释放资源用于其他活动。
要开始使用输出缓存,请使用IServiceCollection 上的AddOutputCache 扩展方法和IApplicationBuilder 上的UseOutputCache 扩展方法。
一旦你这样做了,你就可以开始在你的端点上配置输出缓存了。下面是一个在返回时间戳的端点上使用输出缓存的简单例子:
app.MapGet("/notcached", () => DateTime.Now.ToString());
app.MapGet("/cached", () => DateTime.Now.ToString()).CacheOutput();
发送到"/notcached "的请求将看到当前的时间。但"/cached "端点使用了.CacheOutput() ,所以在第一次请求后,每次对"/cached "的请求都会得到一个缓存的响应(第一次请求后返回的时间不会更新)。
有许多更高级的方法来定制输出缓存。下面的例子显示了如何使用VaryByQuery ,根据查询参数来控制缓存:
// Cached entries will vary by culture, but any other additional query
// is ignored and returns the same cached content.
app.MapGet("/query", () => DateTime.Now.ToString()).CacheOutput(p => p.VaryByQuery("culture"));
有了这个配置,"/query "端点将根据culture 参数的值独特地缓存其输出。对/query?culture=en 的请求将得到一个缓存的响应,对/query?culture=es 的请求将得到一个不同的缓存响应。
除了通过查询字符串变化外,还支持通过头信息(VaryByHeader )或自定义值(VaryByValue )来改变缓存响应。
输出缓存中间件内置了对一些常见的缓存陷阱的保护。想象一下这样的情况:你有一个繁忙的网络服务,你撤销了/无效了一个受欢迎的缓存条目。当这种情况发生时,可能会有很多对该条目的请求同时出现,由于此时的响应不在缓存中,服务器将不得不处理每一个请求。
资源锁定是输出缓存的一个特点,它可以防止服务器被这些 "缓存踩踏 "所淹没。它通过只允许处理一个对资源的请求,而其余的请求则等待缓存条目被更新。一旦发生这种情况,所有等待的请求都可以从缓存中得到服务,防止服务器被多余的工作所淹没。
如果你想看一个更完整的例子来演示输出缓存的这些和其他功能,可以看看ASP.NET Core repo中的OutputCachingSample应用。
如果想更深入地了解输出缓存功能,请务必查看我们在Build 2022上展示的这个点播会议。请注意,自讲座录制以来,所讨论的一些功能/API已经改变。
我们计划继续为这个中间件添加功能,所以请让我们知道你想看到什么。
对速率限制中间件的更新
速率限制中间件现在支持对特定端点的速率限制,这可以与运行于所有请求的全局限制器相结合。下面是一个在端点上添加TokenBucketRateLimiter ,并使用一个全局ConcurrencyLimiter 的例子:
var coolEndpointName = "coolPolicy";
// Define endpoint limiters and a global limiter.
// coolEndpoint will be limited by a TokenBucket Limiter with a max permit count of 6 and a queue depth of 3.
var options = new RateLimiterOptions()
.AddTokenBucketLimiter(coolEndpointName, new TokenBucketRateLimiterOptions(3, QueueProcessingOrder.OldestFirst, 6, TimeSpan.FromSeconds(10), 1))
// The global limiter will be a concurrency limiter with a max permit count of 10 and a queue depth of 5.
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
{
return RateLimitPartition.CreateConcurrencyLimiter<string>("globalLimiter", key => new ConcurrencyLimiterOptions(10, QueueProcessingOrder.NewestFirst, 5));
});
app.UseRateLimiter(options);
app.MapGet("/", () => "Hello World!").RequireRateLimiting(coolEndpointName);
我们还支持通过RateLimiterOptions 上新的AddPolicy 方法来添加自定义速率限制策略。
要了解更多关于ASP.NET Core的速率限制,请查看《宣布.NET的速率限制》博文。
Kestrel对HTTP/2上的WebSockets的支持
WebSockets最初是为HTTP/1.1设计的,但后来被调整为在HTTP/2上工作。在HTTP/2上使用WebSockets可以让你利用头压缩和多路复用等新功能,减少向服务器发出多个请求时需要的时间和资源。现在Kestrel在所有支持HTTP/2的平台上都提供了这种支持。
HTTP版本协商在浏览器和Kestrel中是自动进行的,因此不需要新的API。你可以按照现有的ASP.NET Core样本,在HTTP/2上使用WebSockets。注意HTTP/2 WebSockets使用CONNECT请求而不是GET,所以你的路由和控制器可能需要更新。Chrome和Edge默认启用了HTTP/2 WebSockets,你可以在FireFox的about:config 页面上用network.http.spdy.websockets 标志启用它。
SignalR和SignalR浏览器的JavaScript客户端已经更新,以支持HTTP/2的WebSockets。对ClientWebSocket和YARP的支持也即将到来。
Kestrel在高核心机器上的性能改进
Kestrel使用ConcurrentQueue ,有很多用途。其中一个目的是在Kestrel的默认Socket 运输中调度I/O操作。根据相关的套接字来划分ConcurrentQueue ,可以减少争论,并提高具有许多CPU核心的机器的吞吐量。
最近在新的甚至更高的核心机器上进行的分析,如最近在Azure上提供的80核ARM64 Ampere Altra虚拟机,显示在Kestrel的另一个ConcurrentQueue 实例中存在大量的争用,即Kestrel用来缓存字节缓冲区的PinnedMemoryPool 。
在预览版6中,Kestrel的内存池现在的分区方式与它的I/O队列相同,导致高核心机器上的竞争大大降低,吞吐量也更高。我们看到,在前面提到的80核ARM64虚拟机上,TechEmpower明文基准的RPS提高了500%以上,在我们的HTTPS JSON基准上,48核AMD虚拟机的RPS 提高了近100%。

支持在W3CLogger中记录额外的请求头信息
现在你可以通过调用AdditionalRequestHeaders() onW3CLoggerOptions ,在使用W3C记录器时指定额外的请求头来记录:
services.AddW3CLogging(logging =>
{
logging.AdditionalRequestHeaders.Add("x-forwarded-for");
logging.AdditionalRequestHeaders.Add("x-client-ssl-protocol");
});
空的Blazor项目模板
Blazor有两个新的项目模板,可以从一张白纸开始。新的 "Blazor Server App Empty "和 "Blazor WebAssembly App Empty "项目模板就像它们的非空模板一样,但没有任何额外的演示代码。这些空模板只有一个非常基本的主页,而且我们还删除了Bootstrap,因此你可以从你喜欢的任何CSS框架开始。
一旦你安装了.NET 7 SDK,就可以在Visual Studio中使用这些新模板。

你也可以从命令行中使用空的Blazor项目模板:
dotnet new blazorserver-empty
dotnet new blazorwasm-empty
WebAssembly上的System.Security.Cryptography支持
在WebAssembly上运行时,.NET 6支持SHA系列的散列算法。.NET 7通过在可能的情况下利用SubtleCrypto,并在无法使用SubtleCrypto时退回到.NET的实现,来实现更多的加密算法。在.NET 7预览6中,以下算法在WebAssembly上得到支持:
- SHA1
- SHA256
- SHA384
- SHA512
- HMACSHA1
- HMACSHA256
- HMACSHA384
- HMACSHA512
对AES-CBC、PBKDF2和HKDF算法的支持计划在未来的.NET 7更新中进行。
欲了解更多信息,请参见dotnet/runtime#40074。
Blazor自定义元素不再是实验性的
之前用于用Blazor构建基于标准的自定义元素的试验性Microsoft.AspNetCore.Components.CustomElements包已不再是试验性的,现在是.NET 7版本的一部分。
要使用Blazor创建一个自定义元素,可以像这样将一个Blazor根组件注册为一个自定义元素:
options.RootComponents.RegisterCustomElement<Counter>("my-counter");
然后,你可以将这个自定义元素用于你想要的任何其他网络框架:
<my-counter increment-amount="10"></my-counter>
有关其他细节,请参考Blazor关于构建自定义元素的文档。
用于Blazor的实验性QuickGrid 组件
QuickGrid QuickGrid 是Blazor的一个新的实验性组件,用于快速有效地以表格形式显示数据。它为最常见的需求提供了一个简单方便的数据网格组件,同时也为构建Blazor数据网格组件的人提供了一个参考架构和性能基准。
要开始使用QuickGrid :
-
添加对Microsoft.AspNetCore.Components.QuickGrid包的引用。
dotnet add package Microsoft.AspNetCore.Components.QuickGrid --prerelease -
添加以下Razor代码来渲染一个非常简单的网格:
<QuickGrid Items="@people"> <PropertyColumn Property="@(p => p.PersonId)" Sortable="true" /> <PropertyColumn Property="@(p => p.Name)" Sortable="true" /> <PropertyColumn Property="@(p => p.BirthDate)" Format="yyyy-MM-dd" Sortable="true" /> </QuickGrid> @code { record Person(int PersonId, string Name, DateOnly BirthDate); IQueryable<Person> people = new[] { new Person(10895, "Jean Martin", new DateOnly(1985, 3, 16)), new Person(10944, "António Langa", new DateOnly(1991, 12, 1)), new Person(11203, "Julie Smith", new DateOnly(1958, 10, 10)), new Person(11205, "Nur Sari", new DateOnly(1922, 4, 27)), new Person(11898, "Jose Hernandez", new DateOnly(2011, 5, 3)), new Person(12130, "Kenji Sato", new DateOnly(2004, 1, 9)), }.AsQueryable(); }
您可以在QuickGrid for Blazor演示网站上看到QuickGrid ,其中包括以下实例:
QuickGrid 是高度优化的,并使用先进的技术来实现最佳的渲染性能。QuickGrid演示网站是使用Blazor WebAssembly构建的,并托管在GitHub Pages上。由于使用了jsakamoto的BlazorWasmPrerendering.Build项目的静态预渲染,该网站加载速度很快。
在QuickGrid ,这不是我们的目标,因为全面的商业网格往往具有所有的功能,例如分层的行、拖动排序的列或类似Excel的范围选择。如果你需要这些,请继续使用商业网格或其他开源选项。
QuickGrid 目前是实验性的,这意味着它没有得到官方支持,也没有承诺作为.NET 7或任何未来的.NET版本的一部分。但是, QuickGrid是开源的,可以免费使用。我们希望你能试一试,并让我们知道你的想法!
gRPC JSON转码的多段式参数
gRPC JSON转码是.NET 7中的一项新功能,用于将gRPC APIs变成RESTful APIs。
预览6中的新功能是支持gRPC JSON转码路由中的多段参数。这项功能使ASP.NET Core转码与gRPC生态系统中的其他转码解决方案相提并论。
现在可以配置gRPC APIs,将属性与多段参数绑定。例如,/v1/{book=shelves/*/books/*} 指定了一个带有多段图书参数的路由。它与/v1/shelves/user-name/books/cool-book 这样的URL相匹配,而书的参数值为shelves/user-name/books/cool-book 。
关于这种路由语法的更多信息,请参阅gRPC转码路径语法。
MapGroup 支持更多的扩展方法
路由组是在.NET 7 Preview 4中引入的,允许用一个共同的路由前缀和一套共同的约定来定义端点组。惯例包括扩展方法,如RequireAuthorization(),RequireCors() 及其相关属性。惯例将元数据添加到其他中间件(授权、CORS等)通常采取行动的端点。
WithTags(),WithDescription(),ExcludeFromDescription() 和WithSummary() 以前只针对RouteHandlerBuilder 而不是IEndpointConventionBuilder 接口,使得它们与MapGroup() 所返回的RouteGroupBuilder 不兼容。从Preview 6开始,这些扩展方法现在针对IEndpointConventionBuilder,使它们与路由组兼容。
同样地,WithOpenApi() 和AddFilter() 的目标是RouteHandlerBuilder 而不是IEndpointConventionBuilder ,这使得它们与路由组不兼容。支持这些需要重新思考组的内部工作方式,以允许组约定既观察关于单个端点的元数据,又修改元数据,甚至为端点生成的最终RequestDelegate 。这是通过改变EndpointDataSource实现的,但关键的收获是,你现在可以在Preview 6中调用路由组的WithOpenApi() 和AddFilter() 。
注意。
AddFilter()在Preview 6中已更名为AddRouteHandlerFilter(),在Preview 7中开始将再次更名为AddEndpointFilter()。
var todos = app.MapGroup("/todos")
.WithTags("todos", "some_other_tag")
.WithOpenApi()
.AddRouteHandlerFilter((endpointContext, next) =>
{
var logger = endpointContext.ApplicationServices.GetRequiredService<ILogger<Program>>();
return invocationContext =>
{
logger.LogInformation($"Received request for: {invocationContext.HttpContext.Request.Path}");
return next(invocationContext);
};
});
todos.MapGet("/{id}", GetTodo);
todos.MapGet("/", GetAllTodos).WithTags("get_all_todos");
todos.MapPost("/", CreateTodo).WithOpenApi(operation =>
{
operation.Summary = "Create a todo.";
return operation;
});
提供反馈
我们希望你喜欢这个.NET 7中的ASP.NET Core预览版。通过在GitHub上提交问题,让我们知道你对这些新的改进有什么看法。
感谢你尝试使用ASP.NET Core!