Повний огляд REST: нюанси, поради, приклади
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті
Привіт, я Senior Java Developer Валентин Вівчарик i сьогодні хочу зачепити доволі обширну та важливу тему, а саме — REST. Я детально розповім про нюанси роботи з REST та дам поради щодо взаємодії з ним.
REST (REpresentational State Transfer) — це архітектурний стиль для розподілених гіпермедійних систем, який Рой Філдінг вперше представив у 2000 році в своїй знаменитій дисертації. З того часу він став одним із найбільш широко використовуваних підходів для створення вебінтерфейсів API (інтерфейсів прикладного програмування).
REST — це не протокол чи стандарт, це архітектурний стиль, а точніше — один з наявних типів синхронної комунікації вебсервісів. На етапі розробки розробники API можуть реалізувати REST різними способами.
Як і інші архітектурні стилі, REST також має свої керівні принципи та обмеження. Ці принципи мають бути дотримані, якщо інтерфейс служби має називатися RESTful.
Принципи REST
Ресурси
Ресурси у контексті REST представляють будь-який об’єкт або дані, з якими взаємодіє клієнт через сервер. Ресурс може бути будь-чим: користувач, файл, замовлення, продукт тощо. У REST API ресурси ідентифікуються через унікальні URL-адреси (Uniform Resource Locator), тобто звичні нам посилання.
Сам REST запит складається з таких частин:
- Метод HTTP.
- Посилання.
- Заголовки.
- Параметри запиту.
- Тіло запиту.
- Змінні шляху.
- Версія HTTP.
А REST відповідь в свою чергу містить:
- Статус код.
- Заголовки.
- Тіло відповіді (необовʼязково).
- Версія HTTP.
Посилання
URL (уніфікований локатор ресурсу) є унікальним ідентифікатором ресурсу в REST API. Він слугує як шлях до конкретного ресурсу або його колекції. Наприклад:
/users
— колекція всіх користувачів./users/123
— конкретний користувач із ідентифікатором 123.
URL має бути інтуїтивно зрозумілим і відображати структуру та ієрархію ресурсів. При цьому важливо дотримуватися таких принципів:
1. Використання іменників
- URL повинен представляти ресурс, тому використовуйте іменники в множині для позначення колекцій ресурсів, наприклад:
GET /users
— отримати список користувачів.POST /users
— створити нового користувача.GET /users/123
— отримати інформацію про конкретного користувача з ID 123.
2. Уникання дієслів
- Операції визначаються HTTP-методами, тому не варто використовувати дієслова в URL:
- Правильно:
GET /users, DELETE /users/123
. - Неправильно:
/getUsers, /deleteUser/123
.
- Правильно:
3. Використання ресурсів у множині
- Використовуйте множину для позначення колекцій ресурсів:
GET /orders
— отримати всі замовлення.POST /orders
— створити нове замовлення.
4. Вкладені ресурси
- Якщо ресурс має підресурс, можна використовувати вкладені URL:
GET /users/123/orders
— отримати всі замовлення користувача з ID 123.
5. Використання Параметрів шляху для конкретних ресурсів
- Використовуйте Параметри шляху для конкретних ресурсів або ідентифікаторів:
GET /products/45
— отримати інформацію про товар з ID 45.PUT /products/45
— оновити інформацію про товар.
6. Використання Параметрів запиту для фільтрації та сортування
- Для фільтрації, сортування або інших уточнень використовуйте Параметри запиту:
GET /users?age=30&status=active
— отримати всіх користувачів віком 30 років зі статусом «active».GET /products?category=books&sort=price
— отримати книги, відсортовані за ціною.
7. Уникання великих літер та спеціальних символів
- URL має бути написаний у нижньому регістрі та не містити спеціальних символів (окрім — для розділення слів):
- Правильно:
/users, /products/45
. - Неправильно:
/Users, /Get_Products
.
- Правильно:
8. Версіонування API через URL або заголовки
- Додавайте версію API в URL або заголовки для підтримки кількох версій:
GET /v1/users
— для версії 1 API.- Або через заголовок:
Accept: application/vnd.myapi.v1+json
.
Параметри запиту чи Параметри шляху
У REST API є два типи параметрів для взаємодії з ресурсами: Path Parameters (параметри шляху) та Query Parameters (параметри запиту).
- Параметри шляху — використовуються для чіткого ідентифікатора ресурсу, який знаходиться безпосередньо у URL. Це обов’язкові параметри. Наприклад:
/users/123
— тут 123 є параметром шляху, що вказує на конкретного користувача з ID 123.
- Параметри запиту — використовуються для передачі додаткових умов або фільтрів для запитів. Вони додаються після символу ? і не є обов’язковими. Наприклад:
/users?age=30&status=active
— цей запит поверне всіх користувачів, у яких вік 30 і статус активний.
Заголовки
HTTP Headers (заголовки) використовуються для передачі додаткової інформації між клієнтом і сервером.
Наприклад, популярні заголовки запиту:- Accept — вказує формати даних, які клієнт може приймати у відповіді (application/json, application/xml тощо).
- Authorization — передає дані для автентифікації клієнта. Зазвичай використовується для передачі токенів доступу, наприклад, Bearer <token> у JWT або OAuth.
- Content-Type — вказує формат даних, що передаються у запиті (application/json, application/xml тощо).
- Cache-Control — управляє політикою кешування запитів. Наприклад, no-cache, max-age=3600.
- User-Agent — ідентифікує клієнтське програмне забезпечення, яке виконує запит. Це може бути корисним для відстеження запитів від різних клієнтів або версій додатків.
- Accept-Language — вказує, якою мовою або для якого регіону клієнт очікує отримати відповідь. Наприклад, en-US для англійської мови в США або uk-UA для української мови в Україні.
- Referer — вказує на URL сторінки, з якої було надіслано запит. Корисний для аналітики та безпеки, зокрема, для захисту від атак типу CSRF.
- If-Modified-Since — вказує дату останньої модифікації ресурсу, яку клієнт знає. Якщо ресурс не змінювався з того часу, сервер може повернути 304 Not Modified.
- If-None-Match — використовується разом з ETag для перевірки, чи змінився ресурс. Якщо ресурс не змінився, сервер повертає 304 Not Modified.
- X-Requested-With — використовується для вказання типу запиту (часто XMLHttpRequest для AJAX-запитів). Це може бути корисно для визначення, запит виконаний через браузер чи інший клієнт.
- Accept-Encoding — вказує формати кодування, які клієнт може приймати. Наприклад, gzip, deflate.
- Access-Control-Allow-Credentials — використовується в CORS для дозволу відправлення автентифікаційних даних (кукі, токени) разом із крос-доменними запитами.
- Content-Disposition — визначає спосіб обробки контенту. Наприклад, вкладення файлів.
- Content-Security-Policy — забезпечує політику безпеки для обмеження завантаження несанкціонованих скриптів чи контенту.
- Expect — вказує специфічні вимоги клієнта до сервера перед відправленням повного запиту. Наприклад,
100-continue. - Forwarded / X-Forwarded-For — вказує IP-адресу клієнта або проміжних проксі, якщо запит проходив через них.
- If-Unmodified-Since — дозволяє операцію тільки якщо ресурс не змінювався після вказаної дати.
- Content-Type — вказує формат даних, які сервер повертає у відповіді. Наприклад, application/json.
- Cache-Control — вказує правила кешування відповіді, як-то максимальний час кешування або заборону кешування.
- Expires — вказує час, після якого відповідь вважається застарілою.
- ETag — унікальний ідентифікатор версії ресурсу, який допомагає кешуванню та зменшенню навантаження на сервер шляхом повернення лише змінених даних.
- Location — використовується для перенаправлення після успішної операції, наприклад, після створення нового ресурсу (201 Created).
- Retry-After — вказує, через скільки секунд клієнт може повторити запит (або дату). Це часто використовується разом із кодом 429 Too Many Requests.
- X-RateLimit-Limit — вказує максимальну кількість запитів, яку клієнт може зробити протягом певного періоду.
- X-RateLimit-Remaining — вказує, скільки запитів залишилося до досягнення ліміту.
- X-RateLimit-Reset — вказує час, коли ліміт на кількість запитів буде скинутий.
- Access-Control-Allow-Origin — визначає, з яких доменів дозволено крос-доменний доступ до API (CORS).
- Access-Control-Allow-Methods — вказує, які HTTP-методи дозволено для крос-доменного доступу.
- Access-Control-Allow-Headers — вказує, які заголовки можуть використовуватися у крос-доменних запитах.
- Strict-Transport-Security — примушує клієнтів використовувати лише HTTPS для підключення до сервера.
- TE (Transfer-Encoding) — вказує, які методи кодування передачі можуть використовуватися. Наприклад, chunked.
- Vary — вказує, які заголовки запиту можуть впливати на кешування відповіді. Наприклад, Accept-Encoding.
- WWW-Authenticate — визначає, який метод аутентифікації використовується для захищеного ресурсу.
- X-Content-Type-Options — захищає від спроб браузера вгадати MIME-тип контенту. Корисно для безпеки.
- X-Frame-Options — захищає від атак типу «clickjacking», забороняючи відображення сторінки у фреймах інших сайтів.
- X-XSS-Protection — вмикає захист від атак типу Cross-Site Scripting (XSS) у браузерах.
- Link — надає засоби для серіалізації одного або кількох посилань у заголовках HTTP. Цей заголовок має таку саму семантику, як елемент HTML <link>. Перевага використання заголовка Link полягає в тому, що браузер може почати попереднє підключення або завантаження ресурсів до того, як сам HTML буде отримано та оброблено.
Заголовки допомагають точно налаштувати взаємодію між клієнтом і сервером, передаючи метадані, що допомагають інтерпретувати запити та відповіді.
Методи HTTP
У REST використовуються HTTP-методи для визначення дій, які виконуються над ресурсами:
- GET — отримати дані ресурсу.
- POST — створити новий ресурс.
- PUT — повністю оновити ресурс або створити новий, якщо його не існує.
- PATCH — частково оновити ресурс.
- DELETE — видалити ресурс.
- HEAD — отримати метадані ресурсу (тіло відповіді не повертається).
- OPTIONS — запитує, які методи підтримуються для ресурсу.
- CONNECT — створює тунель до сервера для двонаправленої комунікації.
- TRACE — діагностичний метод, який повертає вихідний запит для тестування маршруту.
Вибір методу залежить від операції, яку потрібно виконати над ресурсом. Методи також визначають, чи є операція безпечною та ідемпотентною (поясню трохи пізніше).
Типи даних
Дані, які передаються між клієнтом і сервером у REST API, можуть мати різні формати:
- JSON (JavaScript Object Notation) — найпоширеніший формат для передачі структурованих даних через REST API через його простоту та легкість для аналізу як клієнтами, так і серверами.
- XML (Extensible Markup Language) — використовується для передачі структурованих даних, але поступово втрачає популярність через складність у порівнянні з JSON. Однак все ще застосовується в деяких інтеграціях із системами або стандартами, які вимагають XML.
- Plain Text (текст) — може використовуватися для передачі простих текстових даних або повідомлень. Часто застосовується для службових повідомлень або відповідей, які не вимагають складної структури даних.
- HTML — використовується для передачі вебсторінок або фрагментів HTML-коду, зазвичай у випадках, коли REST API генерує контент для рендерингу у браузері.
- Binary Data (бінарні дані)— REST API може передавати файли або інші бінарні дані, наприклад:
- Зображення: image/jpeg, image/png, image/gif.
- Документи: application/pdf, application/msword, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (Excel).
- Аудіо/відео: audio/mpeg, video/mp4.
- CSV (Comma-Separated Values) — формат для передачі табличних даних, який використовується для експорту/імпорту даних у текстовому форматі, де поля розділені комами.
- YAML (YAML Ain’t Markup Language) — менш поширений, але використовується у випадках, коли потрібно передавати дані в структурованому та зручному для людини форматі, подібному до JSON.
- Form Data (форматовані дані форми) — дані, що передаються через форми (зазвичай використовуються в POST або PUT-запитах):
- application/x-www-form-urlencoded — стандартний формат для передавання полів форми.
- multipart/form-data — використовується для передавання даних із форм, включно з файлами.
Content-Type
Формат даних завжди вказується у заголовку Content-Type для правильного розуміння клієнтом або сервером структури переданих даних. Наприклад:
- Content-Type: application/json
- Content-Type: application/xml
- Content-Type: text/html
- Content-Type: multipart/form-data
Статус коди
Коди відповіді HTTP повідомляють про результат виконання запиту.
1xx Інформація:
- 100 Continue — клієнт може продовжувати відправку запиту, оскільки сервер отримав початкову частину без помилок.
- 101 Switching Protocols — сервер погодився на перехід до іншого протоколу, зазначеного клієнтом у запиті.
- 102 Processing — сервер обробляє запит, але поки не може надати відповідь.
- 103 Early Hints — сервер повертає ранні заголовки для оптимізації (наприклад, для передзавантаження ресурсів).
2xx Успіх:
- 200 OK — запит успішно виконаний, і сервер повернув очікувані дані.
- 201 Created — запит виконаний, і в результаті був створений новий ресурс.
- 202 Accepted — запит прийнятий, але ще не оброблений (обробка виконується асинхронно).
- 203 Non-Authoritative Information — запит виконаний, але інформація, що повертається, отримана від стороннього джерела.
- 204 No Content — запит виконаний успішно, але у відповіді немає вмісту.
- 205 Reset Content — запит виконаний успішно, але клієнту слід скинути стан відображення.
- 206 Partial Content — сервер повертає лише частину ресурсу у відповідь на запит із заголовком Range.
- 207 Multi-Status — сервер повертає кілька статусів для різних ресурсів.
- 208 Already Reported — елемент уже був перелічений у попередній відповіді.
- 226 IM Used — сервер успішно виконав запит і використовував варіант відповіді для цього запиту.
Перенаправлення (3xx):
- 300 Multiple Choices — запитаний ресурс має кілька можливих варіантів, з яких клієнт може обирати.
- 301 Moved Permanently — запитаний ресурс був переміщений на нову постійну URL-адресу.
- 302 Found — запитаний ресурс тимчасово переміщений на іншу URL-адресу.
- 303 See Other — клієнт повинен використовувати інше URL для отримання ресурсу.
- 304 Not Modified — запитаний ресурс не змінився з часу останнього доступу.
- 307 Temporary Redirect — запитаний ресурс тимчасово доступний за іншою URL-адресою, але метод запиту не змінюється.
- 308 Permanent Redirect — запитаний ресурс переміщений на нову постійну URL-адресу, і метод запиту не змінюється.
Помилки клієнта (4xx):
- 400 Bad Request — сервер не може обробити запит через помилку на стороні клієнта (некоректний синтаксис).
- 401 Unauthorized — для доступу до ресурсу потрібна аутентифікація.
- 402 Payment Required — зарезервований для майбутнього використання (наприклад, для платних послуг).
- 403 Forbidden — сервер відмовляє у виконанні запиту через відсутність прав доступу.
- 404 Not Found — сервер не може знайти запитаний ресурс.
- 405 Method Not Allowed — метод запиту не дозволений для цього ресурсу.
- 406 Not Acceptable — сервер не може повернути дані у форматі, прийнятному для клієнта.
- 407 Proxy Authentication Required — клієнт повинен пройти аутентифікацію через проксі-сервер.
- 408 Request Timeout — сервер чекав на запит клієнта занадто довго і припинив очікування.
- 409 Conflict — конфлікт в обробці запиту, наприклад, при одночасній зміні ресурсу.
- 410 Gone — ресурс більше недоступний і був видалений постійно.
- 411 Length Required — сервер очікує заголовок Content-Length у запиті, але його немає.
- 412 Precondition Failed — одна з передумов запиту не виконана (використовується із заголовками If-None-Match, If-Modified-Since тощо).
- 413 Content Too Large — тіло запиту занадто велике для обробки сервером.
- 414 URI Too Long — URI запиту занадто довгий для обробки сервером.
- 415 Unsupported Media Type — сервер не підтримує тип даних, переданих у запиті.
- 416 Range Not Satisfiable — запитаний діапазон не може бути задоволений, наприклад, якщо ресурс менший за запитаний діапазон байтів.
- 417 Expectation Failed — сервер не може задовольнити заголовок Expect, надісланий клієнтом.
- 418 I’m a teapot — жартівливий статус-код з RFC 2324 («Я чайник», використовується для гумористичних відповідей).
- 421 Misdirected Request — запит був надісланий на неправильний сервер.
- 422 Unprocessable Content — сервер не може обробити запит через проблеми з його вмістом (наприклад, некоректний формат).
- 423 Locked — доступ до ресурсу заблокований.
- 424 Failed Dependency — запит не може бути виконаний через невдачу іншого запиту.
- 425 Too Early — сервер відмовляє в обробці запиту, оскільки він був надісланий занадто рано.
- 426 Upgrade Required — клієнт повинен перейти на іншу версію протоколу для виконання запиту.
- 428 Precondition Required — сервер вимагає, щоб запит мав передумови для виконання.
- 429 Too Many Requests — клієнт надіслав занадто багато запитів за короткий час (обмеження швидкості).
- 431 Request Header Fields Too Large — заголовки запиту занадто великі для обробки сервером.
- 451 Unavailable For Legal Reasons — доступ до ресурсу заблоковано через юридичні причини.
Помилки сервера (5xx)
- 500 Internal Server Error — загальна помилка сервера, яка не може бути деталізована.
- 501 Not Implemented — сервер не підтримує метод або функцію, необхідну для обробки запиту.
- 502 Bad Gateway — сервер отримав неправильну відповідь від іншого сервера.
- 503 Service Unavailable — сервер тимчасово недоступний через перевантаження або технічне обслуговування.
- 504 Gateway Timeout — сервер не отримав відповідь від іншого сервера вчасно.
- 505 HTTP Version Not Supported — сервер не підтримує версію HTTP, яку використовує клієнт.
- 506 Variant Also Negotiates — сервер не може завершити запит через внутрішній конфлікт у вмісті.
- 507 Insufficient Storage — сервер не може зберігати необхідні дані для виконання запиту.
- 508 Loop Detected — сервер виявив нескінченний цикл у запиті.
- 510 Not Extended — сервер вимагає додаткових розширень для виконання запиту.
- 511 Network Authentication Required — клієнт повинен пройти автентифікацію для доступу до мережі.
Ці коди дозволяють клієнту розуміти, чи був запит успішним, і, якщо ні, то яка саме проблема сталася.
Ідемпотентність
Ідемпотентність — це властивість операцій, яка означає, що багаторазове виконання однієї і тієї ж операції призведе до того самого результату, що і перше її виконання. Незалежно від того, скільки разів операція буде викликана, результат залишатиметься незмінним.
У контексті HTTP ідемпотентність важлива для забезпечення надійності запитів. Наприклад, якщо під час виконання операції мережа зазнала тимчасової проблеми, клієнт може безпечно повторити запит, не ризикуючи створити додаткові або небажані зміни на сервері.
Які HTTP-методи є ідемпотентними та чому
- GET — цей метод лише отримує дані із сервера, не змінюючи їх. Незалежно від кількості запитів, відповідь завжди буде тією самою (за умови, що дані на сервері не змінилися).
- PUT — метод призначений для створення або оновлення ресурсу на сервері. Якщо той самий ресурс відправляється кілька разів, він лише оновлюється або перезаписується, що не змінює результату операції.
- DELETE — видаляє ресурс. Незалежно від кількості викликів цього методу, результат завжди один і той самий — ресурс буде видалений. Повторні запити не призведуть до нових змін.
- HEAD — аналогічний до GET, але без повернення тіла відповіді. Цей метод також не змінює стан ресурсів на сервері.
- OPTIONS — використовується для запиту про доступні методи на сервері, тому не змінює дані й також є ідемпотентним.
Отже, основною характеристикою ідемпотентних методів є те, що їхнє багаторазове виконання не впливає на стан сервера після першого запиту, що робить їх безпечними для повторного використання у випадку помилок або збоїв.
Версіонування
Версіонування у REST API є важливим аспектом для підтримки зворотної сумісності при внесенні змін до API. Це дозволяє одночасно підтримувати кілька версій сервісу для різних клієнтів або застосунків, що можуть використовувати старіші версії.
Існують кілька способів реалізації версіонування API:
1. Версія у URL
Найпоширеніший спосіб — включати версію API у шлях URL. Це простий та інтуїтивно зрозумілий підхід.
Приклад:
GET /v1/users
GET /v2/users
- У цьому прикладі /v1 і /v2 вказують на різні версії API. Це дозволяє змінювати структуру ресурсів або поведінку методів у нових версіях без порушення роботи старих клієнтів.
2. Версія у заголовках
Ще один підхід — передача версії через HTTP-заголовки. Це дозволяє зберігати чисті URL і перемикати версію API за допомогою спеціальних заголовків.
Приклад:
GET /users
Accept: application/vnd.myapi.v1+json
- У цьому прикладі заголовок Accept містить версію API, яку очікує клієнт.
3. Версія у параметрах запиту
Також можна використовувати параметри запиту для передачі версії API. Це дозволяє передавати версію разом з іншими параметрами.
Приклад:
GET /users?version=1
Коли і як використовувати версіонування
Версіонування потрібне, коли:
- Ви плануєте вносити зміни, які порушать зворотну сумісність.
- Необхідно підтримувати кількох клієнтів, які використовують різні версії API.
Краще за все починати із версії з самого початку розробки API, навіть якщо ви не плануєте змін найближчим часом. Це дозволить вам уникнути проблем у майбутньому.
Принципи успішного версіонування
- Зміни, які не впливають на клієнтів, не вимагають нової версії. Наприклад, додавання нових полів у відповіді.
- Створюйте нову версію лише для критичних змін у логіці або структурі.
- Старі версії слід підтримувати, але за можливості заохочувати клієнтів до міграції на новіші.
HATEOAS
HATEOAS (Hypermedia as the Engine of Application State) — це один із ключових принципів REST, що означає використання гіпермедіа для управління станом застосунку. Гіпермедіа тут означає використання гіперпосилань у відповідях API для надання клієнту можливості переходити між різними станами або ресурсами системи.
HATEOAS дозволяє клієнту взаємодіяти з сервером без попереднього знання про структуру ресурсів. Сервер надає всі необхідні посилання у відповідях на запити клієнта, що дозволяє клієнту «вивчати» API динамічно. Це підвищує гнучкість API та зменшує залежність клієнта від документованої структури URL.
Приклад. Клієнт запитує інформацію про користувача:
GET /users/123
Сервер відповідає, надаючи інформацію про користувача, а також гіперпосилання на інші пов’язані дії:
{
"id": 123,
"name": "John Doe",
"links": [
{
"rel": "self",
"href": "/users/123"
},
{
"rel": "orders",
"href": "/users/123/orders"
},
{
"rel": "friends",
"href": "/users/123/friends"
}
]
}
У цьому прикладі сервер надає клієнту посилання на пов’язані ресурси, такі як замовлення користувача чи список друзів. Клієнт може використовувати ці посилання для подальших запитів, не знаючи наперед структури URL-адрес.
Модель зрілості Річардсона (Richardson Maturity Model)
Модель зрілості Річардсона описує етапи розвитку REST API за рівнями відповідності REST-принципам. Вона складається з чотирьох рівнів:
- Рівень 0 — POX (Plain Old XML) або RPC API: API використовує HTTP як транспортний протокол, але не використовує його можливості. Всі запити надсилаються на одну кінцеву точку, а методи роботи з даними обираються через параметри запиту. (POST-запит на одну url з різними request body.)
- Рівень 1 — Ресурси: API визначає різні кінцеві точки для різних ресурсів, таких як /users, /orders, тощо. Кожен ресурс має свою URL-адресу, але HTTP-методи використовуються ще не повною мірою. (POST-запити на різні url з різними request body.)
- Рівень 2 — HTTP-методи: API використовує стандартні HTTP-методи (GET, POST, PUT, DELETE) для взаємодії з ресурсами. Це дозволяє покращити масштабованість і зрозумілість API. (Будь-які HTTP-методи на різні url з різними request body.)
- Рівень 3 — HATEOAS: API не лише використовує ресурси та HTTP-методи, але й надає гіпермедійні посилання у відповідях, що дозволяють клієнту динамічно досліджувати API. Це найвищий рівень зрілості REST API.
Модель зрілості Річардсона допомагає оцінити, наскільки ваше API відповідає принципам REST та де воно може бути покращене для досягнення більшої гнучкості та масштабованості.
Як працювати з гіпермедіа
Щоб успішно інтегрувати HATEOAS у свій REST API, вам необхідно:
- Надавати корисні посилання. Для кожного ресурсу сервер повинен включати посилання на пов’язані ресурси або доступні дії. Наприклад, для користувача можна надати посилання на його замовлення, профіль, друзів тощо.
- Використовувати стандартні типи відношень. Посилання повинні містити атрибути rel (relationship), які визначають тип зв’язку між ресурсами. Наприклад, rel: self для посилання на поточний ресурс, або rel: orders для посилання на пов’язані замовлення.
- Документувати гіпермедіа. Попри те, що клієнт може динамічно досліджувати API через посилання, важливо надавати документацію для того, щоб розробники розуміли структуру і логіку взаємодії з вашим API.
Rate Limiting (Обмеження кількості запитів)
Rate Limiting — техніка обмеження кількості запитів, які клієнт може надіслати до сервера за певний проміжок часу. Це необхідний механізм для запобігання перевантаженню серверів і захисту від зловмисних дій, таких як DDoS-атаки або несанкціоноване використання API.
Rate Limiting працює за допомогою встановлення меж для кількості запитів, які клієнт може зробити протягом певного періоду часу. Коли клієнт перевищує ці межі, сервер може відповідати спеціальним кодом помилки (зазвичай 429 Too Many Requests), інформуючи про необхідність зменшити кількість запитів.
Приклад. Якщо сервер встановлює ліміт у 100 запитів на годину, після
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
Заголовок Retry-After повідомляє клієнту, через скільки секунд можна повторити запит.
Чому це важливо
- Захист від перевантаження: обмеження швидкості запитів допомагає запобігти перевантаженню серверів, що може спричинити погіршення продуктивності або збої в системі.
- Захист від зловмисних дій: rate limiting захищає від ботів або хакерів, які можуть намагатися несанкціоновано використовувати ваш API через часті запити.
- Ефективне використання ресурсів: це дозволяє ефективно керувати доступом до ресурсів і забезпечує справедливий розподіл пропускної здатності між клієнтами.
Як реалізувати Rate Limiting
- Token Bucket або Leaky Bucket. Це популярні алгоритми для обмеження кількості запитів.
- Алгоритм Token Bucket — кожен клієнт має певний бакет з токенами, кожен токен дозволяє зробити один запит. Токени поступово додаються з певною швидкістю, і коли бакет порожній, клієнт має почекати перед новим запитом.
- Алгоритм Leaky Bucket — подібний до Token Bucket, але всі запити обробляються з постійною швидкістю, навіть якщо клієнт намагається зробити кілька запитів одночасно. Це допомагає рівномірно розподіляти навантаження.
- Заголовки для інформації про ліміти. API може використовувати заголовки X-RateLimit-Limit (максимальна кількість запитів), X-RateLimit-Remaining (залишок запитів у межах ліміту) і X-RateLimit-Reset (час, коли ліміт буде скинутий).
- Поведінка при перевищенні ліміту. Сервер повинен повернути відповідь з кодом 429 Too Many Requests і додати заголовок Retry-After, щоб вказати, коли клієнт може повторити запит.
CORS (Cross-Origin Resource Sharing)
CORS — це механізм, який дозволяє веб-браузерам контролювати доступ до ресурсів з одного домену на іншому. Це критично важливий аспект безпеки REST API, оскільки він запобігає несанкціонованим доступам до API з інших доменів.
Коли клієнт намагається звернутися до ресурсу з іншого домену, сервер має вказати в заголовку відповіді, чи дозволяє він цей доступ. Це відбувається через заголовки:
- Access-Control-Allow-Origin. Вказує, з яких доменів дозволено доступ до ресурсу.
- Access-Control-Allow-Methods. Вказує, які HTTP-методи дозволено для крос-доменного доступу.
- Access-Control-Allow-Headers. Вказує, які заголовки можуть бути надіслані під час крос-доменного запиту.
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT
Preflight-запити — коли браузер виконує запит із методом, відмінним від GET або POST, або з нестандартними заголовками, він спершу відправляє preflight-запит з методом OPTIONS, щоб дізнатися, чи дозволено виконання основного запиту. Це критично для безпеки, оскільки дозволяє серверам чітко контролювати, які запити можна виконувати.
Пагінація та фільтрація
Пагінація та фільтрація є необхідними для оптимізації роботи з великими наборами даних у REST API.
Пагінація дозволяє поділити великі набори даних на сторінки, щоб уникнути передачі занадто великої кількості інформації за один раз. Зазвичай використовується для списків користувачів, замовлень або інших об’єктів.
Приклад:
GET /users?page=2&size=50
У цьому прикладі клієнт запитує другу сторінку з 50 користувачами на сторінці.
Фільтрація допомагає клієнтам отримати тільки ті дані, які їм необхідні, замість всього набору даних. Це може бути здійснено через параметри запиту.
Приклад:
GET /users?age=30&status=active
Клієнт отримує список користувачів віком 30 років зі статусом «active».
Додаткові заголовки для пагінації:- X-Total-Count. Вказує загальну кількість доступних ресурсів.
- Link. Містить посилання на наступну та попередню сторінки.
Безпека у REST API
HTTPS (SSL/TLS)
HTTPS (Hypertext Transfer Protocol Secure) — це безпечний протокол передачі даних через інтернет, який забезпечує шифрування даних між клієнтом і сервером за допомогою технологій SSL (Secure Sockets Layer) або TLS (Transport Layer Security). Він є критичним елементом для захисту REST API та веб-застосунків від атак і перехоплення даних під час передачі.
Як працює HTTPS
- Шифрування
- HTTPS використовує SSL або TLS для шифрування всіх даних, які передаються між клієнтом і сервером. Навіть якщо зловмисник перехопить трафік, він не зможе розшифрувати дані.
- Під час встановлення з’єднання клієнт і сервер обмінюються сертифікатами для перевірки автентичності та встановлення шифрованого каналу.
- Аутентифікація
- HTTPS забезпечує аутентифікацію сервера, що дозволяє клієнту впевнитись у тому, що він спілкується саме з тим сервером, до якого звертався, і не є жертвою атак типу «людина посередині» (Man-in-the-Middle).
- Цілісність даних
- SSL/TLS гарантує, що дані, передані між клієнтом і сервером, не можуть бути змінені або пошкоджені під час передачі без виявлення.
Основні компоненти HTTPS
- SSL/TLS сертифікат
- Для встановлення HTTPS-з’єднання сервер повинен мати SSL/TLS сертифікат, виданий довіреним центром сертифікації (CA).
- Сертифікат містить публічний ключ сервера та інші дані, які підтверджують його автентичність.
- Рукостискання (Handshake)
- Під час першого з’єднання між клієнтом і сервером відбувається процес «рукостискання», де клієнт перевіряє сертифікат сервера, а сервер і клієнт узгоджують параметри шифрування для сесії.
- Після цього встановлюється безпечне шифроване з’єднання.
- Шифрування даних
- Після встановлення з’єднання всі дані передаються у зашифрованому вигляді. Навіть якщо зловмисник зможе перехопити трафік, він не зможе розшифрувати дані без приватного ключа сервера.
Чому HTTPS важливий для REST API
- Захист конфіденційних даних
- REST API часто передає конфіденційні дані, такі як токени автентифікації, паролі, платіжну інформацію тощо. HTTPS забезпечує, що ці дані залишаються захищеними під час передачі.
- Запобігання атакам типу «людина посередині» (Man-in-the-Middle)
- HTTPS захищає від атак, коли зловмисник намагається перехопити або змінити трафік між клієнтом і сервером. Без HTTPS зловмисник міг би отримати доступ до конфіденційної інформації або змінювати запити і відповіді.
- Безпечний доступ до API
- Багато сторонніх сервісів, таких як платежі або авторизація (OAuth2, JWT), вимагають використання HTTPS для роботи з REST API, щоб забезпечити безпечну передачу даних.
- Захист від підроблених сайтів
- HTTPS гарантує, що користувач або клієнт звертається до справжнього сервера, а не до підробленого ресурсу, який може бути створений зловмисниками.
Приклад налаштування HTTPS для Java/Spring
- Отримання SSL-сертифікату
- Ви можете отримати сертифікат від довіреного центру сертифікації (CA) або використовувати безкоштовні сертифікати, наприклад від Let’s Encrypt.
- Налаштування HTTPS у Spring Boot
- Після отримання сертифіката його потрібно налаштувати в Spring Boot.
Додайте налаштування до файлу application.properties або application.yml:
server.port=8443
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=yourpassword
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=youralias
- Використання Java Keystore (JKS)
Створіть keystore для зберігання сертифікатів за допомогою інструменту keytool, який входить у JDK:
keytool -genkeypair -alias youralias -keyalg RSA -keystore keystore.p12 -storetype PKCS12 -validity 365 -keysize 2048
- Переведення Spring Boot додатка на HTTPS
Після налаштування сертифіката всі запити до вашого REST API будуть автоматично зашифровані за допомогою HTTPS.
Висновок
HTTPS (SSL/TLS) є обов’язковим для захисту REST API від перехоплення даних і атак. Він забезпечує:
- Шифрування переданих даних.
- Автентифікацію сервера.
- Цілісність даних під час передачі.
Це робить HTTPS необхідним компонентом для будь-якого API, який працює з конфіденційною інформацією або передає критично важливі дані.
XSS (Cross-Site Scripting)
Cross-Site Scripting (XSS) — це тип вразливості безпеки веб-додатків, який дозволяє зловмисникам вставляти шкідливі скрипти на вебсторінки, що можуть виконуватися на стороні клієнта (у браузері). Зазвичай це відбувається, коли дані, введені користувачем або отримані з іншого джерела, не фільтруються належним чином перед відображенням у браузері.
Основні типи XSS
- Stored XSS (Збережений XSS)
- Шкідливий скрипт зберігається на сервері в базі даних або іншому сховищі. Коли інший користувач відвідує сторінку, скрипт завантажується та виконується у його браузері.
- Приклад. Зловмисник вводить шкідливий скрипт у форму коментарів на сайті. Коли інші користувачі переглядають сторінку з коментарями, їхні браузери виконують цей скрипт.
- Reflected XSS (Відображений XSS)
- Шкідливий скрипт передається у URL або параметрах запиту і негайно відображається в відповіді сервера без збереження на сервері.
- Приклад. Зловмисник створює посилання, яке містить шкідливий скрипт. Коли користувач переходить за цим посиланням, скрипт виконується у браузері жертви.
- DOM-Based XSS
- Вразливість виникає через маніпуляції з Document Object Model (DOM) на клієнтській стороні без взаємодії з сервером.
- Приклад. Сайт виконує небезпечні зміни в DOM на основі неконтрольованого вводу даних користувача.
Як XSS впливає на REST API
REST API, хоч і не безпосередньо пов’язані з відображенням контенту на вебсторінці, все ж можуть бути вразливі до XSS-атак. Особливо коли дані, отримані від API, вставляються у вебсторінку без належної фільтрації. Це може статися, наприклад, при розробці SPA (Single Page Applications) або у випадках, коли API повертає HTML або JavaScript.
Наслідки XSS
- Викрадення сесій— зловмисники можуть викрасти сесійні кукі або токени автентифікації користувача.
- Виконання шкідливого коду — шкідливий код може виконуватись на боці клієнта, відкриваючи можливості для подальших атак.
- Фішинг — зловмисники можуть використовувати XSS для створення підроблених сторінок або форм для отримання конфіденційної інформації.
Захист від XSS
- Фільтрація вводу
- Валідуйте і фільтруйте всі вхідні дані, перш ніж зберігати їх або використовувати для відображення.
- Використовуйте списки дозволених символів (whitelisting), а не списки заборонених (blacklisting).
- Екранування виводу (Output Escaping)
- Будь-який динамічний контент, що відображається на сторінці, повинен бути екранований. Це означає, що небезпечні символи, такі як <, >, «, ’, повинні бути замінені на їхні HTML-коди (<, >, », ').
- Екрануйте дані залежно від контексту (в HTML, JavaScript, атрибутах тощо).
- Content Security Policy (CSP)
- Впровадження політики безпеки контенту допоможе обмежити виконання скриптів, які не були явно дозволені. Це може запобігти виконанню вбудованих або сторонніх скриптів. Приклад заголовку CSP:
Content-Security-Policy: default-src ’self’; script-src ’self’;
- Впровадження політики безпеки контенту допоможе обмежити виконання скриптів, які не були явно дозволені. Це може запобігти виконанню вбудованих або сторонніх скриптів. Приклад заголовку CSP:
- HTTP-only кукі
- Встановлюйте кукі з прапором HTTP-only, щоб вони не були доступні для JavaScript у браузері. Це знижує ризик їх викрадення через XSS.
- Фреймворки з вбудованим захистом
- Використовуйте фреймворки з вбудованими механізмами захисту від XSS (React, Angular) — ці фреймворки автоматично екранують динамічний контент, що робить додатки більш безпечними.
Якщо ваш REST API повертає дані, які будуть відображені у веб-інтерфейсі, екрануйте та валідуйте їх перед відображенням.
<p>User name: <script>alert('XSS')</script></p>
Після екранування:
<p>User name: <script>alert('XSS')</script></p>
CSRF (Cross-Site Request Forgery)
Cross-Site Request Forgery (CSRF) — це тип атак на веб-застосунки, в якому зловмисник змушує користувача виконати небажану дію на іншому сайті, на якому користувач вже автентифікований. Атаки CSRF відбуваються, коли користувач завантажує шкідливий сайт, який таємно відправляє запит до іншого сайту, використовуючи сесію або кукі користувача для автентифікації.
Як працює CSRF
- Зловмисник створює шкідливий сайт або посилання.
- Користувач заходить на цей шкідливий сайт або натискає на посилання, будучи автентифікованим на іншому сайті (зазвичай через збережені сесійні кукі).
- Запит відправляється до іншого сайту (де користувач автентифікований), використовуючи його сесію без його відома.
- Сайт виконує дію, оскільки запит виглядає законним з боку автентифікованого користувача.
Приклад атаки CSRF
Припустимо, користувач вже увійшов на банківський сайт, і у нього збережена сесія через кукі. Зловмисник надсилає користувачу посилання на шкідливий сайт, який таємно виконує POST-запит до банку на переведення коштів:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="toAccount" value="hacker_account">
<input type="hidden" name="amount" value="10000">
<input type="submit">
</form>
Як тільки користувач відвідує шкідливий сайт або посилання викликається через JavaScript, відправляється запит, і банк виконує дію, оскільки користувач уже автентифікований.
Наслідки CSRF
- Небажані дії — користувач виконує дію, яку він не мав наміру виконати (наприклад, перерахування грошей, зміна налаштувань, видалення даних тощо).
- Безпека даних — CSRF може призвести до компрометації конфіденційної інформації або даних користувачів.
- Витрата ресурсів — сайти можуть отримувати небажані запити, що призводить до навантаження на сервери або втрати ресурсів.
Захист від CSRF
CSRF-токени:
- Найбільш ефективним методом захисту від CSRF є використання унікальних CSRF-токенів. Кожен запит до сервера, що змінює стан (наприклад, POST, PUT, DELETE), повинен містити CSRF-токен, який перевіряється сервером.
- Як це працює: Коли користувач завантажує форму або вебсторінку, сервер генерує CSRF-токен і додає його до форми або в заголовок. При відправленні запиту сервер перевіряє наявність та коректність токена.
Приклад CSRF-токена в HTML:
<form action="/submit" method="POST">
<input type="hidden" name="csrf_token" value="RANDOM_TOKEN_VALUE">
<input type="submit" value="Submit">
</form>HTTP-заголовки (SameSite Cookie Attribute):
- Встановлення атрибуту SameSite для кукі, щоб обмежити можливість використання кукі з іншими сайтами:
- SameSite=Lax — дозволяє надсилати кукі тільки з першорядними запитами (GET-запитами, коли користувач натискає посилання).
- SameSite=Strict — кукі не відправляються при переході з іншого домену, що забезпечує кращий захист від CSRF.
Приклад кукі із атрибутом SameSite:Set-Cookie: sessionId=abc123; SameSite=Strict
- Використання методів HTTP, що безпечні від CSRF
- CSRF атаки зазвичай працюють через методи GET або POST, але методи PUT, PATCH, DELETE рідко використовуються у випадках CSRF, оскільки форми зазвичай використовують метод POST. Втім, вони також повинні бути захищені.
- Перевірка Referer або Origin заголовків
- Сервер може перевіряти заголовки Referer або Origin, щоб переконатися, що запити надходять з довіреного джерела (наприклад, із сайту вашого додатку). Це не ідеальний захист, але може бути використаний як додатковий рівень безпеки.
Приклад:
Referer: https://yourapp.com/
Origin: https://yourapp.com/
- Double Submit Cookie
- В цьому методі сервер генерує токен CSRF і зберігає його у вигляді кукі, а також відправляє його на клієнтську сторону для додавання в запити (в заголовок або тіло запиту). Сервер потім порівнює токен з кукі з тим, що надійшов у запиті.
Приклад захисту з CSRF-токенами
Якщо ви використовуєте форму для відправлення запиту, додайте унікальний CSRF-токен до кожної форми:
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="generated_csrf_token">
<input type="text" name="toAccount">
<input type="submit" value="Transfer">
</form>
Висновок
CSRF є однією з основних загроз для безпеки REST API і веб-додатків, яка може бути вирішена за допомогою CSRF-токенів, атрибуту SameSite для кукі, і додаткових заходів, таких як перевірка заголовків або використання методів HTTP з кращим захистом. Правильне впровадження цих технік допоможе захистити ваші API та користувачів від несанкціонованих дій.
Асинхронність у REST API
REST API за своєю природою підтримує синхронну модель комунікації, де клієнт відправляє запит і отримує відповідь у межах одного з’єднання. Проте існують різні підходи, які дозволяють реалізувати асинхронну комунікацію в REST API, щоб обробляти довготривалі операції або отримувати дані без постійного опитування сервера.
1. Polling (Опитування)
Polling — це метод, при якому клієнт періодично надсилає запити на сервер, щоб дізнатися про статус операції або отримати нові дані. Це може бути корисно, коли обробка запиту займає певний час, і клієнт періодично опитує сервер на предмет змін.
- Приклад. Клієнт може надіслати запит на створення ресурсу і отримати відповідь з кодом 202 Accepted. Потім він періодично надсилає запити на статус ресурсу через певні інтервали часу.
- Недолік. Постійне опитування збільшує навантаження на сервер і може бути неефективним.
2. Long Polling (Довге опитування)
Long Polling працює подібно до звичайного опитування, але сервер тримає з’єднання відкритим, поки не з’являться нові дані або зміни. Це дозволяє знизити частоту запитів, оскільки сервер відповідає тільки тоді, коли є нові дані.
- Приклад. Клієнт робить запит до сервера, і сервер затримує відповідь до моменту появи необхідних даних (наприклад, завершення операції).
3. Webhooks
Webhooks дозволяють серверу надсилати дані безпосередньо клієнту, коли подія відбувається. Це ефективний спосіб для асинхронної комунікації між сервісами або для оновлення статусу клієнта без необхідності постійного опитування.
- Приклад. Клієнт реєструє URL для webhook, і коли сервер завершує обробку завдання, він надсилає HTTP POST-запит на цей URL з результатами. Це типово використовується для сервер-до-сервер інтеграцій.
- Недоліки. Webhooks можуть бути ненадійними, якщо клієнтський сервер недоступний у момент надсилання запиту.
Висновок
Хоча REST API зазвичай асоціюється з синхронними запитами, існують кілька перевірених підходів для впровадження асинхронної комунікації, таких як polling, long polling, webhooks, та WebSockets. Вибір методу залежить від специфіки вашого проєкту і частоти оновлень, які необхідні клієнту.
86 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів