ASP.NET Core 在 .NET 7 RC1 中的更新

ASP.NET Core 在 .NET 7 RC1 中的更新

.NET 7 Release Candidate 1 (RC1) 現已推出,其中包括對 ASP.NET Core 的許多重大新改進。

最後更新 2022/9/15 上午9:23
Daniel Roth
預計閱讀 17 分鐘
分類
.NET
標籤
.NET C# ASP.NET Core

原文連結:https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-rc-1/

原文作者:Daniel Roth

翻譯:沙漠盡頭的狼 (Google 翻譯加持)

.NET 7 Release Candidate 1 (RC1) 現已推出,其中包括對 ASP.NET Core 的許多重大新改進。

以下是此預覽版中新增功能的摘要:

  • Blazor WebAssembly 中的動態驗證請求
  • 處理位置變更事件
  • Blazor WebAssembly 偵錯改進
  • .NET 6 專案的 .NET WebAssembly 專案建置工具
  • WebAssembly 上的 .NET JavaScript 互通操作
  • Kestrel 完整的憑證鏈改進
  • 更快的 HTTP/2 上傳
  • HTTP/3 改進
  • 透過 HTTP/3 對 WebTransport 的實驗性 Kestrel 支援
  • 對 gRPC JSON 轉碼的實驗性 OpenAPI 支援
  • 速率限制中介軟體改進
  • macOS 開發憑證改進

有關為 .NET 7 計劃的 ASP.NET Core 工作的更多詳細資訊,請參閱 GitHub 上的 .NET 7 的完整 ASP.NET Core 路線圖

開始使用

要開始使用 .NET 7 Release Candidate 1 中的 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

升級現有專案

要將現有的 ASP.NET Core 應用從 .NET 7 Preview 7 升級到 .NET 7 RC1:

  • 將所有 Microsoft.AspNetCore._ 套件參考更新為 .7.0.0-rc.1._
  • 將所有 Microsoft.Extensions._ 套件參考更新為 .7.0.0-rc.1._

另請參閱 .NET 7 的 ASP.NET Core 中的重大變更的完整列表。

Blazor WebAssembly 中的動態驗證請求

Blazor 為使用 OpenID Connect 和各種身分提供者(包括 Azure Active Directory (Azure AD) 和 Azure AD B2C)的驗證提供開箱即用的支援。在 .NET 7 中,Blazor 現在支援在執行時期使用自訂參數建立動態驗證請求,以處理 Blazor WebAssembly 應用中更高階的驗證方案。要指定其他參數,請使用新的 InteractiveRequestOptions 類型和 NavigateToLogin 輔助方法 NavigationManager

例如,您可以為身分提供者指定一個登入提示,以便像這樣進行驗證:

InteractiveRequestOptions requestOptions = new()
{
    Interaction = InteractionType.SignIn,
    ReturnUrl = NavigationManager.Uri,
};
requestOptions.TryAddAdditionalParameter("login_hint", "user@example.com");
NavigationManager.NavigateToLogin("authentication/login", requestOptions);

同樣,您可以指定 OpenID Connect prompt 參數,例如當您想要強制互動式登入時:

InteractiveRequestOptions requestOptions = new()
{
    Interaction = InteractionType.SignIn,
    ReturnUrl = NavigationManager.Uri,
};
requestOptions.TryAddAdditionalParameter("prompt", "login");
NavigationManager.NavigateToLogin("authentication/login", requestOptions);

您可以使用 IAccessTokenProvider 直接用於請求權杖時指定這些選項:

var accessTokenResult = await AccessTokenProvider.RequestAccessToken(
    new AccessTokenRequestOptions
    {
        Scopes = new[] { "SecondAPI" }
    });

if (!accessTokenResult.TryGetToken(out var token))
{
    accessTokenResult.InteractiveOptions.AddAdditionalParameter("login_hint", "user@example.com");
    NavigationManager.NavigateToLogin(accessTokenResult.InteractiveRequestUrl, accessTokenResult.InteractionOptions);
}

當無法獲取權杖時,您還可以透過 AuthorizationMessageHandler 指定驗證請求選項:

try
{
    await httpclient.Get("/orders");

}
catch (AccessTokenNotAvailableException ex)
{
    ex.Redirect(requestOptions =>
    {
        requestOptions.AddAdditionalParameter("login_hint", "user@example.com");
    });
}

為驗證請求指定的任何其他參數都將傳遞到底層驗證程式庫,然後由其處理。

注意:為 msal.js 指定附加參數尚未完全實作,但預計將在即將發佈的版本中完成。

處理位置變更事件

.NET 7 中的 Blazor 現在支援處理位置變更事件。這允許您在用戶執行頁面導航時警告用戶未儲存的工作或執行相關操作。

使用 NavigationManager 服務的 RegisterLocationChangingHandler 方法註冊處理程式用於處理位置變更事件。然後,您的處理程式可以在導航時執行非同步工作,或者透過呼叫 LocationChangingContextPreventNavigation 取消導航。RegisterLocationChangingHandler 返回一個 IDisposable 實例,該實例在釋放時會刪除相應的位置變更處理程式。

例如,以下處理程式阻止導航到計數器頁面:

var registration = NavigationManager.RegisterLocationChangingHandler(async context =>
{
    if (context.TargetLocation.EndsWith("counter"))
    {
        context.PreventNavigation();
    }
});

請注意,您的處理程式只會被用於應用程式內的內部導航呼叫。外部導航只能使用 JavaScript 中的 beforeunload 事件同步處理。

新的 NavigationLock 元件使處理位置變更事件的常見場景更容易。NavigationLock 公開一個 OnBeforeInternalNavigation 回呼,您可以使用它來攔截和處理內部位置變更事件。如果您希望用戶也確認外部導航,您可以使用該 ConfirmExternalNavigations 屬性,它將攔截 beforeunload 事件並觸發瀏覽器特定提示。

<EditForm EditContext="editContext" OnValidSubmit="Submit">
    ...
</EditForm>
<NavigationLock OnBeforeInternalNavigation="ConfirmNavigation" ConfirmExternalNavigation />

@code {
    private readonly EditContext editContext;

    ...

    // Called only for internal navigations
    // External navigations will trigger a browser specific prompt
    async Task ConfirmNavigation(LocationChangingContext context)
    {
        if (editContext.IsModified())
        {
            var isConfirmed = await JS.InvokeAsync<bool>("window.confirm", "Are you sure you want to leave this page?");

            if (!isConfirmed)
            {
                context.PreventNavigation();
            }
        }
    }
}

Blazor WebAssembly 偵錯改進

.NET 7 中的 Blazor WebAssembly 偵錯現在具有以下改進:

  • 支援 Just My Code 設定以顯示或隱藏不在用戶程式碼中的類型成員
  • 支援檢查多維陣列
  • 呼叫堆疊現在顯示非同步方法的正確名稱
  • 改進的運算式評估
  • 正確處理衍生成員的 new 關鍵字
  • System.Diagnostics 中支援偵錯器相關的屬性

為 .NET 6 專案的 .NET WebAssembly 建置工具

現在,在使用 .NET 7 SDK 時,您可以將 .NET WebAssembly 建置工具用於 .NET 6 專案。新的 wasm-tools-net6 工作負載包括用於 .NET 6 專案的 .NET WebAssembly 建置工具,以便它們可以與 .NET 7 SDK 一起使用。要安裝新的 wasm-tools-net6 工作負載,請從提升的命令提示字元執行以下命令:

dotnet workload install wasm-tools-net6

安裝 .NET WebAssembly 建置工具的現有 wasm-tools 工作負載是為 .NET 7 專案準備的。但是,.NET 7 版本的 .NET WebAssembly 建置工具與使用 .NET 6 建置的現有專案不相容。需要同時支援 .NET 6 和 .NET 7 使用 .NET WebAssembly 建置工具的專案將需要使用 multi-targeting。

WebAssembly 上的 .NET JavaScript 互通操作

.NET 7 引入了一種新的低階 (low-level) 機制,用於在基於 JavaScript 的應用程式中使用 .NET。借助這一新的 JavaScript 互通操作功能,您可以使用 .NET WebAssembly 執行時期從 JavaScript 呼叫 .NET 程式碼,也可以從 .NET 呼叫 JavaScript 功能,而無需依賴 Blazor UI 元件模型。

查看新的 JavaScript 互通操作功能的最簡單方法是在 wasm-experimental 工作負載中使用新的實驗模板:

dotnet workload install wasm-experimental

此工作負載包含兩個專案模板:WebAssembly Browser App 和 WebAssembly Console App。這些模板是實驗性的,這意味著它們的開發人員工作流程尚未完全整理好(例如,這些模板尚未在 Visual Studio 中執行)。但是 .NET 7 支援這些模板中使用的 .NET 和 JavaScript API,並為透過 JavaScript 在 WebAssembly 上使用 .NET 提供了基礎。

您可以透過執行以下命令來建立 WebAssembly 瀏覽器應用程式:

dotnet new wasmbrowser

此模板建立一個簡單的 Web 應用程式,示範如何在瀏覽器中同時使用 .NET 和 JavaScript。WebAssembly 主控台應用程式類似,但作為 Node.js 主控台應用程式而不是基於瀏覽器的 Web 應用程式執行。

建立的範例專案中的 main.js 中的 JavaScript 模組示範了如何從 JavaScript 執行 .NET 程式碼。相關 API 是從 dotnet.js 匯入的。這些 API 使您能夠設定可以匯入到 C# 程式碼中的命名模組,以及呼叫 .NET 程式碼公開的方法,包括 Program.Main

import { dotnet } from "./dotnet.js";

const is_browser = typeof window != "undefined";
if (!is_browser) throw new Error(`Expected to be running in a browser`);

// Setup the .NET WebAssembly runtime
const { setModuleImports, getAssemblyExports, getConfig, runMainAndExit } =
  await dotnet
    .withDiagnosticTracing(false)
    .withApplicationArgumentsFromQuery()
    .create();

// Set module imports that can be called from .NET
setModuleImports("main.js", {
  window: {
    location: {
      href: () => globalThis.window.location.href,
    },
  },
});

const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
const text = exports.MyClass.Greeting(); // Call into .NET from JavaScript
console.log(text);

document.getElementById("out").innerHTML = `${text}`;
await runMainAndExit(config.mainAssemblyName, ["dotnet", "is", "great!"]); // Run Program.Main

要匯入 JavaScript 函式以便可以從 C# 呼叫它,請在匹配的方法簽章上使用新的 JSImportAttribute

[JSImport("window.location.href", "main.js")]
internal static partial string GetHRef();

JSImportAttribute 的第一個參數是要匯入的 JavaScript 函式的名稱,第二個參數是模組的名稱,這兩個參數都是由 main.js 中的 setModuleImports 呼叫設定的。

在匯入的方法簽章中,您可以對參數和回傳值使用 .NET 類型,這些類型將為您進行封送處理。使用 JSMarshalAsAttribute<T> 控制匯入的方法參數的封送處理方式。例如,您可以選擇將一個 long 封送處理為 JSType.NumberJSType.BigInt。您可以將 Action/Func 回呼作為參數傳遞,這些參數將被封送處理為可呼叫的 JavaScript 函式。您可以同時傳遞 JavaScript 和受控物件參考,它們將被封送處理為代理物件,使物件在邊界上保持活動狀態,直到代理被垃圾回收。您還可以匯入和匯出帶有 Task 回傳值的非同步方法,它將作為 JavaScript promise 進行封送處理。在匯入和匯出的方法上,大多數封裝類型作為參數和回傳值雙向工作。

使用 JSExportAttribute 匯出 .NET 方法以便可以從 JavaScript 呼叫:

[JSExport]
internal static string Greeting()
{
    var text = $"Hello, World! Greetings from {GetHRef()}";
    Console.WriteLine(text);
    return text;
}

Blazor 提供了自己的基於 IJSRuntime 介面的 JavaScript 互通操作機制,該機制在所有 Blazor 託管模型中獲得統一支援。這種常見的非同步抽象使程式庫作者能夠建置可在 Blazor 生態系統中共享的 JavaScript 互通操作程式庫,並且仍然是在 Blazor 中執行 JavaScript 互通操作的推薦方式。但是,在 Blazor WebAssembly 應用程式中,您還可以選擇 IJSInProcessRuntime 進行同步 JavaScript 互通操作呼叫,甚至使用 IJSUnmarshalledRuntime 進行解除封送處理呼叫。IJSUnmarshalledRuntime 使用起來很棘手,僅部分支援。在 .NET 7 中,IJSUnmarshalledRuntime 現在已經過時,應該用 [JSImport]/[JSExport] 機制取代。Blazor 不直接公開從 JavaScript 使用的 dotnet 執行時期實例,但仍然可以透過 .getDotnetRuntime(0) 呼叫。您還可以透過在 C# 程式碼中呼叫 JSHost.ImportAsync 匯入 JavaScript 模組,這可以使模組匯出對 [JSImport] 可見。

Kestrel 完整的憑證鏈改進

類型 X509Certificate2CollectionHttpsConnectionAdapterOptions 具有新屬性 ServerCertificateChain,透過允許指定包含中繼憑證的完整鏈,可以更輕鬆地驗證憑證鏈。有關詳細資訊,請參閱 dotnet/aspnetcore#21513

更快的 HTTP/2 上傳

我們已將 Kestrel 的預設 HTTP/2 上傳連線視窗大小從 128 KB 增加到 1 MB,這顯著提高了使用 Kestrel 的預設組態的高延遲連線的 HTTP/2 上傳速度。

在僅引入 10 毫秒的人工延遲後,我們透過在 localhost 上使用單一串流上傳 108 MB 檔案上傳來測試增加此限制的影響,並看到上傳速度提高了大約 6 倍。

下面的螢幕截圖比較了在 Edge 的開發工具網路索引標籤中上傳 108 MB 所需的時間:

  • 之前:26.9 秒
  • 之後:4.3 秒

HTTP/3 改進

.NET 7 RC1 繼續改進 Kestrel 對 HTTP/3 的支援。改進的兩個主要領域是與 HTTP/1.1 和 HTTP/2 的功能對等以及效能。

此版本最大的特色是完全支援 ListenOptions.UseHttps 使用 HTTP/3。Kestrel 提供了用於設定連線憑證的高階選項,例如攔截到 Server Name Indication (SNI)

以下範例顯示如何使用 SNI 回呼來解析 TLS 選項:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(8080, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.UseHttps(new TlsHandshakeCallbackOptions
        {
            OnConnection = context =>
            {
                var options = new SslServerAuthenticationOptions
                {
                    ServerCertificate = ResolveCertForHost(context.ClientHelloInfo.ServerName)
                };
                return new ValueTask<SslServerAuthenticationOptions>(options);
            },
        });
    });
});

我們還做了大量工作來減少 .NET 7 RC1 中的 HTTP/3 配置。您可以在這裡看到其中的一些改進:

透過 HTTP/3 對 WebTransport 的實驗性 Kestrel 支援

我們很高興地宣佈在 Kestrel 中對基於 HTTP/3 的 WebTransport 的內建實驗性支援。此功能是由我們優秀的實習生 Daniel 編寫的!WebTransport 對於類似於 WebSockets 的傳輸協定是一個新的草案規範,它允許每個連線使用多個串流。這對於拆分通訊通道並因此避免線頭阻塞很有用。例如,考慮一個基於網路的線上遊戲,其中遊戲狀態在一個雙向串流上傳輸,玩家對遊戲語音聊天功能的語音在另一個雙向串流上傳輸,而玩家的控制在單向串流上傳輸。使用 WebSockets,這一切都需要放在單獨的連線上或壓縮到單一串流中。使用 WebTransport,您可以將所有流量保留在一個連線上,但將它們分成自己的串流,如果一個串流阻塞,其他串流將繼續不間斷。

其他詳細資訊將在單獨的部落格文章中提供。

對 gRPC JSON 轉碼的實驗性 OpenAPI 支援

gRPC JSON 轉碼是 .NET 7 中的一項新功能,用於將 gRPC API 轉換為 RESTful API。

.NET 7 RC1 增加了從 gRPC 轉碼 RESTful API 產生 OpenAPI 的實驗性支援。帶有 gRPC JSON 轉碼的 OpenAPI 是一個非常需要的功能,我們很高興提供一種結合這些偉大技術的方法。NuGet 套件在 .NET 7 中是實驗性的,讓我們有時間探索整合這些功能的最佳方式。

要使用 gRPC JSON 轉碼啟用 OpenAPI:

  • 新增對 Microsoft.AspNetCore.Grpc.Swagger 的套件參考。版本必須為 0.3.0-xxx 或更高版本。
  • 在啟動時設定 Swashbuckle。該 AddGrpcSwagger 方法將 Swashbuckle 設定為包含 gRPC 端點。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc().AddJsonTranscoding();
builder.Services.AddGrpcSwagger();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1",
        new OpenApiInfo { Title = "gRPC transcoding", Version = "v1" });
});

var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.MapGrpcService<GreeterService>();

app.Run();

要確認 Swashbuckle 正在為 RESTful gRPC 服務產生 Swagger,請啟動應用程式並導航到 Swagger UI 頁面:

速率限制中介軟體改進

我們為 .NET 7 RC1 中的速率限制中介軟體新增了許多功能,使其功能更強大,更易於使用。

我們新增了可用於啟用或停用給定端點上的速率限制的屬性。例如,以下是如何將命名策略應用 MyControllerPolicy 到控制器:

public class MyController : Controller
{
    [EnableRateLimitingAttribute("MyControllerPolicy")]
    public IActionResult Index()
    {
        return View();
    }
}

您還可以在給定端點或一組端點上完全停用速率限制。假設您在一組端點上啟用了速率限制:

app.MapGroup("/public/todos").RequireRateLimiting("MyGroupPolicy");

然後,您可以停用該組中特定端點的速率限制,如下所示:

app.MapGroup("/public/todos/donothing").DisableRateLimiting();

您現在還可以將策略直接應用於端點。與命名策略不同,以這種方式新增的策略不需要在 RateLimiterOptions 中設定。假設您已經定義了一個策略類型:

public class MyRateLimiterPolicy : IRateLimiterPolicy<string>
{
...
}

您可以將其實例直接新增到端點,如下所示:

app.MapGet("/", () => "Hello World!").RequireRateLimiting(new MyRateLimiterPolicy());

最後,我們更新了 RateLimiterOptions 便捷方法以採用 Action<Options> 而不是 Options 實例,還新增了 IServiceCollection 使用速率限制的擴充方法。因此,要在您的應用中啟用上述所有速率限制策略,您可以執行以下操作:

builder.Services.AddRateLimiter(options =>
{
    options.AddTokenBucketLimiter("MyControllerPolicy", options =>
    {
        options.TokenLimit = 1;
        options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        options.QueueLimit = 1;
        options.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
        options.TokensPerPeriod = 1;
    })
    .AddPolicy<string>("MyGroupPolicy", new MyRateLimiterPolicy());
});

這會將 TokenBucketLimiter 應用於您的控制器,將您的自訂 MyRateLimiterPolicy 應用於匹配端點 ./public/todos(除了 /public/todos/donothing),並將您的自訂 MyRateLimiterPolicy 應用於 /

macOS 開發憑證改進

在此版本中,我們對 macOS 用戶使用 HTTPS 開發憑證的體驗進行了一些重大的品質改進,大大減少了在建立、信任、讀取和刪除 ASP.NET Core HTTPS 開發時顯示的驗證提示的憑證。當 macOS 上的 ASP.NET Core 開發人員嘗試在其工作流程中使用開發憑證時,這一直是一個痛點。

在此版本中,透過 dotnet dev-certs 工具在 macOS 上產生的開發憑證具有更窄的信任範圍,現在將設定新增到每個用戶的信任設定中而不是系統範圍內,並且 Kestrel 將能夠繫結到這些新憑證而無需存取系統鑰匙圈。作為這項工作的一部分,還對品質進行了一些改進,例如重新處理一些面向用戶的訊息以提高清晰度和準確性。

在 macOS 上的開發過程中使用 HTTPS 時,這些更改結合在一起可以帶來更流暢的體驗和更少的密碼提示。

查看新的 Blazor 更新的實際應用!

有關 Blazor WebAssembly 中的動態驗證請求、Blazor WebAssembly 偵錯改進以及 WebAssembly 上的 .NET JavaScript 互通操作的即時示範,請參閱我們最近的 Blazor 社群站會

給予回饋

我們希望您喜歡 .NET 7 中的 ASP.NET Core 預覽版。透過在 GitHub 上提交問題,讓我們知道您對這些新改進的看法。

感謝您試用 ASP.NET Core!

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2024/6/20

CodeWF.EventBus:輕量級事件匯流排,讓通訊更流暢

CodeWF.EventBus,一款靈活的事件匯流排庫,實現模組間解耦通訊。支援多種.NET專案類型,如WPF、WinForms、ASP.NET Core等。採用簡潔設計,輕鬆實現命令的發布與訂閱、請求與回應。透過有序的事件處理,確保事件得到妥善處理。簡化您的程式碼,提升系統可維護性。

繼續閱讀
同分類 / 同標籤 2024/1/19

基於 .NET 的 FluentValidation 驗證教學

FluentValidation 是一個基於 .NET 開發的驗證框架,開源免費,而且優雅,支援鏈式操作,易於理解,功能完善,還可與 MVC5、WebApi2 和 ASP.NET CORE 深度整合,組件內提供十幾種常用驗證器,可擴展性好,支援自訂驗證器,支援本地化多語言。

繼續閱讀