Kotlin java.net.http.HttpClientによる非同期通信

webサーバーのリソースを非同期で取得する手法
6.2 HTTPによるアプリケーション連携(RESTサービス、HTTPクライアントAPI、非同期呼び出しなど)~Java Advanced編

6.2.4 HTTPクライアントの作成方法(2):非同期型
こちらで紹介されている方法が面白そうだったので、Kotlinで試してみます。


簡単なサンプル


Javaで紹介されているサンプルを素直にKotlinで実装した結果がこちら。


  1. package org.example
  2. import java.net.URI
  3. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  4. import java.net.http.HttpRequest // リクエストを表すクラス
  5. import java.net.http.HttpResponse // レスポンスを表すクラス
  6. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  7. fun main() {
  8.     // 手順1:HttpClientオブジェクトを生成する
  9.     var client = HttpClient.newBuilder()
  10.         .version(HttpClient.Version.HTTP_2)
  11.         .build()
  12.     // 手順2:HttpRequestオブジェクトを生成する
  13.     var request = HttpRequest.newBuilder()
  14.         .uri(URI.create("https://www.example.com/"))
  15.         .header("User-Agent", "Java SE HttpClient")
  16.         .build();
  17.     
  18.     // 手順3:HttpRequestを送信し、HTTPサーバーを非同期で呼び出す
  19.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  20.     
  21.     // 手順4:後から何らかの方法でHttpResponseを取得し、何らかの後処理を行う
  22.     var response = future.get()
  23.     println(response.body())
  24. }



実行すると、https://www.example.com/のhtmlが表示されます。
これはお手軽なんじゃないでしょうか。

続いて、thenAcceptによるレスポンスの取得を試してみると、


  1. package org.example
  2. import java.net.URI
  3. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  4. import java.net.http.HttpRequest // リクエストを表すクラス
  5. import java.net.http.HttpResponse // レスポンスを表すクラス
  6. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  7. fun main() {
  8.     var client = HttpClient.newBuilder()
  9.         .version(HttpClient.Version.HTTP_2)
  10.         .build()
  11.     var request = HttpRequest.newBuilder()
  12.         .uri(URI.create("https://www.example.com/"))
  13.         .header("User-Agent", "Java SE HttpClient")
  14.         .build();
  15.     
  16.     client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  17.     .thenAccept({
  18.         println("--- get response ---")
  19.         var resBody = it.body();
  20.         println(resBody)
  21.     })
  22.     
  23.     println("--- fin ---")
  24. }



環境にもよると思いますが、htmlは表示されずfinのみ表示されます。
取得前にメインスレッドが終了するからですね。
Thread.sleepでちょっと待ってみます。


  1. package org.example
  2. import java.net.URI
  3. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  4. import java.net.http.HttpRequest // リクエストを表すクラス
  5. import java.net.http.HttpResponse // レスポンスを表すクラス
  6. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  7. fun main() {
  8.     var client = HttpClient.newBuilder()
  9.         .version(HttpClient.Version.HTTP_2)
  10.         .build()
  11.     var request = HttpRequest.newBuilder()
  12.         .uri(URI.create("https://www.example.com/"))
  13.         .header("User-Agent", "Java SE HttpClient")
  14.         .build();
  15.     
  16.     client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  17.     .thenAccept({
  18.         println("--- get response ---")
  19.         var resBody = it.body();
  20.         println(resBody)
  21.     })
  22.     // ちょっと待つ
  23.     Thread.sleep(1000)
  24.     
  25.     println("--- fin ---")
  26. }



これで取得結果が表示できました。



パラメーター付きGET


パラメーターを付けてGETを試してみます。
後にPOSTも試したいのでサーバーにこんなphpファイルをおいておきました。


  1. <?php
  2. echo ('GET'.PHP_EOL);
  3. var_dump($_GET);
  4. echo (PHP_EOL);
  5. echo ('POST'.PHP_EOL);
  6. var_dump($_POST);
  7. echo (PHP_EOL);



GETテスト

  1. ackage org.example
  2. import java.net.URI
  3. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  4. import java.net.http.HttpRequest // リクエストを表すクラス
  5. import java.net.http.HttpResponse // レスポンスを表すクラス
  6. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  7. fun main() {
  8.     var client = HttpClient.newBuilder()
  9.         .version(HttpClient.Version.HTTP_2)
  10.         .build()
  11.     var request = HttpRequest.newBuilder()
  12.         .uri(URI.create("http://192.168.11.200/test.php?name=symfo"))
  13.         .header("User-Agent", "Java SE HttpClient")
  14.         .build();
  15.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  16.     
  17.     var response = future.get()
  18.     println(response.body())
  19. }



実行結果

GET
array(1) {
  ["name"]=>
  string(5) "symfo"
}

POST
array(0) {
}




問題ないですね。


POST送信


POSTも試してみます。

  1. package org.example
  2. import java.net.URI
  3. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  4. import java.net.http.HttpRequest // リクエストを表すクラス
  5. import java.net.http.HttpResponse // レスポンスを表すクラス
  6. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  7. fun main() {
  8.     var client = HttpClient.newBuilder()
  9.         .version(HttpClient.Version.HTTP_2)
  10.         .build()
  11.     var request = HttpRequest.newBuilder()
  12.         .uri(URI.create("http://192.168.11.200/test.php"))
  13.         .header("User-Agent", "Java SE HttpClient")
  14.         .header("Content-Type", "application/x-www-form-urlencoded")
  15.         .POST(HttpRequest.BodyPublishers.ofString("name=symfo"))
  16.         .build();
  17.     
  18.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  19.     
  20.     var response = future.get()
  21.     println(response.body())
  22. }



こちらも狙い通りの実行結果です。

GET
array(0) {
}

POST
array(1) {
  ["name"]=>
  string(5) "symfo"
}





JSON


JSONな文字列をPOSTしてみます。
こちらが参考になりました。
【Java】HttpClientでコンテンツ取得

サーバー側のPHPファイルを変更します。

  1. <?php
  2. var_dump(json_decode(file_get_contents('php://input')));



送信側のプログラムはこうなりました。

  1. package org.example
  2. import java.net.URI
  3. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  4. import java.net.http.HttpRequest // リクエストを表すクラス
  5. import java.net.http.HttpResponse // レスポンスを表すクラス
  6. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  7. fun main() {
  8.     var client = HttpClient.newBuilder()
  9.         .version(HttpClient.Version.HTTP_2)
  10.         .build()
  11.     var request = HttpRequest.newBuilder()
  12.         .uri(URI.create("http://192.168.11.200/test.php"))
  13.         .header("User-Agent", "Java SE HttpClient")
  14.         .header("Content-Type", "application/json")
  15.         .POST(HttpRequest.BodyPublishers.ofString("""{"name":"Symfo"}"""))
  16.         .build();
  17.     
  18.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  19.     
  20.     var response = future.get()
  21.     println(response.body())
  22. }



実行結果

object(stdClass)#1 (1) {
  ["name"]=>
  string(5) "Symfo"
}





バイナリファイルの取得


画像などのバイナリファイルを取得する場合の例です。
こちらが参考になります。
クラスHttpResponse.BodyHandlers

直接ファイルとして取得する場合は、
HttpResponse.BodyHandlers.ofFile
が便利です。


  1. package org.example
  2. import java.nio.file.Files
  3. import java.nio.file.Path;
  4. import java.nio.file.Paths;
  5. import java.net.URI
  6. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  7. import java.net.http.HttpRequest // リクエストを表すクラス
  8. import java.net.http.HttpResponse // レスポンスを表すクラス
  9. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  10. fun main() {
  11.     var client = HttpClient.newBuilder()
  12.         .version(HttpClient.Version.HTTP_2)
  13.         .build()
  14.     var request = HttpRequest.newBuilder()
  15.         .uri(URI.create("http://192.168.11.200/miku.jpg"))
  16.         .header("User-Agent", "Java SE HttpClient")
  17.         .build();
  18.     
  19.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(
  20.         Paths.get("miku.jpg")
  21.     ))
  22.     var response = future.get()
  23.     println(response)
  24. }




バイナリデータを加工に使用したい場合は、ofInputStreamを指定し、
InputStreamとして取得します。

  1. package org.example
  2. import java.nio.file.Files
  3. import java.nio.file.Path;
  4. import java.nio.file.Paths;
  5. import java.net.URI
  6. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  7. import java.net.http.HttpRequest // リクエストを表すクラス
  8. import java.net.http.HttpResponse // レスポンスを表すクラス
  9. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  10. fun main() {
  11.     var client = HttpClient.newBuilder()
  12.         .version(HttpClient.Version.HTTP_2)
  13.         .build()
  14.     var request = HttpRequest.newBuilder()
  15.         .uri(URI.create("http://192.168.11.200/miku.jpg"))
  16.         .header("User-Agent", "Java SE HttpClient")
  17.         .build();
  18.     
  19.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream())
  20.     
  21.     var response = future.get()
  22.     
  23.     runCatching {
  24.         // InputStreamをファイルに出力
  25.         var f = Files.copy(response.body(), Paths.get("miku.jpg"))
  26.     }
  27. }





バイナリファイルのアップロード


こちらが参考になりました。
クラスHttpRequest.BodyPublishers

ファイルを直接指定する場合は
BodyPublishers.ofFile
が便利です。


  1. package org.example
  2. import java.nio.file.Files
  3. import java.nio.file.Path;
  4. import java.nio.file.Paths;
  5. import java.net.URI
  6. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  7. import java.net.http.HttpRequest // リクエストを表すクラス
  8. import java.net.http.HttpResponse // レスポンスを表すクラス
  9. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  10. fun main() {
  11.     var client = HttpClient.newBuilder()
  12.         .version(HttpClient.Version.HTTP_2)
  13.         .build()
  14.     var request = HttpRequest.newBuilder()
  15.         .uri(URI.create("http://192.168.11.200/test.php"))
  16.         .header("User-Agent", "Java SE HttpClient")
  17.         .POST(HttpRequest.BodyPublishers.ofFile(Paths.get("miku.jpg")))
  18.         .build();
  19.     
  20.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  21.     
  22.     var response = future.get()
  23.     println(response.body())
  24. }




バイト配列を指定する場合


  1. package org.example
  2. import java.io.File
  3. import java.nio.file.Path;
  4. import java.nio.file.Paths;
  5. import java.net.URI
  6. import java.net.http.HttpClient // HTTPクライアントを表すクラス
  7. import java.net.http.HttpRequest // リクエストを表すクラス
  8. import java.net.http.HttpResponse // レスポンスを表すクラス
  9. import java.net.http.HttpHeaders // HTTPヘッダを表すクラス
  10. fun main() {
  11.     var uploadBin = File("miku.jpg").readBytes()
  12.     var client = HttpClient.newBuilder()
  13.         .version(HttpClient.Version.HTTP_2)
  14.         .build()
  15.     var request = HttpRequest.newBuilder()
  16.         .uri(URI.create("http://192.168.11.200/test.php"))
  17.         .header("User-Agent", "Java SE HttpClient")
  18.         .POST(HttpRequest.BodyPublishers.ofByteArray(uploadBin))
  19.         .build();
  20.     
  21.     var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  22.     
  23.     var response = future.get()
  24.     println(response.body())
  25. }



関連記事

コメント

プロフィール

Author:symfo
blog形式だと探しにくいので、まとめサイト作成中です。
https://symfo.web.fc2.com/

PR

検索フォーム

月別アーカイブ