JavaRush /Курсы /Java Server /Первый GET к внешн...

Первый GET к внешнему каталогу

Java Server
14 уровень , 2 лекция
Открыта

1. Сценарий GET: поиск и контракт

Очень хочется сразу сделать «универсальный клиент на все времена», но это типичная ловушка начинающего: вместо одного работающего вызова появляется десять абстракций и ноль результата. Поэтому мы делаем то, что обычно делает нормальный backend‑разработчик в начале интеграции: берём один уже изученный в Postman запрос и повторяем его в коде максимально буквально, чтобы доказать простую вещь — «мы реально умеем сходить в сеть и получить ответ».

В рамках ReadLater Starter самый естественный стартовый сценарий — поиск. Пользователь вводит запрос (например, clean code), а мы отправляем GET на endpoint поиска внешнего каталога и получаем JSON со списком результатов. Пока мы не проектируем формат вывода, не маппим JSON в DTO и не делаем «умный UI» — наша цель намного скромнее, но методически очень важна: сделать первый честный outbound HTTP‑вызов.

Четыре JDK‑кирпичика уже на столе: URI, HttpRequest, HttpClient и HttpResponse<String>. Теперь важно не обсуждать их по отдельности, а собрать из них один рабочий GET к каталогу.

Давайте зафиксируем (словами), что нам нужно повторить в Java‑коде:

  • HTTP‑метод: GET (мы ничего не создаём и не изменяем, только читаем данные).
  • Адрес: baseUrl + path + query (конкретный вид вы берёте из своей Postman collection).
  • Заголовок: Accept: application/json (чтобы явно сказать: «ожидаю JSON»).
  • Тело запроса: отсутствует (это нормально для GET).

Чтобы не метаться между разными вариантами адреса, зафиксируем один рабочий запрос к каталогу:

GET https://openlibrary.org/search.json?q=clean%20code
Accept: application/json

Этого достаточно, чтобы увидеть механику первого вызова и потом встроить его в приложение.

Для визуальной фиксации можно держать такую «мини‑схему» того, что мы сейчас строим:

flowchart LR
    A["ReadLater Starter (Java app)"] -->|"HttpRequest (GET + URI + headers)"| B["HttpClient.send()"]
    B --> C["External Book Catalog API"]
    C -->|"HttpResponse<String> status + headers + raw JSON"| A

2. URL в URI

Адрес мы просто берём из уже проверенного search‑запроса и превращаем в URI. Сейчас не нужен отдельный заход в сборку URI из частей: нам важен рабочий адрес, а не новая порция деталей про кодирование query‑параметров.

Минимальные импорты для такого вызова остаются такими:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

Теперь подготавливаем адрес:

// Берём тот же URL, который уже проверили руками
String url = "https://openlibrary.org/search.json?q=clean%20code";

// Превращаем строку в URI: дальше это уже адрес запроса, а не просто текст
URI uri = URI.create(url);

%20 здесь просто фиксирует пробел в query. Нам сейчас важен сам факт: в код уходит тот же рабочий адрес, который уже выдержал проверку в Postman.

3. Сборка HttpRequest: GET и Accept

Теперь переносим в код саму договорённость с внешним каталогом: тот же адрес, тот же GET, тот же Accept: application/json.

HttpRequest request = HttpRequest.newBuilder(uri) // на какой адрес целимся
        .header("Accept", "application/json")     // явно просим JSON
        .GET()                                    // фиксируем метод
        .build();                                 // собираем объект запроса

На этом шаге ничего «умнее» не нужно. Accept фиксирует ожидаемый формат ответа, а GET оставляет запрос без тела — ровно так, как и должно быть у поискового сценария.

4. Отправка: HttpClient.send(...)

Остаётся один раз создать клиента, отправить запрос и прочитать тело как строку. Этого достаточно, чтобы увидеть raw JSON и не утащить первый вызов в преждевременную архитектуру.

Создаём клиент:

HttpClient client = HttpClient.newHttpClient(); // переиспользуемый инструмент для отправки запросов

Теперь отправляем запрос. Метод send(...) принимает два аргумента: сам HttpRequest и body handler. Body handler отвечает на вопрос: «В каком виде я хочу получить тело ответа?».

Сегодня — в виде строки:

HttpResponse<String> response = client.send(
        request, // что отправляем
        HttpResponse.BodyHandlers.ofString() // хотим получить body как строку
);

send(...) здесь блокирующий, а BodyHandlers.ofString() даёт нам HttpResponse<String>. Для первого честного outbound‑вызова этого более чем достаточно: ответ уже можно проверить глазами, не прыгая пока в DTO и transport‑layer cleanup.

5. Метод для raw JSON

Когда кусочки по отдельности уже понятны, полезно собрать их в один маленький spike‑метод. Он не претендует на финальную структуру проекта: сейчас это просто end‑to‑end шаг, после которого вызов будет легко перенести в ReadLaterApplication.

Сделаем минимальный метод, который возвращает raw JSON строкой. Обратите внимание: это ещё не «клиентский слой», не транспортная архитектура и не финальный дизайн. Это просто аккуратная упаковка сегодняшнего шага.

static String fetchSearchJson(HttpClient client) throws Exception {
    // URL можно держать в конфиге, но сейчас — просто фиксируем рабочий пример
    String url = "https://openlibrary.org/search.json?q=clean%20code";

    HttpRequest request = HttpRequest.newBuilder(URI.create(url)) // адрес + сборка запроса
            .header("Accept", "application/json")                 // ожидаем JSON
            .GET()                                                // GET без тела
            .build();

    // Синхронно отправляем запрос и получаем тело ответа строкой
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

    return response.body(); // raw JSON, без парсинга
}

Код получился специально «честным», даже слегка многословным. Это нормально. Мы сейчас учимся, и наша цель — чтобы через две недели вы могли открыть чужой проект и сразу понять: «ага, вот адрес, вот заголовок, вот метод, вот отправка». В backend‑коде читаемость почти всегда выигрывает у микро‑экономии строк.

Если вам не нравится throws Exception, это тоже нормальная реакция. В реальной жизни вы стараетесь не бросаться Exception куда попало. Но в учебном happy‑path шаге это позволяет не размазывать внимание на обработку ошибок, которая (по‑хорошему) должна быть отдельной понятной темой.

6. Минимальная проверка: JSON пришёл

Сейчас будет максимально «приземлённая» проверка. Мы не анализируем статус, не разбираем заголовки, не парсим JSON. Мы делаем самое простое: вызываем метод и выводим в консоль небольшой фрагмент строки, чтобы убедиться, что это действительно JSON, а не HTML‑страница «403 Forbidden» или «Captcha, докажи что ты не робот».

Временный код для проверки может выглядеть так:

HttpClient client = HttpClient.newHttpClient(); // создаём клиент один раз
String json = fetchSearchJson(client);          // делаем реальный outbound GET

System.out.println("JSON length = " + json.length()); // быстрая проверка, что что-то пришло

Если хочется чуть нагляднее, можно вывести preview первых символов:

// Не печатаем весь ответ: он может быть огромным
String preview = json.substring(0, Math.min(json.length(), 200));
System.out.println(preview); // первые ~200 символов (должно начинаться похоже на JSON)

Почему preview — хорошая идея even in учебном коде? Потому что полный JSON‑ответ может быть большим, и консоль превращается в «водопад текста», в котором сложно увидеть, что вообще произошло. Нам сейчас достаточно понять, что строка пришла и похожа на ожидаемый формат.

Пока достаточно зафиксировать победу: первый внешний GET выполнен, и raw JSON у нас в руках. Следующий инженерный вопрос возникает сам собой: как читать этот ответ целиком — по статусу, заголовкам и телу, а не как одну длинную строку.

7. Типичные ошибки первого GET

Ошибка №1: URL копируется «на глаз» и в нём остаются пробелы или лишние символы.
Чаще всего это выглядит так: вы берёте URL из документации, вставляете в строку, и где-то незаметно появляется пробел, перенос строки или неэкранированный символ. В итоге URI.create(...) может кинуть исключение, либо запрос уйдёт не туда. Лечится это скучно, но эффективно: копируйте URL из Postman и сначала просто распечатайте строку, чтобы увидеть, что вы реально отправляете.

Ошибка №2: забыли выбрать BodyHandlers.ofString(...) и удивляетесь типам.
Новички иногда пытаются сделать client.send(request) без handler’а или ожидают, что body() «само станет JSON‑объектом». В JDK всё честно: вы выбираете, как читать body. Если вы выбрали ofString(), то и получаете строку. JSON‑объект появится позже, когда вы сознательно начнёте маппинг через Jackson.

Ошибка №3: ожидание, что send() “всегда возвращает response”, и непонимание checked exceptions.
Если сеть недоступна, DNS не резолвится, провайдер временно лежит или вы просто выключили интернет, send() не вернёт «плохой ответ» — он кинет исключение. Это нормально. Сегодня мы не строим сложную обработку ошибок, но важно не пугаться: исключение на этом шаге не означает «Java плохая», это означает «мир за пределами вашего компьютера не обязан работать стабильно».

Ошибка №4: отсутствует заголовок Accept, а API возвращает неожиданный формат.
Иногда внешний сервис может по умолчанию отдавать HTML (страницу), XML или какую-то «человеческую версию» ответа, если не попросить JSON явно. Это не всегда так, но встречается. Поэтому Accept: application/json — полезная дисциплина, особенно когда вы ещё только учитесь и хотите снизить количество загадок.

Ошибка №5: попытка сразу “улучшить архитектуру”, не убедившись, что базовый вызов вообще работает.
Очень заманчиво на первом же шаге создать пять классов, интерфейс, фабрику клиентов и универсальный метод doRequest(...). А потом выясняется, что URL неверный, и вы полчаса дебажите собственную архитектуру вместо того, чтобы поправить одну строку. На старте лучший друг — простота: сначала один рабочий GET, потом аккуратное улучшение.

1
Задача
Java Server, 14 уровень, 2 лекция
Недоступна
Первый `GET` к каталогу книг
Первый `GET` к каталогу книг
1
Задача
Java Server, 14 уровень, 2 лекция
Недоступна
Поисковая фраза из аргументов и raw JSON
Поисковая фраза из аргументов и raw JSON
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ