JavaRush /Курси /Java Server /Читання каталогу книг у Postman

Читання каталогу книг у Postman

Java Server
Рівень 12 , Лекція 2
Відкрита

1. Читання контракту за рівнями

Тепер method, URL, status, headers і body вже не здаються розсипом окремих вкладок. Настав час застосувати це до двох живих сценаріїв зовнішнього каталогу: спершу до пошуку, потім до деталей. Саме тут і видно, навіщо потрібна послідовність status -> headers -> body.

Почнемо з базового алгоритму. Його варто буквально повторювати як мантру. Так, у програмістів теж бувають мантри — тільки замість свічок у них стек-трейс.

flowchart TD
    A[Збираємо запит] --> B[Надсилаємо]
    B --> C[Читаємо status]
    C --> D[Читаємо response headers]
    D --> E[Читаємо body]
    E --> F[Розуміємо форму даних і важливі поля]

І ще одна маленька, але дуже практична табличка. Вона не про «як правильно за стандартом», а про «як не звести себе з розуму»:

Крок Що саме дивимося Що собі кажемо
1 Method «Я точно виконую GET, а не випадково POST
2 URL + path «Я точно потрапив у потрібну кінцеву точку?»
3 Query params «Параметр називається q чи query? Значення закодовано?»
4 Status «Сервер вважає, що все гаразд? (2xx
5 Headers «Це точно JSON? Який Content-Type
6 Body «Яка верхньорівнева форма: об’єкт чи список? Де потрібні поля?»

Якщо ви дотримуватиметеся цього порядку, Postman перестане бути «іграшкою для запитів» і стане інструментом дослідження — як мікроскоп, тільки без обов’язку носити білий халат.

2. Пошук книги: запит у Postman

Зараз ми вперше робимо те, заради чого Postman узагалі з’явився в нашому курсі: досліджуємо зовнішній каталог книг так, щоб потім написати Java-клієнт не «методом тику», а за зрозумілим контрактом. Сценарій пошуку майже завжди виглядає як GET до певного /search (або схожого шляху) з query-параметром: текстом запиту, автором, назвою тощо. Почнемо з найпростішого — пошуку за рядком.

Уявімо, що наш постачальник каталогу (зовнішній сервіс) підтримує запит такого вигляду:

Method: GET
Path: /search
Query: q=<текст запиту>

У Postman це збирається дуже просто:

  1. У випадаючому списку методу обираєте GET.
  2. У поле URL вставляєте повну адресу, наприклад https://catalog.example/search.
  3. На вкладці Params додаєте параметр q зі значенням clean code.

Чому через Params, а не дописувати ?q=... в URL вручну? Бо тоді Postman допоможе з кодуванням пробілів і спецсимволів, а ви не змагатиметеся в тому, хто швидше забуде про %20.

Щоб пов’язати це з нашим майбутнім Java-кодом, корисно навчитися формулювати запит як набір фактів. Майже як DTO — тільки для людини.

// Міні-модель фактів про запит (як «знімок» перед надсиланням)
record RequestFacts(String method, String path, String query) {}

RequestFacts searchFacts = new RequestFacts(
        "GET",         // HTTP-метод
        "/search",     // шлях (path) кінцевої точки
        "q=clean+code" // query-рядок (тут пробіл уже закодовано)
);

// У реальному коді ви, звісно, так запит не надсилаєте — це просто спосіб зафіксувати контракт.
System.out.println(searchFacts);
// RequestFacts[method=GET, path=/search, query=q=clean+code]

Зверніть увагу на тонкість: clean code майже завжди поїде в URL як clean+code або clean%20code. Це не «примха Postman», а нормальне життя URL-кодування. Вам зараз достатньо розуміти одне: якщо ви вводите параметр на вкладці Params, Postman зробить це коректніше, ніж ваш ручний ввід на швидкість.

Ще одна важлива звичка: перед натисканням Send очима пробіжіться по запиту згори вниз і дайте собі три відповіді. Метод той? URL той? Параметр називається правильно? Це займає 3 секунди, а економить іноді 30 хвилин.

3. Пошук книги: читання відповіді

Відповідь на пошук — класична пастка новачка: ви надіслали запит, побачили JSON, зраділи, а потім раптом зʼясувалося, що читали не дані, а «помилку у форматі JSON». Тому йдемо дисципліновано: спочатку статус, потім заголовки, потім тіло. Це як переходити дорогу: спершу дивитеся на всі боки, а вже потім милуєтеся архітектурою будівлі на іншому боці.

Перевіряємо статус

Якщо ви зробили коректний пошук, найчастіше побачите 200 OK. Це означає, що запит оброблено, а тіло відповіді можна сприймати як дані.

Але навіть у сценарії пошуку можливі інші статуси. Ми зараз не розгортаємо цілу матрицю негативних випадків, але мінімум ви повинні вміти не ігнорувати реальність. Якщо побачили 400, значить запит сформовано неправильно (наприклад, параметр називається не q). Якщо побачили 404, значить ви не туди звернулися (шлях не існує). Якщо побачили 500, серверу погано — і це окрема життєва історія.

Headers: перевіряємо Content-Type

Після статусу переходимо на вкладку Headers відповіді. Найкорисніший заголовок для нас сьогодні — Content-Type. Він має підказати формат body.

Типова картина для JSON:

Content-Type: application/json
інколи з уточненням кодування: application/json; charset=utf-8

Якщо Content-Type раптом text/html, ви, найімовірніше, отримали HTML-сторінку помилки. Так, таке буває навіть в API; так, це сумно; так, ви не самі.

Тут важливо сформувати просту звичку: body читаємо лише разом із Content-Type. Це як читати рецепт, не знаючи, чи це інструкція до пирога, чи до пральної машини.

Body: форма даних

Тепер можна дивитися Body. Для пошуку зазвичай характерна форма «об’єкт, усередині якого список елементів плюс метадані». У наших прикладах курсу ми орієнтуватимемося на зрозумілу схему:

{
  "items": [
    { "externalId": "OL12345M", "title": "Clean Code", "author": "Robert C. Martin" }
  ],
  "count": 1
}

Прямо зараз нас цікавлять дві речі.

Перша — верхній рівень. Це об’єкт? Так. Отже, у відповіді є поля, і серед них є масив результатів. Часто новачки очікують «просто масив», але на практиці багато API загортають список в об’єкт, щоб поруч тримати count, page, limit або щось подібне. Ми не занурюємося в пагінацію, але сам патерн «обгортка навколо списку» ви повинні впізнавати.

Друга — структура елемента списку. У кожного елемента є певний зовнішній ID, назва й автор. Це вже майже готовий кандидат на ваш майбутній CatalogBookSearchItem (нормалізований DTO), тільки поки що ми його не пишемо — ми просто фіксуємо факти контракту.

Щоб не розтікатися в абстракцію, ось як можна подумки стисло звести відповідь пошуку до того, що реально корисно для ReadLater Starter:

import java.util.List;

// Міні-DTO для результатів пошуку: лише те, що справді важливо для проєкту
record CatalogSearchItem(String externalId, String title, String author) {}

// У відповіді API це зазвичай масив items, тут ми просто імітуємо його через List
List<CatalogSearchItem> items = List.of(
        new CatalogSearchItem("OL12345M", "Clean Code", "Robert C. Martin")
);

// Важливий момент: ми читаємо дані через зрозумілі поля DTO, а не «пам’ятаємо JSON очима».
System.out.println(items.getFirst().title()); // Clean Code

Це не «код із проєкту» і не «те, що ми вже маємо реалізувати». Це ілюстрація того, як ваш мозок має перекладати JSON-форму в структуру даних. Postman зараз — місце, де ви добуваєте цю форму.

4. Деталі книги: запит за externalId

Після пошуку зазвичай іде другий базовий сценарій: «отримай деталі конкретної книги». Саме це пізніше стане командою catalog details <externalId> у нашому застосунку. Тут важливо, що externalId зазвичай є частиною шляху, а не query-параметром. Так мислять, коли працюють із ресурсами: список шукають через query, а конкретний ресурс читають через path.

Припустімо, контракт має такий вигляд:

Method: GET
Path: /books/{externalId}

У Postman ви робите майже те саме, тільки URL інший:

  1. Метод усе ще GET.
  2. URL: https://catalog.example/books/OL12345M (де OL12345M — значення з результату пошуку).
  3. Натискаєте Send.

Поки що достатньо вручну підставити externalId у path й побачити, що деталі — це окрема кінцева точка з окремою формою відповіді.

Добра звичка — тримати в голові мінімальний знімок контракту для кожного сценарію. Він має бути настільки простим, щоб поміщатися в один рядок.

// «Знімок контракту» для сценарію: щоб швидко перевіряти себе очима
record ContractSnapshot(String scenario, String method, String path) {}

ContractSnapshot detailsSnapshot = new ContractSnapshot(
        "Деталі книги",        // сценарій
        "GET",                 // метод
        "/books/{externalId}"  // path із плейсхолдером
);

// Цей вивід — не «логування в проді», а спосіб закріпити форму запиту.
System.out.println(detailsSnapshot);
// ContractSnapshot[scenario=Деталі книги, method=GET, path=/books/{externalId}]

І ще одна тонкість: коли ви вручну підставляєте externalId у path, ви перевіряєте себе на уважність. Так, це звучить як жарт, але це не жарт: половина «API не працює» у новачка — це «я вставив не той ID» або «я вставив ID разом із пробілом». Postman лікує це уважністю, а не магією.

5. Деталі книги: читання відповіді

Зараз буде момент, який зазвичай клацає в голові й робить життя простішим: пошук повертає список, а деталі повертають об’єкт. Це здається очевидним, доки не почнеш писати код і раптом не спробуєш розпарсити об’єкт як список або навпаки. Тому ми знову читаємо: status → headers → body, але додатково тримаємо в голові очікувану форму.

Статус: зміст 200 і 404

Якщо ви запросили коректний наявний externalId, ви, найімовірніше, отримаєте 200 OK. Якщо externalId хибний або книги немає, цілком можливо побачити 404 Not Found. Тут нам достатньо простого факту: у сценарії деталей легко отримати 404, якщо externalId хибний або ресурсу не існує.

Якщо вам хочеться «спростити» й завжди дивитися лише на body — не треба. Це як оцінювати здоров’я людини за кольором футболки: іноді збіжиться, але зазвичай не працює.

Headers: Content-Type знову головний

На деталях ви знову перевіряєте Content-Type. В ідеалі це теж application/json. Якщо це не JSON, ви, можливо, потрапили в інший формат відповіді або в помилку.

Так, інколи постачальники повертають помилки теж у JSON. Але ви хоча б знатимете, що це помилка, бо статус не 2xx.

Body: об’єкт і необов’язкові поля

У деталях ви зазвичай побачите об’єкт, де полів більше, ніж у пошуку. Приблизна форма може бути такою:

{
  "externalId": "OL12345M",
  "title": "Clean Code",
  "author": "Robert C. Martin",
  "description": "A handbook of agile software craftsmanship",
  "publishYear": 2008
}

І ось тут дуже корисно згадати рівень про JSON-нюанси: поле може бути відсутнім, може бути null, може бути порожнім рядком. У деталях це особливо часта історія: опис книги іноді є, іноді ні.

Для ReadLater Starter нам на перших кроках зазвичай достатньо невеликої «нормалізованої картки»: ID, title, author, а можливо, ще й короткого опису. Решта — приємні бонуси, але не обов’язковий мінімум.

Можна подумки прикинути, як би виглядав ваш майбутній нормалізований DTO для деталей:

// DTO для деталей: у проєкті ви вирішите, які поля брати, але форма мислиться ось так
record CatalogBookDetails(
        String externalId,
        String title,
        String author,
        String description
) {}

CatalogBookDetails details = new CatalogBookDetails(
        "OL12345M",
        "Clean Code",
        "Robert C. Martin",
        null // поле description може бути відсутнім у відповіді або приходити як null
);

// Важлива перевірка для себе: код має бути готовим до відсутніх даних.
System.out.println(details.description()); // null

Знову ж таки: це не «пишемо код проєкту просто зараз», це вправа для мозку. Postman — місце, де ви вирішуєте: «які поля реально є» і «які поля нам справді потрібні».

6. Контрактні факти проти «випадкових даних»

Коли ви дивитеся на відповідь зовнішнього API, дуже хочеться записати все. Це природно: «Ого, скільки полів! Треба зберегти все, раптом згодиться!» От тільки це пастка. У реальному backend-житті поле може з’явитися, зникнути, змінити назву, стати null, почати приходити в іншому форматі — і якщо ви побудуєте логіку на «всіх полях підряд», станете заручником чужого контракту.

Нам потрібна дисципліна: відрізняти форму і ключові поля від випадкових значень конкретної відповіді.

Контрактні факти — це такі речі, як:

— метод і шлях (GET /search, GET /books/{externalId});
— імена query-параметрів (наприклад, q);
— статус успішної відповіді (зазвичай 200);
Content-Type відповіді;
— верхньорівнева форма body (об’єкт із масивом items і полем count або об’єкт із полями книги);
— ключові поля, які ми точно використовуємо в проєкті (externalId, title, author).

Випадкові дані — це конкретні значення, на кшталт «ось у цієї книги publishYear=2008» або «ось у цієї відповіді count=734». Це корисно як приклад, але не як контракт.

Щоб закріпити, корисно зробити ще один короткий знімок контракту — уже не про конкретні значення, а про форму сценарію. Наприклад так: Search | GET /search?q=... | 200 | Object { items: Array, count: number }.

Коли таких знімків кілька, карта контракту складається майже сама собою. Після цього запити вже хочеться зберігати не як випадкові збіги, а як повторювані сценарії.

7. Навігація великим JSON у Postman

Реальні відповіді зовнішніх API часто більші, ніж хотілося б. Не тому, що розробники шкідливі, а тому, що різним клієнтам потрібні різні поля. Postman дає кілька способів вижити, і саме час навчитися ними користуватися, бо інакше ви скролитимете JSON, мов стрічку соцмереж: швидко, безглуздо й із відчуттям «куди подівся мій вечір».

Перше, що ви робите в response body, — обираєте Pretty (якщо Postman показує Raw за замовчуванням) і переконуєтеся, що формат розпізнано як JSON. Pretty — ваш друг: він розставляє відступи й робить вкладеність видимою.

Друге — ви читаєте відповідь згори вниз за структурою, а не очима, що вчепилися в перше гарне поле. Вам потрібен верхній рівень: які поля є в корені? Де масив результатів? Як він називається? Чи є count?

Третє — ви використовуєте пошук по відповіді (зазвичай звичайний Ctrl+F / Cmd+F). Це не «читерство», а нормальна робота. Якщо вам важливо знайти externalId або title, ви шукаєте за ключем і дивитеся, де він трапляється та в якому контексті.

Дуже корисний мініприйом: якщо ви бачите, що поле є не в усіх елементів, це одразу сигнал необов’язковості. Наприклад, у деталях description може бути в однієї книги й бути відсутнім в іншої. І це нормально — просто важливо помітити це завчасно, а не в момент, коли ваш майбутній код раптом отримує null.

Можна навіть у голові тримати таку мікросхему читання JSON:

flowchart TD
    A[Корінь JSON] --> B[Шукаємо верхньорівневі поля]
    B --> C[Знаходимо список результатів або об’єкт книги]
    C --> D[Виділяємо 3-5 потрібних полів]
    D --> E["Позначаємо необов’язкові поля"]

І так, тут є місце для самоіронії: якщо ви ловите себе на думці «я вже 5 хвилин читаю JSON і нічого не зрозумів», значить ви почали читати дані, а не структуру. Поверніться до кореня й знову запитайте: об’єкт чи масив? Які ключі? Де список? Де count? Це часто «перезавантажує» розуміння.

8. Типові помилки під час читання зовнішнього API в Postman

Помилка №1: дивитися на body, не подивившись на status.
Це найчастіша і найобразливіша помилка. Ви бачите JSON і думаєте, що все добре, а потім виявляється, що статус 404, а в body просто повідомлення про помилку. Лікується однією звичкою: спочатку статус, потім усе інше. Хочете — вважайте це своїм маленьким персональним ритуалом безпеки.

Помилка №2: ігнорувати Content-Type і вірити, що «раз Postman щось показав, значить це JSON».
Postman справді багато чого вміє, але він не зобов’язаний рятувати вас від неправильного формату. Якщо Content-Type не application/json, будьте обережні: ви можете дивитися на HTML-помилку або на інший формат даних. У майбутньому це допоможе вам не писати «парсер усього на світі», а акуратно перевіряти контракт.

Помилка №3: плутати query-параметри та path, особливо в сценарії деталей.
У пошуку параметри майже завжди живуть у query (?q=...). У деталях ідентифікатор майже завжди живе в path (/books/{id}). Якщо ви починаєте тягати ID туди-сюди хаотично, ви втрачаєте ресурсне мислення, а потім дивуєтеся, що API «капризний». Насправді він не капризний — він просто чекає зрозумілого контракту.

Помилка №4: намагатися запам’ятати або «зафіксувати» всю відповідь цілком.
Це призводить до того, що ви починаєте будувати очікування на випадкових полях і випадкових значеннях. На рівні нашого проєкту потрібно значно менше: кілька ключових полів і форма відповіді. Решту можна залишити як «цікаво, але не обов’язково». І це не лінощі, а дисципліна: ми готуємо контракт під конкретну задачу ReadLater Starter, а не пишемо енциклопедію постачальника.

Помилка №5: вважати одну успішну відповідь «повним розумінням контракту».
Одна відповідь — це лише один приклад. Контракт краще видно, якщо ви робите два-три запити: шукаєте різні книги, відкриваєте різні деталі, помічаєте, які поля стабільні, а які іноді зникають. Це не системний негативний сценарій і не тестування, а просто доросла звичка «перевірити форму на кількох прикладах», перш ніж писати код.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ