原文:bit.ly/3wFqDy9
作者:Daniel
譯者:王亮
.NET 6 預覽版 4 現已發布,其中包括對 ASP.NET Core 的許多新改進。
下面是此次預覽版中 ASP.NET Core 的更新內容:
- 引入最小 API
- 異步流
- HTTP 日志中間件
- 新項目中使用 Kestrel 作為默認啟動
- IConnectionSocketFeature
- 改進單頁應用程序(SPA)模板
- 更新了 .NET 熱重載
- Razor 組件中的泛型約束
- Blazor 錯誤邊界(error boundaries)
- Blazor WebAssembly 的 AOT 編譯
- .NET MAUI Blazor 應用程序
- 其他性能改進
開始使用
要開始使用 .NET 6 Preview 4 中的 ASP.NET Core,請安裝 .NET 6 SDK[1]。
如果你在 windows 上使用 Visual Studio,我們建議安裝 Visual Studio 2019 16.11 的最新預覽版。如果你在 macOS 上,我們建議安裝 Visual Studio 2019 for Mac 8.10 的最新預覽版。
升級一個現有的項目
要將一個現有的 ASP.NET Core 應用程序從 .NET 6 Preview 3 升級到.NET 6 Preview 4。
- 將所有 Microsoft.AspNetCore.* 引用包更新為 6.0.0-preview.4.*。
- 更新所有 Microsoft.Extensions.* 引用包更新為 6.0.0-preview.4.*。
請參閱 .NET 6 中 ASP.NET Core 的完整中斷變化列表[2]。
引入最小 API
在 .NET 6 中,我們為 Web 應用的托管和路由引入了最小 API。這為使用 .NET 構建第一個 Web 應用程序以及想要構建小型微服務和 HTTP API 的開發者打開了大門。這些精簡的 API 提供了 ASP.NET MVC 的優點。
要嘗試創建一個最小 API,請創建一個新的 ASP.NET Core 空 Web 應用。
dotnet new web -o MinApi
只需一個文件和幾行代碼,你現在就有一個功能齊全的 HTTP API。
新的路由 API
新的路由 API 允許用戶路由到任何類型的方法。這些方法可以使用類似控制器的參數綁定、JSON 格式化和 Action 結果。
之前(使用現有的 Map APIs):
App.MapGet("/", async httpContext =>
{
await httpContext.Response.WriteAsync("Hello World!");
});
現在(使用新的 Map 重載):
app.MapGet("/", (Func<string>)(() => "Hello World!"));
C# 10 的改進
這些 API 已經利用了較新的 C# 特性,如頂層語句。在今年晚些時候與 .NET 6 一起發布的 C# 10 中,體驗將變得更好。例如,不再需要明確地把類型轉換成 (Func<string>)。下面的圖片展示了 C# 10 支持的特性:
開發者從使用類和方法到使用 lambda,擁有和使用 MVC 控制器及屬性操作一樣的功能。
新的托管(hosting) API
新的空 Web 模板使用的是 .NET 6 Preview 4 中引入的新的托管模式。
var app = WebApplication.Create(args);
app.MapGet("/", (Func<string>)(() => "Hello World!"));
app.Run();
你并不局限于只使用新的路由 API。下面是一個 Web 應用程序的例子,它被更新為使用新的托管模式,配置服務和添加中間件。
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api", Version = "v1" });
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Api v1"));
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
新的托管 API 減少了配置和啟動 ASP.NET 應用程序所需的模板數量。
性能
這些新的路由 API 的開銷比基于控制器的 API 少得多。使用新的路由 API,ASP.NET Core 能夠在 TechEmpower[3] JSON 基準測試中達到約 80 萬 RPS,而 MVC 則達到約 50 萬 RPS。
異步流
ASP.NET Core 現在支持從控制器 Action 一直到響應的 JSON 格式化器的異步流。從 Action 中返回 IAsyncEnumerable,在發送之前不再在內存中緩沖響應內容。這有助于在返回可異步枚舉的大型數據集時減少內存使用。
請注意,Entity Framework Core 提供了用于查詢數據庫的 IAsyncEnumerable 的實現。在 .NET 6 中,ASP.NET Core 對 IAsyncEnumerable 的支持有所改進,可以使 EF Core 與 ASP.NET Core 的使用更加高效。例如,下面的代碼在發送響應前將不再把 Products 數據緩沖到內存中:
public IActionResult GetProducts()
{
return Ok(dbContext.Products);
}
然而,如果你已經將 EF Core 設置為使用懶加載,這種新的行為可能會導致在數據被枚舉時由于并發的查詢執行而產生錯誤。你可以通過自己緩沖數據來恢復到以前的行為:
public async Task<IActionResult> Products()
{
return Ok(await dbContext.Products.ToListAsync());
}
有關這一行為變化的更多細節,見相關公告[4]。
HTTP 日志中間件
HttpLogging 是一個新的內置中間件,可以記錄 HTTP 請求和 HTTP 響應的信息,包括頭信息和整個 Body。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpLogging();
}
HttpLogging 中間件提供了以下日志:
- HTTP 請求信息
- 普通屬性
- 頭信息
- 請求 Body
- HTTP 響應信息
為了配置 HTTP 日志中間件,你可以在對 ConfigureServices() 的調用中指定 HttpLoggingOptions:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpLogging(logging =>
{
// Customize HTTP logging here.
logging.LoggingFields = HttpLoggingFields.All;
logging.RequestHeaders.Add("My-Request-Header");
logging.ResponseHeaders.Add("My-Response-Header");
logging.MediaTypeOptions.AddText("application/JAVAscript");
logging.RequestBodyLogLimit = 4096;
logging.ResponseBodyLogLimit = 4096;
});
}
這會在日志中產生新的帶有
Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware 類別的 HTTP 請求信息。
關于如何使用 HTTP 日志的更多信息,請看 HTTP 日志文檔[5]。
使用 Kestrel 作為默認啟動
對于在 .NET 6 Preview 4 中創建的所有新項目,我們已將默認的啟動配置文件從 IIS Express 改為 Kestrel。 在開發應用程序時,啟動 Kestrel 的速度明顯加快,并帶來了更靈敏的體驗。
IIS Express (ms) Kestrel (ms) % change Debugging 4359 2772 36% No debugging 1999 727 64%
IIS Express 仍然可以作為啟動項,用于 Windows 認證或端口共享等情況。
IConnectionSocketFeature
IConnectionSocketFeature 功能使你能夠訪問與當前請求相關的底層接受 socket。它可以通過 HttpContext 上的 FeatureCollection 訪問。
例如,下面的應用程序在接受 socket 上設置 LingerState 屬性:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ConfigureEndpointDefaults(listenOptions => listenOptions.Use((connection, next) =>
{
var socketFeature = connection.Features.Get<IConnectionSocketFeature>();
socketFeature.Socket.LingerState = new LingerOption(true, seconds: 10);
return next();
}));
});
var app = builder.Build();
app.MapGet("/", (Func<string>)(() => "Hello world"));
await app.RunAsync();
改進單頁應用(SPA)模板
略...(譯注:文字太長,懶得翻譯了,主要 VS 中的 SPA 模板我從來不用)
.NET 熱重載
最新的 Visual Studio 預覽版對.NET Hot 熱重載有一些初步的支持。你可能已經注意到在調試你的應用程序時,新的 Apply Code Changes 按鈕和調試選項。
Apply Code Changes 按鈕將用你所作的代碼修改來更新正在運行的應用程序,甚至不需要保存。下面是一個更新 Counter 組件的例子,它的增量從 1 改為 2。請注意,一旦修改被應用,當前的計數不會丟失:
Visual Studio 中的 .NET 熱重載支持仍在進行中,因此在 ASP.NET Core 應用程序中使用它時有一些限制:
- 你必須在連接調試器的情況下運行以應用更改。
- 代碼修改只能應用于 C# 文件--還不支持對 Razor 文件(.razor, .cshtml)的修改。
- 已應用的更改還不能強制更新用戶界面,因此需要手動觸發用戶界面更新。
- 目前不支持 Blazor WebAssembly 應用程序。
所有這些限制都在解決中,并將在未來的 Visual Studio 更新中得到解決。敬請關注!
如果你通過 dotnet watch 使用 .NET 熱重載,修改將被應用于 ASP.NET Core 托管的 Blazor WebAssembly 應用程序。如果你刷新瀏覽器,修改也會重新應用到你的 Blazor WebAssembly 應用程序。
要了解更多關于.NET 熱重載的信息,你可以在我們的博文中獲得所有細節:介紹.NET 熱重載[6]。
Razor 中的泛型約束
在 Razor 中使用 @typeparam 指令定義通用類型參數時,你現在可以使用標準的 C# 語法指定泛型約束。
@typeparam TEntity where TEntity : IEntity
Blazor 錯誤邊界
Blazor 錯誤邊界提供了一種方便的方式來處理組件層次結構中的異常情況。為了定義一個錯誤邊界,使用新的 ErrorBoundary 組件來包裹一些現有的內容。只要一切運行順利,ErrorBoundary 組件將渲染其子內容。如果一個未處理的異常被拋出,ErrorBoundary 會渲染一些錯誤 UI。
例如,我們可以像這樣在默認 Blazor 應用程序的布局中添加一個錯誤邊界:
<div class="main">
<div class="top-row px-4">
<a href="https://docs.microsoft.com/aspnet/" target="_blank" rel="noopener">
About
</a>
</div>
<div class="content px-4">
<ErrorBoundary> @Body </ErrorBoundary>
</div>
</div>
該應用程序繼續像以前一樣運作,但現在我們的錯誤邊界將處理未處理的異常。例如,我們可以更新 Counter 組件,在計數過大時拋出一個異常。
private void IncrementCount()
{
currentCount++;
if (currentCount > 10)
{
throw new InvalidOperationException("Current count is too big!");
}
}
現在,如果我們過多地點擊計數器,就會拋出一個未處理的異常,這將由我們的錯誤邊界通過渲染一些默認的錯誤界面來處理。
默認情況下,ErrorBoundary 組件為其錯誤內容渲染了一個帶有 blazor-error-boundary css 類的空 div。這個默認界面的顏色、文本和圖標都是在應用程序中使用 CSS 定義的,所以你可以自由地定制它們。你也可以通過設置 ErrorContent 屬性來改變默認的錯誤內容。
<ErrorBoundary>
<ChildContent> @Body </ChildContent>
<ErrorContent>
<p class="my-error">Nothing to see here right now. Sorry!</p>
</ErrorContent>
</ErrorBoundary>
因為我們在布局中定義了錯誤邊界,一旦拋出一個異常,不管我們導航到哪個頁面我們都能看到錯誤內容。一般來說,錯誤邊界的范圍最好比這更窄,但我們可以選擇在隨后的頁面導航中通過調用錯誤邊界的恢復方法將錯誤邊界重置為非錯誤狀態。
...
<ErrorBoundary @ref="errorBoundary">
@Body
</ErrorBoundary>
...
@code {
ErrorBoundary errorBoundary;
protected override void OnParametersSet()
{
// On each page navigation, reset any error state
errorBoundary?.Recover();
}
}
Blazor WebAssembly 的 AOT 編譯
Blazor WebAssembly 現在支持 AOT(ahead-of-time) 編譯,你可以將你的 .NET 代碼直接編譯為 WebAssembly,以顯著提高運行時性能?,F在 Blazor WebAssemby 應用程序使用 WebAssembly 中實現的 .NET IL 解釋器運行。由于 .NET 代碼是被解釋的,通常這意味著在 WebAssembly 上運行的 .NET 代碼比在正常的.NET 運行時要慢得多。.NET WebAssembly AOT 編譯通過將你的 .NET 代碼直接編譯成 WebAssembly 來解決這個性能問題。
AOT 編譯你的 Blazor WebAssembly 應用程序,對于 CPU 密集型任務來說,其性能的提高是相當顯著的。例如,下面的片段顯示了使用相同的 Blazor WebAssembly 應用程序進行一些基本圖像編輯的比較,首先使用解釋器,然后是 AOT 編譯。AOT 編譯后的版本運行速度快了五倍以上。
你可以在 GitHub 上查看這個 PictureFixer[7] 的代碼。
.NET WebAssembly AOT 編譯需要一個額外的構建工具,必須作為一個可選的 .NET SDK 工作負載來安裝才能使用。要安裝 .NET WebAssembly 構建工具,請運行以下命令:
dotnet workload install microsoft-net-sdk-blazorwebassembly-aot
為了在你的 Blazor WebAssembly 項目中啟用 WebAssembly AOT 編譯,在你的項目文件中添加以下屬性:
<RunAOTCompilation>true</RunAOTCompilation>
然后 AOT 將你的應用程序編譯成 WebAssembly,發布應用程序。使用 Release 配置發布將確保 .NET IL 鏈接也被運行,以減少發布應用程序的大小。
dotnet publish -c Release
WebAssembly AOT 編譯只在項目發布時進行。當項目在開發過程中運行時,它并不使用。這是因為 WebAssembly AOT 編譯可能需要一段時間。
AOT 編譯的 Blazor WebAssembly 應用程序的大小通常比作為 .NET IL 的應用程序要大。在我們的測試中,大多數 AOT 編譯的 Blazor WebAssembly 應用程序大約大 2 倍,不過這取決于具體的應用程序。這意味著,使用 WebAssembly 的 AOT 編譯,可以用加載時間的性能換取運行時間的性能。這種權衡是否值得,取決于你的應用程序。
.NET MAUI Blazor 應用
Blazor 能夠用 .NET 構建客戶端 Web UI,但有時你需要的東西比 Web 平臺提供的更多。有時你需要完全訪問設備的本地功能?,F在,你可以在 .NET MAUI 應用程序中托管 Blazor 組件,以使用 Web UI 構建跨平臺的本地應用程序。這些組件在.NET 進程中原生運行,并使用本地互操作通道向嵌入式 Web 視圖控件渲染 Web UI。這種混合方法為你提供了本地和網絡的優點。你的組件可以通過 .NET 平臺訪問本地功能,并呈現標準的 Web UI。.NET MAUI Blazor 應用程序可以運行在任何 .NET MAUI 可以運行的地方(Windows、Mac、IOS 和 Android),盡管我們對 .NET 6 的主要關注是在桌面場景。
要創建一個 .NET MAUI Blazor 應用程序,你首先需要在開發機器上配置 .NET MAUI。最簡單的方法是使用 maui-check 工具。要安裝 maui-check 工具,請運行:
dotnet tool install -g Redth.Net.Maui.Check
然后運行 maui-check 來獲取 .NET MAUI 工具和依賴。有關開始使用 .NET MAUI 的其他信息,請參考 GitHub 上的 wiki 文檔。
一旦一切安裝完畢,使用新的項目模板創建一個 .NET MAUI Blazor 應用程序:
dotnet new maui-blazor -o MauiBlazorApp
你也可以使用 Visual Studio 創建一個 .NET MAUI Blazor 應用程序:
.NET MAUI Blazor 應用程序是 .NET MAUI 應用程序,它使用 BlazorWebView 控件將 Blazor 組件渲染到一個嵌入式 Web 視圖中。應用程序的代碼和邏輯位于 MauiApp 項目中,該項目被設置為多目標 Android、iOS 和 Mac Catalyst。MauiApp.WinUI3 項目用于為 Windows 構建,而 MauiApp.WinUI3(Package) 項目則用于為 Windows 生成 MSIX 包。最終,我們希望將對 Windows 的支持合并到主應用程序項目中,但現在這些獨立的項目是必要的。
在 MauiApp 項目的 MainPage.xaml 中設置了 BlazorWebView 控件:
<b:BlazorWebView HostPage="wwwroot/index.html">
<b:BlazorWebView.RootComponents>
<b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
</b:BlazorWebView.RootComponents>
</b:BlazorWebView>
該應用程序的根 Blazor 組件在 Main.razor 中。其余的 Blazor 組件都在 Pages 和 Shared 目錄下。請注意,這些組件與默認 Blazor 模板中使用的組件相同。你可以在你的應用程序中使用現有的 Blazor 組件,而不用改變代碼,或者引用包含這些組件的現有類庫或包。應用程序的靜態資源在 wwwroot 文件夾中。
Windows
要在 Windows 下運行該應用程序,你需要使用 Visual Studio 構建和運行。
選擇 MauiBlazorApp.WinUI3(Package) 項目作為你的啟動項目:
同時為目標平臺選擇 x64:
然后你可以按 F5 或 Ctrl+F5,使 WinUI 應用作為本地 Windows 桌面應用運行。
Android
要在 Android 上運行該應用程序,首先使用 Android SDK 或 Android 設備管理器啟動 Android 模擬器。
然后使用以下命令從 CLI 運行該應用程序:
dotnet build MauiBlazorApp -t:Run -f net6.0-android
要從 Visual Studio 在 Android 上運行,選擇 MauiBlazorApp 項目作為啟動項目:
然后在運行按鈕下拉菜單中選擇 net6.0-android 作為目標框架:
然后你可以點擊 F5 或 Ctrl+F5,使用安卓模擬器運行該應用程序:
iOS 和 Mac Catalyst
譯注:Mac Catalyst 是一個幫助開發者將 iOS 應用移植到 macOS 上的服務。
要運行 iOS 或 Mac Catalyst 的應用程序,你需要使用一個運行 Big Sur 的 macOS 開發環境。你目前不能從 Windows 開發環境中運行 iOS 或 Mac Catalyst 的應用程序,盡管我們確實期望 .NET MAUI 將支持使用連接的 Mac 構建代理或使用熱重啟在連接的設備上運行 iOS 應用程序。
要運行 iOS 和 Mac Catalyst 的應用程序,請使用以下命令:
dotnet build MauiBlazorApp -t:Run -f net6.0-ios
dotnet build MauiBlazorApp -t:Run -f net6.0-maccatalyst
在這個版本中,.NET MAUI Blazor 應用程序有一些已知的限制:
- 組件范圍內的 CSS 文件(.razor.css)還不能在主 .NET MAUI 項目中使用。這將在未來的更新中得到修復。
了解更多關于 .NET 6 Preview 4 中 .NET MAUI 的新內容[8]。
其他性能改進
略...(注:這部分列舉了一些社區貢獻的提高性能的 PR,沒啥可翻譯的,感興趣的同學可以直接在英文原文點擊鏈接查看。)
提供反饋
我們希望你喜歡此次 .NET 6 中的 ASP.NET Core 預覽版。我們很想聽聽你對這個版本的體驗。在 GitHub[9] 上提交 Issue,讓我們知道你的想法。
感謝你嘗試使用 ASP.NET Core!
文中鏈接:
[1]. https://dotnet.microsoft.com/download/dotnet/6.0
[2]. https://docs.microsoft.com/dotnet/core/compatibility/6.0#aspnet-core
[3]. https://www.techempower.com/benchmarks/
[4]. https://github.com/aspnet/Announcements/issues/463
[5]. https://docs.microsoft.com/aspnet/core/fundamentals/http-logging
[6]. https://aka.ms/build2021-hotreload
[7]. https://aka.ms/picture-fixer
[8]. https://devblogs.microsoft.com/dotnet/announcing-net-maui-preview-4
[9]. https://github.com/dotnet/aspnetcore/issues