C#でHTTP POSTに複数コンテンツを含める


この記事で解説すること

C#での複数コンテンツのPost方法を実例付きで解説する。
(複数コンテンツというのは、例えば、画像データとその名前の2つなどの事です。)

複数コンテンツのHTTP POSTの実現方法

以下を使うと複数コンテンツのPOSTが行えます。

要は、MultipartFromDataContenctにデータを追加していき、HttpClientのPostAsyncメソッドの引数として渡すだけです。(MultipartFormDataContentはHttpContentクラスを継承している)

実践-条件

それではやり方の一例を見ていきます。
今回は、以下のコンテンツをPostで送りたいとします。

Postで送るもの

  • 画像データと、その名前を一度にPostする
    • 画像データ(image):バイナリデータ
    • データ名(name):"hogehoge"

Post結果の確認方法

今回の例では、Post結果の確認にhttpbinを使わせてもらいます。
httpbinは、指定のURLにHTTPリクエストを送ると、送信されたパラメータなどの情報をレスポンスとして返してくれるWebサービス&ソフトウェアです。

以下のJsonがPostのResponseとして返ってくれば成功

今回の場合は、http://httpbin.org/post にPOSTリクエストを送り、以下のようなJSONがReponseとして返ってくれば成功です。
確認するポイントは2つです。

  • "files"内の"image"に指定したデータ(今回だと画像をバイト配列として送るのでバイト配列)が入っているかどうか
  • "form"内の"name"に指定した名前(hogehoge)が入っているどうか

{
    "args": {},
    "data": "",
    "files": {
        "image": "(~byteの羅列がここに入る。長いので省略~)"
    },
    "form": {
        "name": "hogehoge"
    },
    "headers": {
        "Content-Length": "13515",
        "Content-Type": "multipart/form-data; boundary="5cc407cc-3e4e-4d4a-8f67-fd2f29436423"",
        "Host": "httpbin.org",
        "X-Amzn-Trace-Id": "Root=1-5fc1ad93-3233ba1c3f705dd867fdf9c9"
    },
    "json": null,
    "origin": "126.99.210.176",
    "url": "http://httpbin.org/post"
}

実行環境

  • Windows10
  • C#8.0 (.NET Core3.1)

実践

本題のコードが以下です。


// MultipartFormDataContentのインスタンスをつくる。
using MultipartFormDataContent multiContent = new MultipartFormDataContent();

// コンテンツを、それぞれ専用の形式でインスタンス化する。
// imageBytes変数には本当は画像をバイナリ化したものが代入されている想定。
// ここでは簡単のため適当なバイナリを入れておく。
using ByteArrayContent imageContent = new ByteArrayContent(new byte[4] { 0, 1, 2, 3 });
using StringContent nameContent = new StringContent("hogehoge");

// 専用の形式にしたコンテンツを、MultipartFormDataContentにAddしていく。
multiContent.Add(imageContent, "image", "imageData");
multiContent.Add(nameContent, "name");

// HttpClientでPostする。
// (本当はHttpClientは都度インスタンスを生成するのではなく、アプリケーション内で使いまわしたほうがよい。)
using HttpClient client = new HttpClient();
var uri = new Uri("http://httpbin.org/post");
var msg = await client.PostAsync(uri, multiContent );

// Responseの表示
var responseContent = await msg.Content.ReadAsStringAsync();
Console.WriteLine($"{responseContent}");

コードについて解説していきます。

  • MultipartFormDataContentのインスタンスをつくる
// MultipartFormDataContentのインスタンスをつくる。
using MultipartFormDataContent multiContent = new MultipartFormDataContent();

ここは特に解説の必要もないと思います。
MultipartFormDataContentの派生大元であるHttpContentクラスはIDisposableを継承しているので、usingをつけて勝手に開放してくれるようにしておきます。
(この記事の趣旨とは関係ないですが、C#8.0から、using変数宣言により、その変数のスコープに基づいて自動開放してくれるようになりました。usingステートメントによる多重{}はコードが見にくくなるので、ここではusing変数宣言を使っています。)

  • コンテンツをそれぞれ専用の形式でインスタンス化する
// コンテンツを、それぞれ専用の形式でインスタンス化する。
// imageBytes変数には本当は画像をバイナリ化したものが代入されている想定。
// ここでは簡単のため適当なバイナリを入れておく。
using ByteArrayContent imageContent = new ByteArrayContent(new byte[4] { 0, 1, 2, 3 });
using StringContent nameContent = new StringContent("hogehoge");

MultipartFormDataContentに追加していく(もといHttpClientのコンテントとして扱う)ためには、各種コンテンツを専用の形式にしていく必要あります。
専用の形式とは、System.Net.Http.HttpClientクラスの派生クラスのことです。

ここでは、画像データ(それを事前にバイナリ化したもの)と文字列をそれぞれHttpContent化します。画像データはByteArrayContentクラス、文字列はStringContentクラスでHttpContent化します。(ここでは結果が見やすいよう画像データには適当なバイナリをいれてます。)

この部分は送りたいデータの形式に合わせて変えます。(他にはStreamContentクラスや、JsonContentクラスがあります。詳細はHttpContentクラスのドキュメントを読んでください。)

ちなみに、MultipartFormDataContentクラスももちろんHttpContentクラスの派生クラスです。(正確にはMultipartFormDataContentクラス → MultipartContentクラス → HttpContentクラスという継承関係)

  • 専用の形式にしたコンテンツをMultipartFormDataContentにAddしていく
// 専用の形式にしたコンテンツを、MultipartFormDataContentにAddしていく。
multiContent.Add(imageContent, "image", "imageData");
multiContent.Add(nameContent, "name");

上で専用の形式(HttpContentクラスの派生クラス)にしたデータを追加していくだけです。
MultipartFormDataContent.Add(HttpContent, string)メソッドを使って追加していきます。

ここで、画像データと文字列データでは、引数の異なるAdd()メソッドを使っています。

MultipartFormDataContent.Add()メソッドには、引数のとり方が3種類あります。

  1. HttpContentのみ
  2. HttpContentとString(HTTPコンテンツの名前)
  3. HttpContentとStringとString(HTTPコンテンツの名前とコレクションに追加する HTTP コンテンツのファイル名)

1, 2番目の方法だとコンテンツがRequestの"form"に含まれ、3番目の方法だとコンテンツがRequestの"files"に含まれます。

今回は、画像データは"files"に含まれて欲しいので3番目、文字列データは"forms"に含まれて欲しいので2番目のメソッドを使います。なお、3番目の方法の場合は、引数の3つ目に文字列が必要ですがこれは今回の場合何でもよいです。
(ちなみに1番目の方法を使った場合は、コンテンツはform"に含まれ、そのkeyにあたるものがnullとなります。)

  • POSTする
// HttpClientでPostする。
// (本当はHttpClientは都度インスタンスを生成するのではなく、アプリケーション内で使いまわしたほうがよい。)
using HttpClient client = new HttpClient();
var uri = new Uri("http://httpbin.org/post");
var msg = await client.PostAsync(uri, multiContent );

// Responseの表示
var responseContent = await msg.Content.ReadAsStringAsync();
Console.WriteLine($"{responseContent}");

通常通りHttpClientのPostAsync()メソッドを使ってPostするだけです。
(上のコードではPostとそのResponseの表示をしています。)

結果
コードを実行すると以下がResponseとして返ってきました。
大丈夫そうですね。

  • "files"内の"image"に指定したデータが入っている("\u0000\u0001\u0002\u0003"は1, 2, 3, 4をそれぞれ16進数表記にしたもの)
  • "form"内の"name"に指定した名前(hogehoge)が入っている

{
    "args": {},
    "data": "",
    "files": {
        "image": "\u0000\u0001\u0002\u0003"
    },
    "form": {
        "name": "hogehoge"
    },
    "headers": {
        "Content-Length": "13515",
        "Content-Type": "multipart/form-data; boundary="5cc407cc-3e4e-4d4a-8f67-fd2f29436423"",
        "Host": "httpbin.org",
        "X-Amzn-Trace-Id": "Root=1-5fc1ad93-3233ba1c3f705dd867fdf9c9"
    },
    "json": null,
    "origin": "126.99.210.176",
    "url": "http://httpbin.org/post"
}

まとめ

この記事では、C#での複数コンテンツのPost方法を実例付きで解説しました。

簡単にやり方をまとめると以下です。

  • MultipartFormDataContentのインスタンスをつくる
  • コンテンツをそれぞれ専用の形式でインスタンス化する
  • 専用の形式にしたコンテンツをMultipartFormDataContentにAddしていく
    • 追加したい形式によって複数あるAddメソッドを使い分ける
  • POSTする

リンク