C#

【C#】HttpClientの使い方 POST/GET

HttpClientとは何か

HttpClientクラス(System.Net.Http)は、C#でHTTP要求を行う場合に利用します。
他にもHTTP要求を行うためのWebClientクラスがありますが、HttpClientを使うことがMicrosoftからも推奨されています。

https://docs.microsoft.com/ja-jp/dotnet/api/system.net.http.httpclient?view=net-6.0

HttpClientの注意点

上記の例のように、リクエストごとにHttpClientインスタンスを何度も生成するのはパフォーマンス上よくありません。

そのため、実際に使う場合はシングルトンパターンを使って何度もインスタンスが生成されることを防ぐ必要があります。

後述する使い方では行っていませんが、下記のように実装することをお勧めします。

class MyHttpClinet
{
    // アクセス修飾子がprivateのstatic変数に生成したインスタンスを保存する
    private static HttpClient _client;

    // コンストラクタのアクセス修飾子をprivateにする
    static MyHttpClinet()
    {
        // 初期化処理
        _client = new HttpClient();
    }

    public async Task<HttpResponseMessage> GetAsync()
    {
        var res = await _client.GetAsync("URL");
        return res;
    }
    public async Task<HttpResponseMessage> PostAsync()
    {
        var res = await _client.PostAsync("URL", "CONTENT");
        return res;
    }
  
  // プロパティとして公開する場合
	//public static HttpClient Clinet 
	//{ 
	//    get
	//    {
	//        if (_httpClient == null)
	//        {
	//            _httpClient = new HttpClient();
	//        }
	//        return _httpClient;
	//    } 
	//}
}

HttpClientの使い方

APIのサンプルを公開しているサイトがあるため、こちらを利用します。

https://www.umayadia.com/Note/Note028WebAPISample.htm

GET

GETの場合は、GetAsync メソッドを使います。
※例に使うAPIのGET仕様は登録されているすべてのデータをJSONで返却します。

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

var client = new HttpClient();
var result = await client.GetAsync(@"https://umayadia-apisample.azurewebsites.net/api/persons");
var json = await result.Content.ReadAsStringAsync();
Console.WriteLine($"{(int)result.StatusCode} { result.StatusCode }");
Console.WriteLine(json);

以下の通り、JSON文字列で返却されたことが確認できました。

返却結果JSONのデシリアライズ

基本的にレスポンスのJSON文字列をC#のクラスにマッピングする必要があると思います。

デシリアライズにはSystem.Text.Json名前空間を使用した方法があるため、下記にまとめています。

もしくはJson.NETを使用した方法があります。

POST

POSTの場合は、Postasync メソッドを使います。
※例に使うAPIのPOST仕様はJSONでpersonオブジェクトを渡して、データを登録します。

using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text;

// ボディの設定
var param = new Dictionary<string, object>()
{
    ["name"] = "ぺんた",
    ["note"] = "大阪府出身",
    ["age"] = 30,
    ["registerDate"] = "2021-12-01",
};
var jsonString = System.Text.Json.JsonSerializer.Serialize(param);
var content = new StringContent(jsonString, Encoding.UTF8, @"application/json");
//POST
var result = await client.PostAsync(@"https://umayadia-apisample.azurewebsites.net/api/persons", content);
Console.WriteLine($"{(int)result.StatusCode} { result.StatusCode }");
Console.WriteLine(result.StatusCode);

//GET
var resultGet = await client.GetAsync(@"https://umayadia-apisample.azurewebsites.net/api/persons");
var json = await resultGet.Content.ReadAsStringAsync();
Console.WriteLine($"{(int)resultGet.StatusCode} { resultGet.StatusCode }");
Console.WriteLine(json);

POSTした後にGETで登録されているか確認します。
すると、以下の通り登録されていることが確認できました。

リクエストヘッダの設定

すべてのリクエストで共通して利用する場合と、個別に設定したい場合があると思います。

共通の場合

インスタンスのDefaultRequestHeadersに追加します。

_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_beareToken}");

個別の場合

HttpRequestMessageをメソッド名とuriを指定して作成します。

その後、Headersに追加します。

こちらの場合、SendAsyncメソッドを使います(GETやPOSTはHttpRequestMessageで指定します)

var req = new HttpRequestMessage(HttpMethod.Get,uri);
req.Headers.Add("Authorization", $"Bearer {_beareToken}");
var r = await Clinet.SendAsync(req);

ベースアドレスの指定

APIのエンドポイントごとに共通する部分があると思います。HttpClientのインスタンス生成時にBaseAddressを共通のURLで指定しておけば、リクエストごとに毎度フルパスで書く必要が無くなります。

_httpClient = new HttpClient()
{
    BaseAddress = new Uri("https://api.twitter.com"),
};

上記の時点で、このhttpClientインスタンスのリクエストURLはhttps://api.twitter.comになりました。

この状態で、リクエストごとにパスパラメータだけを指定すれば、上記と連結されたURLでリクエストが送られます。

例えば下記のコードのように、パスパラメータだけを渡したら、ベースアドレスと連結されたhttps://api.twitter.com/2/users/by/username へのリクエストになります。

// パスパラメータ
var uri = $"/2/users/by/username";
// https://api.twitter.com/2/users/by/username へのリクエストになる
var res = await Clinet.GetAsync(uri);

クエリパラメータの指定

URLに文字列で直接書いてもいいと思いますが、保守性を考えた際には下記のように書くこともできます。

// ベースURL
var uri = "https://pg-life.net";

// クエリパラメータ
Dictionary<string, string> _params = new Dictionary<string, string>()
{
    {"key1", "value1" }, 
    {"key2", "value2" }, 
};
uri += $"?{await new FormUrlEncodedContent(_params).ReadAsStringAsync()}";

上記の様に指定すると、下記の様なURLになります。

https://pg-life.net?key1=value2&key2=value2

RestSharp ライブラリ

HttpClientをラップしたRestSharpというクラスライブラリがあります。こちらを使うとよりHTTP通信が直感的に書けると思いますのでおすすめです。

サンプルコード

HttpClientを使って画像をダウンロード

HttpClientを使って画像をダウンロードする処理を作ってみました。
imgUriに画像パスを、fileNameに保存するファイル名を渡すと、D:\Image に保存します。

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

public class MyHttpClinet 
{
    private static HttpClient Client = new HttpClient();

    public async Task DownloadImgAsync(string imgUri, string fileName)
    {
        //GET
        var res = await Client.GetAsync(imgUri);

        var date = DateTime.Today.ToString("yyyy_MM_dd");
        //保存先ディレクトリ
        var directory = $@"D:\Image\{date}";
        //存在しなければディレクトリ作成
        if (!Directory.Exists(directory))
        {
            Directory.CreateDirectory(directory);
        }
        //fileName + .jpg で保存
        using (var fileStream = File.Create($@"{directory}\{fileName}.jpg"))
        {
            using (var httpStream = await res.Content.ReadAsStreamAsync())
            {
                httpStream.CopyTo(fileStream);
            }
        }
    }
}

multipart/form-dataをアップロード

WebブラウザからファイルをWebサーバにアップロードする場合、multipart/form-dataという形式にします。

下記は、Whisper API(文字起こしサービス)に音声ファイル(.mp3)を投げる処理です。

using var httpClient = new HttpClient();
var url = "https://api.openai.com/v1/audio/transcriptions";

var fileStream = File.OpenRead(filePath);
using var formData = new MultipartFormDataContent();
// ファイルを指定
formData.Add(new StreamContent(fileStream), "file", "audio.mp3");
// パラメータを指定
formData.Add(new StringContent(model), "model");
// Bearer認証
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// multipart/form-data
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("multipart/form-data"));


// リクエスト送信
var response = await httpClient.PostAsync(url, formData);

if (response.IsSuccessStatusCode)
{
    // レスポンスをJSONとしてパースし、必要な情報を取得
    var responseContent = await response.Content.ReadAsStringAsync();
    var whisperResponse = JsonSerializer.Deserialize<WhisperResponse>(responseContent);
    return whisperResponse == null ? "" : whisperResponse.Text;    
}
else
{
	// ...
}

余計な情報が含まれていますが、ここでポイントなのは下記です。

// ①ファイル読み込み
var fileStream = File.OpenRead(filePath);
using var formData = new MultipartFormDataContent();
formData.Add(new StreamContent(fileStream), "file", "audio.mp3");

// ②multipart/form-data指定
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("multipart/form-data"));

// ③リクエスト送信
var response = await httpClient.PostAsync(url, formData);

参考サイト

https://qiita.com/rawr/items/f78a3830d894042f891b

https://blog.70-10.net/2020/06/27/csharp-httpclient-baseaddress/

エンジニアの転職ならこれ!

【第二新卒向け】マイナビジョブ20's

マイナビジョブ20'sは、20代・第二新卒・既卒向けの転職エージェントです。

▼こんな方におすすめ
・はじめて転職しようと思っている
・転職できるだけのスキルが自分にあるか不安
・手厚いサポートを受けたい

【経験者向け】レバテックキャリア

ITエンジニア専門の転職エージェントです。

元エンジニアなど高い専門性を持つアドバイザーが理想の求人を提案してくれます。

▼こんな方におすすめ
・20代後半~40代前半
・年収を上げたい
・スキルアップしたい
・手厚いサポートを受けたい

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です