質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • C#

    9065questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • REST

    165questions

    REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。

C#でのHttpClientを使用したRestAPIの使用について

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,353

rx79bd1

score 2

お世話になっております。

【 解決したい課題 】

C#のhttpClientを利用してPOSTを行った際、パケットを見ると
ヘッダーとボディーが分割されているように見える。これをhttpclientで同じパケットで送ることは出来ないか?

pythonではpostが成功するのに、c#のhttpclientでは失敗する。何が原因なのか?

ということがお聞きしたい内容です。(2018/03/10 18:28 アップデート)

【 質問者のスキル 】

プログラムを始めて1年程度です。
アプリを専門に開発している部署ではないため、周りに
詳しい方がいません。

【 開発環境 】

現在、C#を利用し、RestAPIを使用したアプリの開発を行っております。
これは外部に出す前に、仕様を確認するための試験的なものです。
私はクライアント側のアプリを作成しており、RestAPIおよびサーバー管理は外部の
企業が担当しております。

クライアントの開発環境は以下のとおりです。

IDE:VisualStudio2015
開発言語:C#
使用ライブラリ:Livet(WPF)
.NET Framework 4.5.0

今回は、methodのうち、GETとPOSTしか使いません。また、POSTで送る内容は
JSONとなります。

ひとまず反応を見るため、次のようなプログラムを作成しました。
なお、プログラムは初心者ですので、至らない点があればご容赦下さい。

using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Livet;
using System.Windows;


[JsonObject]
        public class Person
        {
            [JsonProperty("age")]
            public int Age { get; private set; }

            [JsonProperty("name")]
            public string Name { get; private set; }

            public Person(int age, string name)
            {
                this.Age = age;
                this.Name = name;
            }
        }

 public async void Login_test()
        {

            var person = new Person(20, "test");
            var json = JsonConvert.SerializeObject(person);

            using (var client = new HttpClient())
            {
                var content = new StringContent(json, Encoding.UTF8, "application/json");
                MessageBox.Show(content.ToString());
                var response = await client.PostAsync("http://somehost/someapi", content);
            }
       }

この後、responseのデータを取り出すというものです。

最初に、この内容をベースとして、GETのプログラムを組んだのですが、そちらでは問題が
ありませんでした。

ただ、POSTにすると、'System.Net.Http.HttpRequestException' というエラーが発生します。

これに対して、まずはパケット解析ソフトのWiresharkを使い、パケット解析を行いました。
内容を見ると、送信は出来ていましたが、受信が出来ていませんでした。
通常、200や404などが帰ってきますが、それがありません。

次に、RestAPIのツールを使い、そもそもサーバーが正しく動いているか、という確認を行いました。
使用したツールは、Chromeの拡張であるARC、POSTMAN、ウィンドウズアプリであるInsomniaの3種類を
使いました。

その結果、上記3種類では全て通信が正常に行えました。WireSharkで見ても、きちんと返信が
帰ってきていました。ヘッダーを最低限にしたり、足したり、JSONの書き方を変えたりと試したのですが、
最終的にはツールを使うと何をやっても成功し、C#の自作アプリを使うと何をやっても駄目、
という状況に陥りました。

ひとまず、ツールで成功しているわけですから、ツールと同じパケットを送信すれば受け付けて
もらえるだろうと考え、JSONのフォーマットやタグなどを可能な限り内容を近づけてC#から送信したのですが、
何をやっても成功しませんでした。

そうなると、やはりコード自体がおかしいのでは、ということになりますが、何故かパケット解析ツールの
Fiddlerを立ち上げ、F12キーでパケットをキャプチャしている時は正常に動作します。
200が返され、こちらの欲しいデータが帰ってきます。
パケットキャプチャを停止すると、またデータも返信も何も帰ってこない状態に陥ります。

そのうち、送信パケットについて、成功しているパターンと成功していないパターンを比較すると、
成功していないパターンではWireshark上でReassembled TCPという文字列があることがわかりました。
下図は、失敗しているパケットをキャプチャした際の画像です。
(キャプチャは例で、先に挙げたプログラムの内容と全くの同一ではありません。)
イメージ説明

イメージ説明

初心者ですので正確なことは分かりませんが、ヘッダーとボディーを別パケットとして送信しているのでは、
と見ています。成功したPOSTのパケットを見ると、これらの表記はありません。
なお、PCのMTUは1500程度になっております。

この見解が正しいのかは分かりませんが、まず、この表記が出ない設定で通信を行い、状況に変化があるか
を見たいと考えております。

上図のような表記を出さず、httpClientでPOST命令を出す方法をご存じの方がいれば、
ご教示下さい。
今回はデモ用であるため、最悪、Fiddlerを立ち上げ、パケットキャプチャを行いながらでもOKでは
あるのですが、可能であれば単体で動作させたいと思います。

以上、よろしくお願い致します。


以降、追記部分となります。


2018/03/08 9:17 追記

1,ファイアウォールとアンチウイルスソフトについて

先程、開発用PCにインストールされている該当のソフトを一時的にオフにし、さらにウィンドウズのファイアウォールも
オフにしましたが、状況に変化はございませんでした。

2,エラーメッセージの詳細

本日あらためてトライしたところ、次のようなエラーとなりました。
昨日と異なるエラーとなっています。(名前等は修正してあります。)

System.Threading.Tasks.TaskCanceledException はユーザー コードによってハンドルされませんでした。
HResult=-2146233029
Message=タスクが取り消されました。
Source=mscorlib
StackTrace:
場所 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
場所 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
場所 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()        場所 origin2.ViewModels.MainWindowViewModel.<Login_test>d__5.MoveNext() 場所 C:\Users\dummy\Documents\sample_prog\test\test\ViewModels\MainWindowViewModel.cs:行 253   InnerException:

Wiresharkでパケット解析をしながら行ったのですが、POSTは出来てきますが、その返事がないという状況です。

2018/03/08 10:16 追記

筐体の問題およびネットワークの問題なのか、プログラム言語の問題なのかを 切り分けるため、次の作業を行いました。

1、プロキシの確認 2、別言語(Python)での確認

1、についてですが、こちらは設定されておりませんでした。ただし、社内ネットワークであるため、何らかの制限がかかっている可能性は否めません。 実際に、一部のサイトは閲覧制限がかかっているため、閲覧できないサイトもあります。

2、1、の結果を受けて、問題の切り分けのため、別言語での検証を行いました。

開発環境はVisualStudio2017と、Anacondaを利用したpython3.6となっています。

import urllib.request, json

if __name__ == '__main__':
    url = "http://dummy" 
    method = "POST"
    headers = {"Content-Type" : "application/json"}

    # PythonオブジェクトをJSONに変換する
    obj = {"test" : "test", 123 : 123} 
    json_data = json.dumps(obj).encode("utf-8")

    # httpリクエストを準備してPOST
    request = urllib.request.Request(url, data=json_data, method=method, headers=headers)
    with urllib.request.urlopen(request) as response:
        response_body = response.read().decode("utf-8")

JSONは適当に入力したため、正常なデータは帰ってきませんが、 構文が違うという400(Bad Request)が帰ってきました。この後、jsonを 正しく設定すると、必要なデータが取得できました。

このことから、ネットワーク自体に問題は無いものと思われます。

Wiresharkで確認すると、reassembled TCP、及びTCP Segmentsの項目がありました。 そのため、パケットが別れているから駄目だった、という私の考えは間違っていたことが 判明しました。


2018/03/10 18:28 追記

いろいろな方のご意見を受けて、改めて調査を行いました。

① 問題がネットワークにあるかどうか

自宅を含めた別ネットワークについては一度確認していることですが、 改めて調査を行いました。 なお、自分のパソコン、ネットワークはプロキシがないことを確認しています。 会社のパソコン、ネットワークはプロキシは不明です。

確認項目

  1. ツール(Insomnia)では返信があるか
  2. 自作のc#アプリでは返信があるか
  3. 自作c#で別API(今回はニコニコ動画の情報取得APIを利用)では返信があるか
  • 会社のパソコン & 会社のネットワーク → 1,OK 2,NG 3,OK

  • 会社のパソコン & 自分のテザリング → 1,OK 2,NG 3,OK

  • 自宅のパソコン & 自宅のネットワーク → 1,OK 2,NG 3,OK

  • 自宅のパソコン & 自分のテザリング → 1,OK 2,NG 3,OK

  • 自宅のパソコン & 自分のポケットWIFI → 1,OK 2,NG 3,OK

  • 自宅のパソコン & 自分のポケットWIFI(一度初期化してSIMだけ再設定) → 1,OK 2,NG 3,OK

となりました。この結果から、ネットワークの可能性は低いと判断しました。

② ソケットを食いつぶしているかどうか

日をまたいで、起動直後に試しても、状況は変わりませんでした。

③ Fiddlerはパケットに対して何か影響を与えるのか

調査の結果、Fiddlerのインストール直後の状態では、パケットに対して影響がありました。 わかりやすいところで言うと、Fiddlerを介すると、Wireshark上でReassembled TCP が無くなることが分かりました。

④ 同一パケットでbodyを送ると成功するのか

Fiddlerの結果から、改めてbodyとheaderを同時に送りどうなるか 確認したくなったため、無理やりですが、実際に試してみました。 結論から言えば、成功しました。 C#で、以下のようなコードでPOSTを行いました。 反応が見たいだけですので、今は命令を出すところしか構築していません。

using (var socket = new TcpClient())
            {

                string json = "リクエストボディ部";

                socket.Connect("ip", port);
                var body = Encoding.UTF8.GetBytes(json);
                var bodyLength = Encoding.UTF8.GetByteCount(json);

                var headerContent = new StringBuilder();
                headerContent.AppendLine("POST /v1/dummy HTTP/1.1");
                headerContent.AppendLine("Accept: */*");
                headerContent.AppendLine("Host: " + "ip:port");
                headerContent.AppendLine("Content-Type: application/json; charset=utf-8");
                headerContent.AppendLine("Content-Length: " + bodyLength);
                headerContent.AppendLine("Connection: Close");
                headerContent.AppendLine();
                headerContent.AppendLine(json);
                var headerString = headerContent.ToString();
                var header = Encoding.UTF8.GetBytes(headerString);
                var headerLength = Encoding.UTF8.GetByteCount(headerString);

                using (var stream = socket.GetStream())
                {
                    stream.Write(header, 0, headerLength);
                    //stream.Write(body, 0, bodyLength);
                }
            }

いろいろ粗が目立ちますが、ひとますこれでテストしました。
ヘッダーを自分で書いてそのまま投げるイメージです。
こうすると、末尾に無理やりJSONをつけて渡すことができます。

この方法だと、Fiddlerを介さずとも100%返信が来るようになりました。
(Wireshark上で確認)

以上のことから、ヘッダーとボディをひとつのパケットで送り出すことが
鍵である可能性が高いと考えております。
(pythonだと分割しても大丈夫なようですが、c#だと駄目、というのは解せない
ですが、正常に通信できる方法が確立した後に考えたいと思います。)

無理やりの方法は判明しましたが、可能であればデバッグの手間を省くため、
httpclientなどの既存ライブラリを使いたく存じます。

やり方をご存知の方がいましたらご教示ください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • rx79bd1

    2018/03/08 01:17

    tamoto様 回答ありがとうございます。こちらについても、明日調査し、トライしてみます。

    キャンセル

  • tamoto

    2018/03/09 22:22

    TaskCanceledだと話が全然違ってくるのですが……追記のスタックトレースにMainWindowViewModel.cs:行 253っていう超重要な情報がありますが、これは質問のPostAsync行のことで間違いないですか?

    キャンセル

  • rx79bd1

    2018/03/10 19:05

    tamoto様 PostAsyncのところになります。また、上記質問文に現在までに判明したことを追記致しました。

    キャンセル

回答 3

+3

Fiddler を介すると問題なくて、Fiddler 無しだと問題が出るというところから思いつくのはプロキシの影響なんですが、IE にプロキシ設定をしていますか?

IE でプロキシ設定をしている場合、HttpClient は IE のプロキシ設定を使ってプロキシ経由でアクセスするはずです。なので、Fiddler を起動してない場合、質問者さんの環境で IE に設定しているプロキシ経由で通信が行われるているはずです。 

Fiddler はプロキシなので、Fiddler を起動すると IE のプロキシ設定が Fiddler を経由するようになるはずです。そのあたりが影響しているのではないかを確認してみてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/07 23:54 編集

    SurferOnWww 様

    回答ありがとうございます。
    現在退社し、自宅の環境にてどうなるかを確認しました。

    少々環境が異なりますが、VisualBasic2017communityにてC#のコンソールアプリを
    作成し、結果がどうなるか確認しました。(上司には自宅からのアクセスは相談済みです。)

    結果としては、変わらない状態となりました。
    自宅のネットワークでも同様の現象が発生しております。
    PCが複数台あるため、exeをコピーして検証しましたが、正常に反応が戻ってくるものは
    ありませんでした。

    自宅の環境では、プロキシを経由している設定にはなっておりません。
    会社のネットワークについては、全容を把握しているわけではないのですが、
    おそらくプロキシは経由してないものと思われます。

    Fiddlerがデバッキングプロキシとのことですが、通常は

    C#アプリ → インターネット になるところが、Fiddlerを起動し、パケットキャプチャを起動すると
    C#アプリ → Fiddler  → インターネット になると解釈しておりますが、正しいでしょうか。

    上記が正しい場合、Fiddlerを経由してパケットがインターネットに送受信されるわけですが、
    そこで編集される(例えば、MTU以下のパケットは結合される等)、という可能性はございます
    でしょうか。
    Fiddlerはパケットの書き換えも出来ると聞いておりますが、初期設定の状態では特に何もせず、
    スルーするものと勝手に認識しておりました。ただ、現在の環境を鑑みると、その可能性も考慮すべきと
    考えております。
    (先のumyu様の回答を参考に、ファイアウォール、アンチウイルスを一時的に遮断してみましたが、
     結果は変わりませんでした。)

    その他、考えられる原因がございましたら、ご教示下さい。

    以上、よろしくお願い致します。

    キャンセル

  • 2018/03/08 10:46 編集

    > 会社のネットワークについては、全容を把握しているわけではないのですが、おそらくプロキシは経由してないものと思われます。

    ネットワーク管理者に聞いてみることをお勧めします。

    Fiddler を介すれば期待通りの結果が得られるということは、クライアントアプリもサーバーも動いている。質問の追記にあった TaskCanceledException は多分タイムアウト(デフォルトで 100 秒とのこと)で発生している、即ち応答が戻ってこない(そもそも、サーバーに届いてないかも)・・・と言う状況ですよね。

    であれば、まず最初に疑うべきは「POST 要求の時はネットワークで通信が遮断されているのでは?」だと思うのですが。

    プロキシが疑わしいと思うのですが、他にも何かあるかもしれません。ネットワーク管理者が情報を持っているのではないでしょうか。

    キャンセル

  • 2018/03/08 11:11

    > C#アプリ → インターネット になるところが、Fiddlerを起動し、パケットキャプチャを起動するとC#アプリ → Fiddler  → インターネット になると解釈しておりますが、正しいでしょうか。

    「インターネットオプション」で「プロキシの設定」を調べてみてください。プロキシが設定されてなければ[プロキシサーバー」にチェックははいっておらず、Fiddler を起動するとチェックが入って[詳細設定]をみると 127.0.0.1 というプロキシ(Fiddler)のアドレスが自動的に設定されるはずです。


    > そこで編集される(例えば、MTU以下のパケットは結合される等)、という可能性はございますでしょうか。

    Fiddler はプロキシですから、TCP/IP レベルの操作は行っているでしょうが、そこは今回の問題の原因とは関係ないと思います。

    最初の質問にあった、

    > C#のhttpClientを利用してPOSTを行った際、パケットを見ると ヘッダーとボディーが分割されているように見える。これを同じパケットで送ることは出来ないか?

    に固執しておられるようですが、何か根拠はあるのでしょうか? なければ、それはとりあえず置いといて、まずネットワーク関連を先に調べた方がよさそうな気がします。

    キャンセル

  • 2018/03/08 13:45

    SurferOnWww 様

    回答ありがとうございます。

    アドバイスを頂いた上で反論できる身分ではないのですが、
    ネットワークについては、素人考えでは問題ないのではと考えております。
    (お気持ちを害したら申し訳ありません。)

    第一に、ツールを使えば成功していること、第二にpythonでのpostが成功していること、
    (質問文追記をお読み下さい)
    第三に自宅(および追加で試したテザリング環境)でも改善が見られなかったこと、

    上記の理由から、httpClientの設定の方に問題があるように思うのですが、如何でしょうか。
    プロキシ等、ネットワークが問題であるならば、プロキシの設定されていない自宅や
    テザリング環境では改善されても良いのでは、と考えております。
    ネットワーク経路を変更する以外に、ここを調べたら良い、という案がありましたらご教示下さい。

    また、パケットの分離についても現在は問題と考えておりません。
    同じく、pythonでの検証により、分離した状態でも正常にレスポンスが帰ってくることが
    確認されたためです。
    そのため、現在は原因は別のところにあると考えております。
    現時点では、C#のhttpclientの実装、または初期設定が送信先のサーバーの設定と合っていない
    のではと予想しております。

    以上、よろしくお願いいたします。

    キャンセル

  • 2018/03/08 14:56

    > ネットワークについては、素人考えでは問題ないのではと考えております。

    どうでしょう?

    先の私のコメントで、

    > プロキシが疑わしいと思うのですが、他にも何かあるかもしれません。ネットワーク管理者が情報を持っているのではないでしょうか。

    と書きましたが、ネットワーク管理者の方に確認しましたか?

    聞けない事情があるとか、聞いてもわからないということでしたら、問題があるとき / ない時のサーバーのログを取得して確認・比較してみましたか? 問題のある方は、要求がサーバーに届いてもいないなんてことはありませんか?

    そういうことをしてないとすると、ネットワークに問題がないと結論付けるのはまだ早いのでは?( 一番可能性のありそうなところを徹底的に確認してつぶす前に次のステップに進むのは問題解決の方法としてはどうかと思います。収束するどころか発散するかも)


    > 現時点では、C#のhttpclientの実装、または初期設定が送信先のサーバーの設定と合っていないのではと予想しております。

    Fiddler を介すると期待通りの結果が得られるのですよね。であれば、そこはかなり可能性は低いと思います。

    パケット分割の問題と思いこんだのと同じことのような気がします。


    以上もし余計なお世話でしたら失礼しました。あとは自分の思うようにやってみればいいと思います。

    キャンセル

check解決した方法

+1

最終的に、追記で作成したC#で無理やりJSONを末尾に付加するプログラムが最も安定していたため、
そちらを元に作成する事にいたしました。

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

原因かどうかはわかりませんが HttpClient にはバグがあります。

.NETのHttpClientの取り扱いには要注意という話

using が使われているので該当します。
もしかしたらここでソケットを食いつぶして失敗し、他の手段で通信する時までに復活していたのかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/08 13:53 編集

    Zuishin 様 回答ありがとうございます。ソケットも一定時間経過後でリリースされると認識しておりますので、こちらからのサーバーのアクセスを停止し、一度電源を入れた直後の別の筐体で試してみたいと思います。一晩放置すればリリースされると思いますので、明日の朝か来週の月曜日に試してみます。

    キャンセル

同じタグがついた質問を見る

  • C#

    9065questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • REST

    165questions

    REST(Representational State Transfer)はwebアプリケーションの構築スタイルの一種です。HTTP GET/POSTによってリクエストを送信し、レスポンスはXMLで返されます。SOAPのようなRPCの構築と比べるとサーバからクライアントを分離することが出来る為、人気です。