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

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

新規登録して質問してみよう
ただいま回答率
85.49%
C#

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

REST

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

Q&A

解決済

3回答

3302閲覧

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

rx79bd1

総合スコア8

C#

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

REST

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

1グッド

1クリップ

投稿2018/03/07 11:03

編集2018/03/10 10:15

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

【 解決したい課題 】

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となります。

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

C#

1using System.Net.Http; 2using Newtonsoft.Json; 3using Newtonsoft.Json.Linq; 4using Livet; 5using System.Windows; 6 7 8[JsonObject] 9 public class Person 10 { 11 [JsonProperty("age")] 12 public int Age { get; private set; } 13 14 [JsonProperty("name")] 15 public string Name { get; private set; } 16 17 public Person(int age, string name) 18 { 19 this.Age = age; 20 this.Name = name; 21 } 22 } 23 24 public async void Login_test() 25 { 26 27 var person = new Person(20, "test"); 28 var json = JsonConvert.SerializeObject(person); 29 30 using (var client = new HttpClient()) 31 { 32 var content = new StringContent(json, Encoding.UTF8, "application/json"); 33 MessageBox.Show(content.ToString()); 34 var response = await client.PostAsync("http://somehost/someapi", content); 35 } 36 }

この後、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となっています。

Python

1import urllib.request, json 2 3if __name__ == '__main__': 4 url = "http://dummy" 5 method = "POST" 6 headers = {"Content-Type" : "application/json"} 7 8 # PythonオブジェクトをJSONに変換する 9 obj = {"test" : "test", 123 : 123} 10 json_data = json.dumps(obj).encode("utf-8") 11 12 # httpリクエストを準備してPOST 13 request = urllib.request.Request(url, data=json_data, method=method, headers=headers) 14 with urllib.request.urlopen(request) as response: 15 response_body = response.read().decode("utf-8") 16

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

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

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


2018/03/10 18:28 追記

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

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

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

確認項目
0. ツール(Insomnia)では返信があるか
0. 自作のc#アプリでは返信があるか
0. 自作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を行いました。
反応が見たいだけですので、今は命令を出すところしか構築していません。

C#

1using (var socket = new TcpClient()) 2 { 3 4 string json = "リクエストボディ部"; 5 6 socket.Connect("ip", port); 7 var body = Encoding.UTF8.GetBytes(json); 8 var bodyLength = Encoding.UTF8.GetByteCount(json); 9 10 var headerContent = new StringBuilder(); 11 headerContent.AppendLine("POST /v1/dummy HTTP/1.1"); 12 headerContent.AppendLine("Accept: */*"); 13 headerContent.AppendLine("Host: " + "ip:port"); 14 headerContent.AppendLine("Content-Type: application/json; charset=utf-8"); 15 headerContent.AppendLine("Content-Length: " + bodyLength); 16 headerContent.AppendLine("Connection: Close"); 17 headerContent.AppendLine(); 18 headerContent.AppendLine(json); 19 var headerString = headerContent.ToString(); 20 var header = Encoding.UTF8.GetBytes(headerString); 21 var headerLength = Encoding.UTF8.GetByteCount(headerString); 22 23 using (var stream = socket.GetStream()) 24 { 25 stream.Write(header, 0, headerLength); 26 //stream.Write(body, 0, bodyLength); 27 } 28 }

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

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

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

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

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

umyu👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

umyu

2018/03/07 11:49

async内でMessageBox.Showを行っているのがすごく気になりますが、本題と関係なさそうなので、1,ネットワークのトレースを行うでトレースログを出力してみても、同じでしょうか? https://dobon.net/vb/dotnet/programing/networktrace.html 2,ファイアウォールやアンチウィルスソフトを停止しても同じですか?
rx79bd1

2018/03/07 14:57

umyu様 回答ありがとうございます。messageboxはシリアルデバッグのようなものです。お見苦しいとは思いますが、初心者故、ご容赦下さい。トレースログについては明日トライしてみます。ファイアウォール、アンチウイルスソフトについても明日トライします。
tamoto

2018/03/07 16:06

HttpRequestExceptionが発生したときのエラーメッセージとスタックトレースの情報が出していただけると、問題の解決に役立つかもしれません。
rx79bd1

2018/03/07 16:17

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

2018/03/09 13:22

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

2018/03/10 10:05

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

回答3

0

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

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

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

投稿2018/03/07 12:10

編集2018/03/07 12:35
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rx79bd1

2018/03/07 14:57 編集

SurferOnWww 様 回答ありがとうございます。 現在退社し、自宅の環境にてどうなるかを確認しました。 少々環境が異なりますが、VisualBasic2017communityにてC#のコンソールアプリを 作成し、結果がどうなるか確認しました。(上司には自宅からのアクセスは相談済みです。) 結果としては、変わらない状態となりました。 自宅のネットワークでも同様の現象が発生しております。 PCが複数台あるため、exeをコピーして検証しましたが、正常に反応が戻ってくるものは ありませんでした。 自宅の環境では、プロキシを経由している設定にはなっておりません。 会社のネットワークについては、全容を把握しているわけではないのですが、 おそらくプロキシは経由してないものと思われます。 Fiddlerがデバッキングプロキシとのことですが、通常は C#アプリ → インターネット になるところが、Fiddlerを起動し、パケットキャプチャを起動すると C#アプリ → Fiddler  → インターネット になると解釈しておりますが、正しいでしょうか。 上記が正しい場合、Fiddlerを経由してパケットがインターネットに送受信されるわけですが、 そこで編集される(例えば、MTU以下のパケットは結合される等)、という可能性はございます でしょうか。 Fiddlerはパケットの書き換えも出来ると聞いておりますが、初期設定の状態では特に何もせず、 スルーするものと勝手に認識しておりました。ただ、現在の環境を鑑みると、その可能性も考慮すべきと 考えております。 (先のumyu様の回答を参考に、ファイアウォール、アンチウイルスを一時的に遮断してみましたが、  結果は変わりませんでした。) その他、考えられる原因がございましたら、ご教示下さい。 以上、よろしくお願い致します。
退会済みユーザー

退会済みユーザー

2018/03/08 02:17 編集

> 会社のネットワークについては、全容を把握しているわけではないのですが、おそらくプロキシは経由してないものと思われます。 ネットワーク管理者に聞いてみることをお勧めします。 Fiddler を介すれば期待通りの結果が得られるということは、クライアントアプリもサーバーも動いている。質問の追記にあった TaskCanceledException は多分タイムアウト(デフォルトで 100 秒とのこと)で発生している、即ち応答が戻ってこない(そもそも、サーバーに届いてないかも)・・・と言う状況ですよね。 であれば、まず最初に疑うべきは「POST 要求の時はネットワークで通信が遮断されているのでは?」だと思うのですが。 プロキシが疑わしいと思うのですが、他にも何かあるかもしれません。ネットワーク管理者が情報を持っているのではないでしょうか。
退会済みユーザー

退会済みユーザー

2018/03/08 02:11

> C#アプリ → インターネット になるところが、Fiddlerを起動し、パケットキャプチャを起動するとC#アプリ → Fiddler  → インターネット になると解釈しておりますが、正しいでしょうか。 「インターネットオプション」で「プロキシの設定」を調べてみてください。プロキシが設定されてなければ[プロキシサーバー」にチェックははいっておらず、Fiddler を起動するとチェックが入って[詳細設定]をみると 127.0.0.1 というプロキシ(Fiddler)のアドレスが自動的に設定されるはずです。 > そこで編集される(例えば、MTU以下のパケットは結合される等)、という可能性はございますでしょうか。 Fiddler はプロキシですから、TCP/IP レベルの操作は行っているでしょうが、そこは今回の問題の原因とは関係ないと思います。 最初の質問にあった、 > C#のhttpClientを利用してPOSTを行った際、パケットを見ると ヘッダーとボディーが分割されているように見える。これを同じパケットで送ることは出来ないか? に固執しておられるようですが、何か根拠はあるのでしょうか? なければ、それはとりあえず置いといて、まずネットワーク関連を先に調べた方がよさそうな気がします。
rx79bd1

2018/03/08 04:45

SurferOnWww 様 回答ありがとうございます。 アドバイスを頂いた上で反論できる身分ではないのですが、 ネットワークについては、素人考えでは問題ないのではと考えております。 (お気持ちを害したら申し訳ありません。) 第一に、ツールを使えば成功していること、第二にpythonでのpostが成功していること、 (質問文追記をお読み下さい) 第三に自宅(および追加で試したテザリング環境)でも改善が見られなかったこと、 上記の理由から、httpClientの設定の方に問題があるように思うのですが、如何でしょうか。 プロキシ等、ネットワークが問題であるならば、プロキシの設定されていない自宅や テザリング環境では改善されても良いのでは、と考えております。 ネットワーク経路を変更する以外に、ここを調べたら良い、という案がありましたらご教示下さい。 また、パケットの分離についても現在は問題と考えておりません。 同じく、pythonでの検証により、分離した状態でも正常にレスポンスが帰ってくることが 確認されたためです。 そのため、現在は原因は別のところにあると考えております。 現時点では、C#のhttpclientの実装、または初期設定が送信先のサーバーの設定と合っていない のではと予想しております。 以上、よろしくお願いいたします。
退会済みユーザー

退会済みユーザー

2018/03/08 05:56

> ネットワークについては、素人考えでは問題ないのではと考えております。 どうでしょう? 先の私のコメントで、 > プロキシが疑わしいと思うのですが、他にも何かあるかもしれません。ネットワーク管理者が情報を持っているのではないでしょうか。 と書きましたが、ネットワーク管理者の方に確認しましたか? 聞けない事情があるとか、聞いてもわからないということでしたら、問題があるとき / ない時のサーバーのログを取得して確認・比較してみましたか? 問題のある方は、要求がサーバーに届いてもいないなんてことはありませんか? そういうことをしてないとすると、ネットワークに問題がないと結論付けるのはまだ早いのでは?( 一番可能性のありそうなところを徹底的に確認してつぶす前に次のステップに進むのは問題解決の方法としてはどうかと思います。収束するどころか発散するかも) > 現時点では、C#のhttpclientの実装、または初期設定が送信先のサーバーの設定と合っていないのではと予想しております。 Fiddler を介すると期待通りの結果が得られるのですよね。であれば、そこはかなり可能性は低いと思います。 パケット分割の問題と思いこんだのと同じことのような気がします。 以上もし余計なお世話でしたら失礼しました。あとは自分の思うようにやってみればいいと思います。
guest

0

自己解決

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

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

投稿2018/03/12 09:14

rx79bd1

総合スコア8

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

投稿2018/03/08 03:28

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rx79bd1

2018/03/08 04:54 編集

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問