Адаптация статьи REST WORST PRACTICES, (c) Jacob Kaplan-Moss.
NOVEMBER 14, 2008
https://jacobian.org/writing/rest-worst-practices/

Перевел и озвучил Шиляев Дмитрий dima@justhost.ru

Статья написана применительно к Django, но информация будет актуальна для широкого круга специалистов.

Несколько недель назад я отправил этот текст своему коллеге, который просил моего совета при проектировании REST API в Django. С тех пор я цитировал сам себя несколько раз, в связи с чем решил опубликовать эти советы, возможно, кому-то еще они покажутся ценными.

Думаю что лучший способ понять как нужно делать, изучить как делать НЕ нужно. Представляю вашему вниманию вредные советы проектировщикам REST API.

Объединяй модели и ресурсы

В мире REST, ресурс — это центральное понятие. Очень заманчиво просто взять модель (строка в таблице), сказать что это и есть ресурс — одна модель, один ресурс. Это решение проваливается как только возникает задача — представить некий вид составного ресурса и уж точно проваливается в сильно денормализованных моделях.

Представьте модель Супергерой: вот единственный запрос GET /heros/superman/ который должен вернуть все его характеристики: список друзей, список связанных с ним артефактов и т.д. Смотрите, данные, ассоциированные с ресурсом, на самом деле могут приходить из нескольких моделей. Тут же полезно вспомнить паттерн фасад — ресурс это фасад для моделей, а не конкретная модель.

Жестко зашивай конкретную подсистему контроля доступа в код API

Способ контроля доступа должен быть подключаемым. Модуль контроля доступа должен определять какие операции над какими объектами допустимы — это единственный способ заставить работать что-то сложное.
Так вы сможете реализовать любой сценарий и сохранить контроль над системой разграничения доступа. Например, в одной системе вам может потребоваться реализовать анонимное чтение, чтение/запись при доступе с токеном разработчика и полный доступ при HTTP-аутентификации с данными администратора через админку.

Делай формат возвращаемых данных зависимым от типа ресурса

Бывает очень заманчиво, по различным причинам, использовать различный формат результатов в зависимости от типа ресурса. Но это очень плохая идея, поскольку такой подход делает клиентский код очень сложным.
Вспомните Yahoo APIs — формат ResultSet/Result везде одинаков. Так же и Atom (GData).

Основная мысль — клиентский код не должен знать как парсить множество различных форматов данных.

Пусть твой API поддерживает только один формат возвращаемых данных

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

Причем, формат вывода должен определяться нативно, через заголовок HTTP Accept, а не через лишние конструкции вида ?format=xml.

Перегружай семантикой стандартные методы HTTP-протокола

Большинство виденных мною REST API отображают основные стандартные методы HTTP (POST/GET/PUT/DELETE) в соответствующие CRUD-методы (create/retrieve/update/delete). Это не очень хорошая идея, потому что одни ресурсы могут использовать паттерн POST-как-создатель-связанных-ресурсов, а другие могут использовать POST-как-редактирование для обратной совместимости с HTML-формами. Любая форма должна быть допустима.

Более того, такие системы не допускают использование расширенных HTTP-методов. WebDAV определяет несколько полезных методов, так же новый метод HTTP PATCH уже официально часть стандарта. Ни кто не говорит о том что вы должны ограничивать себя четырьмя основными методами HTTP, речь идет лишь о том чтобы эти методы широко поддерживались, т.к. нестандартные HTTP-запросы могут просто игнорироваться веб-сервером.

Используй индексы для определения связей между ресурсами

Итак, вы хотите чтобы один ресурс ссылался на другой. Например, PhotoAlbum ссылается на объекты Photo. Обычно вы делаете это как-то так:

{
    'album': 'whatever', 'photos': [1, 2, 3, 4]
}

Просто указываете ID объектов, да? Печально, но это означает что клиентский код должен «просто знать» о том как конструировать URL на ресурсы Photo. Это так же означает что клиентский и серверный код становятся зависимыми. Как бонус, вы получаете все проблемы, связанные с несовместимостью API и клиентского кода. Почти все API на нашей планете допускают эту ошибку, поэтому малейшее изменение в формате URL автоматически сломает всех клиентов вашего API.

Просто используйте готовые URL:

{
    'photos': ['http//example.com/ph/1', ...]
}

Или, если волосы встают дыбом от дублирования, сообщите формат URL:

{
    'photos': [1, 2, 3],
    'photo_uri_template': 'http://example.com/ph/{id}'
}

Кстати, смотрите — Resource != Model.

Жестко привяжи REST API к своему приложению

Любой большой API должен иметь выделенный сервер (серверы) для своей работы: характеристики производительности и факторы, от которых они зависят, так отличаются от обычных веб-приложений, что для больших API требуются отдельные, специальным образом настроенные, серверы. Это факт. Более того, если API-сервер падает, это не должно влиять на общедоступный веб-сайт.

Отмечено:

Добавить комментарий