.NET クライアントから Web API を呼び出す (C#)

このコンテンツは、以前のバージョンの .NET 用です。 新しい開発では、ASP.NET Coreを使用する必要があります。 ASP.NET Core Web API の使用の詳細については、次を参照してください。

完成したプロジェクトをダウンロードします

このチュートリアルでは、System.Net.Http.HttpClient を使用して.NET アプリケーションから Web API を呼び出す方法について説明します。

このチュートリアルでは、次の Web API を使用するクライアント アプリを記述します。

アクション HTTP メソッド 相対 URI
ID で製品を取得する GET /api/products/id
新しい製品の作成 POST /api/products
製品を更新する PUT /api/products/id
製品を削除する DELETE /api/products/id

ASP.NET Web APIを使用してこの API を実装する方法については、「CRUD 操作をサポートする Web API の作成」を参照してください。

わかりやすくするために、このチュートリアルのクライアント アプリケーションは Windows コンソール アプリケーションです。 HttpClient は、Windows Phone アプリと Windows ストア アプリでもサポートされています。 詳細については、「ポータブル ライブラリを使用した複数プラットフォーム用の Web API クライアント コードの記述」を参照してください。

メモ: ベース URL と相対 URI をハードコーディングされた値として渡す場合は、API を利用するための規則に HttpClient 注意してください。 プロパティは HttpClient.BaseAddress 、末尾にスラッシュ (/) を付けてアドレスに設定する必要があります。 たとえば、ハードコーディングされたリソース URI を メソッドに HttpClient.GetAsync 渡す場合は、先頭にスラッシュを含めないでください。 ID で を Product 取得するには:

  1. client.BaseAddress = new Uri("https://localhost:5001/"); を設定します
  2. を要求します Product。 たとえば、「 client.GetAsync<Product>("api/products/4"); 」のように入力します。

コンソール アプリケーションを作成する

Visual Studio で、 HttpClientSample という名前の新しい Windows コンソール アプリを作成し、次のコードを貼り付けます。

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace HttpClientSample
{
    public class Product
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }

    class Program
    {
        static HttpClient client = new HttpClient();

        static void ShowProduct(Product product)
        {
            Console.WriteLine($"Name: {product.Name}\tPrice: " +
                $"{product.Price}\tCategory: {product.Category}");
        }

        static async Task<Uri> CreateProductAsync(Product product)
        {
            HttpResponseMessage response = await client.PostAsJsonAsync(
                "api/products", product);
            response.EnsureSuccessStatusCode();

            // return URI of the created resource.
            return response.Headers.Location;
        }

        static async Task<Product> GetProductAsync(string path)
        {
            Product product = null;
            HttpResponseMessage response = await client.GetAsync(path);
            if (response.IsSuccessStatusCode)
            {
                product = await response.Content.ReadAsAsync<Product>();
            }
            return product;
        }

        static async Task<Product> UpdateProductAsync(Product product)
        {
            HttpResponseMessage response = await client.PutAsJsonAsync(
                $"api/products/{product.Id}", product);
            response.EnsureSuccessStatusCode();

            // Deserialize the updated product from the response body.
            product = await response.Content.ReadAsAsync<Product>();
            return product;
        }

        static async Task<HttpStatusCode> DeleteProductAsync(string id)
        {
            HttpResponseMessage response = await client.DeleteAsync(
                $"api/products/{id}");
            return response.StatusCode;
        }

        static void Main()
        {
            RunAsync().GetAwaiter().GetResult();
        }

        static async Task RunAsync()
        {
            // Update port # in the following line.
            client.BaseAddress = new Uri("http://localhost:64195/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            try
            {
                // Create a new product
                Product product = new Product
                {
                    Name = "Gizmo",
                    Price = 100,
                    Category = "Widgets"
                };

                var url = await CreateProductAsync(product);
                Console.WriteLine($"Created at {url}");

                // Get the product
                product = await GetProductAsync(url.PathAndQuery);
                ShowProduct(product);

                // Update the product
                Console.WriteLine("Updating price...");
                product.Price = 80;
                await UpdateProductAsync(product);

                // Get the updated product
                product = await GetProductAsync(url.PathAndQuery);
                ShowProduct(product);

                // Delete the product
                var statusCode = await DeleteProductAsync(product.Id);
                Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadLine();
        }
    }
}

上記のコードは、完全なクライアント アプリです。

RunAsync が実行され、完了するまでブロックされます。 ほとんどの HttpClient メソッドは、ネットワーク I/O を実行するため、非同期です。 非同期タスクはすべて 内で RunAsync実行されます。 通常、アプリはメイン スレッドをブロックしませんが、このアプリでは操作を許可しません。

static async Task RunAsync()
{
    // Update port # in the following line.
    client.BaseAddress = new Uri("http://localhost:64195/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

    try
    {
        // Create a new product
        Product product = new Product
        {
            Name = "Gizmo",
            Price = 100,
            Category = "Widgets"
        };

        var url = await CreateProductAsync(product);
        Console.WriteLine($"Created at {url}");

        // Get the product
        product = await GetProductAsync(url.PathAndQuery);
        ShowProduct(product);

        // Update the product
        Console.WriteLine("Updating price...");
        product.Price = 80;
        await UpdateProductAsync(product);

        // Get the updated product
        product = await GetProductAsync(url.PathAndQuery);
        ShowProduct(product);

        // Delete the product
        var statusCode = await DeleteProductAsync(product.Id);
        Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");

    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

    Console.ReadLine();
}

Web API クライアント ライブラリをインストールする

NuGet パッケージ マネージャーを使用して、Web API クライアント ライブラリ パッケージをインストールします。

[ツール] メニューで、[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] の順に選択します。 パッケージ マネージャー コンソール (PMC) で、次のコマンドを入力します。

Install-Package Microsoft.AspNet.WebApi.Client

上記のコマンドは、次の NuGet パッケージをプロジェクトに追加します。

  • Microsoft.AspNet.WebApi.Client
  • Newtonsoft.Json

Newtonsoft.Json (Json.NET とも呼ばれます) は、.NET 用の一般的なハイ パフォーマンス JSON フレームワークです。

モデル クラスを追加する

次の Product クラスを調べます。

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}

このクラスは、Web API で使用されるデータ モデルと一致します。 アプリは HttpClient を使用して、HTTP 応答からインスタンスを Product 読み取ることができます。 アプリは逆シリアル化コードを記述する必要はありません。

HttpClient の作成と初期化

静的 HttpClient プロパティを調べます。

static HttpClient client = new HttpClient();

HttpClient は、1 回インスタンス化され、アプリケーションの有効期間中に再利用されることを目的としています。 次の条件により 、SocketException エラーが発生する可能性があります。

  • 要求ごとに新しい HttpClient インスタンスを作成する。
  • 負荷の高いサーバー。

要求ごとに新しい HttpClient インスタンスを作成すると、使用可能なソケットが使い果たされる可能性があります。

次のコードは 、HttpClient インスタンスを初期化します。

static async Task RunAsync()
{
    // Update port # in the following line.
    client.BaseAddress = new Uri("http://localhost:64195/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

上記のコードでは次の操作が行われます。

  • HTTP 要求のベース URI を設定します。 ポート番号をサーバー アプリで使用されているポートに変更します。 サーバー アプリのポートを使用しない限り、アプリは機能しません。
  • Accept ヘッダーを "application/json" に設定します。 このヘッダーを設定すると、JSON 形式でデータを送信するようにサーバーに指示されます。

リソースを取得するための GET 要求を送信する

次のコードは、製品の GET 要求を送信します。

static async Task<Product> GetProductAsync(string path)
{
    Product product = null;
    HttpResponseMessage response = await client.GetAsync(path);
    if (response.IsSuccessStatusCode)
    {
        product = await response.Content.ReadAsAsync<Product>();
    }
    return product;
}

GetAsync メソッドは HTTP GET 要求を送信します。 メソッドが完了すると、HTTP 応答を含む HttpResponseMessage が返されます。 応答の状態コードが成功コードの場合、応答本文には製品の JSON 表現が含まれます。 ReadAsAsync を呼び出して、JSON ペイロードをインスタンスにProduct逆シリアル化します。 ReadAsAsync メソッドは非同期です。応答本文は任意に大きくなる可能性があるためです。

HTTP 応答 にエラー コードが含まれている場合、HttpClient は例外をスローしません。 状態がエラー コードの場合、 IsSuccessStatusCode プロパティは false になります。 HTTP エラー コードを例外として扱う場合は、応答オブジェクトで HttpResponseMessage.EnsureSuccessStatusCode を呼び出します。 EnsureSuccessStatusCode は、状態コードが 200 から 299 の範囲外にある場合に例外をスローします。 HttpClient は、要求がタイムアウトした場合など、他の理由で例外をスローできることに注意してください。

逆シリアル化するフォーマッタのMedia-Type

ReadAsAsync がパラメーターなしで呼び出されると、既定のメディア フォーマッタ セットを使用して応答本文が読み取られます。 既定のフォーマッタでは、JSON、XML、およびフォーム URL でエンコードされたデータがサポートされます。

既定のフォーマッタを使用する代わりに、 ReadAsAsync メソッドにフォーマッタの一覧を指定できます。 フォーマッタのリストを使用すると、カスタムメディアタイプフォーマッタがある場合に便利です。

var formatters = new List<MediaTypeFormatter>() {
    new MyCustomFormatter(),
    new JsonMediaTypeFormatter(),
    new XmlMediaTypeFormatter()
};
resp.Content.ReadAsAsync<IEnumerable<Product>>(formatters);

詳細については、「ASP.NET Web API 2 のメディア フォーマッタ」を参照してください。

POST 要求を送信してリソースを作成する

次のコードは、JSON 形式のインスタンスを含む POST 要求を Product 送信します。

static async Task<Uri> CreateProductAsync(Product product)
{
    HttpResponseMessage response = await client.PostAsJsonAsync(
        "api/products", product);
    response.EnsureSuccessStatusCode();

    // return URI of the created resource.
    return response.Headers.Location;
}

PostAsJsonAsync メソッド:

  • オブジェクトを JSON にシリアル化します。
  • POST 要求で JSON ペイロードを送信します。

要求が成功した場合:

  • 201 (Created) 応答を返す必要があります。
  • 応答には、Location ヘッダーに作成されたリソースの URL が含まれている必要があります。

リソースを更新するための PUT 要求の送信

次のコードは、製品を更新するための PUT 要求を送信します。

static async Task<Product> UpdateProductAsync(Product product)
{
    HttpResponseMessage response = await client.PutAsJsonAsync(
        $"api/products/{product.Id}", product);
    response.EnsureSuccessStatusCode();

    // Deserialize the updated product from the response body.
    product = await response.Content.ReadAsAsync<Product>();
    return product;
}

PutAsJsonAsync メソッドは PostAsJsonAsync と同様に動作しますが、POST の代わりに PUT 要求を送信する点が異なる点が異なる。

DELETE 要求を送信してリソースを削除する

次のコードは、製品を削除する DELETE 要求を送信します。

static async Task<HttpStatusCode> DeleteProductAsync(string id)
{
    HttpResponseMessage response = await client.DeleteAsync(
        $"api/products/{id}");
    return response.StatusCode;
}

GET と同様に、DELETE 要求には要求本文がありません。 DELETE を使用して JSON または XML 形式を指定する必要はありません。

サンプルをテストする

クライアント アプリをテストするには:

  1. サーバー アプリをダウンロードして実行します。 サーバー アプリが動作していることを確認します。 たとえば、 http://localhost:64195/api/products は製品の一覧を返す必要があります。

  2. HTTP 要求のベース URI を設定します。 ポート番号をサーバー アプリで使用されるポートに変更します。

    static async Task RunAsync()
    {
        // Update port # in the following line.
        client.BaseAddress = new Uri("http://localhost:64195/");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
    
  3. クライアント アプリを実行します。 次の出力が生成されます。

    Created at http://localhost:64195/api/products/4
    Name: Gizmo     Price: 100.0    Category: Widgets
    Updating price...
    Name: Gizmo     Price: 80.0     Category: Widgets
    Deleted (HTTP Status = 204)