Note
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 10 バージョンを参照してください。
Warning
このバージョンの ASP.NET Coreはサポートされなくなりました。 詳細については、.NETおよびコア サポート ポリシー.NETを参照してください。 現在のリリースについては、この記事の .NET 10 バージョンを参照してください。
この記事では、Blazor アプリから Web API を呼び出す方法について説明します。
外部 Web API を呼び出すためのサーバー側のシナリオ
サーバーベースのコンポーネントからは HttpClient インスタンス (通常、IHttpClientFactory を使用して作成されます) を使用して外部 Web API を呼び出します。 サーバー側アプリに適用されるガイダンスについては、ASP.NET Core で IHttpClientFactory を使用して HTTP リクエストを行う を参照してください。
サーバー側アプリには、HttpClient サービスが含まれません。 ファクトリ インフラストラクチャ HttpClient を使用して HttpClient アプリにを提供します。
Program ファイルで次の操作を行います。
builder.Services.AddHttpClient();
次の Razor コンポーネントは、ASP.NET Core の IHttpClientFactory を使用して HTTP 要求を行う 記事の 基本的な使用法 の例のように、GitHub のブランチに対して Web API にリクエストを送信します。
CallWebAPI.razor:
@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization
@inject IHttpClientFactory ClientFactory
<h1>Call web API from a server-side Razor component</h1>
@if (getBranchesError || branches is null)
{
<p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
<ul>
@foreach (var branch in branches)
{
<li>@branch.Name</li>
}
</ul>
}
@code {
private IEnumerable<GitHubBranch>? branches = [];
private bool getBranchesError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
protected override async Task OnInitializedAsync()
{
using var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
var client = ClientFactory.CreateClient();
using var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
getBranchesError = true;
}
shouldRender = true;
}
public class GitHubBranch
{
[JsonPropertyName("name")]
public string? Name { get; set; }
}
}
C# 12 以降の前の例では、[] 変数のために空の配列 (branches) が作成されています。 .NET 8 より前の SDK でコンパイルされた以前のバージョンの C# の場合は、空の配列 (Array.Empty<GitHubBranch>()) を作成します。
その他の作業例については、ASP.NET Core Blazor ファイルのアップロード記事の Web API コントローラーにファイルをアップロードするサーバー側のファイルアップロードの例を参照してください。
Web API 呼び出しのサービス抽象化
このセクションは、サーバー プロジェクトで Web API を管理する Blazor Web App、または Web API 呼び出しを外部 Web API に変換する場合に適用されます。
対話型の WebAssembly および Auto レンダリング モードを使用する場合、コンポーネントは既定でプリレンダリングされます。 また、Blazor バンドルがクライアントにダウンロードされ、クライアント側ランタイムがアクティブ化される前に、Auto コンポーネントが最初にサーバーから対話形式でレンダリングされます。 つまり、これらのレンダリング モードを使用するコンポーネントは、クライアントとサーバーの両方から正常に実行されるように設計する必要があります。 コンポーネントでサーバー プロジェクト ベースの API を呼び出すか、クライアントでの実行時に外部 Web API (Blazor Web App の外部にあるもの) に要求を変換する必要がある場合は、サービス インターフェイスの背後でその API 呼び出しを抽象化し、サービスのクライアントとサーバーのバージョンを実装することをお勧めします。
- クライアント バージョンは、構成済みの HttpClient を使用して Web API を呼び出します。
- サーバー バージョンは通常、サーバー側のリソースに直接アクセスできます。 ネットワーク要求は通常不要であるため、サーバーへの呼び出しを行う HttpClient をサーバーに挿入することは推奨されません。 または、API がサーバー プロジェクトの外部にある可能性がありますが、プロキシされた要求にアクセス トークンを追加するなど、何らかの方法で要求を変換するにはサーバーのサービス抽象化が必要です。
WebAssembly レンダリング モードを使用する場合は、コンポーネントがクライアントからのみレンダリングされるように、プリレンダリングを無効にすることもできます。 詳細については、「Prerender ASP.NET Core Razor コンポーネントを参照してください。
例 (サンプル アプリ):
-
BlazorWebAppCallWebApiサンプル アプリのムービー リスト Web API。 -
BlazorWebAppCallWebApi_Weatherサンプル アプリでの気象データ レンダリング Web API のストリーミング。 -
BlazorWebAppOidc(BFF 以外のパターン) またはBlazorWebAppOidcBff(BFF パターン) サンプル アプリのいずれかでクライアントに返される気象データ。 これらのアプリは、セキュリティで保護された (Web) API 呼び出しを示しています。 詳細については、「OpenID Connect (OIDC) をセキュリティで保護する>を参照してください。
Blazor Web App 外部 Web API
このセクションは、別の (外部) プロジェクトによって管理されている Web API を呼び出す Blazor Web Appに適用されます。別のサーバーでホストされている可能性があります。
Blazor Web App は通常、クライアント側の WebAssembly コンポーネントをプリレンダリングし、Auto コンポーネントは静的または対話型のサーバー側のレンダリング (SSR) 中にサーバー上にレンダリングします。
HttpClient サービスは、既定では Blazor Web App のメイン プロ ジェクトに登録されません。 「HttpClient サービスの追加」セクションの説明に従って、.Client プロジェクトに登録されているHttpClient サービスのみでアプリを実行すると、アプリを実行するとランタイム エラーが発生します。
InvalidOperationException: Cannot provide a value for property 'Http' on type '...{COMPONENT}'. (InvalidOperationException: {COMPONENT}' 型では 'Http' プロパティの値を指定できません。) There is no registered service of type 'System.Net.Http.HttpClient'. ('System.Net.Http.HttpClient' 型の登録済みサービスはありません。)
次 のいずれかの 方法を使用します。
HttpClient サービスをサーバー プロジェクトに追加して、SSR 中に HttpClient を使用できるようにします。 サーバー プロジェクトの
Programファイルで、次のサービス登録を使います。builder.Services.AddHttpClient();HttpClient サービスは共有フレームワークによって提供されるため、アプリのプロジェクト ファイル内のパッケージ参照は必要ありません。
例:
BlazorWebAppCallWebApiサンプル アプリの Todo list Web APIWeb API を呼び出す WebAssembly コンポーネントにプリレンダリングが必要ない場合は、Prerender ASP.NET Core Razor コンポーネントのガイダンスに従ってプリレンダリングを無効にします。 この方法を採用する場合は、サーバー側でコンポーネントがプリレンダリングされないため、HttpClient のメイン プロジェクトに Blazor Web App サービス を追加する必要はありません。
詳細については、プリレンダリングに関する記事の「プリレンダリング中にクライアント側サービスが解決できない」セクションを参照してください。
外部 Web API を呼び出すためのクライアント側のシナリオ
クライアント ベースのコンポーネントは、HttpClient インスタンスを使用して外部 Web API を呼び出します。通常は、HttpClient ファイルに登録された構成済みの Program で作成されます。
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
次の Razor コンポーネントは、ASP.NET Core で IHttpClientFactory を使用して HTTP 要求を行う 記事の 基本的な使用方法 の例のように、GitHub ブランチに対してウェブ API に要求を行います。
CallWebAPI.razor:
@page "/call-web-api"
@using System.Text.Json
@using System.Text.Json.Serialization
@inject HttpClient Client
<h1>Call web API from a Blazor WebAssembly Razor component</h1>
@if (getBranchesError || branches is null)
{
<p>Unable to get branches from GitHub. Please try again later.</p>
}
else
{
<ul>
@foreach (var branch in branches)
{
<li>@branch.Name</li>
}
</ul>
}
@code {
private IEnumerable<GitHubBranch>? branches = [];
private bool getBranchesError;
private bool shouldRender;
protected override bool ShouldRender() => shouldRender;
protected override async Task OnInitializedAsync()
{
using var request = new HttpRequestMessage(HttpMethod.Get,
"https://api.github.com/repos/dotnet/AspNetCore.Docs/branches");
request.Headers.Add("Accept", "application/vnd.github.v3+json");
request.Headers.Add("User-Agent", "HttpClientFactory-Sample");
using var response = await Client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
branches = await JsonSerializer.DeserializeAsync
<IEnumerable<GitHubBranch>>(responseStream);
}
else
{
getBranchesError = true;
}
shouldRender = true;
}
public class GitHubBranch
{
[JsonPropertyName("name")]
public string? Name { get; set; }
}
}
C# 12 以降の前の例では、[] 変数のために空の配列 (branches) が作成されています。 .NET 8 より前の SDK でコンパイルされた以前のバージョンの C# の場合は、空の配列 (Array.Empty<GitHubBranch>()) を作成します。
.NET/C# コードとデータを保護するには、サーバー側の ASP.NET Core バックエンド Web API で ASP.NET Core Data Protection 機能を使用します。 クライアント側の Blazor WebAssembly アプリは、セキュリティで保護されたアプリ機能とデータ処理のためにサーバー側 Web API を呼び出します。
Blazor WebAssembly アプリは多くの場合、 クロスオリジン要求共有 (CORS) セキュリティにより、配信元間で Web API への直接呼び出しを行うことができなくなります。 一般的な例外は次のようになります。
'{URL}' で オリジン 'https://localhost:{PORT}'' からのフェッチへのアクセスは CORS ポリシーによってブロックされました: 要求されたリソースに 'Access-Control-Allow-Origin' ヘッダーが存在しません。 不透明な応答がニーズに応える場合は、要求のモードを "no-cors" に設定して、CORS を無効にしてリソースをフェッチします。
上記の例外を回避しようとしている SetBrowserRequestMode (1) の BrowserRequestMode フィールドを使用して NoCors を呼び出した場合でも、Web API の配信元に対する CORS 制限 (特定の配信元からの呼び出しのみを許可する制限や、ブラウザーからの JavaScript fetch 要求を妨げる制限など) によって要求が失敗することがよくあります。 このような呼び出しを成功させる唯一の方法は、呼び出している Web API が、正しい CORS 構成でのオリジンの呼び出しを許可することです。 ほとんどの外部 Web API では、CORS ポリシーを構成できません。 この制限に対処するには、次のいずれかの方法を採用します。
独自のサーバー側 ASP.NET Coreバックエンド Web API を維持します。 クライアント側 Blazor WebAssembly アプリはサーバー側の Web API を呼び出し、Web API は(ブラウザーではなく) サーバーベースの C# コードから、正しい CORS ヘッダーを使用して外部 Web API に要求を行い、結果をクライアント側の Blazor WebAssembly アプリに返します。
プロキシ サービスを使用して、クライアント側の Blazor WebAssembly アプリから外部 Web API に要求をプロキシします。 プロキシ サービスは、サーバー側アプリを使用してクライアントに代わって要求を行い、呼び出しが成功した後に結果を返します。 CloudFlare の CORS PROXY 基づく次の例では、
{REQUEST URI}プレースホルダーは要求 URI です。@using System.Net @inject IHttpClientFactory ClientFactory ... @code { public async Task CallApi() { var client = ClientFactory.CreateClient(); var urlEncodedRequestUri = WebUtility.UrlEncode("{REQUEST URI}"); using var request = new HttpRequestMessage(HttpMethod.Get, $"https://corsproxy.io/?{urlEncodedRequestUri}"); using var response = await client.SendAsync(request); ... } }
HttpClient サービスを追加する
このセクションのガイダンスは、クライアント側のシナリオに適用されます。
クライアント側コンポーネントは、事前に構成された HttpClient サービスを使用して Web API を呼び出します。このサービスは、元のサーバーに要求を行うことに重点を置いています。 他の Web API 用の追加の HttpClient サービス構成は、開発者コードで作成できます。 要求は、Blazor JSON ヘルパーを使用して、または HttpRequestMessage で構成されます。 要求には Fetch API オプション構成を含めることができます。
このセクションの構成例は、アプリ内の 1 つの HttpClient インスタンスに対して 1 つの Web API が呼び出される場合にのみ役立ちます。 アプリで複数の Web API を呼び出す必要がある場合は、それぞれ独自のベース アドレスと構成を使用して、次のアプローチを採用できます。この方法については、この記事の次の 2 つのセクションで説明します。
-
HttpClientを使用した名前付きIHttpClientFactory: 各 Web API には一意の名前が付けられます。 アプリ コードまたは Razor コンポーネントが Web API を呼び出すと、名前付き HttpClient インスタンスを使用して呼び出しが行われます。 -
型指定された
HttpClient: 各 Web API は型指定されています。 アプリ コードまたは Razor コンポーネントが Web API を呼び出すと、型指定 HttpClient インスタンスを使用して呼び出しが行われます。
Program ファイルで、アプリの作成に使用した HttpClient プロジェクト テンプレートから Blazor サービスを追加します (このサービスが存在していない場合)。
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
上記の例では、ベース アドレスの設定に builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress) を使っています。これを使って、アプリのベース アドレス (通常は、ホスト ページの <base> タグの href 値に由来します) を取得できます。
クライアント独自のベース アドレスを使用する最も一般的なユース ケースは次のとおりです。
-
.Client(.NET 8 以降) のクライアント プロジェクト (Blazor Web App) は、WebAssembly のクライアントで実行される WebAssembly コンポーネントまたはコードから、サーバー アプリの API に対して Web API 呼び出しを行います。 - ホストされている Client アプリのクライアント プロジェクト (Blazor WebAssembly) は、サーバー プロジェクト (Server) に対して Web API 呼び出しを行います。 Hosted Blazor WebAssembly プロジェクト テンプレートは、.NET 8 以降では使用できなくなりました。 ただし、ホストされている Blazor WebAssembly アプリは、.NET 8 で引き続きサポートされます。
外部 Web API (クライアント アプリと同じ URL 空間にない) を呼び出す場合は、URI を Web API のベース アドレスに設定します。 次の例では、Web API のベース アドレスを https://localhost:5001 に設定します。これは、別の Web API アプリが実行される場所で、クライアント アプリからの要求に応答する準備ができています。
builder.Services.AddScoped(sp =>
new HttpClient { BaseAddress = new Uri("https://localhost:5001") });
HttpClient による名前付き IHttpClientFactory
IHttpClientFactory サービスおよび名前付き HttpClient の構成がサポートされています。
Note
HttpClient からの名前付き IHttpClientFactory を使用する代替手段は、型指定された HttpClient を使うことです。 詳細については、「TypedHttpClient」セクションを参照してください。
Microsoft.Extensions.Http NuGet パッケージをアプリに追加します。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「
クライアント プロジェクトの Program ファイルで、次のとおりです。
builder.Services.AddHttpClient("WebAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
Blazor Web App のプリレンダリングされたクライアント側コンポーネントで名前付きクライアントを使用する場合は、サーバー プロジェクトと .Client プロジェクトの両方に、上記のサービス登録が表示されます。 サーバーでは、builder.HostEnvironment.BaseAddress が Web API のベース アドレスで置き換えられます。これは、以下で詳しく説明します。
上記のクライアント側の例では、ベース アドレスの設定に builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress) を使っています。これを使って、クライアント側アプリのベース アドレス (通常は、ホスト ページの <base> タグの href 値に由来します) を取得できます。
クライアント独自のベース アドレスを使用する最も一般的なユース ケースは次のとおりです。
- WebAssemblyやAutoコンポーネントから、またはWebAssemblyでクライアント上で実行されるコードから、同一ホストアドレスのサーバーアプリのAPIに対してWeb API呼び出しを行う
.Clientのクライアントプロジェクト(Blazor Web App)。 - サーバー プロジェクト (Client) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Server)。
クライアント独自のベース アドレスを使用する最も一般的なユース ケースは、サーバー プロジェクト (Client) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Server) にあります。
(クライアント アプリと同じ URL 空間ではなく) 外部 Web API を呼び出す場合、またはサーバー側アプリでサービスを構成している場合 (たとえば、サーバー上のクライアント側コンポーネントのプリレンダリングを処理する場合)、URI を Web API のベース アドレスに設定します。 次の例では、Web API のベース アドレスを https://localhost:5001 に設定します。これは、別の Web API アプリが実行される場所で、クライアント アプリからの要求に応答する準備ができています。
builder.Services.AddHttpClient("WebAPI", client =>
client.BaseAddress = new Uri("https://localhost:5001"));
次のようなコンポーネント コードがあるとします。
- IHttpClientFactory のインスタンスによって、名前付きの HttpClient が作成されます。
- 名前付き HttpClient を使用して、
/forecastの Web API から JSON の気象予報データの GET 要求が発行されます。
@inject IHttpClientFactory ClientFactory
...
@code {
private Forecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
var client = ClientFactory.CreateClient("WebAPI");
forecasts = await client.GetFromJsonAsync<Forecast[]>("forecast") ?? [];
}
}
BlazorWebAppCallWebApi
サンプル アプリは、HttpClient コンポーネントで名前付きCallTodoWebApiCsrNamedClientを使用して Web API を呼び出す方法を示しています。 追加の動作デモンストレーションは、名前付きHttpClientを使用してMicrosoft Graphを呼び出すクライアント アプリでご覧いただけますので、「ASP.NET CoreでGraph APIを使用するBlazor WebAssembly」を参照してください。
HttpClient という名前の Microsoft Graph の呼び出しに基づくクライアント アプリでの動作デモについては、「ASP.NET Core で Graph API を使用する」をご覧ください。
型指定された HttpClient
型指定された HttpClient では、1 つ以上のアプリの HttpClient インスタンス (既定または名前付き) を使用して、1 つ以上の Web API エンドポイントからデータを返します。
Note
型指定された HttpClient を使用する代替手段は、HttpClient からの名前付き IHttpClientFactory を使うことです。 詳細については、「 名前付き HttpClient と IHttpClientFactory 」セクションを参照してください。
Microsoft.Extensions.Http NuGet パッケージをアプリに追加します。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「
次の例では、/forecast の Web API から JSON の気象予報データの GET 要求を発行します。
ForecastHttpClient.cs:
using System.Net.Http.Json;
namespace BlazorSample.Client;
public class ForecastHttpClient(HttpClient http)
{
public async Task<Forecast[]> GetForecastAsync() =>
await http.GetFromJsonAsync<Forecast[]>("forecast") ?? [];
}
クライアント プロジェクトの Program ファイルで、次のとおりです。
builder.Services.AddHttpClient<ForecastHttpClient>(client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
Blazor Web App のプリレンダリングされたクライアント側コンポーネントで型指定されたクライアントを使用する場合は、サーバー プロジェクトと .Client プロジェクトの両方に、上記のサービス登録が表示されます。 サーバーでは、builder.HostEnvironment.BaseAddress が Web API のベース アドレスで置き換えられます。これは、以下で詳しく説明します。
上記の例では、ベース アドレスの設定に builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress) を使っています。これを使って、クライアント側アプリのベース アドレス (通常は、ホスト ページの <base> タグの href 値に由来します) を取得できます。
クライアント独自のベース アドレスを使用する最も一般的なユース ケースは次のとおりです。
- WebAssemblyやAutoコンポーネントから、またはWebAssemblyでクライアント上で実行されるコードから、同一ホストアドレスのサーバーアプリのAPIに対してWeb API呼び出しを行う
.Clientのクライアントプロジェクト(Blazor Web App)。 - サーバー プロジェクト (Client) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Server)。
クライアント独自のベース アドレスを使用する最も一般的なユース ケースは、サーバー プロジェクト (Client) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Server) にあります。
(クライアント アプリと同じ URL 空間ではなく) 外部 Web API を呼び出す場合、またはサーバー側アプリでサービスを構成している場合 (たとえば、サーバー上のクライアント側コンポーネントのプリレンダリングを処理する場合)、URI を Web API のベース アドレスに設定します。 次の例では、Web API のベース アドレスを https://localhost:5001 に設定します。これは、別の Web API アプリが実行される場所で、クライアント アプリからの要求に応答する準備ができています。
builder.Services.AddHttpClient<ForecastHttpClient>(client =>
client.BaseAddress = new Uri("https://localhost:5001"));
コンポーネントによって型指定された HttpClient が挿入され、Web API が呼び出されます。
次のようなコンポーネント コードがあるとします。
- 前の
ForecastHttpClientのインスタンスが挿入され、型指定された HttpClient を作成します。 - 型指定された HttpClient を使用して、 JSON の気象データの GET 要求が Web API から発行されます。
@inject ForecastHttpClient Http
...
@code {
private Forecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetForecastAsync();
}
}
BlazorWebAppCallWebApi
サンプル アプリは、HttpClient コンポーネントで型指定されたCallTodoWebApiCsrTypedClientを使用して Web API を呼び出す方法を示しています。 コンポーネントはInteractiveWebAssemblyを使用してクライアント側レンダリング (CSR) ( レンダリング モード) を採用しているため、型指定されたクライアント サービスの登録は、サーバー プロジェクトと Program プロジェクトの両方の.Client ファイルに表示されることに注意してください。
HttpClientのスコープ外のサービスへのアクセス
IHttpClientFactory は DelegatingHandler インスタンスをアプリとは別の依存関係挿入 (DI) スコープで作成します。 派生 DelegatingHandler 型にスコープサービスを挿入した場合、ハンドラーは Blazor 回線からサービスにアクセスできません。
アプリケーション スコープ ハンドラーまたは回線アクティビティ ハンドラーを使用して送信要求ミドルウェア内のサービスにアクセスする方法の例については、「ASP.NET Core サーバー側および Blazor Web App 追加のセキュリティ シナリオを参照してください。
HttpRequestMessage、HttpResponseMessage、およびHttpClientの処分
本文のない HttpRequestMessage では、明示的な破棄は必要ありません。 ただし、次のいずれかのパターンで破棄できます。
using宣言 (C# 8 以降):using var request = new HttpRequestMessage(...);-
using (var request = new HttpRequestMessage(...)) { ... }
次の理由により、すべての HttpRequestMessage をすべての使用で破棄することをお勧めします。
- ファイナライザーを回避してパフォーマンスを向上させる。
- 将来的に、最初から要求本文が含まれていないHttpRequestMessageに要求本文が追加される可能性に備えて、コードを堅牢化します。
- 委任ハンドラーが Dispose/DisposeAsyncの呼び出しを想定している場合に、機能上の問題を回避する可能性があります。
- 特定のケースを覚えるよりも、あらゆる場所に一般的なルールを適用する方が簡単です。
常にHttpResponseMessageインスタンスを破棄します。
を呼び出して作成されたインスタンスはフレームワークによって管理されるため、破棄HttpClient。
Example:
using var request = new HttpRequestMessage(HttpMethod.Get, "/weather-forecast");
var client = clientFactory.CreateClient("ExternalApi");
using var response = await client.SendAsync(request);
プリレンダリングされるデータ
プリレンダリング時に、コンポーネントは 2 回レンダリングされます。最初は静的に、次に対話形式でレンダリングされます。 状態は、プリレンダリングされたコンポーネントから対話型コンポーネントに自動的に流れるわけではありません。 コンポーネントが非同期の初期化操作を実行し、初期化中に異なる状態に対して別のコンテンツ ("読み込み中..." 進行インジケーターなど) をレンダリングする場合、コンポーネントが 2 回レンダリングされるときにちらつきが発生することがあります。
これは、BlazorWebAppCallWebApiアプリと BlazorWebAppCallWebApi_Weatherサンプル アプリが示す永続的なコンポーネント状態 API を使用して、プリレンダリングされた状態をフローさせることで対処できます。 コンポーネントが対話形式でレンダリングされる場合、同じ状態を使用して同じ方法でレンダリングできます。 詳細については、次のリソースを参照してください。
Note
永続的なコンポーネント状態 API では、.NET 10 以降での拡張ナビゲーションのみがサポートされます。 .NET 8 または .NET 9 を対象とするアプリの場合は、data-enhance-nav 属性を false に設定して、ページへのリンクの拡張ナビゲーションを無効にすることができます。 詳細については、「ASP.NET Core Blazor ナビゲーションを参照してください。
JSON 補助機能
System.Net.Http.Json パッケージには、System.Net.Http.HttpClientを使用した自動シリアル化と逆シリアル化を実行するSystem.Net.Http.HttpContentとSystem.Text.Jsonの拡張メソッドが用意されています。
System.Net.Http.Json パッケージは、.NET共有フレームワークによって提供され、アプリへのパッケージ参照を追加する必要はありません。
HttpClient は、元のサーバーに対して要求を行うための構成済みのサービスとして用意されています。 HttpClient と JSON のヘルパー (System.Net.Http.Json.HttpClientJsonExtensions) は、サード パーティの Web API エンドポイントを呼び出すためにも使用されます。 HttpClient はブラウザーの Fetch API を使用して実装され、同じ配信元ポリシーの適用など、その制限の対象となります。これについては、 クロスオリジン リソース共有 (CORS) セクションのこの記事で後述します。
クライアントのベース アドレスは、生成元のサーバーのアドレスに設定されます。
HttpClient ディレクティブを使用して @inject インスタンスをコンポーネントに挿入します。
@using System.Net.Http
@inject HttpClient Http
System.Net.Http.Json にアクセスするには、HttpClientJsonExtensions 名前空間を使用します。これには、GetFromJsonAsync、PutAsJsonAsync、および PostAsJsonAsync が含まれます。
@using System.Net.Http.Json
次のセクションでは、JSON ヘルパーについて説明します。
System.Net.Http には、HTTP 要求を送信し、HTTP 応答を受信する (たとえば DELETE 要求を送信する) ための追加のメソッドが含まれています。 詳細については、「 DELETE と追加の拡張メソッド 」セクションを参照してください。
JSON から取得する (GetFromJsonAsync)
GetFromJsonAsync は、HTTP GET 要求を送信し、JSON 応答本文を解析してオブジェクトを作成します。
次のコンポーネント コードでは、コンポーネントによって todoItems が表示されます。
GetFromJsonAsync は、コンポーネントの初期化が完了すると呼び出されます (OnInitializedAsync)。
todoItems = await Http.GetFromJsonAsync<TodoItem[]>("todoitems");
JSON として投稿する (PostAsJsonAsync)
PostAsJsonAsync は、要求本文に JSON としてシリアル化された値が含まれる POST 要求を、指定された URI に送信します。
次のコンポーネント コードでは、コンポーネントのバインドされた要素によって newItemName が提供されています。
AddItem メソッドは、<button> 要素を選択することにでトリガーされます。
await Http.PostAsJsonAsync("todoitems", addItem);
PostAsJsonAsync では HttpResponseMessage が返されます。 応答メッセージから JSON コンテンツを逆シリアル化するには、ReadFromJsonAsync 拡張メソッドを使用します。 次の例では、JSON の気象データを配列として読み取ります。
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
Array.Empty<WeatherForecast>();
JSON として配置する (PutAsJsonAsync)
PutAsJsonAsync は、JSON でエンコードされたコンテンツを含む HTTP PUT 要求を送信します。
次のコンポーネント コードでは、editItem および Name の IsCompleted 値が、コンポーネントのバインドされた要素によって提供されています。 項目の Id は、UI の別の部分 (表示されない) で項目が選択され、EditItem が呼び出されたときに設定されます。
SaveItem メソッドは、<button> 要素を選択することでトリガーされます。 次の例では、簡潔にするために todoItems の読み込みを示していません。 項目の読み込みの例については、 JSON からの GET (GetFromJsonAsync) セクションを参照してください。
await Http.PutAsJsonAsync($"todoitems/{editItem.Id}", editItem);
PutAsJsonAsync では HttpResponseMessage が返されます。 応答メッセージから JSON コンテンツを逆シリアル化するには、ReadFromJsonAsync 拡張メソッドを使用します。 次の例では、JSON の気象データを配列として読み取ります。
var content = await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
Array.Empty<WeatherForecast>();
JSON 形式での PATCH (PatchAsJsonAsync)
PatchAsJsonAsync では、JSON でエンコードされたコンテンツを含む HTTP PATCH 要求を送信します。
Note
詳細については、ASP.NET Core Web API の JsonPatch を参照してください。
次の例で、PatchAsJsonAsync はエスケープされた引用符を含むプレーン テキスト文字列として JSON パッチ ドキュメントを受け取ります。
await Http.PatchAsJsonAsync(
$"todoitems/{id}",
"[{\"operationType\":2,\"path\":\"/IsComplete\",\"op\":\"replace\",\"value\":true}]");
C# 11 (.NET 7) 以降では、JSON 文字列を raw 文字列リテラル として作成できます。 コード分析ツールの StringSyntaxAttribute.Json[StringSyntax] 属性に対して、 フィールドを使用して JSON 構文を指定します。
@using System.Diagnostics.CodeAnalysis
...
@code {
[StringSyntax(StringSyntaxAttribute.Json)]
private const string patchOperation =
"""[{"operationType":2,"path":"/IsComplete","op":"replace","value":true}]""";
...
await Http.PatchAsJsonAsync($"todoitems/{id}", patchOperation);
}
PatchAsJsonAsync では HttpResponseMessage が返されます。 応答メッセージから JSON コンテンツを逆シリアル化するには、ReadFromJsonAsync 拡張メソッドを使用します。 次の例では、JSON の ToDo 項目データを配列として読み取ります。 メソッドによって項目データが返されない場合は空の配列が作成されるため、次のようにステートメントの実行後も content は null 値にはなりません。
using var response = await Http.PatchAsJsonAsync(...);
var content = await response.Content.ReadFromJsonAsync<TodoItem[]>() ??
Array.Empty<TodoItem>();
インデント、スペース、エスケープされていない引用符を使用してレイアウトした、エンコードされていない PATCH ドキュメントは、次の JSON のようになります。
[
{
"operationType": 2,
"path": "/IsComplete",
"op": "replace",
"value": true
}
]
PATCH 要求を発行するアプリケーションでPATCHドキュメントを簡略化するために、次のガイダンスが示すように、アプリケーションは.NETのJSON PATCHサポートを使用することができます。
Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet パッケージをインストールし、パッケージの API 機能を使用して PATCH 要求のJsonPatchDocumentを作成します。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「
@using、System.Text.Json、System.Text.Json.Serialization、および Microsoft.AspNetCore.JsonPatch.SystemTextJson 名前空間のディレクティブを Razor コンポーネントの先頭に追加します。
@using System.Text.Json
@using System.Text.Json.Serialization
@using Microsoft.AspNetCore.JsonPatch.SystemTextJson
JsonPatchDocument 用の TodoItem を作成するために、IsComplete を true に設定し、JsonPatchDocument.Replace メソッドを使用します。
var patchDocument = new JsonPatchDocument<TodoItem>()
.Replace(p => p.IsComplete, true);
Microsoft.AspNetCore.JsonPatch NuGet パッケージをインストールし、パッケージの API 機能を使用して PATCH 要求のJsonPatchDocumentを作成します。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「
@using System.Text.Json
@using System.Text.Json.Serialization
@using Microsoft.AspNetCore.JsonPatch
JsonPatchDocument 用の TodoItem を作成するために、IsComplete を true に設定し、Replace メソッドを使用します。
var patchDocument = new JsonPatchDocument<TodoItem>()
.Replace(p => p.IsComplete, true);
ドキュメントの操作 (patchDocument.Operations) を PatchAsJsonAsync 呼び出しに渡します。
private async Task UpdateItem(long id)
{
await Http.PatchAsJsonAsync(
$"todoitems/{id}",
patchDocument.Operations,
new JsonSerializerOptions()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
});
}
プロパティを、その型の既定値と等しい場合にのみ無視するように、JsonSerializerOptions.DefaultIgnoreCondition が JsonIgnoreCondition.WhenWritingDefault に設定されています。
JSON ペイロードを見やすい形式で表示するには、JsonSerializerOptions.WriteIndented を true に設定して追加します。 インデントされた JSON の作成は、PATCH 要求の処理とは関係がなく、運用アプリにおける Web API 要求では通常実行されません。
ASP.NET Core Web API の JsonPatch に関する記事のガイダンスに従って、PATCH コントローラー アクションを Web API に追加します。 または、PATCH 要求処理は、次の手順で 最小限の API として実装できます。
Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet パッケージのパッケージ参照を Web API アプリに追加します。
Program ファイルで、@using 名前空間の Microsoft.AspNetCore.JsonPatch.SystemTextJson ディレクティブを追加します。
using Microsoft.AspNetCore.JsonPatch.SystemTextJson;
Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet パッケージのパッケージ参照を Web API アプリに追加します。
Note
Microsoft.AspNetCore.JsonPatch パッケージのパッケージ参照をアプリに追加する必要はありません。これは、Microsoft.AspNetCore.Mvc.NewtonsoftJson パッケージへの参照によって、Microsoft.AspNetCore.JsonPatch のパッケージ参照が自動的に推移的に追加されるためです。
Program ファイルで、@using 名前空間の Microsoft.AspNetCore.JsonPatch ディレクティブを追加します。
using Microsoft.AspNetCore.JsonPatch;
Web API の要求処理パイプラインへのエンドポイントを指定します。
app.MapPatch("/todoitems/{id}", async (long id, TodoContext db) =>
{
if (await db.TodoItems.FindAsync(id) is TodoItem todo)
{
var patchDocument =
new JsonPatchDocument<TodoItem>().Replace(p => p.IsComplete, true);
patchDocument.ApplyTo(todo);
await db.SaveChangesAsync();
return TypedResults.Ok(todo);
}
return TypedResults.NotFound();
});
Warning
ASP.NET Core Web API の JsonPatch に関する記事の他の例と同様に、上記の PATCH API では、Web API が過剰に投稿される攻撃から保護されません。 詳細については、「
完全に動作する PATCH エクスペリエンスについては、 BlazorWebAppCallWebApiサンプル アプリを参照してください。
DELETE (DeleteAsync) およびその他の拡張メソッド
System.Net.Http には、HTTP 要求を送信し、HTTP 応答を受信するための追加の拡張メソッドが含まれています。 HttpClient.DeleteAsync は、HTTP DELETE 要求を Web API に送信するために使用します。
次のコンポーネント コードでは、<button> 要素で DeleteItem メソッドを呼び出しています。 バインドされた <input> 要素では、削除する項目の id が提供されます。
await Http.DeleteAsync($"todoitems/{id}");
クライアント側のリクエストストリーミング
HTTP/2 プロトコルを使用する Chromium ベースのブラウザー (Google Chrome や Microsoft Edge など) と HTTPS の場合、クライアント側の
要求ストリーミングを有効にするには、SetBrowserRequestStreamingEnabledで true を HttpRequestMessage に設定します。
次のファイルアップロードの例では、
-
contentファイルの HttpContentです。 -
/Filesaveは Web API エンドポイントです。 -
Httpは HttpClientです。
using var request = new HttpRequestMessage(HttpMethod.Post, "/Filesave");
request.SetBrowserRequestStreamingEnabled(true);
request.Content = content;
using var response = await Http.SendAsync(request);
ストリーミング要求:
- HTTPS プロトコルが必要であり、HTTP/1.x では機能しません。
- 本文は含めますが
Content-Lengthヘッダーは含めません。 クロスオリジン ストリーミング要求には、プレフライト要求を含む CORS が必要です。
InputFile コンポーネントを使用したファイルのアップロードの詳細については、「ASP.NET Core Blazor ファイルのアップロード」および「クライアント側レンダリング (CSR) を使用したサーバーへのファイルのアップロードの例」を参照してください。
Cookie に基づく要求資格情報
このセクションのガイダンスは、認証 cookieに依存するクライアント側のシナリオに適用されます。
ベアラー トークン認証よりも安全性が高いとみなされる cookie ベースの認証では、構成済みの cookie で AddHttpMessageHandler を指定して DelegatingHandler を呼び出して、Web API 要求ごとに HttpClient 資格情報を送信できます。 ハンドラーは、SetBrowserRequestCredentials を使用して BrowserRequestCredentials.Include を構成します。これは、クロスオリジン要求を含む要求ごとに、Cookie や HTTP 認証ヘッダーなどの資格情報を送信するようにブラウザーに通知します。
CookieHandler.cs:
public class CookieHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);
return base.SendAsync(request, cancellationToken);
}
}
Note
AuthenticationStateProvider から DelegatingHandler にアクセスする方法のガイダンスについては、「ASP.NET Core サーバー側および Blazor Web App追加のセキュリティ シナリオを参照してください。
CookieHandler は、次のように Program ファイルに登録されます。
builder.Services.AddTransient<CookieHandler>();
メッセージ ハンドラーは、HttpClient 認証を必要とするすべての構成済み cookie に次のように追加されます。
builder.Services.AddHttpClient(...)
.AddHttpMessageHandler<CookieHandler>();
デモについては、「Secure ASP.NET Core Blazor WebAssembly with ASP.NET Core Identityを参照してください。
HttpRequestMessage を作成するときは、次のようにブラウザー要求の資格情報とヘッダーを直接設定します。
using var request = new HttpRequestMessage() { ... };
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
request.Headers.Add("X-Requested-With", [ "XMLHttpRequest" ]);
Web API 呼び出しにトークン ハンドラーを使用する
Blazor Web AppOIDC 認証では、トークン ハンドラーアプローチを使用して、外部 Web API 呼び出しをセキュリティで保護するための送信要求を行うことができます。 この方法は、この記事の「サンプル アプリ」セクションで説明されているBlazorWebAppOidcおよびBlazorWebAppOidcServerサンプル アプリによって使用されます。
詳細については、次のリソースを参照してください。
- ASP.NET Coreサーバー側およびBlazor Web App追加のセキュリティ シナリオ
- ASP.NET Core を OpenID Connect (OIDC) でセキュリティ保護する Blazor Web App
Microsoft ID プラットフォーム を使用した Web API 呼び出し
Blazor Web AppMicrosoft ID プラットフォーム を使用する Microsoft Identity Web パッケージ および Microsoft Entra ID を使用すると、Microsoft.Identity.Web.DownstreamApi NuGet パッケージによって提供される API を使用して、合理化された Web API 呼び出しを行うことができます。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「
アプリ設定ファイル (appsettings.json) で、ベース URL とスコープを指定します。 次の例では、 {BASE ADDRESS} プレースホルダーは Web API のベース URL です。 単一のスコープは、アプリ ID URI ({APP ID URI} プレースホルダー) とスコープ名 ({SCOPE NAME} プレースホルダー) で指定されます。
"DownstreamApi": {
"BaseUrl": "{BASE ADDRESS}",
"Scopes": [ "{APP ID URI}/{SCOPE NAME}" ]
}
Example:
"DownstreamApi": {
"BaseUrl": "https://localhost:7277",
"Scopes": [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ]
}
アプリの Program ファイルで、次を呼び出します。
- EnableTokenAcquisitionToCallDownstreamApi: トークン取得を有効にして Web API を呼び出します。
-
AddDownstreamApi: Microsoft Identity Web パッケージは、Web API 呼び出しを行う名前付きダウンストリーム Web サービスを作成する API を提供します。
IDownstreamApiは、外部 Web API (CallApiForUserAsync プロジェクト) から気象データを取得するために
MinimalApiJwtを呼び出すために使用されるサーバー側クラスに挿入されます。 - AddDistributedTokenCaches: .NET分散トークン キャッシュをサービス コレクションに追加します。
- AddDistributedMemoryCache: キャッシュ項目をメモリに格納する IDistributedCache の既定の実装を追加します。
- 分散トークン キャッシュ オプション (MsalDistributedTokenCacheAdapterOptions) を構成します。
- デバッグを目的とした開発では、 DisableL1Cache を
trueに設定することで、L1 キャッシュを無効にすることができます。 運用環境では、必ずfalseにリセットしてください。 -
L1CacheOptions.SizeLimitを使用して L1 キャッシュの最大サイズを設定して、キャッシュがサーバーのメモリをオーバーランしないようにします。 既定値は 500 MB です。 - デバッグ目的での開発では、 Encrypt を
false(既定値) に設定することで、保存時のトークン暗号化を無効にすることができます。 運用環境では、必ずtrueにリセットしてください。 - SlidingExpirationを使用してキャッシュからトークンの削除を設定します。 既定値は 1 時間です。
- L2 キャッシュ エラー (OnL2CacheFailure) のコールバックと非同期の L2 キャッシュ書き込み (EnableAsyncL2Write) に関するガイダンスなど、詳細については、「 MsalDistributedTokenCacheAdapterOptions と トークン キャッシュのシリアル化: 分散トークン キャッシュ」を参照してください。
- デバッグを目的とした開発では、 DisableL1Cache を
キャッシュを暗号化することを選択できます。運用環境では常に暗号化する必要があります。
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("DownstreamApi",
builder.Configuration.GetSection("DownstreamApi"))
.AddDistributedTokenCaches();
// Requires the 'Microsoft.Extensions.Caching.Memory' NuGet package
builder.Services.AddDistributedMemoryCache();
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
options =>
{
// The following lines that are commented out reflect
// default values. We recommend overriding the default
// value of Encrypt to encrypt tokens at rest.
//options.DisableL1Cache = false;
//options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024;
options.Encrypt = true;
//options.SlidingExpiration = TimeSpan.FromHours(1);
});
メモリ内分散トークン キャッシュは、 AddDistributedTokenCaches を呼び出すときに作成され、分散トークン キャッシュに使用できる基本実装があることを確認します。
運用 Web アプリと Web API では、運用分散トークン キャッシュ (例: Redis、Microsoft SQL Server、Microsoft Azure Cosmos DB) を使用する必要があります。
Note
1 台のコンピューターでのローカル開発とテストでは、分散トークン キャッシュの代わりにメモリ内トークン キャッシュを使用できます。
builder.Services.AddInMemoryTokenCaches();
開発とテストの期間の後半で、運用分散トークン キャッシュ プロバイダーを採用します。
AddDistributedMemoryCache では、キャッシュ項目をメモリに格納する IDistributedCache の既定の実装が追加されます。これは、トークン キャッシュのために Microsoft Identity Web によって使用されます。
AddDistributedMemoryCache には、Microsoft.Extensions.Caching.Memory NuGet パッケージへのパッケージ参照が必要です。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「
運用分散キャッシュ プロバイダーを構成するには、
Warning
運用環境にアプリをデプロイするときは、常にメモリ内分散トークン キャッシュを実際のトークン キャッシュ プロバイダーに置き換えます。 運用分散トークン キャッシュ プロバイダーを採用できない場合、アプリのパフォーマンスが大幅に低下する可能性があります。
詳細については、「 トークン キャッシュのシリアル化: 分散キャッシュ」を参照してください。 ただし、示されているコード例は、AddDistributedMemoryCache ではなく、AddDistributedTokenCache を介して分散キャッシュを構成する ASP.NET Core アプリには適用されません。
運用環境で共有 Data Protection キー リングを使用すると、Web ファーム内のサーバー間でアプリのインスタンスが、 MsalDistributedTokenCacheAdapterOptions.Encrypt が true に設定されているときにトークンを復号化できます。
Note
1 台のコンピューターでの早期開発とローカル テストでは、 Encrypt を false に設定し、後で共有 Data Protection キー リングを構成できます。
options.Encrypt = false;
開発とテストの期間の後半で、トークン暗号化を有効にし、共有データ保護キー リングを採用します。
次の例は、共有キー リングに Azure Blob Storage Azure Key Vault (PersistKeysToAzureBlobStorage/ProtectKeysWithAzureKeyVault) を使用する方法を示しています。 サービス構成は、デモンストレーションを目的としたベース ケース シナリオです。 運用アプリをデプロイする前に、Azure サービスについて理解し、このセクションの最後にリンクされているAzure サービスの専用ドキュメント セットを使用してベスト プラクティスを採用してください。
Blazor Web Appのサーバー プロジェクトに次のパッケージを追加します。
Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「
Note
次の手順に進む前に、アプリがMicrosoft Entraに登録されていることを確認します。
データ保護キーを維持するようにAzure Blob Storageを構成します。 ASP.NET Core の Key ストレージ プロバイダーに関するガイダンスに従います。
Azure Key Vaultを構成して、保存されているデータ保護キーを暗号化します。 データ保護 ASP.NET Core構成のガイダンスに従ってください。
サービスが登録されている Program ファイルで、次のコードを使用します。
TokenCredential? credential;
if (builder.Environment.IsProduction())
{
credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
// Local development and testing only
DefaultAzureCredentialOptions options = new()
{
// Specify the tenant ID to use the dev credentials when running the app locally
// in Visual Studio.
VisualStudioTenantId = "{TENANT ID}",
SharedTokenCacheTenantId = "{TENANT ID}"
};
credential = new DefaultAzureCredential(options);
}
builder.Services.AddDataProtection()
.SetApplicationName("{APPLICATION NAME}")
.PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential)
.ProtectKeysWithAzureKeyVault(new Uri("{KEY IDENTIFIER}"), credential);
{MANAGED IDENTITY CLIENT ID}: Azure マネージド Identity クライアント ID (GUID)。
{TENANT ID}: テナント ID。
{APPLICATION NAME}: SetApplicationName データ保護システム内でこのアプリの一意の名前を設定します。 この値は、アプリのデプロイ間で一致する必要があります。
{BLOB URI}: キー ファイルへの完全な URI。 URI は、キー ファイルを作成するときにAzure Storageによって生成されます。 SAS は使用しないでください。
{KEY IDENTIFIER}: キーの暗号化に使用Azure Key Vaultキー識別子。 アクセス ポリシーを使用すると、アプリケーションは、 Get、 Unwrap Key、および Wrap Key のアクセス許可を持つキー コンテナーにアクセスできます。 キー識別子は、作成後に Entra または Azure ポータルのキーから取得されます。 キー コンテナー キーの自動ローテーションを有効にする場合は、キー GUID が識別子の末尾に配置されないバージョンレスキー識別子をアプリのキー コンテナー構成で使用してください (例: https://contoso.vault.azure.net/keys/data-protection)。
Note
Production 以外の環境では、前の例では、DefaultAzureCredential を使用して認証を簡略化し、Azureホスティング環境で使用される資格情報とローカル開発で使用される資格情報を組み合わせてAzureに展開するアプリを開発しています。 運用環境に移行する場合は、前の例に示した ManagedIdentityCredential など、別の選択肢が適しています。 詳細については、「システム割り当てマネージドIDを使って、Azureホスト型の.NETアプリがAzureリソースに認証する方法」を参照してください。
ユーザーの代わりに呼び出すときに IDownstreamApi を挿入し、 CallApiForUserAsync を呼び出します。
internal sealed class ServerWeatherForecaster(IDownstreamApi downstreamApi) : IWeatherForecaster
{
public async Task<IEnumerable<WeatherForecast>> GetWeatherForecastAsync()
{
var response = await downstreamApi.CallApiForUserAsync("DownstreamApi",
options =>
{
options.RelativePath = "/weather-forecast";
});
return await response.Content.ReadFromJsonAsync<WeatherForecast[]>() ??
throw new IOException("No weather forecast!");
}
}
この方法は、この記事の「サンプル アプリ」セクションで説明されているBlazorWebAppEntraおよびBlazorWebAppEntraBffサンプル アプリによって使用されます。
詳細については、次のリソースを参照してください。
ASP.NET Core のキー ストレージ プロバイダー - ASP.NET Coreデータ保護の構成
- ASP.NET Core アプリで.NETのAzure SDKを使用します
- Web API のドキュメント |Microsoft ID プラットフォーム
- Web API を呼び出す Web API: API を呼び出す: オプション 2: ヘルパー クラスを使用してダウンストリーム Web API を呼び出す
- IDownstreamApi
- Microsoft Entra ID を用いてBlazor Web App ASP.NET Core をセキュアに保護します
- ASP.NET Core を Webファーム内でホストする: データプロテクション
- Azure Key Vault ドキュメント
- Azure Storage ドキュメント
- Azureのロールベースのアクセス制御を使用して、Key Vaultのキー、証明書、シークレットにアクセスを提供
HttpClient と HttpRequestMessage と Fetch API 要求オプション
このセクションのガイダンスは、ベアラー トークン認証に依存するクライアント側のシナリオに適用されます。
HttpClient (API ドキュメント) と HttpRequestMessage を使用して、要求をカスタマイズできます。 たとえば、HTTP メソッドや要求ヘッダーを指定できます。 次のコンポーネントは、Web API エンドポイントに POST 要求を行い、応答本文を表示します。
TodoRequest.razor:
@page "/todo-request"
@using System.Net.Http.Headers
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
@inject IAccessTokenProvider TokenProvider
<h1>ToDo Request</h1>
<h1>ToDo Request Example</h1>
<button @onclick="PostRequest">Submit POST request</button>
<p>Response body returned by the server:</p>
<p>@responseBody</p>
@code {
private string? responseBody;
private async Task PostRequest()
{
using var request = new HttpRequestMessage()
{
Method = new HttpMethod("POST"),
RequestUri = new Uri("https://localhost:10000/todoitems"),
Content =
JsonContent.Create(new TodoItem
{
Name = "My New Todo Item",
IsComplete = false
})
};
var tokenResult = await TokenProvider.RequestAccessToken();
if (tokenResult.TryGetToken(out var token))
{
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token.Value);
request.Content.Headers.TryAddWithoutValidation(
"x-custom-header", "value");
using var response = await Http.SendAsync(request);
var responseStatusCode = response.StatusCode;
responseBody = await response.Content.ReadAsStringAsync();
}
}
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
}
Blazorのクライアント側の実装はHttpClientを利用して、Fetch APIを使用し、リクエスト固有のFetch APIオプションをHttpRequestMessageの拡張メソッドおよびWebAssemblyHttpRequestMessageExtensionsで設定します。 汎用的な SetBrowserRequestOption 拡張メソッドを使用して、その他のオプションを設定できます。 Blazor と、基盤になる Fetch API は、要求ヘッダーを直接追加または変更しません。 ブラウザーなどのユーザー エージェントとヘッダーの対話方法の詳細については、外部ユーザー エージェントのドキュメント セットとその他の Web リソースを参照してください。
応答ストリーミングは既定で有効になっています。
HttpContent.ReadAsStreamAsync (HttpResponseMessage.Content) のresponse.Content.ReadAsStreamAsync()を呼び出すと、BrowserHttpReadStreamではなく、 が返されます。
BrowserHttpReadStream では、 Stream.Read(Span<Byte>)などの同期操作はサポートされていません。 コードで同期操作を使用している場合は、応答ストリーミングをオプトアウトするか、 Stream を自分で MemoryStream にコピーできます。
Note
.NET参照ソースへのドキュメント リンクは、通常、リポジトリの既定のブランチを読み込みます。これは、.NETの次のリリースの現在の開発を表します。 特定のリリースのタグを選択するには、[ 分岐またはタグの切り替え ] ドロップダウン リストを使用します。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法 (dotnet/AspNetCore.Docs #26205)を参照してください。
応答ストリーミングをグローバルにオプトアウトするには、次のいずれかの方法を使用します。
<WasmEnableStreamingResponse>の値を持つプロジェクト ファイルにfalseプロパティを追加します。<WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>DOTNET_WASM_ENABLE_STREAMING_RESPONSE環境変数をfalseまたは0に設定します。
個々の要求の応答ストリーミングをオプトアウトするには、SetBrowserResponseStreamingEnabledをfalseでHttpRequestMessageに設定します (次の例request)。
request.SetBrowserResponseStreamingEnabled(false);
通常、HTTP 応答は、応答コンテンツでの同期読み取りのサポートを有効にするためにバッファリングされます。 応答ストリーミングのサポートを有効にするには、SetBrowserResponseStreamingEnabledをtrueでHttpRequestMessageに設定します。
request.SetBrowserResponseStreamingEnabled(true);
既定では、 HttpCompletionOption.ResponseContentRead が設定されているため、コンテンツを含む応答全体を読み取った後、 HttpClient が完了します。 大きなファイルで SetBrowserResponseStreamingEnabled オプションを使用できるようにするには、 HttpCompletionOption.ResponseHeadersRead を設定して、ファイルのコンテンツをメモリにキャッシュしないようにします。
- using var response = await Http.SendAsync(request);
+ using var response = await Http.SendAsync(request,
+ HttpCompletionOption.ResponseHeadersRead);
クロスオリジン要求に資格情報を含めるには、SetBrowserRequestCredentials 拡張メソッドを使用します。
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
Fetch API オプションの詳細については、 MDN Web ドキュメント「WindowOrWorkerGlobalScope.fetch(): Parameters」を参照してください。
エラーの処理
Web API 応答エラーの発生時には、開発者コードでエラーを処理します。 たとえば、GetFromJsonAsync では、Content-Type が application/json である Web API からの JSON 応答が想定されています。 応答が JSON 形式ではない場合、コンテンツ検証により NotSupportedException がスローされます。
次の例では、天気予報データ要求の URI エンドポイントのスペルが間違っています。 URI は WeatherForecast である必要がありますが、呼び出しでは WeatherForcast として表示されます。これには、e に文字 Forecast がありません。
GetFromJsonAsync の呼び出しでは、JSON が返されることが想定されていますが、Content-Type が text/html の未処理の例外について、Web API から HTML が返されます。
/WeatherForcast へのパスが見つからず、ミドルウェアから要求のページまたはビューを提供できないため、未処理の例外が発生します。
クライアント上の OnInitializedAsync では、応答コンテンツが非 JSON であると検証されると NotSupportedException がスローされます。 例外は catch ブロックでキャッチされます。ここで、カスタム ロジックを使用してエラーをログに記録したり、わかりやすいエラー メッセージをユーザーに表示したりすることができます。
ReturnHTMLOnException.razor:
@page "/return-html-on-exception"
@using {PROJECT NAME}.Shared
@inject HttpClient Http
<h1>Fetch data but receive HTML on unhandled exception</h1>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>Temperatures by Date</h2>
<ul>
@foreach (var forecast in forecasts)
{
<li>
@forecast.Date.ToShortDateString():
@forecast.TemperatureC ℃
@forecast.TemperatureF ℉
</li>
}
</ul>
}
<p>
@exceptionMessage
</p>
@code {
private WeatherForecast[]? forecasts;
private string? exceptionMessage;
protected override async Task OnInitializedAsync()
{
try
{
// The URI endpoint "WeatherForecast" is misspelled on purpose on the
// next line. See the preceding text for more information.
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForcast");
}
catch (NotSupportedException exception)
{
exceptionMessage = exception.Message;
}
}
}
Note
前の例は、デモンストレーションを目的としています。 エンドポイントが存在しない場合や、未処理の例外がサーバー上で発生した場合でも、JSON を返すように Web API を構成できます。
詳細については、「ASP.NET Core Blazor アプリでのハンドル エラーを参照してください。
クロスオリジン リソース共有 (CORS)
多くの場合、ブラウザーのセキュリティにより、Web ページが Web ページを提供した元とは異なる配信元への要求を行うことが制限されます。 この制限は、 同じ配信元ポリシーと呼ばれます。 同一オリジン ポリシーにより、悪意のあるサイトが別のサイトから機密データを読み取ることが制限されます (ただし、阻止されません)。 ブラウザーから別の配信元のエンドポイントに要求を行うには、 エンドポイント で クロスオリジン リソース共有 (CORS) を有効にする必要があります。
サーバー側 CORS の詳細については、「ASP.NET Core の
クライアント側の CORS 要求の詳細については、「ASP.NET Core Blazor WebAssembly 追加のセキュリティ シナリオを参照してください。
偽造防止のサポート
HTTP 要求に偽造防止のサポートを追加するには、AntiforgeryStateProvider を挿入し、ヘッダー コレクションに RequestToken として RequestVerificationToken を追加します。
@inject AntiforgeryStateProvider Antiforgery
private async Task OnSubmit()
{
var antiforgery = Antiforgery.GetAntiforgeryToken();
using var request = new HttpRequestMessage(HttpMethod.Post, "action");
request.Headers.Add("RequestVerificationToken", antiforgery.RequestToken);
using var response = await client.SendAsync(request);
...
}
詳細については、「ASP.NET Core Blazor 認証と承認を参照してください。
Web API アクセスのテストのための Blazor フレームワーク コンポーネントの例
Firefox Browser Developer など、Web API バックエンド アプリを直接テストするために、さまざまなネットワーク ツールが公開されています。 Blazor フレームワークの参照ソースには、テストに役立つ HttpClient テスト資産が含まれています。
HttpClientTest GitHub リポジトリ内の dotnet/aspnetcore アセット
Note
.NET参照ソースへのドキュメント リンクは、通常、リポジトリの既定のブランチを読み込みます。これは、.NETの次のリリースの現在の開発を表します。 特定のリリースのタグを選択するには、[ 分岐またはタグの切り替え ] ドロップダウン リストを使用します。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法 (dotnet/AspNetCore.Docs #26205)を参照してください。
サンプル アプリ
実際の例については、Blazor サンプル GitHub リポジトリ (dotnet/blazor-samples) (ダウンロード方法) のサンプル アプリを参照してください。
BlazorWebAppCallWebApi
Blazor Web App から外部の (Blazor Web App にはない) ToDo リスト Web API を呼び出します。
-
Backend: 最小限の API に基づいて todo リストを維持するための Web API アプリ。 Web API アプリは、Blazor Web App とは別のアプリであり、別のサーバーでホストされている可能性があります。 -
BlazorApp/BlazorApp.Client: ToDo リストからのアイテムの作成、読み取り、更新、削除 (CRUD) などの ToDo リスト操作をBlazor Web App を使用して Web API アプリを呼び出す HttpClient。
CSR を採用した対話型 WebAssembly コンポーネントと Auto コンポーネントを含むクライアント側レンダリング (CSR) の場合、クライアント プロジェクト (HttpClient) の Program ファイルに登録された構成済みの BlazorApp.Client を使用して呼び出しが行われます。
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.Configuration["FrontendUrl"] ??
"https://localhost:5002")
});
事前レンダリングされた対話型のサーバー コンポーネント、プリレンダリングされた WebAssembly コンポーネント、プリレンダリングされた、または SSR を採用した Auto コンポーネントを含む、サーバー側レンダリング (SSR) の場合、呼び出しは、サーバー プロジェクト (HttpClient) の Program ファイルに登録された BlazorApp で行われます。
builder.Services.AddHttpClient();
内部 (Blazor Web App 内) のムービー リスト API を呼び出します。この API は、Blazor Web App のサーバー プロジェクトに存在します。
-
BlazorApp: ムービー リストを保持する Blazor Web App。- サーバー上のアプリ内でムービー リストに対して操作が実行されると、通常の API 呼び出しが使用されます。
- Web ベースのクライアントによって API 呼び出しが行われる場合、最小限の API に基づいて、ムービー リストの操作に Web API が使用されます。
-
BlazorApp.Client: Blazor Web App のクライアント プロジェクト。ムービー リストのユーザー管理用の対話型 WebAssembly コンポーネントと自動コンポーネントが含まれています。
CSR を採用した対話型 WebAssembly コンポーネントと Auto コンポーネントを含む CSR の場合、API の呼び出しは、クライアント プロジェクト (ClientMovieService) の HttpClient ファイルに登録された構成済みの Program を使用するクライアントベースのサービス (BlazorApp.Client) 経由で行われます。 これらの呼び出しはパブリック Web またはプライベート Web 経由で行われるため、ムービー リスト API は Web API です。
次の例では、/movies エンドポイントからムービー リストを取得します。
public class ClientMovieService(HttpClient http) : IMovieService
{
public async Task<Movie[]> GetMoviesAsync(bool watchedMovies) =>
await http.GetFromJsonAsync<Movie[]>("movies") ?? [];
}
事前レンダリングされた対話型のサーバー コンポーネント、プリレンダリングされた WebAssembly コンポーネント、プリレンダリングされた、または SSR を採用した Auto コンポーネントを含む、SSR の場合、呼び出しは、サーバーベースのサービス (ServerMovieService) 経由で直接行われます。 API はネットワークに依存しないため、これはムービー リスト CRUD 操作用の標準 API です。
次の例では、映画の一覧を取得します。
public class ServerMovieService(MovieContext db) : IMovieService
{
public async Task<Movie[]> GetMoviesAsync(bool watchedMovies) =>
watchedMovies ?
await db.Movies.Where(t => t.IsWatched).ToArrayAsync() :
await db.Movies.ToArrayAsync();
}
このシナリオで映画データをセキュリティで保護する方法の詳細については、 インタラクティブ自動レンダリングを使用して Blazor Web Appのデータをセキュリティで保護する方法で説明されている気象データの例を参照してください。
BlazorWebAppCallWebApi_Weather
気象データにストリーミング レンダリングを使用する気象データ サンプル アプリ。
BlazorWebAssemblyCallWebApi
Blazor WebAssembly アプリから ToDo リスト Web API を呼び出します。
-
Backend: 最小限の API に基づいて todo リストを維持するための Web API アプリ。 -
BlazorTodo: 構成済みのBlazor WebAssemblyを使用してToDoリストのCRUD操作を行うWeb APIを呼び出すHttpClientアプリ。
BlazorWebAssemblyStandaloneWithIdentity
ASP.NET Core Blazor WebAssembly でセキュリティ保護されたスタンドアロン Identity アプリ:
-
Backend: ASP.NET Core Identity のユーザー ID ストアを保持するバックエンド Web API アプリ。 -
BlazorWasmAuth: ユーザー認証を使用するスタンドアロン Blazor WebAssembly フロントエンド アプリ。
このソリューションでは、次のセキュリティで保護された Web API の呼び出しを示します。
BlazorWebAppOidcBffAuto
Microsoft Entraで、Entra固有のパッケージを使用せずに、OIDC認証を利用したグローバルなインタラクティブ自動レンダリング機能を備えたBlazor Web App。 このサンプルでは、 Web API 呼び出しにトークン ハンドラーを使用 して、外部のセキュリティで保護された Web API を呼び出す方法を示します。
BlazorWebAppOidcBffServer
グローバル対話型サーバーレンダリングを使用し、Microsoft EntraでOIDC認証を行うBlazor Web Appでは、Entra固有のパッケージを使用しません。 このサンプルでは、 アクセス トークンを渡 して外部のセキュリティで保護された Web API を呼び出す方法を示します。
BlazorWebAppOidcBffYarpAspire
グローバルの対話型自動レンダリングを使用するBlazor Web App。
このソリューションには、対話型自動レンダリングを採用するコンポーネントがクライアントにレンダリングされるときに、外部 Web API を介して気象データを安全に取得するデモが含まれています。
BlazorWebAppEntraBff
Blazor Web App は、Microsoft ID プラットフォーム とMicrosoft Identity Web パッケージを使用して、Microsoft Entra IDのためにグローバルな自動インタラクティビティ機能を持っています。 このソリューションには、対話型自動レンダリングを採用するコンポーネントがクライアントにレンダリングされるときに、外部 Web API を介して気象データを安全に取得するデモが含まれています。
BlazorWebAppEntraBffYarpAspire
以下を使用するグローバル自動対話機能を備えた Blazor Web App:
このソリューションには、対話型自動レンダリングを採用するコンポーネントがクライアントにレンダリングされるときに、外部 Web API を介して気象データを安全に取得するデモが含まれています。
その他のリソース
General
- W3C でのクロスオリジン リソース共有 (CORS)
ASP.NET Core コンテンツはコンポーネントではなく、ASP.NET Core アプリに適用されますが、この記事では一般的な CORS の概念について説明します。 - Blazor Web App を用いて におけるデータをセキュリティで保護します。
- W3C でのクロスオリジン リソース共有 (CORS)
ASP.NET Core コンテンツはコンポーネントではなく、ASP.NET Core アプリに適用されますが、この記事では一般的な CORS の概念について説明します。
オーバーポスティング攻撃の防止
Web API は、大量割り当て攻撃とも呼ばれる過剰ポスティング攻撃に対して脆弱になる可能性があります。 オーバーポスト攻撃は、悪意のあるユーザーが、レンダリングされたフォームの一部ではなく、開発者がユーザーに修正を許可しないプロパティのデータを処理する HTML フォームの POST をサーバーに発行したときに発生します。 "オーバーポスティング" という用語は、文字どおり、悪意のあるユーザーがフォームで "過剰に" ポストすることを意味します。
オーバーポスト攻撃の軽減に関するガイダンスについては、「
Server-side
- ASP.NET Coreサーバー側およびBlazor Web App追加のセキュリティ シナリオ: HttpClient を使用してセキュリティで保護された Web API 要求を行う方法について説明します。
ASP.NET Core - ASP.NET Core で HTTPS を強制する
- Kestrel HTTPS エンドポイントの構成
Client-side
- ASP.NET Core Blazor WebAssembly追加のセキュリティ シナリオ: HttpClient を使用してセキュリティで保護された Web API 要求を行う方法について説明します。
- ASP.NET CoreでGraph APIを使用するBlazor WebAssembly
- フェッチ API
ASP.NET Core