ASP.NET Core Blazor から Web API を呼び出す

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

この記事では、Blazor アプリから Web API を呼び出す方法について説明します。

Package

System.Net.Http.Json パッケージには、System.Text.Json を使用した自動シリアル化と逆シリアル化を実行する System.Net.Http.HttpClientSystem.Net.Http.HttpContent の拡張メソッドが用意されています。 System.Net.Http.Json パッケージは .NET 共有フレームワークによって提供され、アプリへのパッケージ参照を追加する必要はありません。

サンプル アプリ

dotnet/blazor-samples GitHub リポジトリでサンプル アプリを参照してください。

BlazorWebAppCallWebApi

Blazor Web アプリから外部の (Blazor Web アプリにはない) todo リスト Web API を呼び出します。

  • Backend: 最小限の API に基づいて ToDo リストを維持するための Web API アプリ。 Web API アプリは、Blazor Web アプリとは別のアプリであり、別のサーバーでホストされている可能性があります。
  • BlazorApp/BlazorApp.Client: ToDo リストからのアイテムの作成、読み取り、更新、削除 (CRUD) などの ToDo リスト操作を HttpClient を使用して Web API アプリを呼び出す Blazor Web アプリ。

CSR を採用した対話型 WebAssembly コンポーネントと Auto コンポーネントを含むクライアント側レンダリング (CSR) の場合、クライアント プロジェクト (BlazorApp.Client) の Program ファイルに登録された構成済みの HttpClient を使用して呼び出しが行われます。

builder.Services.AddScoped(sp =>
    new HttpClient
    {
        BaseAddress = new Uri(builder.Configuration["FrontendUrl"] ?? "https://localhost:5002")
    });

事前レンダリングされた対話型のサーバー コンポーネント、プリレンダリングされた WebAssembly コンポーネント、プリレンダリングされた、または SSR を採用した Auto コンポーネントを含む、サーバー側レンダリング (SSR) の場合、呼び出しは、サーバー プロジェクト (BlazorApp) の Program ファイルに登録された HttpClient で行われます。

builder.Services.AddHttpClient();

内部 ( Blazor Web アプリ内) のムービー リスト API を呼び出します。この API は、Blazor Web アプリのサーバー プロジェクトに存在します。

  • BlazorApp: ムービー リストを保持する Blazor Web アプリ。
    • サーバー上のアプリ内でムービー リストに対して操作が実行されると、通常の API 呼び出しが使用されます。
    • Web ベースのクライアントによって API 呼び出しが行われる場合、最小限の API に基づいて、ムービー リスト操作に Web API が使用されます。
  • BlazorApp.Client: Blazor Web アプリのクライアント プロジェクト。ムービー リストのユーザー管理用の対話型 WebAssembly コンポーネントと Auto コンポーネントが含まれています。

CSR を採用した対話型 WebAssembly コンポーネントと Auto コンポーネントを含む CSR の場合、API の呼び出しは、クライアント プロジェクト (BlazorApp.Client) の Program ファイルに登録された構成済みの HttpClient を使用するクライアントベースのサービス (ClientMovieService) 経由で行われます。 これらの呼び出しはパブリックまたはプライベートの Web 経由で行われるため、ムービー リスト API は Web API です。

次の例では、/movies エンドポイントからムービー リストを取得します。

public class ClientMovieService(HttpClient http) : IMovieService
{
    public async Task<Movie[]> GetMoviesAsync(bool watchedMovies)
    {
        return 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)
    {
        return watchedMovies ? 
            await db.Movies.Where(t => t.IsWatched).ToArrayAsync() : 
            await db.Movies.ToArrayAsync();
    }
}

BlazorWebAppCallWebApi_Weather

気象データにストリーミング レンダリングを使用する気象データ サンプル アプリ。

BlazorWebAssemblyCallWebApi

Blazor WebAssembly アプリから ToDo リスト Web API を呼び出します。

  • Backend: 最小限の API に基づいて ToDo リストを維持するための Web API アプリ。
  • BlazorTodo: ToDo リスト CRUD 操作用の構成済みの HttpClient を使用して Web API を呼び出す Blazor WebAssembly アプリ。

外部 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 Blazor Server 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()
    {
        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();

        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 変数のために空の配列 ([]) が作成されています。 以前のバージョンの C# では、空の配列 (Array.Empty<GitHubBranch>()) を作成します。

その他の実際の例については、「ASP.NET Core Blazor ファイルのアップロード」の Web API コントローラーにファイルをアップロードするサーバー側ファイル アップロードの例を参照してください。

Web API 呼び出しのサービス抽象化

"このセクションは、サーバー プロジェクトで Web API を維持したり、Web API 呼び出しを外部 Web API に変換したりする Blazor Web アプリに適用されます。"

対話型の WebAssembly および Auto レンダリング モードを使用する場合、コンポーネントは既定でプリレンダリングされます。 また、Blazor バンドルがクライアントにダウンロードされ、クライアント側ランタイムがアクティブ化される前に、Auto コンポーネントが最初にサーバーから対話形式でレンダリングされます。 つまり、これらのレンダリング モードを使用するコンポーネントは、クライアントとサーバーの両方から正常に実行されるように設計する必要があります。 コンポーネントでサーバー プロジェクト ベースの API を呼び出すか、クライアントでの実行時に外部 Web API (Blazor Web アプリの外部にあるもの) に要求を変換する必要がある場合は、サービス インターフェイスの背後でその API 呼び出しを抽象化し、サービスのクライアントとサーバーのバージョンを実装することをお勧めします。

  • クライアント バージョンは、構成済みの HttpClient を使用して Web API を呼び出します。
  • サーバー バージョンは通常、サーバー側のリソースに直接アクセスできます。 ネットワーク要求は通常不要であるため、サーバーへの呼び出しを行う HttpClient をサーバーに挿入することは推奨 "されません"。 または、API がサーバー プロジェクトの外部にある可能性がありますが、プロキシされた要求にアクセス トークンを追加するなど、何らかの方法で要求を変換するにはサーバーのサービス抽象化が必要です。

WebAssembly レンダリング モードを使用する場合は、コンポーネントがクライアントからのみレンダリングされるように、プリレンダリングを無効にすることもできます。 詳細については、「ASP.NET Core Blazor レンダリング モード」を参照してください。

例 (サンプル アプリ):

  • BlazorWebAppCallWebApi サンプル アプリのムービー リスト Web API。
  • BlazorWebAppCallWebApi_Weather サンプル アプリでの気象データ レンダリング Web API のストリーミング。
  • BlazorWebAppOidc (BFF 以外のパターン) または BlazorWebAppOidcBff (BFF パターン) サンプル アプリのいずれかでクライアントに返される気象データ。 これらのアプリは、セキュリティで保護された (Web) API 呼び出しを示しています。 詳しくは、「OpenID Connect (OIDC) を使用して ASP.NET Core Blazor Web アプリをセキュリティで保護する」を参照してください。

Blazor Web アプリの外部 Web API

"このセクションは、別の (外部) プロジェクトによって管理される (場合によっては別のサーバーでホストされている) Web API を呼び出す Blazor Web Apps に適用されます。"

Blazor Web Apps は通常、クライアント側の WebAssembly コンポーネントをプリレンダリングし、Auto コンポーネントは静的または対話型のサーバー側のレンダリング (SSR) 中にサーバー上にレンダリングします。 HttpClient サービスは、既定では Blazor Web アプリのメイン プロジェクトに登録されません。 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 リスト Web API

  • Web API を呼び出す WebAssembly コンポーネントにプリレンダリングが必要ない場合は、「ASP.NET Core Blazor のレンダー モード」のガイダンスに従ってプリレンダリングを無効にします。 この方法を採用する場合は、サーバー側でコンポーネントがプリレンダリングされないため、HttpClient サービスを Blazor Web アプリのメイン プロジェクトに追加する必要はありません。

詳細については、「プリレンダリング中にクライアント側サービスによる解決が失敗する」を参照してください。

プリレンダリングされるデータ

プリレンダリング時に、コンポーネントは 2 回レンダリングされます。最初は静的に、次に対話形式でレンダリングされます。 状態は、プリレンダリングされたコンポーネントから対話型コンポーネントに自動的に流れるわけではありません。 コンポーネントが非同期の初期化操作を実行し、初期化中に異なる状態に対して別のコンテンツ ("読み込み中..." 進行インジケーターなど) をレンダリングする場合、コンポーネントが 2 回レンダリングされるときにちらつきが発生することがあります。

これに対処するために、Persistent Component State API を使用してプリレンダリングされた状態をフローさせることができます。これは、BlazorWebAppCallWebApiBlazorWebAppCallWebApi_Weather サンプル アプリで示されています。 コンポーネントが対話形式でレンダリングされる場合、同じ状態を使用して同じ方法でレンダリングできます。 ただし、この API は現在、拡張ナビゲーションでは機能しません。これは、ページへのリンクの拡張ナビゲーションを無効にする (data-enhanced-nav=false) ことで回避できます。 詳細については、次のリソースを参照してください。

HttpClient サービスを追加する

このセクションのガイダンスは、クライアント側のシナリオに適用されます。

クライアント側コンポーネントは、事前に構成された HttpClient サービスを使用して Web API を呼び出します。このサービスは、元のサーバーに要求を行うことに重点を置いています。 他の Web API 用の追加の HttpClient サービス構成は、開発者コードで作成できます。 要求は、BlazorJSON ヘルパーを使用して、または HttpRequestMessage で構成されます。 要求には、Fetch API オプションの構成を含めることができます。

このセクションの構成例は、アプリ内の 1 つの HttpClient インスタンスに対して 1 つの Web API が呼び出される場合にのみ役立ちます。 アプリで複数の Web API を呼び出す必要がある場合は、それぞれ独自のベース アドレスと構成を使用します。その場合、この記事の後半で説明する方法を採用できます。

  • IHttpClientFactory を使用する名前付き HttpClient: 各 Web API には一意の名前が付けられます。 アプリ コードまたは Razor コンポーネントが Web API を呼び出すと、名前付き HttpClient インスタンスを使用して呼び出しが行われます。
  • 型指定 HttpClient: 各 Web API が型指定されます。 アプリ コードまたは Razor コンポーネントが Web API を呼び出すと、型指定 HttpClient インスタンスを使用して呼び出しが行われます。

Program ファイルで、アプリの作成に使用した Blazor プロジェクト テンプレートから HttpClient サービスを追加します (このサービスが存在していない場合)。

builder.Services.AddScoped(sp => 
    new HttpClient
    {
        BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
    });

上記の例では、ベース アドレスの設定に builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress) を使っています。これを使って、アプリのベース アドレス (通常は、ホスト ページの <base> タグの href 値に由来します) を取得できます。

クライアント独自のベース アドレスを使用する最も一般的なユース ケースは次のとおりです。

  • Blazor Web アプリ (.NET 8 以降) のクライアント プロジェクト (.Client) は、WebAssembly のクライアントで実行される WebAssembly コンポーネントまたはコードから、サーバー アプリの API への Web API 呼び出しを行います。
  • ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Client) は、サーバー プロジェクト (Server) に対して Web API 呼び出しを行います。 ホストされている 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")
    });

JSON ヘルパー

HttpClient は、元のサーバーに対して要求を行うための構成済みのサービスとして用意されています。

HttpClient と JSON のヘルパー (System.Net.Http.Json.HttpClientJsonExtensions) は、サード パーティの Web API エンドポイントを呼び出すためにも使用されます。 HttpClient は、ブラウザーの Fetch API を使用して実装され、同一オリジン ポリシーの適用など、制限の対象となります。これについては、この記事の「クロスオリジン リソース共有 (CORS)」セクションで後述します。

クライアントのベース アドレスは、生成元のサーバーのアドレスに設定されます。 @inject ディレクティブを使用して HttpClient インスタンスをコンポーネントに挿入します。

@using System.Net.Http
@inject HttpClient Http

HttpClientJsonExtensions にアクセスするには、System.Net.Http.Json 名前空間を使用します。これには、GetFromJsonAsyncPutAsJsonAsync、および PostAsJsonAsync が含まれます。

@using System.Net.Http.Json

次のセクションでは、JSON ヘルパーについて説明します。

System.Net.Http には、HTTP 要求を送信し、HTTP 応答を受信する (たとえば DELETE 要求を送信する) ための追加のメソッドが含まれています。 詳細については、「DELETE とその他の拡張メソッド」セクションを参照してください。

JSON からの GET (GetFromJsonAsync)

GetFromJsonAsync は、HTTP GET 要求を送信し、JSON 応答本文を解析してオブジェクトを作成します。

次のコンポーネント コードでは、コンポーネントによって todoItems が表示されます。 GetFromJsonAsync は、コンポーネントの初期化が完了すると呼び出されます (OnInitializedAsync)。

todoItems = await Http.GetFromJsonAsync<TodoItem[]>("todoitems");

JSON としての POST (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 としての PUT (PutAsJsonAsync)

PutAsJsonAsync は、JSON でエンコードされたコンテンツを含む HTTP PUT 要求を送信します。

次のコンポーネント コードでは、Name および IsCompletededitItem 値が、コンポーネントのバインドされた要素によって提供されています。 項目の 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 PATCH ドキュメントを受け取ります。

await Http.PatchAsJsonAsync(
    $"todoitems/{id}", 
    "[{\"operationType\":2,\"path\":\"/IsComplete\",\"op\":\"replace\",\"value\":true}]");

PatchAsJsonAsync では HttpResponseMessage が返されます。 応答メッセージから JSON コンテンツを逆シリアル化するには、ReadFromJsonAsync 拡張メソッドを使用します。 次の例では、JSON のToDo 項目データを配列として読み取ります。 メソッドによって項目データが返されない場合は空の配列が作成されるため、次のようにステートメントの実行後も content は null 値にはなりません。

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 NuGet パッケージをインストールし、そのパッケージの API 機能を使用して、PATCH 要求における JsonPatchDocument を作成します。

注意

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

System.Text.JsonSystem.Text.Json.SerializationMicrosoft.AspNetCore.JsonPatch 名前空間に対する @using ディレクティブを、Razor コンポーネントの上部に追加します。

@using System.Text.Json
@using System.Text.Json.Serialization
@using Microsoft.AspNetCore.JsonPatch

Replace メソッドを使用して、IsCompletetrue に設定して、TodoItemJsonPatchDocument を作成します。

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.DefaultIgnoreConditionJsonIgnoreCondition.WhenWritingDefault に設定されています。

JSON ペイロードを見やすい形式で表示するには、JsonSerializerOptions.WriteIndentedtrue に設定して追加します。 インデントされた JSON の作成は、PATCH 要求の処理とは関係がなく、運用アプリにおける Web API 要求では通常実行されません。

ASP.NET Core Web API における JsonPatch」の記事のガイダンスに従って、PATCH コントローラー アクションを Web API に追加します。 または、PATCH 要求処理を、次の手順を使用して最小限の API として実装できます。

Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet パッケージのパッケージ参照を Web API アプリに追加します。

注意

Microsoft.AspNetCore.Mvc.NewtonsoftJson パッケージへの参照によって自動的に Microsoft.AspNetCore.JsonPatch のパッケージ参照が推移的に追加されるため、Microsoft.AspNetCore.JsonPatch パッケージのパッケージ参照をアプリに追加する必要はありません。

Program ファイルに、Microsoft.AspNetCore.JsonPatch 名前空間の @using ディレクティブを追加します。

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.NoContent();
});

警告

ASP.NET Core Web API における JsonPatch」の記事にある他の例と同様に、上記の PATCH API では、Web API がオーバーポスティング攻撃から保護されません。 詳細については、「チュートリアル: ASP.NET Core で 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}");

IHttpClientFactory による名前付き HttpClient

IHttpClientFactory サービスおよび名前付き HttpClient の構成がサポートされています。

注意

IHttpClientFactory からの名前付き HttpClient を使用する代替手段は、型指定された HttpClient を使うことです。 詳細については、「型指定された HttpClient」セクションを参照してください。

アプリに Microsoft.Extensions.Http NuGet パッケージを追加します。

Note

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

クライアント プロジェクトの Program ファイルで、次のとおりです。

builder.Services.AddHttpClient("WebAPI", client => 
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));

Blazor Web アプリのプリレンダリングされたクライアント側コンポーネントで名前付きクライアントを使用する場合は、サーバー プロジェクトと .Client プロジェクトの両方に、上記のサービス登録が表示されます。 サーバーでは、builder.HostEnvironment.BaseAddress が Web API のベース アドレスで置き換えられます。これは、以下で詳しく説明します。

上記のクライアント側の例では、ベース アドレスの設定に builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress) を使っています。これを使って、クライアント側アプリのベース アドレス (通常は、ホスト ページの <base> タグの href 値に由来します) を取得できます。

クライアント独自のベース アドレスを使用する最も一般的なユース ケースは次のとおりです。

  • WebAssembly のクライアントで実行される WebAssembly/Auto コンポーネントまたはコードから、同じホスト アドレスでサーバー アプリの API への Web API 呼び出しを行う、Blazor Web アプリのクライアント プロジェクト (.Client)。
  • サーバー プロジェクト (Server) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Client)。

クライアント独自のベース アドレスを使用する最も一般的なユース ケースは、サーバー プロジェクト (Server) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Client) にあります。

(クライアント アプリと同じ 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サンプル アプリでは、CallTodoWebApiCsrNamedClient コンポーネントで名前付き HttpClient を使用した Web API の呼び出しを示します。 名前付き HttpClient を使用した Microsoft Graph の呼び出しに基づくクライアント アプリの実際の追加デモンストレーションについては、「ASP.NET Core Blazor WebAssembly での Graph API の使用」を参照してください。

名前付き HttpClient を使用した Microsoft Graph の呼び出しに基づくクライアント アプリの実際のデモンストレーションについては、「ASP.NET Core Blazor WebAssembly での Graph API の使用」を参照してください。

型指定された HttpClient

型指定された HttpClient では、1 つ以上のアプリの HttpClient インスタンス (既定または名前付き) を使用して、1 つ以上の Web API エンドポイントからデータを返します。

注意

型指定された HttpClient を使用する代替手段は、IHttpClientFactory からの名前付き HttpClient を使うことです。 詳細については、「IHttpClientFactory による名前付きHttpClient」セクションを参照してください。

アプリに Microsoft.Extensions.Http NuGet パッケージを追加します。

Note

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

次の例では、/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()
    {
        return await http.GetFromJsonAsync<Forecast[]>("forecast") ?? [];
    }
}

クライアント プロジェクトの Program ファイルで、次のとおりです。

builder.Services.AddHttpClient<ForecastHttpClient>(client => 
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));

Blazor Web アプリのプリレンダリングされたクライアント側コンポーネントで型指定のあるクライアントを使用する場合は、サーバー プロジェクトと .Client プロジェクトの両方に、上記のサービス登録が表示されます。 サーバーでは、builder.HostEnvironment.BaseAddress が Web API のベース アドレスで置き換えられます。これは、以下で詳しく説明します。

上記の例では、ベース アドレスの設定に builder.HostEnvironment.BaseAddress (IWebAssemblyHostEnvironment.BaseAddress) を使っています。これを使って、クライアント側アプリのベース アドレス (通常は、ホスト ページの <base> タグの href 値に由来します) を取得できます。

クライアント独自のベース アドレスを使用する最も一般的なユース ケースは次のとおりです。

  • WebAssembly のクライアントで実行される WebAssembly/Auto コンポーネントまたはコードから、同じホスト アドレスでサーバー アプリの API への Web API 呼び出しを行う、Blazor Web アプリのクライアント プロジェクト (.Client)。
  • サーバー プロジェクト (Server) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Client)。

クライアント独自のベース アドレスを使用する最も一般的なユース ケースは、サーバー プロジェクト (Server) に対して Web API 呼び出しを行う、ホストされている Blazor WebAssembly アプリのクライアント プロジェクト (Client) にあります。

(クライアント アプリと同じ 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サンプル アプリでは、CallTodoWebApiCsrTypedClient コンポーネントで型指定のある HttpClient を使用した Web API の呼び出しを示します。 コンポーネントは "プリレンダリングを使用した" クライアント側レンダリング (CSR) (InteractiveWebAssembly レンダリング モード) を採用しているため、型指定されたクライアント サービス登録は、サーバー プロジェクトと .Client プロジェクトの両方の Program ファイルに表示されることに注意してください。

このセクションのガイダンスは、認証 cookie に依存するクライアント側のシナリオに適用されます。

ベアラー トークン認証よりも安全性が高いとみなされる cookie ベースの認証では、構成済みの HttpClientDelegatingHandler を指定して AddHttpMessageHandler を呼び出して、Web API 要求ごとに cookie 資格情報を送信できます。 ハンドラーは、BrowserRequestCredentials.Include を使用して SetBrowserRequestCredentials を構成します。これは、クロスオリジン要求を含む要求ごとに、、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);
    }
}

CookieHandler は、次のように Program ファイルに登録されます。

builder.Services.AddTransient<CookieHandler>();

メッセージ ハンドラーは、cookie 認証を必要とするすべての構成済み HttpClient に次のように追加されます。

builder.Services.AddHttpClient(...)
    .AddHttpMessageHandler<CookieHandler>();

HttpRequestMessage を作成するときは、次のようにブラウザー要求の資格情報とヘッダーを直接設定します。

var requestMessage = new HttpRequestMessage() { ... };

requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
requestMessage.Headers.Add("X-Requested-With", ["XMLHttpRequest"]);

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()
    {
        var requestMessage = 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))
        {
            requestMessage.Headers.Authorization =
                new AuthenticationHeaderValue("Bearer", token.Value);

            requestMessage.Content.Headers.TryAddWithoutValidation(
                "x-custom-header", "value");

            var response = await Http.SendAsync(requestMessage);
            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 を使用し、HttpRequestMessage 拡張メソッドと WebAssemblyHttpRequestMessageExtensions を介して、基盤となる 要求固有の Fetch API オプションを構成します。 汎用的な SetBrowserRequestOption 拡張メソッドを使用して、その他のオプションを設定できます。 Blazor と、基盤になる Fetch API は、要求ヘッダーを直接追加または変更しません。 ブラウザーなどのユーザー エージェントとヘッダーの対話方法の詳細については、外部ユーザー エージェントのドキュメント セットとその他の Web リソースを参照してください。

通常、HTTP 応答は、応答コンテンツでの同期読み取りのサポートを有効にするためにバッファリングされます。 応答ストリーミングのサポートを有効にするには、要求に対して SetBrowserResponseStreamingEnabled 拡張メソッドを使用します。

クロスオリジン要求に資格情報を含めるには、SetBrowserRequestCredentials 拡張メソッドを使用します。

requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

Fetch API オプションの詳細については、「MDN Web ドキュメント: WindowOrWorkerGlobalScope (): パラメーター」を参照してください。

エラーの処理

Web API 応答エラーの発生時には、開発者コードでエラーを処理します。 たとえば、GetFromJsonAsync では、Content-Typeapplication/json である Web API からの JSON 応答が想定されています。 応答が JSON 形式ではない場合、コンテンツ検証により NotSupportedException がスローされます。

次の例では、天気予報データ要求の URI エンドポイントのスペルが間違っています。 URI は WeatherForecast である必要がありますが、呼び出しでは WeatherForcast として表示されます。これには、Forecast に文字 e がありません。

GetFromJsonAsync の呼び出しでは、JSON が返されることが想定されていますが、Content-Typetext/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 &#8451;
                @forecast.TemperatureF &#8457;
            </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;
        }
    }
}

注意

前の例は、デモンストレーションを目的としています。 エンドポイントが存在しない場合や、ハンドルされない例外がサーバー上で発生した場合でも、JSON を返すように Web API を構成できます。

詳しくは、「ASP.NET Core Blazor アプリのエラーを処理する」をご覧ください。

クロスオリジン リソース共有 (CORS)

ブラウザーのセキュリティにより、Web ページでは、その Web ページを提供したドメインと異なるドメインに対して要求を行うことはできません。 この制限は、同一オリジン ポリシーと呼ばれます。 同一オリジン ポリシーにより、悪意のあるサイトが別のサイトから機密データを読み取ることが制限されます (ただし、阻止されません)。 オリジンが異なるエンドポイントへの要求をブラウザーから行うには、エンドポイントクロス オリジン リソース共有 (CORS) を有効にする必要があります。

サーバー側の CORS の詳細については、「ASP.NET Core でクロスオリジン要求 (CORS) を有効にする」を参照してください この記事の例は、Razor コンポーネント シナリオに直接関連していませんが、この記事は、一般的な CORS の概念を学習するために役立ちます。

クライアント側の CORS 要求については、「ASP.NET Core のその他のセキュリティ シナリオBlazor WebAssembly」をご覧ください。

偽造防止サポート

HTTP 要求に偽造防止のサポートを追加するには、AntiforgeryStateProvider を挿入し、ヘッダー コレクションに RequestVerificationToken として RequestToken を追加します。

@inject AntiforgeryStateProvider Antiforgery
private async Task OnSubmit()
{
    var antiforgery = Antiforgery.GetAntiforgeryToken();
    var request = new HttpRequestMessage(HttpMethod.Post, "action");
    request.Headers.Add("RequestVerificationToken", antiforgery.RequestToken);
    var response = await client.SendAsync(request);
    ...
}

詳細については、「ASP.NET Core Blazor の認証と認可」を参照してください。

Web API アクセスのテストのための Blazor フレームワーク コンポーネントの例

Web API バックエンド アプリを直接テストするために、Firefox Browser Developer などのさまざまなネットワーク ツールが一般公開されています。 Blazor フレームワークの参照ソースには、テストに役立つ HttpClient テスト資産が含まれています。

dotnet/aspnetcore GitHub リポジトリ内の HttpClientTest 資産

注意

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

その他のリソース

全般

サーバー側

クライアント側