Pull to refresh

Особенности HttpUrlConnection из java.net

Reading time4 min
Views39K
Здравствуйте,

сегодня постараюсь рассказать о том, как можно отправить запрос и прочитать ответ от HTTP сервера, используя URLConnection из библиотеки JRE.

Сейчас изучаем Java в онлайн режиме. Вся наша команда использует Slack для работы и общения. Для получения информации о пользователях, используем Slack API. Чтобы долго не рассказывать про сам API (это тема для отдельной статьи), скажу коротко: Slack API построен на HTTP протоколе, для получения информации о пользователях, нужно отправить запрос с URI в котором должно быть имя метода из API на host-адресс api.slack.com Вот список некторых методов:
  • users.list
  • chat.postMessage
  • conversations.create
  • files.upload
  • im.open

Для получения списка юзеро нужен метод users.list. Формируем URI — /api/users.list в теле запроса должен быть токен аутентификации в форме application/x-www-form-urlencoded, то есть запрос должен выглядеть примерно так (но есть один нюанс который будет ниже):

GET /users.list HTTP/1.1
Content-Type: application/x-www-form-urlencoded

token=xoxp-1234567890-098765-4321-a1b2c3d4e5


Я знал про библиотеку Apache HttpComponents, но для иследовательских целей будем использовать средства доступные в стандартной библиотеке Java 8, а именно имплементацию java.net.URLConnection.

Для получений сущности URLConnection нужно использовать объект класса java.net.URL, его конструктор принимает тип String где помимо всего должен быть указан протокол – в нашем случае https.

После получения сущности URL, вызываем метод openConnection() который возвратит нам сущность HttpsUrlConnection.

String url = “https://slack.com/api/users.list”;
URLConnection connection = new URL(url).openConnection();

При этом нужно обработать или пробросить MalformedUrlException и IOException.

После этого переменная connection будет хранить ссылку на объект HttpsUrlConnectionImpl. По умолчанию будет формироваться GET-запрос, для того чтобы добавить Header используем метод setRequestProperty(), который принимает key и value. Нам здесь нужно установить Content-Type который имеет значение application/x-www-form-urlencoded. Ну, и мы это делаем!
connection.setRequestProperty(“Content-Type”, “application/x-www-form-urlencoded”);

Теперь осталось только отправить запрос записав в тело наш токен и лимит. Для этого нужно установить поле doOutput объекта connection в положение true, используя метод setDoOutput();

connection.setDoOutput(true);

Далее самая интересная часть — нужно как-то передать наше тело запроса в OutputStream. Будем использовать OutputStreamWriter:

OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());

Есть один нюанс: после того, как мы вызвали метод getOutputStream() метод запроса меняется на POST, так как GET не предусматривает наличие request body, но благо что slack ни ставит твёрдое ограничение на метод поэтому всё было хорошо. И поэтому GET-запрос должен выглядеть так:
GET /users.list?token=xoxp-1234567890-098765-4321-a1b2c3d4e5&limit=100 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

Но я не стал уже переделывать. И вместо этого наш запрос получился таким:

POST /users.list HTTP/1.1
Content-Type: application/x-www-form-urlencoded

token=xoxp-1234567890-098765-4321-a1b2c3d4e5

(*некоторые headers ставятся самим HttpsUrlConnection и здесь они отсутствуют)

И так чтоб записать наше тело запроса пользуемся write();.
String reqBody = “token=xoxp-1234567890-098765-4321-a1b2c3d4e5&limit=100”;
writer.write(reqBody);
writer.close();

После этого наш запрос будет отправлен, и мы можем прочитать полученный ответ. Важно закрывать OutputStream или делать flush(), перед тем как получать InputStream, иначе данные не уйдут из буффера(как альтернатива можно использовать PrintStream — в методе println() по умолчанию вызывается flush()). Для чтение использовал BufferedReader:
StringBuilder respBody = new StringBuilder();
BufferedReader reader = new BufferedReader(connection.getInputStream());
reader.lines().forEach(l -> respBody.append(l + “\r\n”);
reader.close();

(*используем lines() чтобы получить Stream на выходе; \r\n – символ CRLF – вставляет переход на новую строку)

И, если мы успешно проходим аутентификацию, переменная respBody должна хранить наш ответ от сервера, что в нашем случае является JSON объектом. После этого, его можно отправлять на следующий этап обработки.

После некоторой оптимизации всё выглядет так:

package main.java.com.bilichenko.learning;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.stream.Collectors;

public class SlackClient {

    private static final String HOST = "https://api.slack.com";
    private static final String GET_USERS_URI = "/api/users.list";

    private static final String TOKEN = "xx-ooo-YOUR-TOKEN-HERE";

    public static void main(String[] args) throws IOException {
        SlackClient slackClient = new SlackClient();
        System.out.println(slackClient.getRawResponse(HOST + GET_USERS_URI,
                "application/x-www-form-urlencoded",
                "token=" + TOKEN).orElse("no response"));
    }

    public Optional<String> getRawResponse(String url, String contentType, String requestBody)
            throws MalformedURLException, IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setRequestProperty("Content-Type", contentType);
        connection.setConnectTimeout(10000);
        connection.setRequestMethod("POST");

        connection.setDoOutput(true);

        try(OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream())) {
            writer.write(requestBody);
        }

        if (connection.getResponseCode() != 200) {
            System.err.println("connection failed");
            return Optional.empty();
        }

        try(BufferedReader reader = new BufferedReader(
                new InputStreamReader(connection.getInputStream(), Charset.forName("utf-8")))) {
            return Optional.of(reader.lines().collect(Collectors.joining(System.lineSeparator())));
        }
    }
}

Надеюсь, было полезно!
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+6
Comments9

Articles