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

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

ただいまの
回答率

88.78%

HttpClientでPostでhttpサーバーに文字を送信して、結果のJsonを取得したい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 10K+

TrainRain

score 20

こんにちは。

 前提・実現したいこと

Windows10を使ってVisual Studo 2017でUWPアプリケーションのプロジェクトを作っています。
Postでhttpサーバーに文字を送信して、結果のJsonを取得したいです。

UWPにはWebClientがないので、もともとWebClientとHttpUtilityを使っていたのをHttpClientに変更したいです。

argsはキーのペアで、
name, "白鷺",
language, "japanese",
kind, "bird"
のようなものです。

urlはhttpサーバーのurlです。

resultはjsonです。

 発生している問題・エラーメッセージ

            client.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.5");
           client.DefaultRequestHeaders.Add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");


あたりで、接続に失敗します。
HttpClientを使うのが初めてで、なにを渡したらよいかわからずにおります。

 該当のソースコード

もとのコードです。

public static string GetJsonResult(Dictionary<string, string> args, string url)
{
    string data = CreateQuerystring(args);
    string result = GetHttpResponse(url, data);
}

public static string CreateQuerystring(Dictionary<string, string> args)
{
    StringBuilder sb = new StringBuilder();
    foreach (string name in args.Keys)
    {
        sb.Append(HttpUtility.UrlEncode(name));
        sb.Append("=");

        sb.Append(HttpUtility.UrlEncode(args[name]));
        sb.Append("&");
    }            
    return sb.ToString(0, Math.Max(sb.Length - 1, 0));
}

public static string GetHttpResponse(string url, string data)
{
    byte[] rawData = client.UploadData(url, Encoding.UTF8.GetBytes(data));
    return Encoding.UTF8.GetString(rawData);
}

 試したこと

public static string GetHttpResponse(string url, string data)
        {
            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.5");
           client.DefaultRequestHeaders.Add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");

            var stringContent = new StringContent(data, System.Text.Encoding.UTF8);
            var message = client.PostAsync(url, stringContent);
            return message.Result.Content.ToString();
        }

        public static string CreateQuerystring(Dictionary<string, string> args)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string name in args.Keys)
            {
                sb.Append(Uri.EscapeDataString(name));
                sb.Append("=");

                sb.Append(Uri.EscapeDataString(args[name]));
                sb.Append("&");
            }            
            return sb.ToString(0, Math.Max(sb.Length - 1, 0));
        }

 求める回答

Q1)HttpClientのPostのコード。GetHttpResponse(string url, string data)のMethod
Q2)client.DefaultRequestHeadersを全部コメントアウトするとつながるが、そのとき結果はmessageのどこに入っているか。
Q3)そもそもはいっていないのか?

 補足情報(FW/ツールのバージョンなど)

Intel Pentium(R) CPU 4415 Y 1.60GHz
RAM 8GB
Windows10Pro 64bit 1803 17134.345 
Microsoft Visual Studio Community 2017 
Version 15.8.5
VisualStudio.15.Release/15.8.5+28010.2036
Microsoft .NET Framework
Version 4.7.03056 
インストールされているバージョン:Community 
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

+5

Q1)HttpClientのPostのコード。GetHttpResponse(string url, string data)のMethod

以下の記事のコードが参考になりませんか? WCF と書いてありますが Web サーバーに HttpClient を使って POST 要求をかけ、JSON 文字列を応答として受け取るところは質問者さんのケースと同じです。

HttpClient で WCF サービスを呼出
http://surferonwww.info/BlogEngine/post/2018/02/24/request-wcf-service-using-httpclient.aspx

応答として受け取った JSON 文字列を C# のオブジェクトにデシリアライズしないと処置できないと思いますが、そのあたりのコードも書いてあります。

質問者さんのケースでは、POST するのは JSON 文字列ではなくて application/x-www-form-urlencoded 形式のようで、その文字列を CreateQuerystring メソッドで取得するのだろうと思いますが、そうであれば紹介した記事のコードを以下のように変更すればよさそうです。

string postData = CreateQuerystring(args);

request.Content = new StringContent(postData, 
                                    Encoding.UTF8, 
                                    "application/x-www-form-urlencoded");

Q2)client.DefaultRequestHeadersを全部コメントアウトするとつながるが、そのとき結果はmessageのどこに入っているか。

Fiddler を使って要求・応答をキャプチャして調べてください。その中にヒントが含まれているはずです。今の情報で言えるのはそれぐらいしかないです。

【追伸】

下の 2018/12/03 13:19 の私のコメントで Fiddler で「どういう情報が得られるかは、後で回答欄に書いておきます」と書きましたが、それを以下に書きます。

以下の画像は上の回答で紹介した記事「HttpClient で WCF サービスを呼出」のコードを実行した際の要求・応答を Fiddler でキャプチャしたものです。期待通り要求がサーバーに送信され、サーバーから応答が返ってきていることが分かります。Fiddler 無しでは要求が出ているか否かさえも分かりません。まして、応答が返ってきているか、それは期待通りかなんてことは分かりません。

イメージ説明

質問者さんの Q2, Q3 の疑問「結果はmessageのどこに入っているか」「そもそもはいっていないのか?」も Text を見るなどすればすぐに分かります。

イメージ説明

なので、Fiddler なしで HTTP 通信を行うアプリの開発は無理と言っても過言ではないと思います。

(注)クライアントにブラウザを使う場合と違って、デスクトップアプリなどからですと localhost はキャプチャできないので注意してください。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/12/03 13:07

    丁寧にありがとうございます。
    Fiddlerというのはなんですか?
    ふだんはVisual Studioのデバッグで、変数の中を見ています。
    messageには見当たらなかったのです。Fiddlerをつかうともっと情報を得られるのでしょうか。

    キャンセル

  • 2018/12/03 13:19

    HTTP 通信を行うアプリの開発には必須と言ってもいいツールです。詳しくは以下の記事を見てください。

    Fiddler のお勧め
    http://surferonwww.info/BlogEngine/post/2011/05/25/Recommendation-of-Fiddler.aspx

    どういう情報が得られるかは、後で回答欄に書いておきます。

    キャンセル

checkベストアンサー

+1

こんにちは。

「接続に失敗する」というのは状況がよくわかりませんが、
PostAsyncの結果から中身を取り出すには、Contentに対してReadAsStringAsyncを使います。

            var message = client.PostAsync(url, stringContent);
            return message.Result.Content.ReadAsStringAsync().Result;


接続ができているなら、上のやり方で結果を取り出すことができると思います。


本来HttpClientは非同期APIなので、基本的には非同期コンテキストで走らせるものです。

            var response = await client.PostAsync(url, stringContent);
            return await response.Content.ReadAsStringAsync();

正しく扱うには非同期処理の基礎を学ぶ必要があるため、習得していないうちは同期記法でもよいです(HttpClientの場合は例外的に。大半の非同期APIは同期で書こうとするとほぼデッドロックするので非常に危険です。)
UWP以降になると非同期処理の習得がほぼ必須なので、そちらの学習を優先したほうが良いです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/12/03 11:56

    早速のご返答ありがとうございます。検証します。

    キャンセル

  • 2018/12/04 10:14

    ありがとうございました。

    そのMethodはうまくできたようなのですが、ご心配頂いていた通り、非同期処理で新たに課題が発生しました。
    var response = GetHttpResponse(baseUrl, data).ToString();
    var json = JArray.Parse(response);//<-このresponseが空のまま実行してしまう。
    return json;
    これを修正するには、
    var response = await GetHttpResponse(baseUrl, data).ToString();
    var json = await JArray.Parse(response);
    と両方にawaitすればよいのでしょうか?
    あるいはJArray.Parse(response);をGetHttpResponseで実行して、Parseずみのjsonをawaitで戻せばよいのか。いずれにしても非同期にするために、あちこちだいぶ手を入れないとダメっぽくて…。

    キャンセル

  • 2018/12/04 10:24

    根本的に非同期が理解できていないまま非同期フローを書くのはおそらく不可能です。
    現状のGetHttpResponseの実装がどうなっているのかがわからないとコメントへの回答は難しいです。
    仮にそのGetHttpResponseの中身が本回答の1つ目で書いた同期処理版であるとすれば、GetHttpResponseの型はstringなのでToString()は不要なはずです。そして、responseが空であるというのは、HTTP通信のレスポンス自体が空ということになります。
    もしGetHttpResponseを非同期メソッドにしようとしてTaskを返していた場合は、それをToString()しても当然正しい結果は得られません。

    キャンセル

  • 2018/12/04 10:35

    おっしゃるとおりです。ありがとうございました。この質問はとりあえずここで完了にします。

    キャンセル

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

  • ただいまの回答率 88.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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