JavaでHttpリクエストする方法とHTMLの取得(Usage of HttpURLConnection)

# HttpURLConnectionの使用について
C/C++でsocketやWinInetなどを使っていたならJavaのHttpURLConnectionの使用に戸惑うだろう。まずリクエストヘッダーを送信する関数がない。いつ送信するのか？レスポンスの処理含め全体的にかなり処理が簡素化している。さらに、GetやPostの内容がHTTPキャプチャーには表示されないのだ。内部処理が特殊なのだろう。それで最初、どのように記述していいのかわらなく迷う。ただ、あまり深く考えずに使ってみると、かなり楽な仕様になっていることが分かった。

今回はそのようなHttpURLConnectionのGETの使い方を見ていく。取得したバッファーの処理とHTMLの取得、それをテキストファイルとして保存をしてみる。またHTTPキャプチャーへの表示方法も載せておく。

コードの編集にはVsCodeを使用した。また環境変数にJAVAのJDKを入れておく必要がある。

# HTTPストリームで確認するには
JavaのMavenにてプロジェクトを作成した。このメインとなるソースファイルはApp.Java。そこと同じ場所にHttpRequest.Javaというファイルを作成する。それをもとにクラスを作成する。Httpキャプチャーにリクエストやレスポンスを表示させるには以下のプロパティを最初のほうに追加する。

```
  System.setProperty("http.proxyHost", "localhost");
  System.setProperty("http.proxyPort", "8888");
```

  すると下のようなコンストラクターとなる。この下にさらにHttpURLConnectionの記述が続くので、次の項目で見てもらいたい。

```java:HttpRequest.java


public class HttpRequest {

    URL url;
    HttpURLConnection connection;
    InputStream ips;
    String strResponseBody;
    byte[] redBuf;
    String strReqMethod;

    public HttpRequest() {

        // HTTPキャプチャーに表示させるには以下のプロパティを追加する。
        // System.setProperty("http.proxyHost", "localhost");
        // System.setProperty("http.proxyPort", "8888");
        // ----

        // HTMLを保存、閲覧するため以下のバッファーを用意する。
        ips = null;
        strResponseBody = null;
        redBuf = null;
        
        //メソッドを指定する
        strReqMethod = "GET";
    
    }
 }

```

# へッダーの記述と接続
最初はURLから指定する。HTMLを取得したい仮のサイトにする（example.com)。

それからurlからopenConnection()関数を呼び出すが、この時まだ接続はされない。その下にヘッダーのプロパティをそれぞれ記述する。openConnection()の上にヘッダーを指定するとエラーとなる。通常、プログラムは上から下に実行されるため、プロパティが先に来そうだが違うようだ。これに関しては、コードを書く上の常識と異なり、迷うところだ。

メソッドはデフォルトでGETが指定されているが、setDoOutput(true)にするとPostになる。これでリクエストヘッダーにそれぞれのプロパティが追加されて接続される。下のようにこまかくヘッダーのプロパティを指定しなくても接続自体は可能だ。

HttpURLConnectionにはconnect()関数があるが、接続が切れている場合以外に使う必要はない。

最終的にコンストラクターは以下のようになる。


```java:HttpRequest.java

public HttpRequest() {

        // HTTPキャプチャーに表示させるには以下のプロパティを追加する。
        // System.setProperty("http.proxyHost", "localhost");
        // System.setProperty("http.proxyPort", "8888");
        // ----

        // HTMLを保存、閲覧するため以下のバッファーを用意する。
        ips = null;
        buffRed = null;
        redBuf = null;
        strReqMethod = "GET";

        // URLを指定する。
        try {

            url = new URL("https://www.example.com/");

        } catch (MalformedURLException e) {
            // TODO: handle exception
            System.out.println(e.getStackTrace().toString());
            return;
        }

        // HTTPリクエストのヘッダーとメソッドを指定する。openConnection()は必ずプロパティ設定の前に置く
        try {

            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod(strReqMethod);
            connection.setInstanceFollowRedirects(true);
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(10000);
            connection.setDoInput(true);
            connection.setDoOutput(false);// falseの場合、自動的にMethodがGETとなる。TrueはPOSTの時、指定する。
            connection.setRequestProperty("Accept",
                    "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
            connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
            connection.setRequestProperty("Accept-Language", "ja,en-US;q=0.9,en;q=0.8");
            connection.setRequestProperty("Host", "www.example.com");

            System.out.println(connection.getResponseCode());

        } catch (IOException e) {
            // TODO: handle exception
            System.out.println(e.getStackTrace().toString());
            return;
        }

```

# バッファーを取得する
次にレスポンスボディからバッファーを取得する。今回はコンストラクターとは別のメソッド内で処理するが、接続する関数内で行っても同じ結果になる。

HttpURLConnectionのgetInputStream()関数から、InputStreamを取得する。これによりバイト配列が取得できる。InputStreamのgetAllBytes()関数でbyte形に変換する。この時点で接続を切ってもよいので、disconnect()関数を呼ぶ。

```java:HttpRequest.java


            // Response code を確認する。 200ならOK
            int iCode = connection.getResponseCode();
            if (iCode == 200) {

                ips = new BufferedInputStream(connection.getInputStream());
                redBuf = ips.readAllBytes();
                connection.disconnect();

            } else {

                // Do Http response Error handling
                System.out.println(connection.getResponseCode());

                return;

            }
```


# テキストファイルにHTML文を保存する
ファイルはユーザードキュメントに保存することにする。もし指定のディレクトリにファイルが存在しない場合は、CreateNewFile（）で作成される。この場合、exist()関数でいちいちファイルの存在を確認しなくてもよい。Stringにするときは String str = new String(redBuf, "UTF-8")のように記述し、UTF-8を指定する。最後にFileWriteは必ずClose()にして終了する。

```java:HttpRequest.java

File f = new File(System.getProperty("user.home") + "/Documents/test.txt");
        try {

            f.createNewFile();

            FileWriter fWrite = new FileWriter(System.getProperty("user.home") + "/Documents/test.txt");
            strResponseBody = new String(redBuf, "UTF-8");
            fWrite.write(strResponseBody);
            fWrite.close();

        } catch (IOException io) {
            // TODO: handle exception
            System.out.println(io.getStackTrace().toString());
            return;
        }

```

# 全体コード
全体のコードを示す。この記述方法は特に意味はなく、使用方法により自由な体裁で書いてもよい。今回省略しているが、アプリケーションで使用する場合はスレッドで処理することが望ましい。

```java:App.java


package com.example;

/**
 * Hello world!
 *
 */
public class App {
    public static void main(String[] args) {

        HttpRequest request = new HttpRequest();
        request.getHtmlString();

    }
}
```


```java:HttpRequest.java


package com.example;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class HttpRequest {

    URL url;
    HttpURLConnection connection;
    InputStream ips;
    String strResponseBody;
    byte[] redBuf;
    String strReqMethod;

    public HttpRequest() {

        // HTTPキャプチャーに表示させるには以下のプロパティを追加する。
        // System.setProperty("http.proxyHost", "localhost");
        // System.setProperty("http.proxyPort", "8888");
        // ----

        // HTMLを保存、閲覧するため以下のバッファーを用意する。
        ips = null;
        redBuf = null;
        strResponseBody = null;
        strReqMethod = "GET";

        // URLを指定する。
        try {

            url = new URL("https://www.example.com/");

        } catch (MalformedURLException e) {
            // TODO: handle exception
            System.out.println(e.getStackTrace().toString());
            return;
        }

        // HTTPリクエストのヘッダーとメソッドを指定する。openConnection()は必ずプロパティ設定の前に置く
        try {

            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod(strReqMethod);
            connection.setInstanceFollowRedirects(true);
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(10000);
            connection.setDoInput(true);
            connection.setDoOutput(false);// falseの場合、自動的にMethodがGETとなる。TrueはPOSTの時、指定する。
            connection.setRequestProperty("Accept",
                    "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
            connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
            connection.setRequestProperty("Accept-Language", "ja,en-US;q=0.9,en;q=0.8");
            connection.setRequestProperty("Host", "www.example.com");

            System.out.println(connection.getResponseCode());

        } catch (IOException e) {
            // TODO: handle exception
            System.out.println(e.getStackTrace().toString());
            return;
        }

    }

    public void getHtmlString() {

        try {

            // Response code を確認する。 200ならOK
            int iCode = connection.getResponseCode();
            if (iCode == 200) {

                ips = new BufferedInputStream(connection.getInputStream());
                redBuf = ips.readAllBytes();
                connection.disconnect();

            } else {

                // Do Http response Error handling
                System.out.println(connection.getResponseCode());

                return;

            }

        } catch (IOException | IllegalArgumentException ioe) {
            // TODO: handle exception

            System.out.println(ioe.getStackTrace().toString());
            return;
        }

        File f = new File(System.getProperty("user.home") + "/Documents/test.txt");
        try {

            f.createNewFile();

            FileWriter fWrite = new FileWriter(System.getProperty("user.home") + "/Documents/test.txt");
            strResponseBody = new String(redBuf, "UTF-8");
            fWrite.write(strResponseBody);
            fWrite.close();

        } catch (IOException io) {
            // TODO: handle exception
            System.out.println(io.getStackTrace().toString());
            return;
        }

    }

}

```