狛ログ

2016年8月2日火曜日

URLConnection(HttpURLConnection)と向き合おう~POSTメソッドでJSONを送信する~

オフィス狛 技術部です。

URLConnection(HttpURLConnection)と向き合うために、今回はPOSTメソッドを使います。
POSTはPOSTでも特にHTMLのフォームタグでPOSTを行ったときと同様のデータを送信します。

今回の目標

  • HttpURLConnectionを使ってPOSTメソッドを行う。
  • ContentTypeはapplication/jsonの形式にする。
  • POSTメソッドのレスポンスデータを取得する。

HttpURLConnectionを使ったPOSTのやり方

HttpURLConnection はGET/POSTに関係なくメソッドの呼び出し順序は大体決まっています。
順序は下記のとおりです。
  • ステップ1 :接続URLを決める。
  • ステップ2 :URLへのコネクションを取得する。
  • ステップ3 :接続設定(メソッドの決定,タイムアウト値,ヘッダー値等)を行う。
  • ステップ4 :コネクションを開く
  • ステップ5 :リクエストボディの書き込みを行う。(POSTのみ行う)
  • ステップ6 :レスポンスの読み出しを行う。
  • ステップ7 :コネクションを閉じる。

ステップ1:接続URLを決める。

接続先URLを決めるには java.net.URL を使います。
URL url = new URL("[接続先のURLを設定してください。]");

ステップ2:URLへのコネクションを取得する。

接続URLのコネクションを取得することができます。
URL url = [ステップ1:接続URLを決めるを参照してください];
HttpURLConnection urlConnection = null;
try {
    urlConnection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
    e.printStackTrace();
}

ステップ3:接続設定(メソッドの決定,タイムアウト値,ヘッダー値等)を行う。

今回はPOSTを使ったデータの送受信を行うので特に下記の設定を行う必要があります。
  • setRequestMethod を使いPOSTを使う設定をする
  • リクエストヘッダーの Content-Type を application/json に設定する。
  • リクエストボディにデータを書き込むため、setDoOutput を使い許可をする。
  • レスポンスボディのデータを読み出しするため、setDoInput を使い許可をする。
//接続タイムアウトを設定する。
urlConnection.setConnectTimeout(100000);
//レスポンスデータ読み取りタイムアウトを設定する。
urlConnection.setReadTimeout(100000);
//ヘッダーにUser-Agentを設定する。
urlConnection.setRequestProperty("User-Agent", "Android");
//ヘッダーにAccept-Languageを設定する。
urlConnection.setRequestProperty("Accept-Language", Locale.getDefault().toString());
//ヘッダーにContent-Typeを設定する
urlConnection.addRequestProperty("Content-Type", "application/json; charset=UTF-8");
//HTTPのメソッドをPOSTに設定する。
urlConnection.setRequestMethod("POST");
//リクエストのボディ送信を許可する
urlConnection.setDoOutput(true);
//レスポンスのボディ受信を許可する
urlConnection.setDoInput(true);

ステップ4:コネクションを開く

コネクションを設定した内容で接続し、実際にデータの送受信ができる状態にします。
このメソッド以降は「接続先にデータを送信する OutputStream 」と「接続先からデータを受信する InputStream 」を使うことができるようになります。
//ステップ4.コネクションを開く
urlConnection.connect();

ステップ5:リクエストボディの書き込みを行う。

「application/json」の送信データはRFC7159に準拠した文字列を送信する必要があります。
今回はorg.json.JSONObjectクラスを使いHashMapをJSON文字列に変換します。
//ステップ5:リクエストボディの書き出しを行う。
OutputStream outputStream = urlConnection.getOutputStream();
HashMap<String, Object> jsonMap = new HashMap<>();
jsonMap.put("text" , "value");
ArrayList<String> array = new ArrayList<>();
array.add("array001");
array.add("array002");
array.add("array003");
jsonMap.put("array" , array);
if (jsonMap.size() > 0) {
    //JSON形式の文字列に変換する。
    JSONObject responseJsonObject = new JSONObject(jsonMap);
    String jsonText = responseJsonObject.toString();
    PrintStream ps = new PrintStream(urlConnection.getOutputStream());
    ps.print(jsonText);
    ps.close();
}
outputStream.close();

ステップ6:レスポンスボディの読み出しを行う。

主なレスポンスデータは下記のメソッドを使って取得します。
  • getResponseCode を使うことでHTTPステータスコードを取得できます。
  • getInputStream を使うことレスポンスボディのデータストリームを取得できます。

//ステップ6:レスポンスボディの読み出しを行う。
int statusCode = urlConnection.getResponseCode();

String responseData = "";
InputStream stream = urlConnection.getInputStream();
StringBuffer sb = new StringBuffer();
String line = "";
BufferedReader br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
while ((line = br.readLine()) != null) {
    sb.append(line);
}
try {
    stream.close();
} catch (Exception e) {
    e.printStackTrace();
}
responseData = sb.toString();
今回はレスポンスボディのデータストリームを文字列に変換しています。

ステップ7:コネクションを閉じる。

使ったコネクションはしっかりと閉じましょう。
//ステップ7:コネクションを閉じる。
urlConnection.disconnect();

サンプルコード

今回のサンプルコードの仕様は下記のとおりです。

  • URLとハッシュマップを引数で渡す。
  • 指定したURLにPOSTメソッドで接続する。
  • Content-Typeはapplication/jsonを使用する。
  • org.json.JSONObjectを使用してハッシュマップをJSON文字列に変換する。
  • リクエストボディにapplication/jsonに適合する文字列を設定する。
  • レスポンスボディデータは文字列として扱う。
  • 非同期処理から呼び出されることを想定している。
  • 呼び出し側には文字列を返却する。
    public String execute(String argStrApiUrl, HashMap<String,Object> jsonMap) {
        String ret = "";
        HttpURLConnection urlConnection = null;
        try {
            //ステップ1.接続URLを決める。
            URL url = new URL(argStrApiUrl);

            //ステップ2.URLへのコネクションを取得する。
            urlConnection = (HttpURLConnection) url.openConnection();

            //ステップ3.接続設定(メソッドの決定,タイムアウト値,ヘッダー値等)を行う。
            //接続タイムアウトを設定する。
            urlConnection.setConnectTimeout(100000);
            //レスポンスデータ読み取りタイムアウトを設定する。
            urlConnection.setReadTimeout(100000);
            //ヘッダーにUser-Agentを設定する。
            urlConnection.setRequestProperty("User-Agent", "Android");
            //ヘッダーにAccept-Languageを設定する。
            urlConnection.setRequestProperty("Accept-Language", Locale.getDefault().toString());
            //ヘッダーにContent-Typeを設定する
            urlConnection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            //HTTPのメソッドをPOSTに設定する。
            urlConnection.setRequestMethod("POST");
            //リクエストのボディ送信を許可する
            urlConnection.setDoOutput(true);
            //レスポンスのボディ受信を許可する
            urlConnection.setDoInput(true);

            //ステップ4.コネクションを開く
            urlConnection.connect();

            //ステップ5:リクエストボディの書き出しを行う。
            OutputStream outputStream = urlConnection.getOutputStream();
            if (jsonMap.size() > 0) {
                //JSON形式の文字列に変換する。
                JSONObject responseJsonObject = new JSONObject(jsonMap);
                String jsonText = responseJsonObject.toString();
                PrintStream ps = new PrintStream(urlConnection.getOutputStream());
                ps.print(jsonText);
                ps.close();
            }
            outputStream.close();

            //ステップ6.レスポンスボディの読み出しを行う。
            int responseCode = urlConnection.getResponseCode();
            ret = convertToString(urlConnection.getInputStream());
            Log.d("execute", "URL:" + argStrApiUrl);
            Log.d("execute", "HttpStatusCode:" + responseCode);
            Log.d("execute", "ResponseData:" + ret);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                //7.コネクションを閉じる。
                urlConnection.disconnect();
            }
        }
        return ret;
    }

    public String convertToString(InputStream stream) throws IOException {
        StringBuffer sb = new StringBuffer();
        String line = "";
        BufferedReader br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        try {
            stream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

今回のまとめ

  • POSTメソッドではリクエストボディを使ったデータ送信を許可するために setDoOutput にtrueを渡すこと。
  • application/json に適合した送信文字列を作成するときはorg.json.JSONObjectクラスなどのJSON文字列を取り扱うクラスを使用すること。
  • テキストとファイルを同時に送信する Content-Type「multipart/form-data」とは異なるので注意すること。

2 件のコメント:

  1. ステップ4のconnect()は送信をイメージしていたのですが、コネクションはステップ2で開いてますよね?

    返信削除
  2. We shall try and fulfill your budget whenever possible. Singapore Souvenirs

    返信削除