[Перевод] 7 вредных советов проектировщику REST API
Думаю что лучший способ понять как нужно делать, изучить как делать НЕ нужно. Представляю вашему вниманию вредные советы проектировщикам REST API.
Несколько недель назад я отправил этот текст своему коллеге, который просил моего совета при проектировании REST API в Django. С тех пор я цитировал сам себя несколько раз, в связи с чем решил опубликовать эти советы, возможно, кому-то еще они покажутся ценными.
Объединяй модели и ресурсы
В мире 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}'
}
Жестко привяжи REST API к своему приложению
Любой большой API должен иметь выделенный сервер (серверы) для своей работы: характеристики производительности и факторы, от которых они зависят, так отличаются от обычных веб-приложений, что для больших API требуются отдельные, специальным образом настроенные, серверы. Это факт. Более того, если API-сервер падает, это не должно влиять на общедоступный веб-сайт.
Всем успехов в проектировании API!