.NET FrameworkのHttpClientを利用した、コンテンツアップロードAPIへのPOSTについて - トークBot - LINE WORKS Developers : コミュニティ

.NET FrameworkのHttpClientを利用した、コンテンツアップロードAPIへのPOSTについて

赤軸

2019.11.29既読 2153

現在、C#(.NET Framework)のHttpClientから、コンテンツアップロードAPIを利用し画像ファイルのアップロードを行うプログラムを作成しています。
ファイルをPostする際にエラーが返され、1日ほど奮闘しましたが解決できなかったため、質問させていただきます。

【やりたいこと】
C#からHttpClientを用いて画像ファイルのアップロードを行いたい。
【現象】
C#のHttpClientにて、コンテンツアップロードAPIに対して画像ファイルをPostしているが、504がレスポンスとして返される。
また、Windows 10 の curl.exe によるアップロードを試したところ、こちらはアップロードに成功した。
【環境】
OS: Windows 10 Pro
C# バージョン: 7.3
.NET Framework バージョン: 4.6.1

【調査結果】
C#のHttpClientとcurlのリクエストをFiddlerで確認したところ、ほとんど違いは見られませんでした。
違いとしては、curlのヘッダには "Connection: Keep-Alive" が設定されていましたが、C#からPostした場合には含まれていませんでした。
ただ、調べた限りではHttpClientについてもデフォルトでKeep-Aliveが有効化されるとのことでした。(とはいえヘッダーに含まれないのは怪しいので、ここを疑っています)
それぞれのヘッダーの内容は以下となります。
[C#] Httpリクエストヘッダー
Accept: */*
User-Agent: プログラム名
consumerKey: 発行されたコンシューマキー
authorization: Bearer 発行されたアクセストークン
x-works-apiid: 発行されたAPIID
Content-Type: multipart/form-data; boundary="f266990d-6b81-4cb2-a892-70b7499909e9"
Host: storage.worksmobile.com
Content-Length: 8302
Expect: 100-continue

[C#] コンテンツヘッダー
Content-Disposition: form-data; name="resourceName"; filename="ファイル名.jpg"
Content-Type: image/jpeg
[curl] Httpリクエストヘッダー
Host: storage.worksmobile.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
consumerKey: 発行されたコンシューマキー
authorization: Bearer 発行されたアクセストークン
x-works-apiid: 発行されたAPIID
Content-Length: 8288
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------04f1f228ec120c0d

[curl] コンテンツヘッダー
Content-Disposition: form-data; name="resourceName"; filename="ファイル名.jpg"
Content-Type: image/jpeg
以上です。
初歩的な事柄かもしれませんが、ご助言お願いいたします。

コメント4

  • 업데이트 된 답글입니다.

    赤軸 投稿者

    お世話になります。

    上記につきまして、その後も調査を行いましたがこれといった成果は得られませんでした。
    結局HttpClientの使用を諦め、HttpWebRequestを使用したところ正常にアップロードすることができました。

    差異としては、やはりKeep-Aliveの有無くらいしかなさそうですが、HttpClientからPostした際に付与されない理由については不明です。
    (.NET Frameworkのソースを読めばわかりそうですが。)

    2019.12.04

    0
  • 업데이트 된 답글입니다.

    LINE WORKS 公式アカウント

    大変申し訳ございませんが、コミュニティの性質上、個別のコードに関してましてはお答えしかねてしまいます。
    HttpClient での動作は確認しておりませんが、こちらで動作を確認したコードでは「"Connection", "keep-alive"」設定の有無による影響はありませんでした。
    ご参考ください。

    2019.12.05

    0
  • 업데이트 된 답글입니다.

    赤軸 投稿者

    お世話になります。

    >HttpClient での動作は確認しておりませんが、こちらで動作を確認したコードでは「"Connection", "keep-alive"」設定の有無による影響はありませんでした。
    ご確認いただきありがとうございます。
    要求ヘッダーの差異がKeep-Aliveのみでしたので疑っていましたが、こちらが直接の原因ではないようですね。
    またどこかで調査してみようと思います。

    ご回答いただき、ありがとうございました。

    2019.12.09

    0
  • 업데이트 된 답글입니다.

    大分時間が経過していますが
    同じ事象で困っている方もおられると思うので投稿します。

    原因はヘッダーのboundaryが「"」で囲まれていることです。
    ---------------------------------------------------
    ・Content-Type: multipart/form-data; boundary="f266990d-6b81-4cb2-a892-70b7499909e9" ←NG
    ・Content-Type: multipart/form-data; boundary=------------------------04f1f228ec120c0d ←OK
    ---------------------------------------------------

    これはLineWorksのAPIの障害と思われるので改修をお願いします。
    APIの処理でリクエストヘッダーからboundaryを取り出すときに["]も含めて取得しているのだと思います。
    そのためコンテンツ内のboundary(こちらは「"」で囲われません)とイコールにならないため
    エラー判定されているのだと推測します。

    サンプルコードを以下に記載しておきます。
    ```C#
    string ApiId = "XXX";
    string ConsumerKey = "XXX";
    string Token = "XXX";
    string URL = "http://storage.worksmobile.com/openapi/message/upload.api";;
    string Path = "XXX.png";

    HttpClient Client = new HttpClient();

    using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, URL))
    using (var dataContent = new MultipartFormDataContent())
    using (var fileStream = File.OpenRead(Path))
    using (var streamContent = new StreamContent(fileStream))
    {
       requestMessage.Headers.Add("consumerKey", ConsumerKey);
       requestMessage.Headers.Add("Authorization", Token);
       requestMessage.Headers.Add("x-works-apiid", ApiId);

       // HTTP応答のContent-Typeから「boundary」の値を取得、「"」を削除して再設定
       var boundary = dataContent.Headers.ContentType.Parameters.First(o => o.Name == "boundary");
       boundary.Value = boundary.Value.Replace("\"", string.Empty);

       streamContent.Headers.ContentDisposition =
              new ContentDispositionHeaderValue("form-data")
              {
                  Name = "\"resourceName\"",
                  FileName = string.Format("\"{0}\"", new FileInfo(Path).Name)
              };

       streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
       dataContent.Add(streamContent);
       requestMessage.Content = dataContent;

       var response = Client.SendAsync(requestMessage).Result;
    }
    ```

    2021.09.08

    0
前の投稿 メンバー追加APIにおけるpasswordConfig.password項目の制約事項について
次の投稿コンテンツアップロードについて