この記事では、.NET 10 の ASP.NET Core における最も重要な変更点と、関連するドキュメントへのリンクについて説明します。
この記事は、新しいプレビュー リリースが利用可能になると更新されます。 重大な変更については、「 .NET での破壊的変更」を参照してください。
Blazor
このセクションでは、Blazorの新機能について説明します。
新規および更新された Blazor Web App セキュリティ サンプル
次の記事でリンクされている Blazor Web App セキュリティ サンプルを追加および更新しました。
- OpenID Connect (OIDC) を使用して ASP.NET Core Blazor Web App をセキュリティで保護する
- Microsoft Entra ID を使用して ASP.NET Core Blazor Web App をセキュリティで保護する
- Windows 認証を使用して ASP.NET Core Blazor Web App をセキュリティで保護する
OIDC と Entra のサンプル ソリューションには、外部 Web API を安全に構成して呼び出す方法を示す別の Web API プロジェクト (MinimalApiJwt) が含まれるようになりました。 Web API の呼び出しは、トークン ハンドラーと、OIDC ID プロバイダーの名前付き HTTP クライアント、または Microsoft Entra ID 用の Microsoft Identity Web パッケージ/API を使用して示されています。
サンプル ソリューションは、 Program ファイル内の C# コードで構成されます。 アプリ設定ファイル (たとえば、appsettings.json) からソリューションを構成するには、OIDC または Entra 記事の JSON 構成プロバイダー (アプリ設定) セクションを使用した新しい Supply 構成を参照してください。
Entra の記事とサンプル アプリには、次のアプローチに関する新しいガイダンスも含まれています。
- Web ファーム ホスティング シナリオで暗号化された分散トークン キャッシュを使用する方法。
- データ保護のために Azure マネージド ID で AzureKey Vault を使用する方法。
QuickGrid
RowClass パラメーター
新しい RowClass パラメーターを使用して、行項目に基づいてグリッドの行にスタイルシート クラスを適用します。 次の例では、各行に対して GetRowCssClass メソッドを呼び出して、行項目に基づいてスタイルシート クラスを条件付きで適用します。
<QuickGrid ... RowClass="GetRowCssClass">
...
</QuickGrid>
@code {
private string GetRowCssClass(MyGridItem item) =>
item.IsArchived ? "row-archived" : null;
}
詳細については、「 ASP.NET Core Blazor 'QuickGrid' コンポーネント」を参照してください。
静的 Web アセットとしての Blazor スクリプト
.NET の以前のリリースでは、Blazor スクリプトは、ASP.NET Core 共有フレームワークの埋め込みリソースから提供されます。 .NET 10 以降では、Blazor スクリプトは、自動圧縮とフィンガープリントを備えた静的 Web 資産として機能します。
詳細については、次のリソースを参照してください。
ルート テンプレートの強調表示
[Route]属性では、ルート テンプレートの構造を視覚化するのに役立つルート構文の強調表示がサポートされるようになりました。
NavigateTo 同じページナビゲーションで上部までスクロールしなくなりました
以前は、NavigationManager.NavigateTo によって同一ページのナビゲーションでページの最上部までスクロールしていました。 この動作は.NET 10 で変更されているため、同じページに移動するときにブラウザーがページの上部までスクロールしなくなりました。 つまり、クエリ文字列やフラグメントの変更など、現在のページのアドレスを更新するときにビューポートがリセットされなくなります。
Blazor Web App プロジェクト テンプレートに追加された再接続 UI コンポーネント
Blazor Web App プロジェクト テンプレートには、クライアントがサーバーへの WebSocket 接続を失ったときに再接続 UI を開発者が制御できるように、併置されたスタイルシートや JavaScript ファイルを含む ReconnectModal コンポーネントが含まれるようになりました。 コンポーネントはプログラムによってスタイルを挿入しないため、style-src ポリシーのより厳密なコンテンツ セキュリティ ポリシー (CSP) 設定に準拠します。 以前のリリースでは、CSP 違反を引き起こす可能性のある方法で、既定の再接続 UI がフレームワークによって作成されていました。 既定の再接続 UI は、プロジェクト テンプレートの ReconnectModal コンポーネントや同様のカスタム コンポーネントを使用するなどして、アプリが再接続 UI を定義していない場合でもフォールバックとして使用されることに注意してください。
新しい再接続 UI 機能:
- 再接続 UI 要素に特定の CSS クラスを設定して再接続状態を示すのとは別に、再接続状態の変更のために新しい
components-reconnect-state-changedイベントがディスパッチされます。 - コードでは、再接続プロセスのステージを、CSS クラスと新しいイベントの両方で示される新しい再接続状態 "
retrying" と区別できます。
詳細については、 ASP.NET Core BlazorSignalR ガイダンスを参照してください。
NavLinkMatch.All を使用する場合は、クエリ文字列とフラグメントを無視します
NavLink コンポーネントは、NavLinkMatch.All パラメーターの Match 値を使用するときに、クエリ文字列とフラグメントを無視するようになりました。 つまり、URL パスが一致するが、クエリ文字列またはフラグメントが変更された場合、リンクは active クラスを保持します。 元の動作に戻すには、Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragmentに設定されているAppContextを使用します。
ShouldMatch の NavLink メソッドをオーバーライドして、一致する動作をカスタマイズすることもできます。
public class CustomNavLink : NavLink
{
protected override bool ShouldMatch(string currentUriAbsolute)
{
// Custom matching logic
}
}
詳細については、「 ASP.NET Core Blazor ルーティングとナビゲーション」を参照してください。
QuickGrid列オプションを閉じる
新しい QuickGrid メソッドを使用して、HideColumnOptionsAsync 列オプション UI を閉じることができるようになりました。
次の例では、HideColumnOptionsAsync メソッドを使用して、タイトル フィルターが適用されるとすぐに列オプション UI を閉じます。
<QuickGrid @ref="movieGrid" Items="movies">
<PropertyColumn Property="@(m => m.Title)" Title="Title">
<ColumnOptions>
<input type="search" @bind="titleFilter" placeholder="Filter by title"
@bind:after="@(() => movieGrid.HideColumnOptionsAsync())" />
</ColumnOptions>
</PropertyColumn>
<PropertyColumn Property="@(m => m.Genre)" Title="Genre" />
<PropertyColumn Property="@(m => m.ReleaseYear)" Title="Release Year" />
</QuickGrid>
@code {
private QuickGrid<Movie>? movieGrid;
private string titleFilter = string.Empty;
private IQueryable<Movie> movies = new List<Movie> { ... }.AsQueryable();
private IQueryable<Movie> filteredMovies =>
movies.Where(m => m.Title!.Contains(titleFilter));
}
応答ストリーミングはオプトイン形式であり、オプトアウトする方法について
以前の Blazor リリースでは、 HttpClient 要求の応答ストリーミングがオプトインされました。 応答ストリーミングが既定で有効になりました。
これは、HttpContent.ReadAsStreamAsync (HttpResponseMessage.Content) のresponse.Content.ReadAsStreamAsync()を呼び出すとBrowserHttpReadStreamが返され、MemoryStreamではなくなったため、重大な変更です。
BrowserHttpReadStream では、 Stream.Read(Span<Byte>)などの同期操作はサポートされていません。 コードで同期操作を使用している場合は、応答ストリーミングをオプトアウトするか、 Stream を自分で MemoryStream にコピーできます。
応答ストリーミングをグローバルにオプトアウトするには、次のいずれかの方法を使用します。
<WasmEnableStreamingResponse>の値を持つプロジェクト ファイルにfalseプロパティを追加します。<WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>DOTNET_WASM_ENABLE_STREAMING_RESPONSE環境変数をfalseまたは0に設定します。
個々の要求の応答ストリーミングをオプトアウトするには、SetBrowserResponseStreamingEnabledをfalseでHttpRequestMessageに設定します (次の例requestMessage)。
requestMessage.SetBrowserResponseStreamingEnabled(false);
詳細については、Fetch API 要求オプションを使用したHttpClientとHttpRequestMessageを参照してください (Web API の呼び出しに関する記事)。
クライアント側のフィンガープリント
.NET 9 のリリースでは、が導入され、Blazor Web App、MapStaticAssets、およびフィンガープリンティングされた JavaScript モジュールを解決するためのが追加されました。 .NET 10 の場合、スタンドアロン Blazor WebAssembly アプリの JavaScript モジュールのクライアント側フィンガープリントをオプトインできます。
スタンドアロンの Blazor WebAssembly アプリでは、ビルド/公開時に、フレームワークが index.html 内のプレースホルダーをビルド中に計算された値で上書きし、静的アセットのフィンガープリントに置き換えます。 フィンガープリントは、 blazor.webassembly.js スクリプト ファイル名に配置されます。
フィンガープリント機能を採用するには、 wwwroot/index.html ファイルに次のマークアップが存在する必要があります。
<head>
...
+ <script type="importmap"></script>
</head>
<body>
...
- <script src="_framework/blazor.webassembly.js"></script>
+ <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>
</html>
プロジェクト ファイル (.csproj) で、<OverrideHtmlAssetPlaceholders>に設定された true プロパティを追加します。
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
+ <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
</Project>
指紋マーカーを使用して index.html 内のすべてのスクリプトは、フレームワークによってフィンガープリントされます。 たとえば、アプリの scripts.js フォルダー内の wwwroot/js という名前のスクリプト ファイルは、ファイル拡張子 (#[.{fingerprint}]) の前に.jsを追加することでフィンガープリントされます。
<script src="js/scripts#[.{fingerprint}].js"></script>
スタンドアロン JS アプリ内の追加のBlazor WebAssembly モジュールをフィンガープリントするには、アプリのプロジェクト ファイル (<StaticWebAssetFingerprintPattern>) で .csproj プロパティを使用します。
次の例では、アプリ内のすべての開発者が指定した .mjs ファイルに指紋が追加されます。
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs"
Expression="#[.{fingerprint}]!" />
ファイルはインポート マップに自動的に配置されます。
- Blazor Web App CSR の場合は自動的に配置されます。
- 上記の手順に従ってスタンドアロン Blazor WebAssembly アプリでモジュールフィンガープリントをオプトインする場合。
JavaScript 相互運用機能のインポートを解決する場合、インポート マップは、フィンガープリントされたファイルを解決するブラウザーによって使用されます。
スタンドアロン Blazor WebAssembly アプリで環境を設定する
Properties/launchSettings.json ファイルは、スタンドアロン Blazor WebAssembly アプリで環境を制御するために使用されなくなりました。
.NET 10 以降では、アプリのプロジェクト ファイル (<WasmApplicationEnvironmentName>) で .csproj プロパティを使用して環境を設定します。
次の例では、アプリの環境を Stagingに設定します。
<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
既定の環境は次のとおりです。
-
Developmentビルド用。 -
Production(発行用)。
インライン化されたブート構成ファイル
Blazorのブート構成は、.NET 10 のリリース以前は blazor.boot.json という名前のファイルに存在していたが、 dotnet.js スクリプトにインライン化されています。 これは、開発者が次のような blazor.boot.json ファイルを直接操作する開発者にのみ影響します。
- ASP.NET Core Blazor WebAssembly キャッシュと整合性チェックの失敗に関するガイダンスに従って、発行された資産のファイル整合性をトラブルシューティング用の整合性 PowerShell スクリプトで確認します。
- ホストのガイダンスに従って既定の Webcil ファイル形式を使用しない場合に DLL ファイルのファイル名拡張子を変更し、ASP.NET Core Blazor WebAssemblyをデプロイします。
現時点では、上記のアプローチに関する文書化された代替戦略はありません。 上記のいずれかの方法が必要な場合は、いずれかの記事の下部にある「ドキュメントの問題を 開く 」リンクを使用して、シナリオを説明する新しいドキュメントの問題を開きます。
コンポーネントとサービスからの状態を保持するための宣言型モデル
[PersistentState]属性を使用して、永続化する状態をコンポーネントやサービスから宣言的に指定できるようになりました。 この属性を持つプロパティは、プリレンダリング中に PersistentComponentState サービスを使用して自動的に永続化されます。 状態は、コンポーネントが対話形式でレンダリングされるか、サービスがインスタンス化されるときに取得されます。
以前の Blazor リリースでは、次の例に示すように、 PersistentComponentState サービスを使用したプリレンダリング中にコンポーネントの状態を保持する際に大量のコードが必要でした。
@page "/movies"
@implements IDisposable
@inject IMovieService MovieService
@inject PersistentComponentState ApplicationState
@if (MoviesList == null)
{
<p><em>Loading...</em></p>
}
else
{
<QuickGrid Items="MoviesList.AsQueryable()">
...
</QuickGrid>
}
@code {
public List<Movie>? MoviesList { get; set; }
private PersistingComponentStateSubscription? persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<List<Movie>>(nameof(MoviesList),
out var movies))
{
MoviesList = await MovieService.GetMoviesAsync();
}
else
{
MoviesList = movies;
}
persistingSubscription = ApplicationState.RegisterOnPersisting(() =>
{
ApplicationState.PersistAsJson(nameof(MoviesList), MoviesList);
return Task.CompletedTask;
});
}
public void Dispose() => persistingSubscription?.Dispose();
}
このコードは、新しい宣言型モデルを使用して簡略化できるようになりました。
@page "/movies"
@inject IMovieService MovieService
@if (MoviesList == null)
{
<p><em>Loading...</em></p>
}
else
{
<QuickGrid Items="MoviesList.AsQueryable()">
...
</QuickGrid>
}
@code {
[PersistentState]
public List<Movie>? MoviesList { get; set; }
protected override async Task OnInitializedAsync()
{
MoviesList ??= await MovieService.GetMoviesAsync();
}
}
状態は、同じ型の複数のコンポーネントに対してシリアル化できます。また、カスタム サービスの種類とレンダリング モードでRegisterPersistentService コンポーネント ビルダー (Razor) でAddRazorComponentsを呼び出すことで、アプリの周囲で使用するサービスで宣言型の状態を確立できます。 詳細については、「 ASP.NET Core Blazor プリレンダリングされた状態の永続化」を参照してください。
新しい JavaScript 相互運用機能
Blazor は、次の JS 相互運用機能のサポートを追加します。
- コンストラクター関数を使用して JS オブジェクトのインスタンスを作成し、インスタンスを参照するための IJSObjectReference/IJSInProcessObjectReference .NET ハンドルを取得します。
- データ プロパティとアクセサー プロパティの両方で、 JS オブジェクト プロパティの値を読み取りまたは変更します。
次の非同期メソッドは、既存のIJSRuntime メソッドと同じスコープ動作を持つIJSObjectReferenceおよびIJSRuntime.InvokeAsyncで使用できます。
InvokeConstructorAsync(string identifier, object?[]? args): 指定した JS コンストラクター関数を非同期的に呼び出します。 この関数は、new演算子を使用して呼び出されます。 次の例では、jsInterop.TestClassはコンストラクター関数を持つクラスであり、classRefは IJSObjectReferenceです。var classRef = await JSRuntime.InvokeConstructorAsync("jsInterop.TestClass", "Blazor!"); var text = await classRef.GetValueAsync<string>("text"); var textLength = await classRef.InvokeAsync<int>("getTextLength");GetValueAsync<TValue>(string identifier): 指定した JS プロパティの値を非同期的に読み取ります。 プロパティをset専用プロパティにすることはできません。 プロパティが存在しない場合は、JSException が発生します。 次の例では、データ プロパティから値を返します。var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync<int>( "jsInterop.testObject.num");SetValueAsync<TValue>(string identifier, TValue value): 指定した JS プロパティの値を非同期的に更新します。 プロパティをget専用プロパティにすることはできません。 プロパティがターゲット オブジェクトで定義されていない場合は、プロパティが作成されます。 プロパティが存在するが書き込みできない場合、または新しいプロパティをオブジェクトに追加できない場合、 JSException がスローされます。 次の例では、numが存在しない場合、testObjectに値 30 のnumが作成されます。await JSRuntime.SetValueAsync("jsInterop.testObject.num", 30);
各メソッドには、CancellationToken 引数またはTimeSpan タイムアウト引数を受け取るオーバーロードが用意されています。
既存の IJSInProcessRuntime メソッドと同じスコープ動作を持つIJSInProcessObjectReferenceおよびIJSInProcessObjectReference.Invokeでは、次の同期メソッドを使用できます。
InvokeConstructor(string identifier, object?[]? args): 指定した JS コンストラクター関数を同期的に呼び出します。 この関数は、new演算子を使用して呼び出されます。 次の例では、jsInterop.TestClassはコンストラクター関数を持つクラスであり、classRefは IJSInProcessObjectReferenceです。var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var classRef = inProcRuntime.InvokeConstructor("jsInterop.TestClass", "Blazor!"); var text = classRef.GetValue<string>("text"); var textLength = classRef.Invoke<int>("getTextLength");GetValue<TValue>(string identifier): 指定した JS プロパティの値を同期的に読み取ります。 プロパティをset専用プロパティにすることはできません。 プロパティが存在しない場合は、JSException が発生します。 次の例では、データ プロパティから値を返します。var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var valueFromDataProperty = inProcRuntime.GetValue<int>( "jsInterop.testObject.num");SetValue<TValue>(string identifier, TValue value): 指定した JS プロパティの値を同期的に更新します。 プロパティをget専用プロパティにすることはできません。 プロパティがターゲット オブジェクトで定義されていない場合は、プロパティが作成されます。 プロパティが存在するが書き込みできない場合、または新しいプロパティをオブジェクトに追加できない場合、 JSException がスローされます。 次の例では、numが存在しない場合、testObject上に値が 20 のnumが作成されます。var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); inProcRuntime.SetValue("jsInterop.testObject.num", 20);
詳細については、 .NET メソッドからの JavaScript 関数の呼び出し に関する記事の次のセクションを参照してください。
Blazor WebAssembly パフォーマンス プロファイリングと診断カウンター
Blazor WebAssembly アプリでは、新しいパフォーマンス プロファイルと診断カウンターを使用できます。 詳細については、次の記事を参照してください。
プリロードされた Blazor フレームワークの静的アセット
Blazor Web Appでは、フレームワークの静的アセットは、Link ヘッダーを使用して自動的にプリロードされます。これにより、初期ページがフェッチおよびレンダリングされる前に、ブラウザーでリソースをプリロードできます。 スタンドアロン Blazor WebAssembly アプリでは、ブラウザー index.html ページ処理の早い段階で、フレームワーク資産が優先度の高いダウンロードとキャッシュがスケジュールされます。
詳しくは、「ASP.NET Core Blazor の静的ファイル」をご覧ください。
を使用して静的サーバー側レンダリング中に NavigationException を回避することをオプトインします。 NavigationManager.NavigateTo
静的サーバー側レンダリング (静的 SSR) 中に NavigationManager.NavigateTo を呼び出すと、 NavigationExceptionがスローされ、リダイレクト応答に変換される前に実行が中断されます。 これにより、デバッグ中に混乱が発生する可能性があり、対話型のレンダリング動作と一貫性がなく、 NavigateTo 後のコードは引き続き正常に実行されます。
.NET 10 では、静的 SSR 中に例外がスローされないようにするために、 <BlazorDisableThrowNavigationException> MSBuild プロパティをアプリのプロジェクト ファイルに true するように設定できます。
<PropertyGroup>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>
MSBuild プロパティを設定すると、静的 SSR 中に NavigationManager.NavigateTo を呼び出しても、 NavigationExceptionがスローされなくなります。 代わりに、例外をスローせずにナビゲーションを実行することで、対話型レンダリングと一貫して動作します。 NavigationManager.NavigateTo後のコードは、リダイレクトが発生する前に実行されます。
.NET 10 Blazor Web App プロジェクト テンプレートでは、MSBuild プロパティが既定で true に設定されます。 .NET 10 に更新するアプリでは、新しい MSBuild プロパティを使用し、以前の動作を回避することをお勧めします。
MSBuild プロパティを使用する場合は、スローされる NavigationException に依存するコードを更新する必要があります。 .NET 10 のリリース前のBlazor プロジェクト テンプレートの既定のIdentityBlazor Web App UI では、IdentityRedirectManagerはInvalidOperationExceptionを呼び出した後にRedirectToをスローし、対話型レンダリング中にメソッドが呼び出されないようにします。 この例外と [DoesNotReturn] 属性 は、MSBuild プロパティを使用するときに削除する必要があります。 詳細については、「 .NET 9 の ASP.NET Core から .NET 10 の ASP.NET Core への移行」を参照してください。
Blazor ルーターに NotFoundPage パラメーターがある
Blazor では、存在しないページに移動するときに "見つかりません" ページを表示する方法が改善されました。
NavigationManager.NotFound パラメーターを使用してページの種類を Router コンポーネントに渡すことで、(次のセクションで説明する) NotFoundPageが呼び出されたときにレンダリングするページを指定できます。 この機能はルーティングをサポートし、コードステータスコードページの再実行ミドルウェア間で動作し、Blazor 以外のシナリオでも互換性があります。
.NET 10 以降では、 NotFound レンダー フラグメント (<NotFound>...</NotFound>) はサポートされていません。
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>This content is ignored because NotFoundPage is defined.</NotFound>
</Router>
Blazor プロジェクト テンプレートに、既定でNotFound.razor ページが含まれるようになりました。 このページは、アプリで NavigationManager.NotFound が呼び出されるたびに自動的にレンダリングされるため、一貫したユーザー エクスペリエンスで不足しているルートを簡単に処理できます。
詳細については、「 ASP.NET Core Blazor ルーティングとナビゲーション」を参照してください。
静的 SSR およびグローバル対話型レンダリングに NavigationManager を使用した応答が見つかりません
NavigationManagerには、静的サーバー側レンダリング (静的 SSR) またはグローバル対話型レンダリング中に要求されたリソースが見つからないシナリオを処理するNotFound メソッドが含まれるようになりました。
静的なサーバー側レンダリング (静的 SSR):
NotFound呼び出すと、HTTP 状態コードが 404 に設定されます。対話型レンダリング: Blazor ルーター (
Routerコンポーネント) に通知して、見つからないコンテンツをレンダリングします。ストリーミング レンダリング: 拡張ナビゲーション がアクティブな場合、 ストリーミング レンダリング はページを再読み込みせずに Not Found コンテンツをレンダリングします。 拡張ナビゲーションがブロックされると、フレームワークはページ更新を使用して Not Found コンテンツにリダイレクトします。
ストリーミング レンダリングでは、NotFoundPage) やNotFoundPage="..." () など、ルートを持つコンポーネントのみをレンダリングできます。
DefaultNotFound 404 コンテンツ ("Not found" プレーン テキスト) にはルートがないため、ストリーミング レンダリング中に使用することはできません。
NavigationManager.NotFound コンテンツ レンダリングでは、応答が開始されたかどうかに関係なく (順番に) 次のものが使用されます。
- NotFoundEventArgs.Pathが設定されている場合は、割り当てられたページの内容をレンダリングします。
-
Router.NotFoundPageが設定されている場合は、割り当てられたページをレンダリングします。 - 状態コード ページの再実行ミドルウェア ページ (構成されている場合)。
- 上記の方法が採用されていない場合は、アクションはありません。
ブラウザーベースのアドレス ルーティングの問題 (ブラウザーのアドレス バーに入力された不適切な URL、アプリにエンドポイントがないリンクの選択など) には、を使用したUseStatusCodePagesWithReExecuteが優先されます。
NavigationManager.OnNotFoundが呼び出されたときに通知にNotFound イベントを使用できます。
詳細と例については、 ASP.NET Core Blazor ルーティングとナビゲーションに関するページを参照してください。
Blazorのルーターを使用しないアプリで見つからない応答のサポート
カスタム ルーターを実装するアプリでは、 NavigationManager.NotFoundを使用できます。
NavigationManager.NotFoundが呼び出されたときにレンダリングするページをレンダラーに通知するには、次の 2 つの方法があります。
応答の状態に関係なく機能する推奨される方法は、 UseStatusCodePagesWithReExecuteを呼び出す方法です。
NavigationManager.NotFoundが呼び出されると、ミドルウェアはメソッドに渡されたパスをレンダリングします。
app.UseStatusCodePagesWithReExecute(
"/not-found", createScopeForStatusCodePages: true);
UseStatusCodePagesWithReExecuteを使用しない場合でも、アプリは既に開始されている応答のNavigationManager.NotFoundをサポートできます。 ルーターの OnNotFoundEvent をサブスクライブし、見つからないページ パスを NotFoundEventArgs.Path に割り当てて、 NavigationManager.NotFound 呼び出されたときにレンダリングするコンテンツをレンダラーに通知します。
CustomRouter.razor:
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Http
@implements IDisposable
@inject NavigationManager NavigationManager
@code {
protected override void OnInitialized() =>
NavigationManager.OnNotFound += OnNotFoundEvent;
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
{
// Only execute the logic if HTTP response has started
// because setting NotFoundEventArgs.Path blocks re-execution
if (HttpContext?.Response.HasStarted == false)
{
return;
}
e.Path = GetNotFoundRoutePath();
}
// Return the path of the Not Found page that you want to display
private string GetNotFoundRoutePath()
{
...
}
public void Dispose() => NavigationManager.OnNotFound -= OnNotFoundEvent;
}
アプリで両方の方法を使用する場合、 OnNotFoundEvent ハンドラーで指定された Not Found パスが、再実行ミドルウェアで構成されたパスよりも優先されます。
メトリックとトレース
このリリースでは、 Blazor アプリの包括的なメトリックとトレース機能が導入され、コンポーネントのライフサイクル、ナビゲーション、イベント処理、回線管理の詳細な監視が提供されます。
詳しくは、「ASP.NET Core Blazor のパフォーマンスに関するベスト プラクティス」をご覧ください。
JavaScript バンドルのサポート
Blazor'のビルド出力は、 Gulp、 Webpack、 Rollup などの JavaScript バンドルと互換性がありません。
Blazor では、 WasmBundlerFriendlyBootConfig MSBuild プロパティを true に設定することで、発行中にバンドルフレンドリ出力を生成できるようになりました。
詳しくは、「ASP.NET Core Blazor のホストと展開」をご覧ください。
Blazor WebAssembly Blazor Web Appでの静的アセットのプリロード
<link>ヘッダーを、webAssembly アセットをResourcePreloaderにプリロードするための<ResourcePreloader /> コンポーネント (Blazor Web App) に置き換えられました。 これにより、アプリのベース パス構成 (<base href="..." />) がアプリのルートを正しく識別できるようになります。
アプリが loadBootResource コールバック を使用して URL を変更している場合、コンポーネントを削除すると機能が無効になります。
Blazor Web App テンプレートは.NET 10 で既定で機能を採用しており、.NET 10 にアップグレードするアプリでは、ResourcePreloader コンポーネントのヘッド コンテンツ (<base>) にベース URL タグ (App) の後にApp.razor コンポーネントを配置することで、この機能を実装できます。
<head>
...
<base href="/" />
+ <ResourcePreloader />
...
</head>
詳細については、「ASP.NET Core サーバー側 Blazor アプリをホストしてデプロイする」を参照してください。
フォーム検証の改善
Blazor では、入れ子になったオブジェクトとコレクション項目のプロパティの検証のサポートなど、フォーム検証機能が強化されました。
検証済みのフォームを作成するには、前と同様に、DataAnnotationsValidator コンポーネント内でEditForm コンポーネントを使用します。
新しい検証機能をオプトインするには:
- サービスが登録されている
AddValidationファイルで、Program拡張メソッドを呼び出します。 -
Razor コンポーネント (
.razor) ではなく、C# クラス ファイルでフォーム モデル型を宣言します。 -
[ValidatableType]属性を使用して、ルート フォーム モデルの種類に注釈を付けます。
前の手順に従わないと、検証動作は以前の .NET リリースと同じままです。
次の例は、フォームの検証が改善された顧客の注文を示しています (簡潔にするために詳細は省略されています)。
Program.csで、サービス コレクションのAddValidationを呼び出して、新しい検証動作を有効にします。
builder.Services.AddValidation();
次の Order クラスでは、最上位モデル型に [ValidatableType] 属性が必要です。 その他の型は自動的に検出されます。
OrderItem と ShippingAddress は簡潔にするために表示されませんが、入れ子になった検証とコレクションの検証は、これらの型で表示された場合と同じように動作します。
Order.cs:
[ValidatableType]
public class Order
{
public Customer Customer { get; set; } = new();
public List<OrderItem> OrderItems { get; set; } = [];
}
public class Customer
{
[Required(ErrorMessage = "Name is required.")]
public string? FullName { get; set; }
[Required(ErrorMessage = "Email is required.")]
public string? Email { get; set; }
public ShippingAddress ShippingAddress { get; set; } = new();
}
次の OrderPage コンポーネントでは、 DataAnnotationsValidator コンポーネントが EditForm コンポーネントに存在します。
OrderPage.razor:
<EditForm Model="Model">
<DataAnnotationsValidator />
<h3>Customer Details</h3>
<div class="mb-3">
<label>
Full Name
<InputText @bind-Value="Model!.Customer.FullName" />
</label>
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
</div>
@* ... form continues ... *@
</EditForm>
@code {
public Order? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
// ... code continues ...
}
Razor コンポーネント (.razor ファイル) の外部でモデル型を宣言する必要がある理由は、新しい検証機能とRazor コンパイラ自体の両方がソース ジェネレーターを使用しているためです。 現在、あるソース ジェネレーターの出力を別のソース ジェネレーターの入力として使用することはできません。
検証のサポートには、次のものが含まれるようになりました。
- 入れ子になった複雑なオブジェクトとコレクションの検証がサポートされるようになりました。
- これには、プロパティ属性、クラス属性、および IValidatableObject 実装によって定義された検証規則が含まれます。
-
[SkipValidation]属性は、プロパティまたは型を検証から除外できます。
- 検証では、リフレクション ベースの実装ではなく、ソース ジェネレーター ベースの実装を使用して、パフォーマンスを向上させ、事前コンパイル (AOT) との互換性を向上させるようになりました。
DataAnnotationsValidator コンポーネントの検証順序と短絡動作は、System.ComponentModel.DataAnnotations.Validatorと同じです。
T型のインスタンスを検証するときに、次の規則が適用されます。
- 入れ子になったオブジェクトを再帰的に検証するなど、
Tのメンバー プロパティが検証されます。 -
Tの型レベルの属性が検証されます。 -
IValidatableObject.Validateメソッドは、実装
T場合に実行されます。
上記のいずれかの手順で検証エラーが発生した場合、残りの手順はスキップされます。
別のアセンブリの検証モデルを使用する
.Client インスタンスを引数として受け取ってBlazor Web Appを呼び出すメソッドをライブラリまたは.Client プロジェクトに作成することで、IServiceCollectionのライブラリやAddValidation プロジェクトなど、別のアセンブリで定義されたモデルを使用してフォームを検証できます。
- アプリで、メソッドと
AddValidationの両方を呼び出します。
詳細と例については、「ASP.NET Core Blazor フォームの検証 を参照してください。
カスタム Blazor キャッシュと MSBuild プロパティ BlazorCacheBootResources 削除
Blazorクライアント側のすべてのファイルがフィンガープリントされ、ブラウザーによってキャッシュされたので、Blazorのカスタム キャッシュ メカニズムとBlazorCacheBootResources MSBuild プロパティがフレームワークから削除されました。 クライアント側プロジェクトのプロジェクト ファイルに MSBuild プロパティが含まれている場合は、プロパティを削除します。このプロパティは無効になります。
- <BlazorCacheBootResources>...</BlazorCacheBootResources>
詳細については、「 コア Blazor WebAssembly キャッシュと整合性チェックの失敗 ASP.NET」を参照してください。
ASP.NET Core の Web 認証 API (パスキー) のサポート Identity
Web 認証 (WebAuthn) API のサポート ( パスキーとして広く知られています) は、公開キー暗号化とデバイス ベースの認証を利用してセキュリティとユーザー エクスペリエンスを向上させる、フィッシングに強い最新の認証方法です。 ASP.NET Core Identity では、WebAuthn および FIDO2 標準に基づくパスキー認証がサポートされるようになりました。 この機能を使用すると、ユーザーは、生体認証やセキュリティ キーなどのセキュリティで保護されたデバイス ベースの認証方法を使用して、パスワードなしでサインインできます。
Blazor Web App プロジェクト テンプレートには、すぐに使用するパスキー管理機能とログイン機能が用意されています。
詳細については、次の記事を参照してください。
回線状態の永続化
サーバー側のレンダリング中に、ページ全体の更新がトリガーされない限り、サーバーへの接続が長時間失われたり、プロアクティブに一時停止されたりしたときに、 Blazor Web Appはユーザーのセッション (回線) 状態を保持できるようになりました。 これにより、ユーザーは次のシナリオで保存されていない作業を失うことなくセッションを再開できます。
- ブラウザー タブの調整
- モバイル デバイス ユーザーによるアプリの切り替え
- ネットワークの中断
- プロアクティブ リソース管理 (非アクティブな回線の一時停止)
- 拡張ナビゲーション
詳細については、「 ASP.NET Core Blazor サーバー側の状態管理」を参照してください。
WebAssembly での Blazor WebAssembly と .NET のホット リロード
WEBAssembly シナリオ用の汎用 ホット リロード に移行された SDK。 新しい MSBuild プロパティWasmEnableHotReloadがあり、これはデフォルトでtrue構成Debugに適用され、ホットリロードConfiguration == "Debug"を有効にします。
カスタム構成名を持つその他の構成の場合は、アプリのプロジェクト ファイルで値を true に設定して、ホット リロードを有効にします。
<PropertyGroup>
<WasmEnableHotReload>true</WasmEnableHotReload>
</PropertyGroup>
Debug構成のホット リロードを無効にするには、値を false に設定します。
<PropertyGroup>
<WasmEnableHotReload>false</WasmEnableHotReload>
</PropertyGroup>
キャッシュの問題を防ぐために PWA サービス ワーカーの登録を更新しました
Blazor プログレッシブ Web アプリケーション (PWA) プロジェクト テンプレートのサービス ワーカー登録に updateViaCache: 'none' オプションが含まれるようになりました。これにより、サービス ワーカーの更新中のキャッシュの問題が回避されます。
- navigator.serviceWorker.register('service-worker.js');
+ navigator.serviceWorker.register('service-worker.js', { updateViaCache: 'none' });
このオプションを使用すると、次のことが保証されます。
- ブラウザーでは、キャッシュされたバージョンのサービス ワーカー スクリプトは使用されません。
- サービス ワーカーの更新は、HTTP キャッシュによってブロックされることなく確実に適用されます。
- PWA アプリケーションでは、サービス ワーカーをより予測可能に更新できます。
これは、サービス ワーカーの更新が正しく適用されない可能性があるキャッシュの問題に対処します。これは、オフライン機能のためにサービス ワーカーに依存する PWA にとって特に重要です。
すべての PWA、特に .NET 9 以前をターゲットとするものにおいて、オプション セットとして none を使用することをお勧めします。
永続的なコンポーネントの状態に対するシリアル化の拡張性
IPersistentComponentStateSerializer インターフェイスを使用してカスタム シリアライザーを実装します。 登録済みのカスタム シリアライザーがない場合、シリアル化は既存の JSON シリアル化にフォールバックします。
カスタム シリアライザーは、アプリの Program ファイルに登録されます。 次の例では、 CustomUserSerializer が User 型に登録されています。
builder.Services.AddSingleton<IPersistentComponentStateSerializer<User>,
CustomUserSerializer>();
型は、カスタム シリアライザーを使用して自動的に永続化および復元されます。
[PersistentState]
public User? CurrentUser { get; set; } = new();
OwningComponentBase が実装されるようになりました IAsyncDisposable
OwningComponentBase には、非同期破棄のサポートが含まれるようになり、リソース管理が改善されました。 サービス スコープの同期破棄と非同期破棄の両方を処理するために、更新されたDisposeAsync メソッドを使用する新しいDisposeAsyncCoreメソッドとDispose メソッドがあります。
フォーム内の非表示の入力フィールドを処理する新しい InputHidden コンポーネント
新しい InputHidden コンポーネントは、文字列値を格納するための非表示の入力フィールドを提供します。
次の例では、フォームの Parameter プロパティに対して非表示の入力フィールドが作成されます。 フォームが送信されると、非表示フィールドの値が表示されます。
<EditForm Model="Parameter" OnValidSubmit="Submit" FormName="InputHidden Example">
<InputHidden id="hidden" @bind-Value="Parameter" />
<button type="submit">Submit</button>
</EditForm>
@if (submitted)
{
<p>Hello @Parameter!</p>
}
@code {
private bool submitted;
[SupplyParameterFromForm]
public string Parameter { get; set; } = "stranger";
private void Submit() => submitted = true;
}
拡張ナビゲーションの永続的なコンポーネント状態のサポート
Blazor では、拡張ナビゲーション中の永続的なコンポーネントの状態の処理がサポートされるようになりました。 拡張ナビゲーション中に保持された状態は、ページ上の対話型コンポーネントで読み取ることができます。
既定では、永続的なコンポーネントの状態は、対話型コンポーネントが最初にページに読み込まれるときにのみ読み込まれます。 これにより、コンポーネントの読み込み後に同じページへの追加の拡張ナビゲーション イベントが発生した場合に、編集された Web フォーム内のデータなどの重要な状態が上書きされなくなります。
データが読み取り専用で頻繁に変更されない場合は、AllowUpdates = true属性に[PersistentState]を設定して、拡張ナビゲーション中に更新を許可するようにオプトインします。 これは、フェッチにコストがかかるが頻繁に変更されないキャッシュされたデータを表示するシナリオに役立ちます。 次の例では、天気予報データに AllowUpdates を使用する方法を示します。
[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastAsync();
}
プリレンダリング中に状態の復元をスキップするには、 RestoreBehavior を SkipInitialValueに設定します。
[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }
再接続中の復元状態をスキップするには、 RestoreBehavior を SkipLastSnapshot に設定します。 これは、再接続後にデータを最新の状態に保つのに役立ちます。
[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }
PersistentComponentState.RegisterOnRestoringを呼び出して、状態の復元方法を命令的に制御するためのコールバックを登録します。これは、PersistentComponentState.RegisterOnPersistingが状態の永続化方法を完全に制御する方法と同様です。
Blazor WebAssembly は、現在の UI カルチャ設定を尊重します
.NET 9 以前では、スタンドアロン Blazor WebAssembly アプリは、 CultureInfo.DefaultThreadCurrentCultureに基づいて UI グローバリゼーション リソースを読み込みます。 CultureInfo.DefaultThreadCurrentUICultureによって定義されたローカライズ カルチャのグローバリゼーション データをさらに読み込む場合は、アプリを .NET 10 以降にアップグレードします。
Blazor Hybrid
このセクションでは、Blazor Hybridの新機能について説明します。
新しい .NET MAUIBlazor Hybrid 記事とサンプルには Blazor Web App と ASP.NET Core Identity が含まれています
ASP.NET Core .NET MAUIを使用して、Blazor HybridIdentity および Web アプリ用の新しい記事とサンプル アプリが追加されました。
詳細については、次のリソースを参照してください。
- .NET MAUI Blazor Hybrid ASP.NET Core を使用した Web アプリ Identity
-
MauiBlazorWebIdentityサンプル アプリ (dotnet/blazor-samplesGitHub リポジトリ)
SignalR
このセクションでは、SignalRの新機能について説明します。
最小限の API
このセクションでは、最小限の API の新機能について説明します。
null 許容値型の場合、フォーム投稿で空の文字列を null として扱います
最小限の API で複雑なオブジェクトで [FromForm] 属性を使用する場合、フォームポストの空の文字列値が解析エラーの原因ではなく null に変換されるようになりました。 この動作は、最小限の API の複雑なオブジェクトに関連付けられていないフォーム投稿の処理ロジックと一致します。
using Microsoft.AspNetCore.Http;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/todo", ([FromForm] Todo todo) => TypedResults.Ok(todo));
app.Run();
public class Todo
{
public int Id { get; set; }
public DateOnly? DueDate { get; set; } // Empty strings map to `null`
public string Title { get; set; }
public bool IsCompleted { get; set; }
}
この変更に貢献した @nvmkpk に感謝します。
最小限の API での検証のサポート
最小限の API での検証のサポートが利用可能になりました。 この機能を使用すると、API エンドポイントに送信されたデータの検証を要求できます。 検証を有効にすると、ASP.NET Core ランタイムは、次に定義されているすべての検証を実行できます。
- Query
- Header
- リクエストの本文
検証は、 DataAnnotations 名前空間の属性を使用して定義されます。 開発者は、次の方法で検証システムの動作をカスタマイズします。
- カスタム
[Validation]属性実装の作成。 - 複雑な検証ロジック用の
IValidatableObjectインターフェイスを実装する。
検証が失敗した場合、ランタイムは検証エラーの詳細を含む 400 Bad Request 応答を返します。
最小限の API に対して組み込みの検証サポートを有効にする
AddValidation拡張メソッドを呼び出して、アプリケーションのサービス コンテナーに必要なサービスを登録することで、最小限の API に対する組み込みの検証サポートを有効にします。
builder.Services.AddValidation();
この実装では、最小限の API ハンドラーで定義されている型、または最小限の API ハンドラーで定義された型の基本型として、自動的に検出されます。 エンドポイント フィルターは、これらの種類に対して検証を実行し、エンドポイントごとに追加されます。
次の例のように、 DisableValidation 拡張メソッドを使用して、特定のエンドポイントに対して検証を無効にすることができます。
app.MapPost("/products",
([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
=> TypedResults.Ok(productId))
.DisableValidation();
Note
ASP.NET Core for .NET 10 で導入された最小限の API 検証ジェネレーターに対して、いくつかの小さな機能強化と修正が行われました。 今後の機能強化をサポートするために、基になる検証リゾルバー API が試験段階としてマークされるようになりました。 最上位レベルの AddValidation API と組み込みの検証フィルターは、安定しており、試験的でありません。
レコードの種類を使用した検証
最小限の API では、C# レコード型での検証もサポートされます。 レコード型は、クラスと同様に、 System.ComponentModel.DataAnnotations 名前空間の属性を使用して検証できます。 例えば次が挙げられます。
public record Product(
[Required] string Name,
[Range(1, 1000)] int Quantity);
最小 API エンドポイントでレコード型をパラメーターとして使用する場合、検証属性はクラス型と同じ方法で自動的に適用されます。
app.MapPost("/products", (Product product) =>
{
// Endpoint logic here
return TypedResults.Ok(product);
});
最小限の API 検証と IProblemDetailsService の統合
最小限の API の検証ロジックからのエラー応答は、アプリケーション サービス コレクション (Dependency Injection コンテナー) で提供される IProblemDetailsService 実装によってカスタマイズできるようになりました。 これにより、より一貫性のあるユーザー固有のエラー応答が可能になります。
Server-Sent イベント (SSE) のサポート
ASP.NET Core では、TypedResults.ServerSentEvents API を使用した ServerSentEvents の結果の返しがサポートされるようになりました。 この機能は、最小限の API とコントローラー ベースのアプリの両方でサポートされています。
Server-Sent イベントは、サーバーが 1 つの HTTP 接続経由でイベント メッセージのストリームをクライアントに送信できるようにするサーバー プッシュ テクノロジです。 .NET では、イベント メッセージは SseItem<T> オブジェクトとして表され、イベントの種類、ID、および T型のデータ ペイロードが含まれる場合があります。
TypedResults クラスには、結果を返すために使用できる ServerSentEvents という名前の新しい静的メソッドがあります。 このメソッドの最初のパラメーターは、クライアントに送信されるイベント メッセージのストリームを表す IAsyncEnumerable<SseItem<T>> です。
次の例は、 TypedResults.ServerSentEvents API を使用して、心拍数イベントのストリームを JSON オブジェクトとしてクライアントに返す方法を示しています。
app.MapGet("/json-item", (CancellationToken cancellationToken) =>
{
async IAsyncEnumerable<HeartRateRecord> GetHeartRate(
[EnumeratorCancellation] CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var heartRate = Random.Shared.Next(60, 100);
yield return HeartRateRecord.Create(heartRate);
await Task.Delay(2000, cancellationToken);
}
}
return TypedResults.ServerSentEvents(GetHeartRate(cancellationToken),
eventType: "heartRate");
});
詳細については、以下を参照してください。
- MDN の Server-Sent イベント。
-
API を使用して、心拍数イベントのストリームを文字列、
TypedResults.ServerSentEvents、JSON オブジェクトとしてクライアントに返すServerSentEvents。 -
API を使用して、心拍数イベントのストリームを文字列、
TypedResults.ServerSentEvents、JSON オブジェクトとしてクライアントに返すServerSentEvents。
検証 API が Microsoft.Extensions.Validation に移動されました
検証 API は、 Microsoft.Extensions.Validation 名前空間と NuGet パッケージに移動しました。 この変更により、ASP.NET コア HTTP シナリオ以外で API を使用できるようになります。 パブリック API と動作は変更されず、パッケージと名前空間のみが異なります。 既存のプロジェクトでは、古い参照が新しい実装にリダイレクトされるため、コードの変更は必要ありません。
クラスとレコードの検証の強化
コード生成と検証動作が一貫したクラスとレコードの両方に検証属性を適用できるようになりました。 この機能強化により、ASP.NET Core アプリでレコードを使用してモデルを設計するときの柔軟性が向上します。
コミュニティへの貢献: @marcominervaのおかげで!
OpenAPI
このセクションでは、OpenAPI の新機能について説明します。
OpenAPI 3.1 のサポート
ASP.NET Core では、.NET 10 で OpenAPI バージョン 3.1 ドキュメントを生成するためのサポートが追加されました。 マイナーなバージョンアップにもかかわらず、OpenAPI 3.1は、特にJSONスキーマのドラフト2020-12を完全にサポートするOpenAPI仕様の重要な更新です。
生成された OpenAPI ドキュメントに表示される変更の一部を次に示します。
- null 許容型のスキーマに、
nullable: trueプロパティが含まれなくなりました。 -
nullable: trueプロパティの代わりに、typeキーワードがあり、その値は型の 1 つとしてnullを含む配列です。 - C#
intまたはlongとして定義されたプロパティまたはパラメーターが、type: integerフィールドなしで生成された OpenAPI ドキュメントに表示され、値を数字に制限するpatternフィールドが追加されました。 これは、NumberHandlingのJsonSerializerOptions プロパティがAllowReadingFromString(ASP.NET Core Web アプリの既定値) に設定されている場合に発生します。 C# のintとlongを OpenAPI ドキュメントでtype: integerとして表せるようにするには、 NumberHandling プロパティをStrictに設定します。
この機能では、生成されたドキュメントの既定の OpenAPI バージョンが3.1。 バージョンは、AddOpenApi の デリゲート パラメーターで configureOptions の OpenApiVersion プロパティを明示的に設定することで変更できます。
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1;
});
ビルド時に OpenAPI ドキュメントを生成する場合は、--openapi-version MSBuild 項目でOpenApiGenerateDocumentsOptionsを設定することで、OpenAPI バージョンを選択できます。
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<!-- Configure build-time OpenAPI generation to produce an OpenAPI 3.1 document. -->
<OpenApiGenerateDocumentsOptions>--openapi-version OpenApi3_1</OpenApiGenerateDocumentsOptions>
</PropertyGroup>
OpenAPI 3.1 のサポートは、主に次の PR で追加されました。
OpenAPI 3.1 の破壊的変更
OpenAPI 3.1 をサポートするには、基になる OpenAPI.NET ライブラリを新しいメジャー バージョン 2.0 に更新する必要があります。 この新しいバージョンには、以前のバージョンからいくつかの破壊的変更があります。 重大な変更は、ドキュメント、操作、またはスキーマ トランスフォーマーがある場合に、アプリに影響を与える可能性があります。 このイテレーションの重大な変更は次のとおりです。
- OpenAPI ドキュメント内のエンティティ (操作やパラメーターなど) は、インターフェイスとして型指定されます。 エンティティのインライン化された参照されるバリアントに対して具象実装が存在します。 たとえば、
IOpenApiSchemaは、インライン化されたOpenApiSchemaまたはドキュメント内の他の場所で定義されたスキーマを指すOpenApiSchemaReferenceにすることができます。 -
NullableプロパティがOpenApiSchema型から削除されました。 型が null 許容かどうかを判断するには、OpenApiSchema.TypeプロパティがJsonSchemaType.Null設定するかどうかを評価します。
最も重要な変更の 1 つは、OpenApiAny を直接使用することを優先して、JsonNode クラスが削除されていることです。
OpenApiAny を使用するトランスフォーマーは、JsonNodeを使用するように更新する必要があります。 次の相違は、.NET 9 から .NET 10 へのスキーマ トランスフォーマーの変更を示しています。
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(WeatherForecast))
{
- schema.Example = new OpenApiObject
+ schema.Example = new JsonObject
{
- ["date"] = new OpenApiString(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd")),
+ ["date"] = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"),
- ["temperatureC"] = new OpenApiInteger(0),
+ ["temperatureC"] = 0,
- ["temperatureF"] = new OpenApiInteger(32),
+ ["temperatureF"] = 32,
- ["summary"] = new OpenApiString("Bracing"),
+ ["summary"] = "Bracing",
};
}
return Task.CompletedTask;
});
これらの変更は、OpenAPI バージョンを 3.0 に構成する場合にのみ必要であることに注意してください。
YAML での OpenAPI
ASP.NET では、生成された OpenAPI ドキュメントを YAML 形式で提供できるようになりました。 YAML は JSON よりも簡潔な記述が可能であり、中括弧や引用符を推測できる場合は省略できます。 YAML では複数行の文字列もサポートされています。これは、長い説明に役立ちます。
生成された OpenAPI ドキュメントを YAML 形式で処理するようにアプリを構成するには、次の例に示すように、MapOpenApi 呼び出しのエンドポイントを ".yaml" または ".yml" サフィックスで指定します。
if (app.Environment.IsDevelopment())
{
app.MapOpenApi("/openapi/{documentName}.yaml");
}
サポート対象:
- YAML は現在、OpenAPI エンドポイントから提供される OpenAPI でのみ使用できます。
- ビルド時に YAML 形式で OpenAPI ドキュメントを生成することは、今後のプレビューで追加されます。
生成された OpenAPI ドキュメントを YAML 形式で提供するためのサポートを追加したこの PR 参照してください。
API コントローラーの ProducesResponseType に関する応答の説明
ProducesAttribute、ProducesResponseTypeAttribute、およびProducesDefaultResponseTypeAttributeは、応答の説明を設定する省略可能な文字列パラメーター (Description) を受け入れるようになりました。
[HttpGet(Name = "GetWeatherForecast")]
[ProducesResponseType<IEnumerable<WeatherForecast>>(StatusCodes.Status200OK,
Description = "The weather forecast for the next 5 days.")]
public IEnumerable<WeatherForecast> Get()
{
生成された OpenAPI データ:
"responses": {
"200": {
"description": "The weather forecast for the next 5 days.",
"content": {
この機能は、 API コントローラー と 最小 API の両方でサポートされています。 最小 API の場合、属性の型と推定される戻り値の型が完全に一致しない場合でも、 Description プロパティが正しく設定されます。
dotnet/aspnetcore によるコミュニティへの貢献 ( #58193)。
OpenAPI ドキュメントに XML ドキュメントのコメントを埋め込みます
ASP.NET Core OpenAPI ドキュメントの生成には、OpenAPI ドキュメント内のメソッド、クラス、およびメンバー定義に関する XML ドキュメント コメントからのメタデータが含まれるようになりました。 この機能を使用するには、プロジェクト ファイルの XML ドキュメント コメントを有効にする必要があります。 これを行うには、次のプロパティをプロジェクト ファイルに追加します。
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
ビルド時に、OpenAPI パッケージはソース ジェネレーターを利用して、現在のアプリケーション アセンブリ内の XML コメントとプロジェクト参照を検出し、ソース コードを出力して OpenAPI ドキュメント トランスフォーマーを介してドキュメントに挿入します。
C# ビルド プロセスではラムダ式に配置された XML ドキュメント コメントはキャプチャされないため、XML ドキュメント コメントを使用して最小限の API エンドポイントにメタデータを追加するには、エンドポイント ハンドラーをメソッドとして定義し、メソッドに XML ドキュメント コメントを配置してから、MapXXX メソッドからそのメソッドを参照する必要があります。 たとえば、XML ドキュメント コメントを使用して、最初にラムダ式として定義された最小限の API エンドポイントにメタデータを追加するには、次のようにします。
app.MapGet("/hello", (string name) =>$"Hello, {name}!");
メソッドを参照するように MapGet 呼び出しを変更します。
app.MapGet("/hello", Hello);
XML ドキュメント コメントを使用して Hello メソッドを定義します。
static partial class Program
{
/// <summary>
/// Sends a greeting.
/// </summary>
/// <remarks>
/// Greeting a person by their name.
/// </remarks>
/// <param name="name">The name of the person to greet.</param>
/// <returns>A greeting.</returns>
public static string Hello(string name)
{
return $"Hello, {name}!";
}
}
前の例では、Hello メソッドは Program クラスに追加されますが、プロジェクト内の任意のクラスに追加できます。
前の例では、<summary>、<remarks>、および XML ドキュメント コメント <param> を示しています。
サポートされているすべてのタグを含む XML ドキュメント コメントの詳細については、 C# のドキュメントを参照してください。
コア機能はソース ジェネレーターを介して提供されるため、次の MSBuild をプロジェクト ファイルに追加することで無効にすることができます。
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.2.*" GeneratePathProperty="true" />
</ItemGroup>
<Target Name="DisableCompileTimeOpenApiXmlGenerator" BeforeTargets="CoreCompile">
<ItemGroup>
<Analyzer Remove="$(PkgMicrosoft_AspNetCore_OpenApi)/analyzers/dotnet/cs/Microsoft.AspNetCore.OpenApi.SourceGenerators.dll" />
</ItemGroup>
</Target>
ソース ジェネレーターは、AdditionalFiles プロパティに含まれる XML ファイルを処理します。 ソースを追加 (または削除) するには、次のようにプロパティを変更します。
<Target Name="AddXmlSources" BeforeTargets="CoreCompile">
<ItemGroup>
<AdditionalFiles Include="$(PkgSome_Package)/lib/net10.0/Some.Package.xml" />
</ItemGroup>
</Target>
ASP.NET Core Web API (ネイティブ AOT) テンプレートに追加された Microsoft.AspNetCore.OpenApi
ASP.NET Core Web API (ネイティブ AOT) プロジェクト テンプレート (短い名前webapiaot) には、既定で Microsoft.AspNetCore.OpenApi パッケージを使用した OpenAPI ドキュメント生成のサポートが含まれるようになりました。 このサポートは、新しいプロジェクトの作成時に --no-openapi フラグを使用して無効になります。
dotnet/aspnetcore によるコミュニティへの貢献 ( #60337)。
DI コンテナーでの IOpenApiDocumentProvider のサポート。
.NET 10 の ASP.NET Core では、依存関係挿入 (DI) コンテナーで IOpenApiDocumentProvider がサポートされています。 開発者は、 IOpenApiDocumentProvider をアプリに挿入し、それを使用して OpenAPI ドキュメントにアクセスできます。 この方法は、バックグラウンド サービスやカスタム ミドルウェアなど、HTTP 要求のコンテキスト外で OpenAPI ドキュメントにアクセスする場合に便利です。
以前は、HTTP サーバーを起動せずにアプリケーションのスタートアップ ロジックを実行するには、no-op HostFactoryResolver 実装でIServerを使用できます。 この新機能は、分散アプリケーションのホスティングと発行に関するアスパイアのフレームワークの一部である、アスパイアの IDistributedApplicationPublisherに着想を得た合理化された API を提供することで、このプロセスを簡略化します。
詳細については、 dotnet/aspnetcore #61463 を参照してください。
XML コメント ジェネレーターの機能強化
XML コメント生成は、.NET 10 の複合型を以前のバージョンの .NET よりも適切に処理します。
- これは、より広い範囲の型に対して正確で完全な XML コメントを生成します。
- より複雑なシナリオを処理します。
- 以前のバージョンでビルド エラーを引き起こす複合型の処理を適切にバイパスします。
これらの機能強化により、特定のシナリオのエラー モードがビルド エラーから不足しているメタデータに変更されます。
さらに、他のアセンブリの XML コメントにアクセスするように XML ドキュメント コメント処理を構成できるようになりました。 これは、現在のアセンブリの外部で定義されている型 (ProblemDetails名前空間のMicrosoft.AspNetCore.Http型など) のドキュメントを生成する場合に便利です。
この構成は、プロジェクト ビルド ファイル内のディレクティブを使用して行われます。 次の例は、 Microsoft.AspNetCore.Http アセンブリ内の型 ( ProblemDetails クラスを含む) の XML コメントにアクセスするように XML コメント ジェネレーターを構成する方法を示しています。
<Target Name="AddOpenApiDependencies" AfterTargets="ResolveReferences">
<ItemGroup>
<!-- Include XML documentation from Microsoft.AspNetCore.Http.Abstractions
to get metadata for ProblemDetails -->
<AdditionalFiles
Include="@(ReferencePath->'
%(RootDir)%(Directory)%(Filename).xml')"
Condition="'%(ReferencePath.Filename)' ==
'Microsoft.AspNetCore.Http.Abstractions'"
KeepMetadata="Identity;HintPath" />
</ItemGroup>
</Target>
ほとんどの場合、この構成の必要性を回避するために、今後のプレビューでは、選択したアセンブリセットの XML コメントを共有フレームワークに含める予定です。
OpenAPI XML コメント ジェネレーターでのドキュメント ID の統合処理
参照されるアセンブリからの XML ドキュメント コメントは、ドキュメント ID に戻り値の型サフィックスが含まれている場合でも、正しくマージされます。 その結果、すべての有効な XML コメントが生成された OpenAPI ドキュメントに確実に含まれるため、参照されるアセンブリを使用する API のドキュメントの精度と完全性が向上します。
フォーム データ列挙型パラメーターは、OpenAPI で実際の列挙型を使用します
MVC コントローラー アクションのフォーム データ パラメーターで、既定の文字列ではなく、実際の列挙型を使用して OpenAPI メタデータが生成されるようになりました。
コミュニティへの貢献: @ascott18のおかげで!
トランスフォーマーでの OpenApiSchemas の生成のサポート
開発者は、Core OpenAPI ドキュメントの生成と同じロジックを使用して C# 型のスキーマ ASP.NET 生成し、OpenAPI ドキュメントに追加できるようになりました。 その後、OpenAPI ドキュメント内の他の場所からスキーマを参照できます。
ドキュメント、操作、およびスキーマ トランスフォーマーに渡されるコンテキストには、型のスキーマを生成するために使用できる新しい GetOrCreateSchemaAsync メソッドが含まれています。
このメソッドには、生成されたスキーマの追加メタデータを指定する省略可能な ApiParameterDescription パラメーターもあります。
OpenAPI ドキュメントへのスキーマの追加をサポートするために、operation および Schema トランスフォーマー コンテキストに Document プロパティが追加されました。 これにより、任意のトランスフォーマーがドキュメントの AddComponent メソッドを使用して OpenAPI ドキュメントにスキーマを追加できます。
Example
ドキュメント、操作、またはスキーマ トランスフォーマーでこの機能を使用するには、コンテキストで提供される GetOrCreateSchemaAsync メソッドを使用してスキーマを作成し、ドキュメントの AddComponent メソッドを使用して OpenAPI ドキュメントに追加します。
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer(async (operation, context, cancellationToken) =>
{
// Generate schema for error responses
var errorSchema = await context.GetOrCreateSchemaAsync(typeof(ProblemDetails), null, cancellationToken);
context.Document?.AddComponent("Error", errorSchema);
operation.Responses ??= new OpenApiResponses();
// Add a "4XX" response to the operation with the newly created schema
operation.Responses["4XX"] = new OpenApiResponse
{
Description = "Bad Request",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/problem+json"] = new OpenApiMediaType
{
Schema = new OpenApiSchemaReference("Error", context.Document)
}
}
};
});
});
Microsoft.OpenApi を 2.0.0 にアップグレードする
ASP.NET Core での OpenAPI ドキュメントの生成に使用される Microsoft.OpenApi ライブラリは、バージョン 2.0.0 (GA) にアップグレードされました。
2.0.0 での破壊的変更
次の破壊的変更はプレビュー リリースで導入され、GA バージョンのままです。 これらは主に、ドキュメント、操作、またはスキーマ トランスフォーマーを実装するユーザーに影響します。
GA バージョンの更新により、OpenAPI ドキュメントの生成でそれ以上の重大な変更は行われません。
OpenAPI スキーマ生成の機能強化
OpenAPI スキーマで oneOf を使用して null 許容型をモデル化する
複合型とコレクションの null 許容プロパティではなく、 oneOf パターンを使用することで、null 許容型の OpenAPI スキーマ生成が改善されました。 実装:
-
oneOfとnullおよび実際の型スキーマを使用して、要求および応答スキーマのnull許容複雑型を扱います。 - リフレクションと
NullabilityInfoContextを使用して、パラメーター、プロパティ、および戻り値の型のnull許容性を検出します。 - 重複を避けるために、コンポーネント化されたスキーマから null 型を削除します。
スキーマ参照解決の修正と機能強化
このリリースでは、ルート スキーマ ドキュメント内の相対 JSON スキーマ参照 ($ref) を適切に解決することで、OpenAPI ドキュメント生成の JSON スキーマの処理が向上します。
OpenAPI スキーマに$refの兄弟としてプロパティの説明を含める
.NET 10 より前 ASP.NET Core では、生成された OpenAPI ドキュメントで $ref で定義されたプロパティに関する説明が破棄されました。OpenAPI v3.0 では、スキーマ定義の $ref と共に兄弟プロパティが許可されていないためです。 OpenAPI 3.1 では、 $refと共に説明を含めることができます。 RC1 では、生成された OpenAPI スキーマに $ref の兄弟としてプロパティの説明を含めるサポートが追加されています。
これはコミュニティの貢献でした。 @desjoerdありがとう!
[AsParameters]型の XML コメントから OpenAPI スキーマにメタデータを追加する
OpenAPI スキーマの生成で、 [AsParameters] パラメーター クラスのプロパティに対する XML コメントが処理され、ドキュメントのメタデータが抽出されるようになりました。
OpenAPI から不明な HTTP メソッドを除外する
OpenAPI スキーマの生成で、生成された OpenAPI ドキュメントから不明な HTTP メソッドが除外されるようになりました。 標準の HTTP メソッドですが、OpenAPI では認識されないクエリ メソッドが、生成された OpenAPI ドキュメントから適切に除外されるようになりました。
これはコミュニティの貢献でした。 @martincostelloありがとう!
JSON パッチ要求本文の説明を改善する
JSON パッチ操作の OpenAPI スキーマ生成によって、JSON パッチを使用する要求本文に application/json-patch+json メディアの種類が正しく適用されるようになりました。 これにより、生成された OpenAPI ドキュメントに、JSON パッチ操作で予想されるメディアの種類が正確に反映されます。 さらに、JSON Patch 要求本文には、実行できる操作を含む JSON Patch ドキュメントの構造を記述する詳細なスキーマがあります。
これはコミュニティの貢献でした。 @martincostelloありがとう!
OpenAPI ドキュメント生成にインバリアント カルチャを使用する
OpenAPI ドキュメントの生成では、生成された OpenAPI ドキュメントの数値と日付の書式設定にインバリアント カルチャが使用されるようになりました。 これにより、生成されるドキュメントの一貫性が確保され、サーバーのカルチャ設定によって異ならないようにします。
これはコミュニティの貢献でした。 @martincostelloありがとう!
認証と承認
認証と承認のメトリック
ASP.NET Core の特定の認証および承認イベントのメトリックが追加されました。 この変更により、次のイベントのメトリックを取得できるようになりました。
- Authentication:
- 認証された要求の期間
- 試行回数
- 数えることを禁止
- サインイン数
- サインアウト数
- Authorization:
- 承認が必要な要求の数
次の図は、アスパイア ダッシュボードの認証済み要求期間メトリックの例を示しています。
詳細については、「 ASP.NET Core 組み込みメトリック」を参照してください。
コア Identity メトリックの ASP.NET
ASP.NET Core Identity .NET 10 では、メトリックを使用して可観測性が向上しました。 メトリックは、システムまたはアプリケーションの動作の時系列測定を提供するカウンター、ヒストグラム、およびゲージです。
たとえば、新しい ASP.NET Core Identity メトリックを使用して、次の内容を確認します。
- ユーザー管理: 新しいユーザーの作成、パスワードの変更、ロールの割り当て。
- ログイン/セッション処理: 2 要素認証を使用したログイン試行、サインイン、サインアウト、ユーザー。
新しいメトリックは、 Microsoft.AspNetCore.Identity メーターに含まれます。
aspnetcore.identity.user.create.durationaspnetcore.identity.user.update.durationaspnetcore.identity.user.delete.durationaspnetcore.identity.user.check_password_attemptsaspnetcore.identity.user.generated_tokensaspnetcore.identity.user.verify_token_attemptsaspnetcore.identity.sign_in.authenticate.durationaspnetcore.identity.sign_in.check_password_attemptsaspnetcore.identity.sign_in.sign_insaspnetcore.identity.sign_in.sign_outsaspnetcore.identity.sign_in.two_factor_clients_rememberedaspnetcore.identity.sign_in.two_factor_clients_forgotten
ASP.NET Core でのメトリックの使用の詳細については、「コア メトリックの ASP.NET」を参照してください。
既知の API エンドポイント cookie ログイン リダイレクトを回避する
既定では、 cookie 認証によって保護されている既知の API エンドポイントに対して行われた認証されていない要求と未承認の要求により、ログインまたはアクセス拒否 URI にリダイレクトされるのではなく、401 と 403 の応答が返されるようになりました。
認証されていない要求をログイン ページにリダイレクトすることは、通常、認証エラーを伝えるために HTML リダイレクトではなく 401 と 403 の状態コードに依存する API エンドポイントでは意味がないため、この変更は 高く要求されました。
既知 の API エンドポイント は、新しい IApiEndpointMetadata インターフェイスを使用して識別され、新しいインターフェイスを実装するメタデータが自動的に次に追加されています。
-
[ApiController]エンドポイント - JSON 要求本文を読み取るか、JSON 応答を書き込む最小限の API エンドポイント
-
TypedResultsの戻り値タイプを使用するエンドポイント - SignalR エンドポイント
IApiEndpointMetadataが存在する場合、cookie認証ハンドラーは、リダイレクトするのではなく、適切な HTTP 状態コード (認証されていない要求の場合は 401、許可されていない要求の場合は 403) を返すようになりました。
この新しい動作を防ぎ、ターゲット エンドポイントに関係なく認証されていない要求または未承認の要求に対してログインとアクセス拒否 URI に常にリダイレクトする場合は、次のように RedirectToLogin および RedirectToAccessDenied イベントをオーバーライドできます。
builder.Services.AddAuthentication()
.AddCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
options.Events.OnRedirectToAccessDenied = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
});
この破壊的変更の詳細については、「 ASP.NET コア破壊的変更のお知らせ」を参照してください。
Miscellaneous
このセクションでは、.NET 10 のその他の新機能について説明します。
例外ハンドラー診断の抑制を構成する
診断出力を制御するための新しい構成オプションが ASP.NET Core 例外ハンドラー ミドルウェア に追加されました: ExceptionHandlerOptions.SuppressDiagnosticsCallback。 このコールバックは要求と例外に関するコンテキストを渡されるため、ミドルウェアが例外ログやその他のテレメトリを書き込むかどうかを決定するロジックを追加できます。
この設定は、例外が一時的であるか、例外ハンドラー ミドルウェアによって処理されていることがわかっている場合に、監視プラットフォームにエラー ログを書き込む必要がない場合に便利です。
ミドルウェアの既定の動作も変更されました。 IExceptionHandlerによって処理される例外の例外診断は書き込めなくなりました。 ユーザーのフィードバックに基づいて、エラー レベルで処理された例外のログ記録は、多くの場合、IExceptionHandler.TryHandleAsynctrue返されたときに望ましくありません。
SuppressDiagnosticsCallbackを構成することで、前の動作に戻すことができます。
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false;
});
この破壊的変更の詳細については、 https://github.com/aspnet/Announcements/issues/524を参照してください。
.localhost Top-Level ドメインのサポート
.localhost最上位ドメイン (TLD) は、RFC2606とRFC6761でテスト目的で予約されており、ユーザーが他のドメイン名と同様にローカルで使用できるように定義されています。 これは、これらの RFC に従って、IP ループバック アドレスに解決されるローカル名 myapp.localhost の使用が許可され、かつ想定されていることを意味します。 さらに、最新の常緑化ブラウザーは既に、 *.localhost 名を IP ループバック アドレス (127.0.0.1/::1) に自動的に解決し、ローカル コンピューター上の localhost で既にホストされているサービスのエイリアスになります。つまり、 http://localhost:6789 に応答するすべてのサービスも、サービスによってさらに具体的なホスト名の検証または強制が実行されていないと仮定して、 http://anything-here.localhost:6789に応答します。
ASP.NET Core が .NET 10 Preview 7 で更新され、 .localhost TLD をより適切にサポートできるようになりました。これにより、ローカル開発環境で ASP.NET Core アプリケーションを作成して実行するときに簡単に使用できるようになりました。 ローカルで実行されているアプリを異なる名前で解決できるため、Cookie など、一部のドメイン名に関連付けられた Web サイト資産をより適切に分離でき、ブラウザーのアドレス バーに表示される名前を使用して閲覧しているアプリを簡単に識別できます。
ASP.NET Core の組み込み HTTP サーバー (Kestrel) では、*.localhostによって設定された名がローカル ループバック アドレスとして正しく処理され、すべての外部アドレスではなくバインドされます (つまり、127.0.0.1/::1ではなく0.0.0.0/::にバインドされます)。 これには、launchSettings.jsonファイルで構成された起動プロファイルの"applicationUrl"プロパティと、ASPNETCORE_URLS環境変数が含まれます。
.localhost アドレスをリッスンするように構成すると、Kestrelは両方の.localhostアドレスとlocalhost アドレスの情報メッセージをログに記録し、両方の名前を使用できることを明確にします。
Web ブラウザーは *.localhost 名をローカル ループバック アドレスに自動的に解決しますが、他のアプリでは、 *.localhost 名を通常のドメイン名として扱い、対応する DNS スタックを介して解決を試みる場合があります。 DNS 構成で *.localhost 名がアドレスに解決されない場合、DNS 構成は接続に失敗します。 Web ブラウザーにない場合は、通常の localhost 名を引き続き使用してアプリに対処できます。
ASP.NET Core HTTPS 開発証明書 (dotnet dev-certs https コマンドを含む) が更新され、証明書が*.dev.localhostドメイン名で使用できることを確認しました。 .NET 10 SDK Preview 7 をインストールした後、コマンド ラインで dotnet dev-certs https --trust を実行して新しい開発者証明書を信頼し、システムが新しい証明書を信頼するように構成されていることを確認します。
最上位ドメイン名にワイルドカード証明書を使用することは無効であるため、*.dev.localhostではなく、*.localhost名がサブジェクト代替名 (SAN) として一覧表示されます。
ASP.NET Core Empty (web) と Blazor Web App (blazor) のプロジェクト テンプレートが新しいオプションで更新されました。このオプションを指定すると、作成されたプロジェクトで .dev.localhost ドメイン名サフィックスが使用されるように構成され、プロジェクト名と組み合わせて、アプリが https://myapp.dev.localhost:5036 のようなアドレスで参照できるようになります。
$ dotnet new web -n MyApp --localhost-tld
The template "ASP.NET Core Empty" was created successfully.
Processing post-creation actions...
Restoring D:\src\MyApp\MyApp.csproj:
Restore succeeded.
$ cd .\MyApp\
$ dotnet run --launch-profile https
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://myapp.dev.localhost:7099
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7099/
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://myapp.dev.localhost:5036
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5036/
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: D:\src\local\10.0.1xx\MyApp
MVC および最小 API での Json+PipeReader 逆シリアル化のサポート
PR: https://github.com/dotnet/aspnetcore/pull/62895
MVC、最小限の API、および HttpRequestJsonExtensions.ReadFromJsonAsync メソッドはすべて、アプリケーションからのコード変更を必要とせずに、新しい Json+ PipeReader サポートを使用するように更新されました。
ほとんどのアプリケーションでは、このサポートの追加は動作に影響しません。 ただし、アプリケーションでカスタム JsonConverterを使用している場合は、コンバーターが Utf8JsonReader.HasValueSequence を正しく処理しない可能性があります。 逆シリアル化すると、データやエラー ( ArgumentOutOfRangeExceptionなど) が見つからない可能性があります。
簡単な回避策 (特に、使用されているカスタム JsonConverter を所有していない場合) は、 "Microsoft.AspNetCore.UseStreamBasedJsonParsing"AppContext スイッチを "true"に設定します。 これは一時的な回避策であり、JsonConverterをサポートするためにHasValueSequenceを更新する必要があります。
JsonConverter実装を修正するには、ReadOnlySequenceから配列を割り当てる簡単な修正プログラムがあり、次の例のようになります。
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
さらに複雑な (ただしパフォーマンスの高い) 修正もあります。これには、 ReadOnlySequence 処理用の別のコード パスが含まれます。
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.HasValueSequence)
{
reader.ValueSequence;
// ReadOnlySequence optimized path
}
else
{
reader.ValueSpan;
// ReadOnlySpan optimized path
}
}
メモリ プールからの自動削除
Kestrel、IIS、および HTTP.sys によって使用されるメモリ プールは、アプリケーションがアイドル状態または負荷が低い場合に、メモリ ブロックを自動的に削除するようになりました。 この機能は自動的に実行され、手動で有効または構成する必要はありません。
メモリの削除が重要な理由
以前は、プールによって割り当てられたメモリは、使用されていない場合でも予約されたままになります。 この機能は、アプリが一定期間アイドル状態のときに、メモリをシステムに解放します。 この削除により、全体的なメモリ使用量が削減され、さまざまなワークロードでアプリケーションの応答性が維持されます。
メモリ削除メトリックを使用する
メトリックは、サーバー実装で使用される既定のメモリ プールに追加されました。 新しいメトリックは、 "Microsoft.AspNetCore.MemoryPool"という名前の下にあります。
メトリックとその使用方法については、「 ASP.NET コア メトリック」を参照してください。
メモリ プールの管理
不要なメモリ ブロックを削除することでメモリ プールをより効率的に使用するだけでなく、.NET 10 ではメモリ プールを作成するエクスペリエンスが向上します。 これを行うには、組み込みの IMemoryPoolFactory と MemoryPoolFactory 実装を提供します。 依存関係の挿入により、アプリケーションで実装を使用できるようになります。
次のコード例は、組み込みのメモリ プール ファクトリ実装を使用してメモリ プールを作成する単純なバックグラウンド サービスを示しています。 これらのプールは、自動削除機能の利点があります。
public class MyBackgroundService : BackgroundService
{
private readonly MemoryPool<byte> _memoryPool;
public MyBackgroundService(IMemoryPoolFactory<byte> factory)
{
_memoryPool = factory.Create();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(20, stoppingToken);
// do work that needs memory
var rented = _memoryPool.Rent(100);
rented.Dispose();
}
catch (OperationCanceledException)
{
return;
}
}
}
}
独自のメモリ プール ファクトリを使用するには、次の例のように、 IMemoryPoolFactory を実装するクラスを作成し、依存関係の挿入に登録します。 この方法で作成されたメモリ プールは、自動削除機能の恩恵も受けられます。
services.AddSingleton<IMemoryPoolFactory<byte>,
CustomMemoryPoolFactory>();
public class CustomMemoryPoolFactory : IMemoryPoolFactory<byte>
{
public MemoryPool<byte> Create()
{
// Return a custom MemoryPool implementation
// or the default, as is shown here.
return MemoryPool<byte>.Shared;
}
}
HTTP.sys のカスタマイズ可能なセキュリティ記述子
HTTP.sys 要求キューのカスタム セキュリティ記述子を指定できるようになりました。 の新しい HttpSysOptions プロパティを使用すると、要求キューのアクセス権をより細かく制御できます。 この細かい制御により、アプリケーションのニーズに合わせてセキュリティを調整できます。
新しいプロパティでできること
HTTP.sys の 要求キュー は、受信 HTTP 要求を処理する準備ができるまで一時的に格納するカーネル レベルの構造です。 セキュリティ記述子をカスタマイズすることで、要求キューへの特定のユーザーまたはグループのアクセスを許可または拒否できます。 これは、オペレーティング システム レベルで HTTP.sys 要求処理を制限または委任するシナリオで役立ちます。
新しいプロパティの使用方法
RequestQueueSecurityDescriptor プロパティは、新しい要求キューを作成する場合にのみ適用されます。 このプロパティは、既存の要求キューには影響しません。 このプロパティを使用するには、HTTP.sys サーバーを構成するときに GenericSecurityDescriptor インスタンスに設定します。
たとえば、次のコードでは、認証されたすべてのユーザーが許可されますが、ゲストは拒否されます。
using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.AspNetCore.Server.HttpSys;
// Create a new security descriptor
var securityDescriptor = new CommonSecurityDescriptor(isContainer: false, isDS: false, sddlForm: string.Empty);
// Create a discretionary access control list (DACL)
var dacl = new DiscretionaryAcl(isContainer: false, isDS: false, capacity: 2);
dacl.AddAccess(
AccessControlType.Allow,
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
-1,
InheritanceFlags.None,
PropagationFlags.None
);
dacl.AddAccess(
AccessControlType.Deny,
new SecurityIdentifier(WellKnownSidType.BuiltinGuestsSid, null),
-1,
InheritanceFlags.None,
PropagationFlags.None
);
// Assign the DACL to the security descriptor
securityDescriptor.DiscretionaryAcl = dacl;
// Configure HTTP.sys options
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseHttpSys(options =>
{
options.RequestQueueSecurityDescriptor = securityDescriptor;
});
詳細については、「HTTP.sys web server implementation in ASP.NET Core」 (ASP.NET Core への HTTP.sys Web サーバーの実装) を参照してください。
最上位レベルのステートメントを使用したアプリのテストのサポートの強化
.NET 10 では、 最上位レベルのステートメントを使用するアプリのテストのサポートが強化されました。 以前の開発者は、テスト プロジェクトが public partial class Programを参照できるように、Program.cs ファイルに Program class を手動で追加する必要がありました。 C# 9 のトップレベルステートメント機能により、内部 public partial class ProgramProgram class が生成されたため、 が必要でした。
.NET 10 では、プログラマが明示的に宣言しなかった場合、 ソース ジェネレーター を使用して public partial class Program 宣言を生成します。 さらに、public partial class Program が明示的に宣言されたタイミングを検出し、削除するよう開発者に勧めるアナライザーが追加されました。
この機能には、以下の PR が貢献しています。
新しい JSON パッチがSystem.Text.Jsonを使用して実装されました。
- JSON ドキュメントに適用する変更を記述するための標準形式です。
- RFC 6902 で定義されており、JSON リソースの部分的な更新を実行するために RESTful API で広く使用されています。
- JSON ドキュメントを変更するために適用できる一連の操作 (追加、削除、置換、移動、コピー、テストなど) を表します。
Web アプリでは、JSON Patch は、一般的に PATCH 操作でリソースの部分的な更新を実行するために使用されます。 クライアントは、更新プログラムのリソース全体を送信するのではなく、変更のみを含む JSON パッチ ドキュメントを送信できます。 修正プログラムを適用すると、ペイロードのサイズが減少し、効率が向上します。
このリリースでは、Microsoft.AspNetCore.JsonPatchシリアル化に基づくSystem.Text.Jsonの新しい実装が導入されています。 この機能を使用すると、次のことが可能になります。
- .NET 用に最適化された
System.Text.Jsonライブラリを利用して、最新の .NET プラクティスに合わせて調整します。 - 従来の
Newtonsoft.Jsonベースの実装と比較して、パフォーマンスが向上し、メモリ使用量が削減されます。
次のベンチマークでは、新しい System.Text.Json 実装のパフォーマンスと、従来の Newtonsoft.Json 実装を比較します。
| Scenario | Implementation | Mean | 割り当てられたメモリ |
|---|---|---|---|
| アプリケーション ベンチマーク | Newtonsoft.JsonPatch | 271.924 μs | 25 KB |
| System.Text.JsonPatch | 1.584 μs | 3 KB | |
| 逆シリアル化ベンチマーク | Newtonsoft.JsonPatch | 19.261 μs | 43 KB |
| System.Text.JsonPatch | 7.917 マイクロ秒 | 7 KB |
これらのベンチマークでは、新しい実装でパフォーマンスの大幅な向上とメモリ使用量の削減が強調されています。
Notes:
- 新しい実装は、レガシ実装のドロップインの代わりではありません。 特に、新しい実装では動的な型 ( ExpandoObjectなど) はサポートされていません。
- JSON Patch 標準には固有の セキュリティ リスクがあります。 これらのリスクは JSON Patch 標準に固有であるため、新しい実装 では固有のセキュリティ リスクを軽減しようとはしません。 JSON パッチ ドキュメントがターゲット オブジェクトに安全に適用されるようにするのは開発者の責任です。 詳細については、「 セキュリティ リスクの軽減」セクションを 参照してください。
Usage
System.Text.Jsonで JSON Patch のサポートを有効にするには、Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet パッケージをインストールします。
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
このパッケージには、JsonPatchDocument<T>型のオブジェクトの JSON Patch ドキュメントを表すT クラスと、System.Text.Jsonを使用して JSON Patch ドキュメントをシリアル化および逆シリアル化するためのカスタム ロジックが用意されています。
JsonPatchDocument<T> クラスのキー メソッドはApplyToであり、T型のターゲット オブジェクトにパッチ操作を適用します。
次の例では、 ApplyTo メソッドを使用して JSON Patch ドキュメントをオブジェクトに適用する方法を示します。
例: JsonPatchDocument の適用
その具体的な例を次に示します:
-
add、replace、およびremove操作。 - 入れ子になったプロパティに対する操作。
- 配列への新しい項目の追加。
- JSON パッチ ドキュメントでの JSON 文字列列挙型コンバーターの使用。
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com",
PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
Address = new Address
{
Street = "123 Main St",
City = "Anytown",
State = "TX"
}
};
// Raw JSON Patch document
var jsonPatch = """
[
{ "op": "replace", "path": "/FirstName", "value": "Jane" },
{ "op": "remove", "path": "/Email"},
{ "op": "add", "path": "/Address/ZipCode", "value": "90210" },
{
"op": "add",
"path": "/PhoneNumbers/-",
"value": { "Number": "987-654-3210", "Type": "Work" }
}
]
""";
// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON Patch document
patchDoc!.ApplyTo(person);
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
// Output:
// {
// "firstName": "Jane",
// "lastName": "Doe",
// "address": {
// "street": "123 Main St",
// "city": "Anytown",
// "state": "TX",
// "zipCode": "90210"
// },
// "phoneNumbers": [
// {
// "number": "123-456-7890",
// "type": "Mobile"
// },
// {
// "number": "987-654-3210",
// "type": "Work"
// }
// ]
// }
ApplyToメソッドは、通常、次のオプションによって制御される動作を含め、System.Text.Jsonを処理するためのJsonPatchDocumentの規則とオプションに従います。
-
NumberHandling: 数値プロパティを文字列から読み取るかどうか。 -
PropertyNameCaseInsensitive: プロパティ名で大文字と小文字が区別されるかどうか。
System.Text.Jsonと新しいJsonPatchDocument<T>実装の主な違い:
- 宣言された型ではなく、ターゲット オブジェクトのランタイム型によって、パッチ
ApplyToプロパティが決まります。 -
System.Text.Json逆シリアル化は、対象となるプロパティを識別するために宣言された型に依存します。
例: エラー処理を使用した JsonPatchDocument の適用
JSON パッチ ドキュメントを適用するときに発生する可能性があるさまざまなエラーがあります。 たとえば、ターゲット オブジェクトに指定されたプロパティがない場合や、指定した値がプロパティ型と互換性がない可能性があります。
JSON Patch では、 test 操作もサポートされています。
test操作は、指定した値がターゲット プロパティと等しいかどうかを確認し、そうでない場合はエラーを返します。
次の例では、これらのエラーを適切に処理する方法を示します。
Important
ApplyTo メソッドに渡されたオブジェクトは、インプレースで変更されます。 操作が失敗した場合、これらの変更を破棄するのは呼び出し元の責任です。
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com"
};
// Raw JSON Patch document
var jsonPatch = """
[
{ "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
{ "op": "test", "path": "/FirstName", "value": "Jane" },
{ "op": "replace", "path": "/LastName", "value": "Smith" }
]
""";
// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON Patch document, catching any errors
Dictionary<string, string[]>? errors = null;
patchDoc!.ApplyTo(person, jsonPatchError =>
{
errors ??= new ();
var key = jsonPatchError.AffectedObject.GetType().Name;
if (!errors.ContainsKey(key))
{
errors.Add(key, new string[] { });
}
errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
});
if (errors != null)
{
// Print the errors
foreach (var error in errors)
{
Console.WriteLine($"Error in {error.Key}: {string.Join(", ", error.Value)}");
}
}
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
// Output:
// Error in Person: The current value 'John' at path 'FirstName' is not equal
// to the test value 'Jane'.
// {
// "firstName": "John",
// "lastName": "Smith", <<< Modified!
// "email": "janedoe@gmail.com", <<< Modified!
// "phoneNumbers": []
// }
セキュリティ リスクの軽減
Microsoft.AspNetCore.JsonPatch.SystemTextJson パッケージを使用する場合は、潜在的なセキュリティ リスクを理解して軽減することが重要です。 以下のセクションでは、JSON Patch に関連付けられている特定されたセキュリティ リスクについて説明し、パッケージの安全な使用を確保するための推奨される軽減策を提供します。
Important
これは、脅威の完全な一覧ではありません。 アプリ開発者は、独自の脅威モデル レビューを実施して、アプリ固有の包括的な一覧を決定し、必要に応じて適切な軽減策を考え出す必要があります。 たとえば、コレクションをパッチ操作に公開するアプリでは、それらの操作がコレクションの先頭に要素を挿入または削除する場合に、アルゴリズムの複雑さの攻撃の可能性を考慮する必要があります。
独自のアプリに対して包括的な脅威モデルを実行し、以下の推奨される軽減策に従って特定された脅威に対処することで、これらのパッケージのコンシューマーは、セキュリティ リスクを最小限に抑えながら、JSON パッチ機能をアプリに統合できます。
これらのパッケージのコンシューマーは、次のようなセキュリティ リスクを最小限に抑えながら、JSON パッチ機能をアプリに統合できます。
- 独自のアプリに対して包括的な脅威モデルを実行します。
- 特定された脅威に対処します。
- 次のセクションで推奨される軽減策に従います。
メモリ増幅によるサービス拒否 (DoS)
-
シナリオ: 悪意のあるクライアントが、大きなオブジェクト グラフを複数回複製する
copy操作を送信すると、メモリが過剰に消費されます。 - 影響: 潜在的なOut-Of-Memory (OOM)状態が発生し、サービスが中断する可能性があります。
-
Mitigation:
-
ApplyToを呼び出す前に、受信 JSON パッチ ドキュメントのサイズと構造を検証します。 - 検証はアプリ固有である必要がありますが、検証の例は次のようになります。
-
public void Validate(JsonPatchDocument<T> patch)
{
// This is just an example. It's up to the developer to make sure that
// this case is handled properly, based on the app's requirements.
if (patch.Operations.Where(op=>op.OperationType == OperationType.Copy).Count()
> MaxCopyOperationsCount)
{
throw new InvalidOperationException();
}
}
ビジネスロジックの覆し
- シナリオ: パッチ操作では、ビジネス上の制約に違反する暗黙的なインバリアント (内部フラグ、ID、計算フィールドなど) を持つフィールドを操作できます。
- 影響:データ整合性の問題と意図しないアプリの動作。
-
Mitigation:
- 変更しても安全な明示的に定義されたプロパティを持つ POCO オブジェクトを使用します。
- ターゲット オブジェクトで機密性の高いプロパティまたはセキュリティ クリティカルなプロパティを公開しないようにします。
- POCO オブジェクトが使用されていない場合は、操作の適用後に修正プログラムが適用されたオブジェクトを検証して、ビジネス ルールとインバリアントが違反していないことを確認します。
認証と承認
- シナリオ: 認証されていないクライアントまたは未承認のクライアントが、悪意のある JSON パッチ要求を送信します。
- 影響:機密データを変更したり、アプリの動作を中断したりするための未承認のアクセス。
-
Mitigation:
- 適切な認証と承認メカニズムを使用して、JSON パッチ要求を受け入れるエンドポイントを保護します。
- 適切なアクセス許可を持つ信頼されたクライアントまたはユーザーへのアクセスを制限します。
RedirectHttpResult.IsLocalUrl を使用して URL がローカルかどうかを検出する
新しい RedirectHttpResult.IsLocalUrl(url) ヘルパー メソッドを使用して、URL がローカルかどうかを検出します。 次の条件に該当する場合、URL はローカルと見なされます。
IsLocalUrl は、 開いているリダイレクト攻撃を防ぐために URL にリダイレクトする前に URL を検証する場合に便利です。
if (RedirectHttpResult.IsLocalUrl(url))
{
return Results.LocalRedirect(url);
}
@martincostello さん、この貢献に感謝します!
重大な変更
.NET の破壊的変更に関する記事を使用して、アプリを新しいバージョンの .NET にアップグレードするときに適用される可能性のある破壊的変更を見つけます。
ASP.NET Core