Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JavaでHTTPアクセスしてみる / HTTP access with Java

JavaでHTTPアクセスしてみる / HTTP access with Java

2020/4/30 JJUGナイトセミナー「みんなの小噺」での登壇資料です

Naoki Kishida

April 30, 2020
Tweet

More Decks by Naoki Kishida

Other Decks in Programming

Transcript

  1. 自己紹介 • きしだ なおき • @kis • https://nowokay.hatenablog.com/ • LINE

    Fukuoka • 都道府県別の感染者状況が一覧 できるサイトつくってます • https://kishida.github.io/covid19/
  2. HTTPアクセスの歴史 • HTTP(1991) • Hyper Text Transfer Protocolプロトコル・・・ • HTML(1993)

    • マークアップテキスト • SOAP(1998) • XMLをベースにHTTPで通信を行うRPC • Ajax(2005) • ブラウザでJavaScriptからXMLをやりとりしてインタラクティブなWebアプリケーションを実装 • アプリケーションぜんぶWebでいいんでは? • JSON(2006) • JavaScriptオブジェクトの表現形式をデータ記述に使う • RESTful(2009?) • RESTでサーバー間通信するといいのでは • 特別なRPC定義をせずHTTPのメソッドを利用する • サーバー間通信もHTTPでいいのでは
  3. JavaでHTTPアクセス • 標準APIを使う • HTTP Client API • Since Java

    11 as Standard • 外部ライブラリを使う • Retrofit • https://square.github.io/retrofit/
  4. HTTPプロトコル • TCP通信を行う • HTTPは一往復のプロトコル • リクエスト & レスポンス •

    送信 • “%s %s HTTP/%s”.formatted(method, resource, version) • “Host: %s”.formatted(hostname) • Request Headers • 空行 • 受信 • “HTTP/%s %d %s”.formatted(version, statusCode, statusMessage) • Response Headers • 空行 • body
  5. Socket API try (var soc = new Socket("example.com", 80); var

    pw = new PrintWriter(soc.getOutputStream()); var isr = new InputStreamReader(soc.getInputStream()); var bur = new BufferedReader(isr)) { pw.println("GET / HTTP/1.1"); pw.println("Host: example.com"); pw.println(); pw.flush(); bur.lines() .dropWhile(not(String::isEmpty)) .skip(1) .limit(10) .forEach(System.out::println); }
  6. Socket API try (var soc = new Socket("example.com", 80); var

    pw = new PrintWriter(soc.getOutputStream()); var isr = new InputStreamReader(soc.getInputStream()); var bur = new BufferedReader(isr)) { pw.println("GET / HTTP/1.1"); pw.println("Host: example.com"); pw.println(); pw.flush(); bur.lines() .dropWhile(not(String::isEmpty)) .skip(1) .limit(10) .forEach(System.out::println); }
  7. Socket API try (var soc = new Socket("example.com", 80); var

    pw = new PrintWriter(soc.getOutputStream()); var isr = new InputStreamReader(soc.getInputStream()); var bur = new BufferedReader(isr)) { pw.println("GET / HTTP/1.1"); pw.println("Host: example.com"); pw.println(); pw.flush(); bur.lines() .dropWhile(not(String::isEmpty)) .skip(1) .limit(10) .forEach(System.out::println); }
  8. Socket API try (var soc = new Socket("example.com", 80); var

    pw = new PrintWriter(soc.getOutputStream()); var isr = new InputStreamReader(soc.getInputStream()); var bur = new BufferedReader(isr)) { pw.println("GET / HTTP/1.1"); pw.println("Host: example.com"); pw.println(); pw.flush(); bur.lines() .dropWhile(not(String::isEmpty)) .skip(1) .limit(10) .forEach(System.out::println); }
  9. URLConnection var url = "http://example.com"; URLConnection conn = new URL(url).openConnection();

    System.out.println(((HttpURLConnection)conn).getResponseCode()); System.out.println(conn.getContentType()); System.out.println(conn.getHeaderField("Cache-Control")); try (var is = conn.getInputStream(); var isr = new InputStreamReader(is, "utf-8"); var bur = new BufferedReader(isr)) { bur.lines() .limit(10) .forEach(System.out::println); }
  10. URLConnection var url = "http://example.com"; URLConnection conn = new URL(url).openConnection();

    System.out.println(((HttpURLConnection)conn).getResponseCode()); System.out.println(conn.getContentType()); System.out.println(conn.getHeaderField("Cache-Control")); try (var is = conn.getInputStream(); var isr = new InputStreamReader(is, "utf-8"); var bur = new BufferedReader(isr)) { bur.lines() .limit(10) .forEach(System.out::println); }
  11. URLConnection var url = "http://example.com"; URLConnection conn = new URL(url).openConnection();

    System.out.println(((HttpURLConnection)conn).getResponseCode()); System.out.println(conn.getContentType()); System.out.println(conn.getHeaderField("Cache-Control")); try (var is = conn.getInputStream(); var isr = new InputStreamReader(is, "utf-8"); var bur = new BufferedReader(isr)) { bur.lines() .limit(10) .forEach(System.out::println); }
  12. URLConnection var url = "http://example.com"; URLConnection conn = new URL(url).openConnection();

    System.out.println(((HttpURLConnection)conn).getResponseCode()); System.out.println(conn.getContentType()); System.out.println(conn.getHeaderField("Cache-Control")); try (var is = conn.getInputStream(); var isr = new InputStreamReader(is, "utf-8"); var bur = new BufferedReader(isr)) { bur.lines() .limit(10) .forEach(System.out::println); }
  13. URLConnection var url = "http://example.com"; URLConnection conn = new URL(url).openConnection();

    System.out.println(((HttpURLConnection)conn).getResponseCode()); System.out.println(conn.getContentType()); System.out.println(conn.getHeaderField("Cache-Control")); try (var is = conn.getInputStream(); var isr = new InputStreamReader(is, "utf-8"); var bur = new BufferedReader(isr)) { bur.lines() .limit(10) .forEach(System.out::println); }
  14. URLConnection • URL指定をして通信する様々なプロトコルに対応 • 結局HTTPしかなかった • 細かく使うにはHttpURLConnectionにキャストが必要 • 主にドキュメントの取得が目的 •

    POSTのパラメータ送信はめんどい • 接続とリクエストとレスポンスがごっちゃ • 同期処理しか行えない • 通信待ちでスレッドが止まる
  15. HTTP Client API • URLConnectionは使いにくい • というか使えない • Commons HttpClientやOkHttpを使うようになっていた

    • しかし標準APIとしてHTTP接続は必要では • Java 9でIncubator Module • Java 11でStandard • なのでもうみんな使ってるはず
  16. HTTP Client API HttpClient client = HttpClient.newHttpClient(); var url =

    "http://example.com"; HttpRequest req = HttpRequest.newBuilder(URI.create(url)).GET().build(); HttpResponse<Stream<String>> res = client.send(req, HttpResponse.BodyHandlers.ofLines()); System.out.println(res.statusCode()); // 各ヘッダーを取得する便利メソッドはない System.out.println( res.headers().firstValue("Content-Type").orElse("")); System.out.println( res.headers().firstValueAsLong("Content-Length").orElse(-1)); res.body() .limit(10) .forEach(System.out::println);
  17. HTTP Client API HttpClient client = HttpClient.newHttpClient(); var url =

    "http://example.com"; HttpRequest req = HttpRequest.newBuilder(URI.create(url)).GET().build(); HttpResponse<Stream<String>> res = client.send(req, HttpResponse.BodyHandlers.ofLines()); System.out.println(res.statusCode()); // 各ヘッダーを取得する便利メソッドはない System.out.println( res.headers().firstValue("Content-Type").orElse("")); System.out.println( res.headers().firstValueAsLong("Content-Length").orElse(-1)); res.body() .limit(10) .forEach(System.out::println);
  18. HTTP Client API HttpClient client = HttpClient.newHttpClient(); var url =

    "http://example.com"; HttpRequest req = HttpRequest.newBuilder(URI.create(url)).GET().build(); HttpResponse<Stream<String>> res = client.send(req, HttpResponse.BodyHandlers.ofLines()); System.out.println(res.statusCode()); // 各ヘッダーを取得する便利メソッドはない System.out.println( res.headers().firstValue("Content-Type").orElse("")); System.out.println( res.headers().firstValueAsLong("Content-Length").orElse(-1)); res.body() .limit(10) .forEach(System.out::println);
  19. HTTP Client API HttpClient client = HttpClient.newHttpClient(); var url =

    "http://example.com"; HttpRequest req = HttpRequest.newBuilder(URI.create(url)).GET().build(); HttpResponse<Stream<String>> res = client.send(req, HttpResponse.BodyHandlers.ofLines()); System.out.println(res.statusCode()); // 各ヘッダーを取得する便利メソッドはない System.out.println( res.headers().firstValue("Content-Type").orElse("")); System.out.println( res.headers().firstValueAsLong("Content-Length").orElse(-1)); res.body() .limit(10) .forEach(System.out::println);
  20. HTTP Client API HttpClient client = HttpClient.newHttpClient(); var url =

    "http://example.com"; HttpRequest req = HttpRequest.newBuilder(URI.create(url)).GET().build(); HttpResponse<Stream<String>> res = client.send(req, HttpResponse.BodyHandlers.ofLines()); System.out.println(res.statusCode()); // 各ヘッダーを取得する便利メソッドはない System.out.println( res.headers().firstValue("Content-Type").orElse("")); System.out.println( res.headers().firstValueAsLong("Content-Length").orElse(-1)); res.body() .limit(10) .forEach(System.out::println);
  21. 例:AED検索 • 緯度経度を指定して範囲内のAEDを検索 [ { "DIST": 222, "Id": 182, "LocationName":

    "丹南健康福祉センター", "Perfecture": "福井県", "City": "鯖江市", "AddressArea": "水落町1-2-25", "Latitude": 35.959898,
  22. レスポンス用オブジェクト • レスポンスのJSONをマッピングする class AedData { public int DIST; public

    String Id; public String LocationName; public String Perfecture; public String City; double Latitude; double Longitude; }
  23. RetrofitでのHTTP接続 var mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Retrofit retrofit =

    new Retrofit.Builder() .baseUrl("https://aed.azure-mobile.net/api/") .addConverterFactory(JacksonConverterFactory.create(mapper)) .build(); AedService aedService = retrofit.create(AedService.class); Call<List<AedData>> aedsReq = aedService.aedSearch(33.58, 130.427, 200); System.out.println(aedsReq.request().url()); Response<List<AedData>> aedsRes = aedsReq.execute(); System.out.println(aedsRes.code()); List<AedData> aeds = aedsRes.body(); aeds.forEach( aed -> System.out.printf("%s: %s in %dm%n", aed.Id, aed.LocationName, aed.DIST)); Call<List<AedData>> aedReq = aedService.aedInfo("63825"); List<AedData> aed = aedReq.execute().body(); System.out.println(aed.get(0).LocationName);
  24. RetrofitでのHTTP接続 var mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Retrofit retrofit =

    new Retrofit.Builder() .baseUrl("https://aed.azure-mobile.net/api/") .addConverterFactory(JacksonConverterFactory.create(mapper)) .build(); AedService aedService = retrofit.create(AedService.class); Call<List<AedData>> aedsReq = aedService.aedSearch(33.58, 130.427, 200); System.out.println(aedsReq.request().url()); Response<List<AedData>> aedsRes = aedsReq.execute(); System.out.println(aedsRes.code()); List<AedData> aeds = aedsRes.body(); aeds.forEach( aed -> System.out.printf("%s: %s in %dm%n", aed.Id, aed.LocationName, aed.DIST)); Call<List<AedData>> aedReq = aedService.aedInfo("63825"); List<AedData> aed = aedReq.execute().body(); System.out.println(aed.get(0).LocationName);
  25. RetrofitでのHTTP接続 var mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Retrofit retrofit =

    new Retrofit.Builder() .baseUrl("https://aed.azure-mobile.net/api/") .addConverterFactory(JacksonConverterFactory.create(mapper)) .build(); AedService aedService = retrofit.create(AedService.class); Call<List<AedData>> aedsReq = aedService.aedSearch(33.58, 130.427, 200); System.out.println(aedsReq.request().url()); Response<List<AedData>> aedsRes = aedsReq.execute(); System.out.println(aedsRes.code()); List<AedData> aeds = aedsRes.body(); aeds.forEach( aed -> System.out.printf("%s: %s in %dm%n", aed.Id, aed.LocationName, aed.DIST)); Call<List<AedData>> aedReq = aedService.aedInfo("63825"); List<AedData> aed = aedReq.execute().body(); System.out.println(aed.get(0).LocationName);
  26. RetrofitでのHTTP接続 var mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Retrofit retrofit =

    new Retrofit.Builder() .baseUrl("https://aed.azure-mobile.net/api/") .addConverterFactory(JacksonConverterFactory.create(mapper)) .build(); AedService aedService = retrofit.create(AedService.class); Call<List<AedData>> aedsReq = aedService.aedSearch(33.58, 130.427, 200); System.out.println(aedsReq.request().url()); Response<List<AedData>> aedsRes = aedsReq.execute(); System.out.println(aedsRes.code()); List<AedData> aeds = aedsRes.body(); aeds.forEach( aed -> System.out.printf("%s: %s in %dm%n", aed.Id, aed.LocationName, aed.DIST)); Call<List<AedData>> aedReq = aedService.aedInfo("63825"); List<AedData> aed = aedReq.execute().body(); System.out.println(aed.get(0).LocationName);
  27. RetrofitでのHTTP接続 var mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Retrofit retrofit =

    new Retrofit.Builder() .baseUrl("https://aed.azure-mobile.net/api/") .addConverterFactory(JacksonConverterFactory.create(mapper)) .build(); AedService aedService = retrofit.create(AedService.class); Call<List<AedData>> aedsReq = aedService.aedSearch(33.58, 130.427, 200); System.out.println(aedsReq.request().url()); Response<List<AedData>> aedsRes = aedsReq.execute(); System.out.println(aedsRes.code()); List<AedData> aeds = aedsRes.body(); aeds.forEach( aed -> System.out.printf("%s: %s in %dm%n", aed.Id, aed.LocationName, aed.DIST)); Call<List<AedData>> aedReq = aedService.aedInfo("63825"); List<AedData> aed = aedReq.execute().body(); System.out.println(aed.get(0).LocationName);
  28. RetrofitでのHTTP接続 var mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Retrofit retrofit =

    new Retrofit.Builder() .baseUrl("https://aed.azure-mobile.net/api/") .addConverterFactory(JacksonConverterFactory.create(mapper)) .build(); AedService aedService = retrofit.create(AedService.class); Call<List<AedData>> aedsReq = aedService.aedSearch(33.58, 130.427, 200); System.out.println(aedsReq.request().url()); Response<List<AedData>> aedsRes = aedsReq.execute(); System.out.println(aedsRes.code()); List<AedData> aeds = aedsRes.body(); aeds.forEach( aed -> System.out.printf("%s: %s in %dm%n", aed.Id, aed.LocationName, aed.DIST)); Call<List<AedData>> aedReq = aedService.aedInfo("63825"); List<AedData> aed = aedReq.execute().body(); System.out.println(aed.get(0).LocationName);
  29. RetrofitでのHTTP接続 var mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); Retrofit retrofit =

    new Retrofit.Builder() .baseUrl("https://aed.azure-mobile.net/api/") .addConverterFactory(JacksonConverterFactory.create(mapper)) .build(); AedService aedService = retrofit.create(AedService.class); Call<List<AedData>> aedsReq = aedService.aedSearch(33.58, 130.427, 200); System.out.println(aedsReq.request().url()); Response<List<AedData>> aedsRes = aedsReq.execute(); System.out.println(aedsRes.code()); List<AedData> aeds = aedsRes.body(); aeds.forEach( aed -> System.out.printf("%s: %s in %dm%n", aed.Id, aed.LocationName, aed.DIST)); Call<List<AedData>> aedReq = aedService.aedInfo("63825"); List<AedData> aed = aedReq.execute().body(); System.out.println(aed.get(0).LocationName);
  30. HTTP以外のプロトコル • HTTPS(2000) • HTTP over SSL/TLS • ブラウザとサーバーの通信を暗号化 •

    サーバーがすり替えられてないことを確認したり通信の盗聴を防ぐ • WebSocket(2011) • ブラウザとサーバーで双方向通信を行う • 最初の接続はHTTPで行う • WebRTC(2011) • リアルタイム通信 • UDP • P2P • HTTP/2 (2015) • HTTPを拡張 • 効率化、複数のリクエストに対応 • HTTP/3 • HTTP over QUIC • UDP
  31. まとめ • HTTP接続は大事 • いろいろ便利なライブラリがある • サーバー間もHTTP通信 • 疎結合 •

    つまり疎結合にはHTTP通信 • ソーシャルディスタンスをとるにはHTTP接続が不可欠 • 早く外に出れるよう引きこもりがんばりましょう