ASP.NET Coreの.NET 7 RC1での更新

ASP.NET Coreの.NET 7 RC1での更新

.NET 7 Release Candidate 1 (RC1) が利用可能になり、ASP.NET Coreに対する多くの重要な新機能の改善が含まれています。

最終更新 2022/09/15 9:23
Daniel Roth
読了目安 11 分
カテゴリ
.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 などのさまざまな ID プロバイダーを使用した認証を、すぐに利用できる形でサポートしています。.NET 7 では、Blazor は実行時にカスタム パラメーターを使用して動的な認証要求を作成し、Blazor WebAssembly アプリケーションのより高度な認証シナリオを処理できるようになりました。追加のパラメーターを指定するには、新しい InteractiveRequestOptions 型と NavigationManagerNavigateToLogin ヘルパー メソッドを使用します。

例えば、次のように認証リクエストに ID プロバイダーへのログインヒントを指定できます。

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 を呼び出してナビゲーションをキャンセルしたりできます。RegisterLocationChangingHandlerIDisposable インスタンスを返し、これを破棄すると対応する位置変更ハンドラーが削除されます。

例えば、次のハンドラーはカウンター ページへのナビゲーションをブロックします。

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

ハンドラーはアプリケーション内の内部ナビゲーションに対してのみ呼び出されることに注意してください。外部ナビゲーションは JavaScript の beforeunload イベントを使用して同期的にのみ処理できます。

新しい NavigationLock コンポーネントを使用すると、位置変更イベントの処理に関する一般的なシナリオがより簡単になります。NavigationLockOnBeforeInternalNavigation コールバックを公開しており、内部の位置変更イベントをインターセプトして処理するために使用できます。ユーザーに外部ナビゲーションの確認も求める場合は、ConfirmExternalNavigations プロパティを使用できます。これにより、beforeunload イベントをインターセプトしてブラウザー固有のプロンプトがトリガーされます。

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

@code {
    private readonly EditContext editContext;

    ...

    // 内部ナビゲーションに対してのみ呼び出される
    // 外部ナビゲーションはブラウザー固有のプロンプトをトリガーする
    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 6 プロジェクトでも .NET WebAssembly ビルド ツールを使用できるようになりました。新しい wasm-tools-net6 ワークロードには .NET 6 プロジェクト向けの .NET WebAssembly ビルド ツールが含まれており、.NET 7 SDK と共に使用できます。新しい wasm-tools-net6 ワークロードをインストールするには、管理者コマンド プロンプトから次のコマンドを実行します。

dotnet workload install wasm-tools-net6

既存の wasm-tools ワークロードは、.NET 7 プロジェクト向けの .NET WebAssembly ビルド ツールをインストールします。ただし、.NET 7 バージョンの .NET WebAssembly ビルド ツールは、.NET 6 を使用して構築された既存のプロジェクトと互換性がありません。.NET WebAssembly ビルド ツールを使用するプロジェクトで .NET 6 と .NET 7 の両方をサポートする必要がある場合は、マルチターゲットを使用する必要があります。

WebAssembly 上の .NET JavaScript 相互運用

.NET 7 では、JavaScript ベースのアプリケーションで .NET を使用するための新しい低レベルメカニズムが導入されました。この新しい JavaScript 相互運用機能により、Blazor UI コンポーネント モデルに依存せずに、.NET WebAssembly ランタイムを使用して JavaScript から .NET コードを呼び出したり、.NET から JavaScript 機能を呼び出したりできます。

新しい JavaScript 相互運用機能を確認する最も簡単な方法は、wasm-experimental ワークロードの新しい実験的テンプレートを使用することです。

dotnet workload install wasm-experimental

このワークロードには、WebAssembly Browser App と WebAssembly Console App の 2 つのプロジェクト テンプレートが含まれています。これらのテンプレートは実験的であり、開発者ワークフローがまだ完全に整理されていないことを意味します(たとえば、これらのテンプレートはまだ Visual Studio で実行できません)。ただし、.NET 7 はこれらのテンプレートで使用される .NET および JavaScript API をサポートしており、JavaScript を介して WebAssembly 上で .NET を使用するための基盤を提供します。

次のコマンドを実行して、WebAssembly ブラウザー アプリケーションを作成できます。

dotnet new wasmbrowser

このテンプレートは、ブラウザー内で .NET と JavaScript の両方を使用する方法を示すシンプルな Web アプリケーションを作成します。WebAssembly コンソール アプリケーションも同様ですが、ブラウザーベースの Web アプリケーションではなく、Node.js コンソール アプリケーションとして実行されます。

作成されたサンプル プロジェクトの 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`);

// .NET WebAssembly ランタイムをセットアップ
const { setModuleImports, getAssemblyExports, getConfig, runMainAndExit } =
  await dotnet
    .withDiagnosticTracing(false)
    .withApplicationArgumentsFromQuery()
    .create();

// .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(); // JavaScript から .NET を呼び出す
console.log(text);

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

JavaScript 関数を C# から呼び出せるようにインポートするには、対応するメソッドシグネチャに新しい JSImportAttribute を使用します。

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

JSImportAttribute の最初の引数はインポートする JavaScript 関数の名前、2 番目の引数はモジュールの名前です。これらは main.js 内の setModuleImports 呼び出しによって設定されます。

インポートされたメソッドシグネチャでは、パラメーターと戻り値に .NET 型を使用でき、自動的にマーシャリングされます。JSMarshalAsAttribute<T> を使用して、インポートされたメソッドパラメーターのマーシャリング方法を制御します。例えば、longJSType.Number または JSType.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 アプリケーションでは、同期 JavaScript 相互運用呼び出しに IJSInProcessRuntime を選択したり、アンマーシャリング呼び出しに IJSUnmarshalledRuntime を使用したりすることもできます。IJSUnmarshalledRuntime は扱いが難しく、部分的にしかサポートされていません。.NET 7 では IJSUnmarshalledRuntime は廃止予定となり、代わりに [JSImport]/[JSExport] メカニズムを使用する必要があります。Blazor は JavaScript から使用される dotnet ランタイム インスタンスを直接公開しませんが、.getDotnetRuntime(0) を介して依然としてアクセスできます。また、C# コードで JSHost.ImportAsync を呼び出して JavaScript モジュールをインポートすることもでき、これにより [JSImport] に対してモジュールのエクスポートが表示されるようになります。

Kestrel の完全な証明書チェーン改善

HttpsConnectionAdapterOptions 型に X509Certificate2Collection の新しいプロパティ ServerCertificateChaintype が追加されました。これにより、中間証明書を含む完全なチェーンを指定できるようになり、証明書チェーンの検証が容易になります。詳細については、dotnet/aspnetcore#21513 を参照してください。

高速化された HTTP/2 アップロード

Kestrel のデフォルト HTTP/2 アップロード接続ウィンドウ サイズを 128 KB から 1 MB に増やしました。これにより、Kestrel のデフォルト構成を使用している高遅延接続での HTTP/2 アップロード速度が大幅に向上しました。

人為的に 10 ミリ秒の遅延を追加した後、localost 上で単一ストリームを使用して 108 MB のファイルをアップロードし、この制限の増加による影響をテストしたところ、アップロード速度が約 6 倍向上したことが確認されました。

以下のスクリーンショットは、Edge の開発者ツールのネットワーク タブで 108 MB のアップロードにかかる時間を比較したものです。

  • 以前: 26.9 秒
  • 以後: 4.3 秒

HTTP/3 の改善

.NET 7 RC1 では、Kestrel の HTTP/3 サポートが引き続き改善されています。改善の主な 2 つの分野は、HTTP/1.1 および HTTP/2 との機能パリティとパフォーマンスです。

このリリースの最大の特徴は、HTTP/3 で ListenOptions.UseHttps が完全にサポートされたことです。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 は、ドラフト仕様 で策定中のトランスポート プロトコルであり、WebSocket に似ていますが、接続ごとに複数のストリームを使用できます。これは、通信チャネルを分割して、ヘッドオブラインブロッキングを回避するのに役立ちます。例えば、Web ベースのオンライン ゲームを考えてみてください。ゲームの状態は双方向ストリームで、プレイヤーのゲーム内音声チャット機能の音声は別の双方向ストリームで、プレイヤーの制御は単方向ストリームで送信されます。WebSocket を使用する場合、これらすべてを別々の接続に配置するか、単一のストリームに詰め込む必要があります。WebTransport を使用すると、すべてのトラフィックを 1 つの接続に保持しながら、独自のストリームに分割できます。1 つのストリームがブロックされても、他のストリームは中断なく続行されます。

詳細については、別のブログ記事で提供される予定です。

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 の便利なメソッドを更新して、Options インスタンスではなく Action<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 開発者がワークフローで開発証明書を使用しようとする際の悩みの種でした。

このリリースでは、macOS 上で dotnet dev-certs ツールを使用して生成された開発証明書の信頼範囲が狭くなり、システム全体ではなくユーザーごとの信頼設定に追加されるようになり、Kestrel はシステムキーチェーンにアクセスせずにこれらの新しい証明書にバインドできるようになります。この作業の一環として、画面上のメッセージを再処理して明確さと正確性を向上させるなど、品質面でもいくつかの改善が行われました。

これらの変更により、macOS での開発中に HTTPS を使用する際のエクスペリエンスがよりスムーズになり、パスワードを求められる回数が減ります。

新しい Blazor アップデートの実際の動作を確認!

Blazor WebAssembly での動的な認証要求、Blazor WebAssembly のデバッグ改善、および WebAssembly 上の .NET JavaScript 相互運用のライブデモについては、最近の Blazor コミュニティ スタンドアップ をご覧ください。

フィードバックをお寄せください

.NET 7 の ASP.NET Core プレビューをお楽しみいただければ幸いです。GitHub で問題を報告して、これらの新機能に関するご意見をお聞かせください。

ASP.NET Core をお試しいただきありがとうございます。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2024/06/20

CodeWF.EventBus:軽量イベントバス、コミュニケーションをよりスムーズに

CodeWF.EventBusは、モジュール間の疎結合通信を実現する柔軟なイベントバスライブラリです。WPF、WinForms、ASP.NET Coreなど、さまざまな.NETプロジェクトタイプに対応しています。シンプルな設計で、コマンドのパブリッシュとサブスクライブ、リクエストとレスポンスを簡単に実装できます。順序付けられたイベント処理により、イベントが適切に処理されることを保証します。コードを簡素化し、システムの保守性を向上させます。

続きを読む
同じカテゴリ / 同じタグ 2024/01/19

.NET ベースの FluentValidation 検証チュートリアル

FluentValidationは、.NETベースの検証フレームワークで、オープンソースかつ無料、そしてエレガントです。チェーン操作をサポートし、理解しやすく、機能が充実しています。さらに、MVC5、WebApi2、ASP.NET Coreと深く統合でき、コンポーネント内には十数種類の一般的なバリデーターが用意されており、拡張性が高く、カスタムバリデーターをサポートし、ローカライズ多言語にも対応しています。

続きを読む