我们都知道在6月12日的时候微软发布了.NET Core 3.0的第6个预览版。针对.NET Core
3.0的发布我们国内的微软MVP-汪宇杰还发布的官翻版的博文进行了详细的介绍。具体的可以关注“汪宇杰博客”公众号,或者我的“DotNetCore实战”公众号然后在历史文章里面进行查阅。而我们这篇文章将会介绍本次更新中对ASP.NET
Core和Blazor所做的更新。当然本文的大部分内容翻译自ASP.NET的首席项目经理Daniel Roth
<https://devblogs.microsoft.com/aspnet/author/danroth27/>的介绍。

注:英语能力好的可以直接到文章末尾查看英文链接进行阅读。
作者:依乐祝
本文链接:https://www.cnblogs.com/yilezhu/p/11031441.html

以下是此预览版中的新增功能列表:

* 新Razor特性:@attribute,@code,@key,@namespace,@functions中的标记
* Blazor指令属性
* Blazor应用程序的身份验证和授权支持

* Razor类库中的静态资产

* Json.NET不再在项目模板中引用

* 证书和Kerberos身份验证

* SignalR自动重新连接

* 托管gRPC客户端

* gRPC客户端工厂

* gRPC拦截器
有关其他详细信息和已知问题,请参阅发行说明 <https://aka.ms/netcore3releasenotes>。

开始

要在.NET Core 3.0 Preview 6中开始使用ASP.NET Core,请安装.NET Core 3.0 Preview 6 SDK
<https://aka.ms/netcore3download>

如果您在Windows上使用Visual Studio进行的话,则还需要安装Visual Studio 2019的最新预览
<https://visualstudio.com/preview>。

对于最新的客户端Blazor模板,还可以从Visual Studio Marketplace 安装最新的Blazor扩展
<https://go.microsoft.com/fwlink/?linkid=870389>。

升级现有项目

要将现有的ASP.NET Core应用程序升级到.NET Core 3.0 Preview 6,请按照ASP.NET Core文档中
<https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30>的迁移步骤进行操作
<https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30>。

另请参阅ASP.NET Core 3.0 中的重大更改
<https://github.com/aspnet/announcements/issues?utf8=✓&q=is%3Aissue+label%3A3.0.0+label%3A%22Breaking+change%22>
的完整列表。

要将现有的ASP.NET Core 3.0 Preview 5项目升级到Preview 6:

* 更新Microsoft.AspNetCore.*包引用到3.0.0-preview6.19307.2
* 在Blazor应用程序中:
* 重命名@functions为@code
* 更新Blazor特定属性和事件处理程序以使用新的指令属性语法(参见下文)
* 删除任何关于app.UseBlazor<TStartup>()的调用,换成在app.UseRouting()调用之前调用
app.UseClientSideBlazorFiles<TStartup>()的方式。还要在app.UseEndpoints()的调用中调用
endpoints.MapFallbackToClientSideBlazor<TStartup>("index.html")。
之前的调用方式
app.UseRouting(); app.UseEndpoints(endpoints => {
endpoints.MapDefaultControllerRoute(); }); app.UseBlazor<Client.Startup>();
更新之后的调用方式
app.UseClientSideBlazorFiles<Client.Startup>(); app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html"); });
Razor的新特性

我们(因为是ASP.NET的首席项目经理Daniel Roth写的博客,所以用第一人称)在此版本中添加了对以下新Razor语言功能的支持。

@attribute

新的@attribute指令将指定的属性添加到生成的类中。
@attribute [Authorize]
@code

.razor文件(在.cshtml文件中不支持)中使用了新的@code指令来指定要作为附加成员添加到生成的类中的代码块。它相当于@functions
,但现在有了更好的名称。
@code { int currentCount = 0; void IncrementCount() { currentCount++; } }
@key

.razor文件中使用了新的@key指令属性,以指定Blazor diffing算法可用于保留列表中的元素或组件的值(任何对象或唯一标识符)。
<div> @foreach (var flight in Flights) { <DetailsCard @key="flight"
Flight="@flight" /> } </div>
要了解需要此功能的原因,请想象一下不实用此功能来呈现包含航班详细信息的卡片列表的场景:
<div> @foreach (var flight in Flights) { <DetailsCard Flight="@flight" /> }
</div>
如果将新航班添加到航班列表的中间,则现有的详细信息卡实例应保持不受影响,并且应在呈现的输出中插入一个新的详细信息卡。

要想象这个,如果Flights以前包含[F0, F1, F2],那么这是之前的状态:

* DetailsCard0,Flight = F0
* DetailsCard1,Flight = F1
* DetailsCard2,Flight = F2
......如果我们在索引1中插入一个新的项目fnew,这就是所期望的插入之后的状态:

* DetailsCard0,Flight = F0
* DetailsCardNew,Flight = FNew
* DetailsCard1,Flight = F1
* DetailsCard2,Flight = F2
但是,实际插入后的状态如下:

* DetailsCard0,Flight = F0
* DetailsCard1,Flight = FNew
* DetailsCard2,Flight = F1
* DetailsCardNew,Flight = F2

系统无法知道DetailsCard2或DetailsCard3应保留它们与旧航班实例的关联,因此它只会将它们与列表中与其位置匹配的航班重新关联。因此,DetailsCard1和DetailsCard2使用新数据完全重建自己,这是浪费的,有时甚至会导致用户可见问题(例如,输入焦点意外丢失)。

通过使用@keydiffing算法添加键可以关联新旧元素或组件。

@namespace

在*_Imports.razor*文件中使用时,指定生成的类或名称空间前缀的名称空间。该@namespace
指令现在适用于页面和视图(.cshtml)应用程序,但现在它也支持组件(.razor)。
@namespace MyNamespace
标记@functions和本地功能

在视图和页面(.cshtml文件)中,您现在可以在@functions块和本地函数中的方法内添加标记。
@{ GreetPerson(person); } @functions { void GreetPerson(Person person) {
<p>Hello, <em>@person.Name!</em></p> } }
Blazor指令属性


Blazor使用各种属性来影响组件的编译方式(例如ref,bind,事件处理程序等)。随着时间的推移,这些属性已经有机地添加到Blazor并使用不同的语法。在这个Blazor版本中,我们已经标准化了指令属性的通用语法。这使得Blazor使用的Razor语法更加一致和可预测。它还为未来的可扩展性铺平了道路。

指令属性都遵循以下语法,其中括号中的值是可选的:
@directive(-suffix(:name))(="value")
一些有效的例子:
<!-- directive --> <div @directive>...</div> <div @directive="value"></div>
<!-- directive with key/value arg--> <div @directive:key>...</div> <div
@directive:key="value"></div> <!-- directive with suffix --> <div
@directive-suffix></div> <div @directive-suffix="value"></div> <!-- directive
with suffix and key/value arg--> <div @directive-suffix:key></div> <div
@directive-suffix:key="value"></div>
所有Blazor内置指令属性都已更新为使用此新语法,如下所述。

事件处理程序

在Blazor中指定事件处理程序现在使用新的指令属性语法而不是普通的HTML语法。语法类似于HTML语法,但现在具有前导@
字符。这使得C#事件处理程序与JS事件处理程序不同。
<button @onclick="@Clicked">Click me!</button>
为C#事件处理程序指定委托时,@属性值当前仍需要前缀,但我们希望在将来的更新中删除此要求。

在将来,我们还希望使用指令属性语法来支持事件处理程序的其他功能。例如,停止事件传播可能看起来像这样(尚未实现,但它让您了解现在由指令属性启用的方案):
<button @onclick="Clicked" @onclick:stopPropagation>Click me!</button>
捆绑
<input @bind="myValue">...</input> <input @bind="myValue"
@bind:format="mm/dd">...</input> <MyButton @bind-Value="myValue">...</MyButton>

<div @key="id">...</div>
参考
<button @ref="myButton">...</button>
Blazor应用程序的身份验证和授权支持

Blazor现在内置了对处理身份验证和授权的支持。服务器端Blazor模板现在支持使用ASP.NET Core Identity,Azure
AD和Azure AD B2C启用所有标准身份验证配置的选项。我们还没有更新Blazor WebAssembly模板以支持这些选项,但我们计划在.NET
Core 3.0发布之后这样做。

要创建启用了身份验证的新Blazor应用程序:

*

创建一个新的Blazor(服务器端)项目,然后选择链接以更改身份验证配置。例如,选择“个人用户帐户”和“在应用程序中存储用户帐户”以将Blazor与ASP.NET
Core Identity一起使用:



*
运行应用程序。该应用程序包含顶行中的链接,用于注册为新用户并登录。



*
选择“注册”链接以注册新用户。



*
选择“应用迁移”以将ASP.NET Core Identity迁移应用于数据库。



*
你现在应该登录了。



*
选择您的用户名以编辑您的用户个人资料。



在Blazor应用程序中,Startup使用标准ASP.NET Core中间件在类中配置身份验证和授权。
app.UseRouting(); app.UseAuthentication(); app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers();
endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); });
使用ASP.NET Core Identity时,所有与身份相关的UI问题都由框架提供的默认身份UI处理。
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
应用程序顶行中的身份验证相关链接使用新的内置AuthorizeView组件呈现,该组件根据身份验证状态显示不同的内容。

LoginDisplay.razor
<AuthorizeView> <Authorized> <a href="Identity/Account/Manage">Hello,
@context.User.Identity.Name!</a> <a href="Identity/Account/LogOut">Log out</a>
</Authorized> <NotAuthorized> <a href="Identity/Account/Register">Register</a>
<a href="Identity/Account/Login">Log in</a> </NotAuthorized> </AuthorizeView>
该AuthorizeView组件仅在授权用户时显示其子内容。可替代地,AuthorizeView采用参数用于指定不同模板当用户是Authorized,
NotAuthorized,或Authorizing。当前的身份验证状态通过隐式context参数传递给这些模板。您还可以指定AuthorizeView
用户必须满足的特定角色或授权策略才能查看授权视图。

要授权访问Blazor应用程序中的特定页面,请使用普通的[authorize]属性。可以使用新的@attribute指令将[authorize]
属性应用于组件。。
@using Microsoft.AspNetCore.Authorization @attribute [Authorize] @page
"/fetchdata"
要指定在未授权用户或仍处于授权处理时需要授权的页面上显示的内容,请使用组件上的NotAuthorizedContent和AuthorizingContent
参数Router。这些Router参数仅在此版本的客户端Blazor中提供支持,但在将来的更新中将为服务器端Blazor启用它们。

AuthenticationStateProvider
无论是在服务器上运行还是在浏览器中运行客户端,新服务都会以统一的方式使Blazor应用程序可以使用身份验证状态。在服务器端Blazor应用程序中
AuthenticationStateProvider,用户从HttpContext
建立与服务器的连接的表面。客户端Blazor应用程序可以根据应用程序配置自定义AuthenticationStateProvider
。例如,它可以通过查询服务器上的端点来检索当前用户信息。

Task<AuthenticationState>使用该CascadingAuthenticationState
组件将身份验证状态作为级联值提供给应用程序。然后,AuthorizeView和Router组件使用此级联值来授权对UI的特定部分的访问。

App.razor
<CascadingAuthenticationState> <Router AppAssembly="typeof(Startup).Assembly">
<NotFoundContent> <p>Sorry, there's nothing at this address.</p>
</NotFoundContent> </Router> </CascadingAuthenticationState>
Razor类库中的静态资产

Razor类库现在可以包含静态资源,如JavaScript,CSS和图像。然后,可以通过引用Razor类库项目或通过包引用将这些静态资产包含在ASP.NET
Core应用程序中。

要在Razor类库中包含静态资源,请将一个wwwroot文件夹添加到Razor类库中,并在该文件夹中包含所有必需的文件。

当具有静态资产的Razor类库被引用为项目引用或作为包时,来自库的静态资源在路径前缀*_content / {LIBRARY NAME}
/*下可供应用程序使用。静态资源保留在其原始文件夹中,Razor类库中静态资产内容的任何更改都会反映在应用程序中而不进行重建。

发布应用程序后,所有引用的Razor类库中的伴随资源将以相同的前缀复制到已发布应用程序的wwwroot文件夹中。

要尝试使用Razor类库中的静态资源:

*
创建默认的ASP.NET Core Web App。
dotnet new webapp -o WebApp1
*
创建一个Razor类库并从Web应用程序引用它。
dotnet new razorclasslib -o RazorLib1 dotnet add WebApp1 reference RazorLib1
*
将wwwroot文件夹添加到Razor类库,并包含一个JavaScript文件,该文件将简单消息记录到控制台。
cd RazorLib1 mkdir wwwroot
hello.js
console.log("Hello from RazorLib1!");
*
从Web应用程序中的Index.cshtml引用脚本文件。
<script src="_content/RazorLib1/hello.js"></script>
*
运行应用程序并在浏览器控制台中查找输出。
Hello from RazorLib1!
项目现在默认使用System.Text.Json

现在,新的ASP.NET
Core项目将默认使用System.Text.Json进行JSON处理。在此版本中,我们从项目模板中删除了Json.NET(Newtonsoft.Json)。要启用对使用Json.NET的支持,请将Microsoft.AspNetCore.Mvc.NewtonsoftJson包添加到项目中,并
AddNewtonsoftJson()在Startup.ConfigureServices方法中添加对以下代码的调用。例如:
services.AddMvc() .AddNewtonsoftJson();
证书和Kerberos身份验证

预览6为ASP.NET Core带来了证书和Kerberos身份验证。

证书身份验证要求您将服务器配置为接受证书,然后在Startup.Configure中添加身份验证中间件和在Startup.ConfigureServices
中配置证书身份验证服务。
public void ConfigureServices(IServiceCollection services) {
services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(); //
All the other service configuration. } public void
Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseAuthentication(); // All the other app configuration. }

证书身份验证选项包括接受自签名证书,检查证书吊销以及检查提供的证书中是否包含正确的使用标记的功能。默认用户主体是从证书属性构造的,其中包含一个允许您补充或替换主体的事件。有关如何为证书身份验证配置公共主机的所有选项和说明,请参阅
文档
<https://docs.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-3.0>



我们还将“Windows身份验证”扩展到Linux和macOS上。以前,此身份验证类型仅限于IIS和HttpSys,但现在Kestrel可以使用Microsoft.AspNetCore.Authentication.Negotiate
nuget包在Windows,Linux和macOS上为Windows域加入的主机使用Negotiate,Kerberos和NTLM。与配置身份验证应用程序范围的其他身份验证服务一样,然后配置服务:
public void ConfigureServices(IServiceCollection services) {
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate(); } public void Configure(IApplicationBuilder app,
IHostingEnvironment env) { app.UseAuthentication(); // All the other app
configuration. }

必须正确配置主机。Windows主机必须将SPN添加到托管应用程序的用户帐户。必须将Linux和macOS计算机加入域,然后必须为Web进程创建SPN,以及在主机上生成和配置的
keytab
<https://blogs.technet.microsoft.com/pie/2018/01/03/all-you-need-to-know-about-keytab-files/>
文件。文档
<https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0>
中给出了完整的说明。

SignalR自动重新连接

此预览版本现已通过npm install @aspnet/[email protected] 和.NET Core SignalR
Client方式进行提供,包括一个新的自动重新连接功能。在这个版本中,我们已经将withAutomaticReconnect()方法添加到了
HubConnectionBuilder
。默认情况下,客户端将尝试立即重新连接,并在2、10和30秒后重新连接。参与自动重新连接是可选的,但通过这种新方法很简单。
const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub")
.withAutomaticReconnect() .build();
通过将一系列基于毫秒的持续时间传递给该方法,您可以非常精细地了解重新连接尝试如何随时间发生。
.withAutomaticReconnect([0, 3000, 5000, 10000, 15000, 30000])
//.withAutomaticReconnect([0, 2000, 10000, 30000]) yields the default behavior
或者,您可以传递自定义重新连接策略的实现,该策略可以让您完全控制。


如果30秒后重新连接失败(或您设置的最大值),客户端会假定连接处于脱机状态,并停止尝试重新连接。在这些重新连接尝试期间,您将希望更新应用程序UI,以向用户提供尝试重新连接的提示。

重新连接事件处理程序

为了简化这一过程,我们将SignalR客户端API扩展为包含onreconnecting和onreconnected事件处理程序。第一个处理程序
onreconnecting为开发人员提供了一个禁用UI或让用户知道应用程序处于脱机状态的好机会。
connection.onreconnecting((error) => { const status = `Connection lost due to
error "${error}". Reconnecting.`;
document.getElementById("messageInput").disabled = true;
document.getElementById("sendButton").disabled = true;
document.getElementById("connectionStatus").innerText = status; });
同样,onreconnected处理程序使开发人员有机会在重新建立连接后更新UI。
connection.onreconnected((connectionId) => { const status = `Connection
reestablished. Connected.`; document.getElementById("messageInput").disabled =
false; document.getElementById("sendButton").disabled = false;
document.getElementById("connectionStatus").innerText = status; });
了解有关自定义和处理重新连接的详细信息

预览版本中已经部分记录了自动重新连接。请访问https://aka.ms/signalr/auto-reconnect
<https://aka.ms/signalr/auto-reconnect>,查看有关该主题的更深入的文档,以及有关使用的更多示例和详细信息。

托管gRPC客户端

在之前的预览中,我们依靠Grpc.Core库来获取客户端支持。HttpClient在此预览中添加HTTP / 2支持使我们能够引入完全托管的gRPC客户端。

要开始使用新客户端,请添加包引用Grpc.Net.Client,然后您可以创建新客户端。
var httpClient = new HttpClient() { BaseAddress = new
Uri("https://localhost:5001") }; var client =
GrpcClient.Create<GreeterClient>(httpClient);
gRPC客户端工厂

基于我们介绍的固定模式HttpClientFactory,我们添加了一个gRPC客户端工厂,用于在项目中创建gRPC客户端实例。我们添加了两种工厂:
Grpc.Net.ClientFactory和Grpc.AspNetCore.Server.ClientFactory。

该Grpc.Net.ClientFactory设计用于non-ASP.NET应用模型的使用(如工人服务)仍然使用Microsoft.Extensions.*
原语不会对ASP.NET核心的依赖。

该Grpc.Net.ClientFactory
设计用于仍使用Microsoft.Extensions.*基元(不依赖于ASP.NET核心)的非ASP.NET应用程序模型(如Worker Services)。

在执行服务到服务通信的应用程序中,我们经常发现大多数服务器也是使用其他服务的客户端。在这些情况下,我们建议使用
Grpc.AspNetCore.Server.ClientFactory它具有自动传播gRPC截止日期和取消令牌的功能。

要使用客户端工厂,请在将以下代码添加到configureServices()之前,将适当的包引用添加到项目(
Grpc.AspNetCore.Server.Factory或Grpc.Net.ClientFactory)。
services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new
Uri("https://localhost:5001"); });
gRPC拦截器


gRPC公开了一种机制来拦截客户端和服务器上的RPC调用。拦截器可以与现有的HTTP中间件结合使用。与HTTP中间件不同,拦截器允许您在序列化之前(在客户端上)和反序列化之后(在服务器上)访问实际的请求/响应对象,反之亦然。所有中间件都在请求端的拦截器之前运行,反之亦然。

客户端拦截器

与客户端工厂一起使用时,可以添加客户端拦截器,如下所示。
services .AddGrpcClient<GreeterClient>(options => { options.BaseAddress = new
Uri("https://localhost:5001"); }) .AddInterceptor<CallbackInterceptor>();
服务器拦截器

服务器拦截器可以ConfigureServices()如下所示进行注册。
services .AddGrpc(options => { // This registers a global interceptor
options.Interceptors.Add<MaxStreamingRequestTimeoutInterceptor>(TimeSpan.FromSeconds(30));
}) .AddServiceOptions<GreeterService>(options => { // This registers an
interceptor for the Greeter service
options.Interceptors.Add<UnaryCachingInterceptor>(); });
有关如何编写拦截器的示例
<https://github.com/grpc/grpc-dotnet/tree/master/examples/Server/Interceptors>
,请查看grpc-dotnet repo中的这些示例
<https://github.com/grpc/grpc-dotnet/tree/master/examples/Server/Interceptors>。

给予反馈

我们希望您喜欢ASP.NET Core和Blazor预览版中的新功能!请通过在GitHub上
<https://github.com/aspnet/aspnetcore/issues>
提交问题告诉我们您的想法。(再次声明,本文大多内容翻译自:ASP.NET首席项目经理Daniel Roth
<https://devblogs.microsoft.com/aspnet/author/danroth27/>的介绍,因此才会有这段话。)

感谢您试用ASP.NET Core和Blazor!

原文地址:
https://devblogs.microsoft.com/aspnet/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6/

<https://devblogs.microsoft.com/aspnet/asp-net-core-and-blazor-updates-in-net-core-3-0-preview-6/>