JavaRush /Курси /Java Server /GET: читання колекц...

GET: читання колекції та ресурсу

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

1. GET як домовленість про читання

Коли шлях перестає бути командою і стає іменем ресурсу, перше природне запитання — як читати цей ресурс.

Коли ви вперше бачите HTTP, GET часто здається чимось на кшталт «ну, це коли я просто відкриваю посилання». І так, браузер справді найчастіше надсилає GET. Але в бекенд-мисленні GET — це не «метод за замовчуванням», а домовленість про читання: клієнт просить сервер показати стан ресурсу і очікує, що сам запит нічого не змінить, не створить, не оновить і не видалить.

Уявіть бібліотеку. GET — це коли ви підійшли до бібліотекаря і сказали: «Покажіть, будь ласка, що у вас є на тему “чистий код”». Ви не просите бібліотекаря переписати книгу, не просите додати нову книгу до фонду — ви просто хочете інформацію. В HTTP логіка та сама: GET — про отримати дані, а не про змінити дані.

Важливо ще ось що: на практиці GET буде одним із найчастіших запитів у вашому коді. Навіть коли ви робите CRUD, операцій читання зазвичай більше, ніж змін. Тому якщо ви навчитеся правильно мислити GET уже зараз, вам потім буде легше і з перевіркою в Postman, і з написанням Java-клієнта, і з розумінням серверної частини.

Мінінагадування у вигляді «рядка запиту» — просто щоб тримати перед очима форму method + path:

// Рядок запиту (request line) у найпростішому вигляді: "метод + шлях"
String requestLine = "GET /reading-list";

// Друкуємо рівно те, що «йде» в запиті на цьому рівні абстракції
System.out.println(requestLine); // GET /reading-list

Тут немає жодної магії: це просто зрозуміла для людини форма «метод + шлях».

2. Ресурс: колекція та елемент

Для GET нам потрібне лише коротке нагадування: ресурс зазвичай живе у двох формах — як колекція та як окремий елемент. У ReadLater це все та сама знайома пара: /reading-list означає весь список, а /reading-list/42 — один конкретний елемент.

Один і той самий GET працює в обох випадках, але ціль різна: або читаємо набір цілком, або читаємо обʼєкт за id. Цієї різниці вже досить, щоб далі не вигадувати /getReadingList і /getItem: шлях сам показує, що саме ми читаємо.

// Шлях до колекції: «усі елементи»
String collectionPath = "/reading-list";

// Шлях до одного елемента: той самий шлях + ідентифікатор наприкінці
String itemPath = "/reading-list/42";

System.out.println(collectionPath); // /reading-list
System.out.println(itemPath);       // /reading-list/42

3. GET колекції: «дай список»

Коли клієнт робить GET до шляху колекції, він каже просту річ: «Покажи мені набір елементів цього типу». Це може бути порожній набір, може бути великий набір, але зміст один: клієнт читає колекцію. У домені ReadLater це буде читання вашого списку читання цілком: що ви планували, що читаєте, що вже завершили.

Важливо не намагатися вбудувати дію в URL. Типова для новачків звичка — робити так: /getReadingList. Але GET уже означає «get». Виходить «get-get-reading-list», тобто подвійне «покажи-покажи». У програмуванні такі речі зазвичай закінчуються тим, що десь з’являється getGetGet(), і далі вже пізно.

У термінах запитів це виглядає так:

// Читання всієї колекції без умов
String listAll = "GET /reading-list";

// Читання колекції з фільтром через query-параметри
String listPlanned = "GET /reading-list?status=PLANNED";

System.out.println(listAll);     // GET /reading-list
System.out.println(listPlanned); // GET /reading-list?status=PLANNED

Ми поки не обговорюємо, як саме сервер відповідає — які статуси, які заголовки, який JSON. Нам зараз важливий сенс: обидва рядки — це читання колекції, просто у другому варіанті клієнт просить не все підряд, а лише за умовою.

Щоб відчути це «на пальцях» без сервера і без мережі, можна уявити, що колекція вже лежить у пам’яті як список, а читання колекції — це просто «повернути весь список»:

import java.util.List;

// Колекція «ніби вже існує», і ми просто дивимося на неї
List<String> titles = List.of("Clean Code", "Effective Java", "Refactoring");

// size() — це «скільки елементів у колекції»
System.out.println(titles.size()); // 3

// Друк списку — це «покажи всі елементи»
System.out.println(titles);        // [Clean Code, Effective Java, Refactoring]

Це не реалізація API — ще зарано, — а просто аналогія: запит до колекції означає «дай список».

4. GET елемента: за ідентифікатором

Тепер друга форма ресурсу: окремий елемент колекції. Коли клієнт робить GET /reading-list/42, він каже: «Покажи мені елемент списку читання з ідентифікатором 42». Тут ключова думка така: ідентифікатор обирає обʼєкт, а не дію. Це не команда «прочитай 42», це адреса конкретного ресурсу.

Ідентифікатор може бути числом, як у нашому локальному списку читання, а може бути рядком, як у зовнішньому каталозі книг, де трапляються зовнішні ID на кшталт OL12345M. Але принцип один: наприкінці шляху стоїть ключ, який відрізняє один елемент від іншого.

Знову на рівні «рядка запиту»:

// Два запити до двох різних елементів однієї й тієї самої колекції
String getItem42 = "GET /reading-list/42";
String getItem43 = "GET /reading-list/43";

System.out.println(getItem42); // GET /reading-list/42
System.out.println(getItem43); // GET /reading-list/43

Якщо продовжити аналогію з in-memory-даними, читання одного ресурсу — це як доступ до Map за ключем:

import java.util.Map;

// «Ідентифікатор -> значення» як проста модель ресурсу в памʼяті
Map<Long, String> readingList = Map.of(
        42L, "Clean Code",
        43L, "Effective Java"
);

// get(42L) — це «дай елемент за ID», дуже схоже за змістом на /reading-list/42
System.out.println(readingList.get(42L)); // Clean Code

Ми не будуємо репозиторій і не пишемо CRUD — ми просто закріплюємо сенс. GET до одного ресурсу — це «дай значення за ключем».

5. GET з query params: фільтр колекції

З query-параметрами в GET найчастіше й починається плутанина. Здається, що якщо ми додали ?щось=щось, то вже йдеться не про колекцію, а про щось інше. Насправді в нашій прикладній моделі простіше думати так: query-параметри уточнюють запит до колекції, але не перетворюють його на запит одного конкретного елемента.

Порівняйте два речення — і читайте їх як людина, а не як компілятор:

GET /reading-list/42 — «дай елемент з ID 42».

GET /reading-list?title=clean — «дай елементи зі списку читання, у назві яких є “clean”».

У другому випадку ми наперед не знаємо, скільки елементів підійде за умовою: нуль, один, два, десять. Тому це залишається читанням колекції, просто з фільтром.

У вигляді простого конструктора URL — на рівні рядків — це можна побачити так:

// Базовий шлях колекції
String basePath = "/reading-list";

// «Хвіст» query-параметрів (у реальному світі він часто збирається з різних частин)
String query = "status=PLANNED&title=clean";

// Склеювання дає той самий вигляд URL, який буде в запиті
String filtered = basePath + "?" + query;

System.out.println(filtered); // /reading-list?status=PLANNED&title=clean

А щоб закріпити сенс фільтрації на звичному Java-коді, знову без сервера, уявіть, що ми фільтруємо колекцію:

import java.util.List;

// Вихідні дані «ніби вже отримали»
List<String> titles = List.of("Clean Code", "Clean Architecture", "Effective Java");

// Фільтрація за підрядком: це не вибір за ID, а вибір підмножини
var filtered = titles.stream()
        // Приводимо до нижнього регістру, щоб пошук був простішим
        .filter(t -> t.toLowerCase().contains("clean"))
        .toList();

System.out.println(filtered); // [Clean Code, Clean Architecture]

Фільтрація не обирає один конкретний елемент за ID — вона обирає підмножину колекції.

6. Карта GET-сценаріїв ReadLater

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

Нижче — табличка з прикладами. Ми спеціально тримаємо її простою і не обговорюємо тут статуси та заголовки.

Що хоче клієнт Який ресурс читаємо Приклад запиту Людський сенс
Переглянути весь список читання Колекція GET /reading-list «Покажи все, що є у моєму списку»
Переглянути один елемент списку Один ресурс GET /reading-list/42 «Покажи елемент № 42»
Показати тільки заплановане Колекція (з фільтром) GET /reading-list?status=PLANNED «Покажи тільки заплановане»
Знайти за частиною назви Колекція (з фільтром) GET /reading-list?title=clean «Покажи, де в назві є clean»

І додамо два приклади для зовнішнього каталогу книг. У нас далі буде етап Catalog Client, але вже зараз форми зрозумілі. Тут ми не фіксуємо реальний шлях провайдера, а просто показуємо загальну ідею:

// Пошук: читання колекції з умовою (query-параметр)
String catalogSearch = "GET /catalog/books?q=clean code";

// Деталі: читання одного ресурсу за ідентифікатором
String catalogDetails = "GET /catalog/books/OL12345M";

System.out.println(catalogSearch);  // GET /catalog/books?q=clean code
System.out.println(catalogDetails); // GET /catalog/books/OL12345M

Сенс такий самий: пошук — це читання колекції з умовою, а деталі — це читання одного ресурсу за ідентифікатором.

7. Типові помилки з GET

Помилки з GET зазвичай виглядають нешкідливо, тому що «ну, запит же відправився, що може піти не так». Проблема в тому, що неправильні форми GET майже завжди роблять API менш передбачуваним, а отже — менш зручним і для клієнта, і для вас через пару тижнів, коли ви забудете, як тут було задумано.

Помилка №1: додавати дієслово в шлях і дублювати зміст методу.
Коли ви пишете /getReadingList, ви фактично повідомляєте двічі одне й те саме: метод GET уже говорить «прочитай», і шлях теж намагається сказати «прочитай». Набагато стійкіша звичка — «шлях = імʼя ресурсу», а «дія = метод». У підсумку це економить нерви, бо вам не потрібно вигадувати окремі шляхи на кожну дію.

Помилка №2: плутати вибір одного елемента і фільтрацію колекції.
GET /reading-list/42 і GET /reading-list?title=clean — це два різні сценарії. Перший передбачає, що клієнт знає ідентифікатор і хоче конкретний обʼєкт. Другий передбачає, що клієнт хоче підібрати кілька придатних обʼєктів. Якщо змішати ці сенси, ви дуже швидко прийдете до дивних адрес на кшталт /reading-list/clean, де незрозуміло: clean — це ID чи фільтр?

Помилка №3: використовувати GET як «універсальну кнопку запуску дії».
Іноді хочеться зробити «зручно»: /reading-list/remove/42 або /reading-list/42/delete. Але ви тим самим ламаєте ресурсну модель і перетворюєте API на набір команд. Сьогодні ми спеціально тримаємося правила: GET — це читання. Про створення, заміну, часткову зміну та видалення ми поговоримо в окремих лекціях.

Помилка №4: вважати, що query-параметри перетворюють запит на «інший ресурс», і починати плодити ендпоінти.
Часто новачки роблять так: якщо потрібно «показати заплановане», вони створюють /reading-list-planned, а якщо потрібно «показати finished» — /reading-list-finished. Це працює, але дуже швидко перетворюється на зоопарк адрес. Набагато простіше зберегти один шлях колекції й уточнювати вибір через query-параметри в розумних межах.

Помилка №5: намагатися заздалегідь «ідеально спроєктувати всі варіанти читання» і ускладнити API раніше часу.
На старті корисніше триматися базових форм: колекція, елемент, прості фільтри. Якщо ви почнете будувати суперрозумний DSL для пошуку одразу, ви витратите сили не на розуміння HTTP, а на винахід мови запитів.

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