Керівництво по побудові HTTP API


Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.

Вступ

Це керівництво містить рекомендації щодо проектування HTTP API, які були почерпнуті з роботи API хмарної платформи Heroku, крім того, воно також має інформацію про новий функціонал і внутрішній API в Heroku.

Нашими основними цілями при побудові API є дотримання послідовності й концентрація на реалізації бізнес-логіки. Ми шукаємо різні, не обов’язково найкращі, але належно документовані способи розробки API.

Сподіваємося, що Ви ознайомлені з основними принципами HTTP і JSON.

Засади

Принцип розподілу відповідальності

Під час проектування намагайтеся зберігати простоту системи, розподіляючи відповідальність між різними частинами циклу “запит-відповідь”. При цьому простота прийнятих рішень дозволить сконцентруватися на розв’язанні складніших задач. Запити і відповіді виконуються з метою діставання доступу до певного ресурсу або набору ресурсів. Для визначення сутності, яку необхідно отримати, використайте шлях і тіло відповіді для передачі вмісту, а заголовки – для передачі метаданих. Можна передавати всі параметри в тілі запиту, але, як показує практика, таке рішення є менш гнучким. Правильнішим підходом буде передача частини параметрів у заголовках.

Вимагайте використання захищених з’єднань

Для отримання даних за допомогою API використовуйте тільки захищені з’єднання з TLS.
Кращим рішенням було б відхиляти всі запити, які не використовують TLS, а саме: запити по http або на 80-й порт, щоб уникнути небезпечного обміну даними. У випадках, коли це неможливо, давайте відповідь 403 Forbidden.

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

Вимагайте наявність версії в заголовку Accept

Наявність декількох версій і переходи між ними можуть виявитись одним з найскладніших аспектів проектування і використання API, тому краще заздалегідь це врахувати.

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

Краще за все додати версію в заголовок разом з іншими метаданими, використовуючи заголовок Accept з призначеним для користувача типом вмісту:

Accept: application/vnd.heroku+json; version=3

Використайте заголовок ETags для кешування

Включайте заголовок ETags у всі запити, визначаючи при цьому версію поверненого ресурсу. Це дозволить користувачам кешувати ресурси і реалізовувати умовні запити за допомогою використання заголовка If- None-Match, який допоможе визначити, чи треба оновлювати кеш.

Використайте Request-ID для інтроспекції

Включайте заголовок Request - Id, що містить UUID значення, в кожну відповідь сервера. Реєструючи ці значення на клієнтові, сервері або іншому сервісі, Ви матимете можливість відлагоджувати і діагностувати проблеми, пов’язані із запитами.

Розділяйте великі відповіді сервера на декілька малих за допомогою заголовка Range

Великі відповіді необхідно розділяти на малі, використовуючи заголовок Range. Для отримання детальнішої інформації про заголовки запитів/відповідей, коди станів і обмеження вивчіть Обговорення використання заголовка Range в API платформи Heroku .

Запити

Повертайте відповідні коди станів

Повертайте відповідний код стану HTTP у кожній відповіді. Успішні відповіді повинні містити такі коди станів:

  • 200GET запит завершився успішно, синхронний DELETE чи PATCH запит завершився успішно або синхронний PUT запит оновив наявний ресурс.
  • 201 – Синхронний POST запит завершився, синхронний PUT запит створив новий ресурс.
  • 202 – Прийнятий POST, PUT, DELETE чи PATCH запит, який буде оброблений асихронно.
  • 206GET запит завершився успішно, але буде повернена часткова відповідь (див. розділ про заголовок Range).

Зверніть увагу на використання кодів стану помилок авторизації та аутентифікації:

  • 401 Unauthorized – запит завершився з помилкою, оскільки користувач не пройшов аутентифікацію.
  • 403 Forbidden – запит завершився з помилкою, оскільки користувач не авторизувався для отримання доступу до певного ресурсу.

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

  • 422 Unprocessable Entity – Ваш запит був розпізнаний, але містить неправильні параметри.
  • 429 Too Many Requests – Перевищений ліміт запитів, спробуйте пізніше.
  • 500 Internal Server Error – Проблема на стороні сервера, перевірте стан сайту та/або повідомте про проблему.

Для отримання детальнішої інформації про коди стану HTTP вивчіть специфікацію.

За змогою, надайте повні версії ресурсів

Повертайте користувачам Вашого API повне представлення ресурсу, тобто об’єкт з усіма атрибутами, в усіх відповідях, де це можливо. Завжди надавайте повну версію ресурсу у відповідях на запити з кодами стану 200 і 201, у тому числі на PUT, PATCH і DELETE запити.

$ curl - X DELETE  https://service.com/apps/1f9b/domains/0fd4HTTP/1.1 200 OKContent-Type: application/json;charset=utf - 8...{ "created_at": "2012 - 01-01T12:00:00Z", " hostname": "subdomain.example.com"
"id": "01234567 - 89ab - cdef - 0123-456789abcdef", "updated_at": "2012 - 01-01T12:00:00Z"}

Відповіді на запити з кодом стану 202 не повинні містити всі поля об’єкта:

$ curl - X DELETE  https://service.com/apps/1f9b/dynos/05bdHTTP/1.1 202 AcceptedContent-Type: application/json;charset=utf - 8...{}

Ваш API повинен приймати серіалізований JSON у тілі запиту

Ваш API повинен передбачати можливість передачі серіалізованого JSON у тілі PUT/PATCH/POST запитів замість або як додаток до передаваних даних форми. Так створюється симетрія в JSON-відповідях:

$ curl - X POST https://service.com/apps  - H "Content-Type: application/json"  - d '{"name": " demoapp"}'{
"id": "01234567 - 89ab - cdef - 0123-456789abcdef", " name": " demoapp", " owner": { "email": "username@example.com", " id": "01234567 - 89ab - cdef - 0123-456789abcdef" }, ...}

Будьте послідовними при конструюванні шляху до ресурсу

Назви ресурсів

Використайте множинну форму назви ресурсу, за винятком тих випадків, коли запитуваний ресурс у системі завжди один – наприклад, у більшості систем, у користувача завжди тільки один акаунт. Такий підхід допомагає зберігати одноманітність при доступі до конкретного ресурсу.

Дії

Намагайтеся проектувати такі кінцеві url, які не вимагають додаткових дій для окремих ресурсів. У випадках, коли це необхідно, додайте в загальний шлях компонент “action” для того, щоб чітко визначити дії:

/resources/:resource/actions/:action

Наприклад:

/runs/{run id}/actions/stop

Використовуйте назви компонентів шляху і атрибутів у нижньому регістрі

Для назв компонентів шляху до ресурсу використайте нижній регістр і розділіть їх за допомогою дефіса.

service-api.com/usersservice-api.com/app-setups

Назви атрибутів краще писати в нижньому регістрі, а в ролі роздільника краще використати нижнє підкреслення – назви полів можна писати без дужок в Javascript:

service _class: " first"

Ваш API повинен підтримувати доступ до ресурсу не лише за його id

У деяких випадках для кінцевих користувачів є незручним доступ до ресурсу за його ідентифікатором. Наприклад, користувачеві зручніше для доступу до конкретного додатка Heroku використати назву додатка, а не його UUID. У таких випадках треба організувати доступ як за ім’ям, так і за ідентифікатором:

У кожного ресурсу за умовчанням має бути атрибут id. Як значення ідентифікатора ресурсу намагайтеся завжди використовувати UUID. Уникайте використання ідентифікаторів, які не будуть унікальними в масштабі Вашого сервісу, особливо автоінкрементних ідентифікаторів.
UUID ресурсу виводьте у форматі 8-4-4-4-12:

"id": "01234567 - 89ab - cdef - 0123-456789abcdef"

Надавайте інформацію про дату створення і зміни ресурсу

За умовчанням ресурс повинен зберігати інформацію про дату його створення created_at та оновлення updated_at.

{ // ... "created_at": "2012 - 01-01T12:00:00Z", "updated_at": "2012 - 01-01T13:00:00Z", //...}

Часові величини мають бути форматовані згідно з ISO8601

Приймайте і повертайте часові дані тільки в UTC, а виводьте у форматі ISO8601:

"finished_at": "2012 - 01-01T12:00:00Z"

Відносини із зовнішніми сутностями мають бути винесені у вкладений об’єкт

Зовнішні відносини мають бути серіалізовані як вкладений об’єкт:

 "name": "service-production", " owner": { "id": "5d8201b0.". }, // ...

А не як поле об’єкта:

{ "name": "service-production", "owner_id": "5d8201b0."., // ...}

Такий підхід дозволяє додати більше інформації про пов’язаний об’єкт без необхідності міняти структуру відповіді:

{ "name": "service-production", " owner": { "id": "5d8201b0."., "name": " Alice", " email": "alice@heroku.com" }, // ...}

Створюйте структуровані відповіді у разі виникнення помилок

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

HTTP/1.1 429 Too Many Requests{ "id": "rate_limit", " message": "Account reached its API rate limit"., "url": "https://docs.service.com/rate-limits"}

Показуйте обмеження за кількістю запитів

Обмеження за кількістю запитів вводиться для підтримки працездатності системи і можливості якісного обслуговування інших клієнтів. Для розрахунку обмежень на кількість запитів можна використати алгоритм поточного відра. Повертайте кількість запитів, що залишилася, для кожного запиту в заголовку відповіді RateLimit - Remaining.

JSON у всіх відповідях має бути мінімізованим

Зайвий пропуск збільшує розмір відповіді, й багато Javascript клієнти для легкості читання автоматично відформатують JSON. Внаслідок цього краще мінімізувати JSON відповіді:

{"beta": false," email": "alice@heroku.com"," id": "01234567 - 89ab - cdef - 0123-456789abcdef", "last_login": "2012 - 01-01T12:00:00Z","created_at": "2012 - 01-01T12:00:00Z","updated_at": "2012 - 01-01T12:00:00Z"}

замість

{ "beta": false, " email": "alice@heroku.com", " id": "01234567 - 89ab - cdef - 0123-456789abcdef", "last_login": "2012 - 01-01T12:00:00Z", "created_at": "2012 - 01-01T12:00:00Z", "updated_at": "2012 - 01-01T12:00:00Z"}

Ви можете опціонально додати можливість отримувати розгорнуту відповідь, указуючи додатковий параметр (наприклад, ?pretty=true) або задаючи значення для заголовка Accept (Accept: application/vnd.heroku+json; version=3; indent=4;).

Артефакти

Надайте зручну для обробки JSON-схему

Для точного опису Вашого API надайте JSON- схему. Для управління схемою використайте prmd, також упевніться в тому, що вона проходить валідацію за допомогою команди prmd verify.

Надавайте легку для читання документацію

Для того щоб розробники розбиралися в принципах роботи Вашого API, надайте їм зручну документацію. Якщо Ви створили JSON-схему, використовуючи prmd, як описано вище, то можете легко згенерувати Markdown документацію для всіх кінцевих url, використовуючи команду prmd doc.

До опису кінцевих url додайте огляд API, супроводжуваний такою інформацією:

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

Надавайте приклади запитів, які можна протестувати

Надавайте приклади запитів, які користувачі можуть протестувати. Для тестування цих запитів користувач повинен виконати мінімум дій:

$ export TOKEN=... # acquire from dashboard$ curl - is https://$TOKEN@service.com/users

Якщо Ви використовуєте prmd для створення документації, то такі приклади будуть згенеровані автоматично для кожного кінцевого url.

Опишіть стабільність Вашого API

Ви можете описати ступінь стабільності Вашого API або окремих кінцевих url за допомогою установлення прапорів prototype/development/production.

Для отримання додаткової інформації, Ви можете вивчити документ Політика сумісності Heroku API.

Щойно Ви оголосили API готовим до релізу і стабільним, не варто здійснювати модифікацій, які порушують зворотну сумісність усередині цієї версії. Для внесення таких змін створіть нову гілку API з новим індексом версії.

Переклад статті “HTTP API Design Guide”

Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?


Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *