RESTful API - Проектирование под-ресурсов
Я проектирую RESTful API и столкнулся с проблемой, связанной с подресурсами.
Я вижу другие API, использующие полный URL для работы над подресурсами. Возьмите пример, когда в Company has Departments
а в Department has Employees
.
В начале я думал о реализации всех возможных URL. В результате чего:
Подход А
01. ### COMPANY URLS ###
02. DELETE /companies/{companyId}
03. GET /companies/{companyId}
04. POST /companies
05. PUT /companies/{companyId}
06.
07. ### DEPARTMENT URLS ###
08. DELETE /companies/{companyId}/departments/{departmentId}
09. GET /companies/{companyId}/departments/{departmentId}
10. POST /companies/{companyId}/departments
11. PUT /companies/{companyId}/departments/{departmentId}
12. DELETE /departments/{departmentId}
13. GET /departments/{departmentId}
14. PUT /departments/{departmentId}
15.
16. ### EMPLOYEE URLS ###
17. DELETE /companies/{companyId}/departments/{departmentId}/employees/{employeeId}
18. GET /companies/{companyId}/departments/{departmentId}/employees/{employeeId}
19. POST /companies/{companyId}/departments/{departmentId}/employees
20. PUT /companies/{companyId}/departments/{departmentId}/employees/{employeeId}
21. DELETE /departments/{departmentId}/employees/{employeeId}
22. GET /departments/{departmentId}/employees/{employeeId}
23. POST /departments/{departmentId}/employees
24. PUT /departments/{departmentId}/employees/{employeeId}
25. DELETE /employees/{employeeId}
26. GET /employees/{employeeId}
27. PUT /employees/{employeeId}
Как видите, есть много URL, которые делают то же самое. Пример: 08 дублируется из 12; 09 дублируется из 13; 17 дублируется из 21 и 25...
Я хочу удалить дубликаты, но сохранить последовательность. Таким образом, перепроектированный API с учетом принципа sup-resources are fine but sub-sub-resources are not
. Что привело к следующему:
Подход Б
01. ### COMPANY URLS ###
02. DELETE /companies/{companyId}
03. GET /companies/{companyId}
04. POST /companies
05. PUT /companies/{companyId}
06.
07. ### DEPARTMENT URLS ###
08. DELETE /departments/{departmentId}
09. GET /departments/{departmentId}
10. GET /companies/{companyId}/departments
11. POST /companies/{companyId}/departments
12. PUT /departments/{departmentId}
13.
14. ### EMPLOYEE URLS ###
15. DELETE /employees/{employeeId}
16. GET /employees/{employeeId}
17. GET /departments/{departmentId}/employees
18. POST /departments/{departmentId}/employees
19. PUT /employees/{employeeId}
Мои вопросы
Q1. Подход B считается RESTful? (Я предполагаю, что да)
Q2. Существуют ли подводные камни, которые следует рассмотреть в подходе BI при условии, что документация также предоставлена?
Бонусные баллы, если вы указываете на другие API в соответствии с подходом B.
РЕДАКТИРОВАТЬ
Elad Tabak
представил хорошие идеи.
Я увлекаюсь API, используя подход B:
https://developers.google.com/youtube/v3/docs/
https://developer.github.com/guides/getting-started/
https://dev.twitter.com/rest/public
Ответы
Ответ 1
Оба подхода могут считаться RESTful, если вы не нарушаете ограничения REST, определенные в главе 5 диссертации Роя Томаса Филдинга:
Я не вижу серьезных подводных камней в обоих подходах, но я бы предпочел подход B вместо подхода A: URL-адреса короче, их легче запомнить и не требуется много параметров.
Бонусные баллы: API Spotify и Facebook следуют этому подходу.Конечно, есть и другие API, но это те, которые мне приходили в голову.
Ответ 2
-
REST ничего не говорит о дизайне URL. Любая схема URL, которую вы придумали, - RESTful. Вы должны спросить, хороший ли он. И да, второй подход предпочтительнее первого. Первая - тонна шума для клиентов и огромная проблема обслуживания для владельца. Это также ограничивает гибкость в будущем.
-
Нет значительных ошибок, о которых я знаю, пока вы четко документируете, как использовать конечные точки. Например, типичным для вложенных конечных точек является только возврат связанных элементов и что DELETEing вложенного элемента будет удалять только ассоциацию, а не удалять сам элемент. Такое поведение должно быть документировано так или иначе.
Бонусные баллы: запрос на внешние ресурсы выходит за рамки.
Ответ 3
Подход к дизайну вызывает несколько вопросов, которые необходимо учитывать при выборе между ними:
Существование
В A очень интуитивно понятно, что когда вы удаляете компанию, вы также удаляете все ее вспомогательные ресурсы - отделы и сотрудников.
В B пользователь API должен подумать о таком действии - нужно ли мне вызывать удаление для всех сотрудников, или достаточно ли удалить компанию? конечно, это может быть документировано, но все же, это не прямолинейно.
A имеют преимущество здесь, потому что это очень ясно - при удалении resouce вы удаляете все его вспомогательные ресурсы.
Конечная точка операции
Другой вопрос, который он поднимает - как мне обновить объект? с какого конца?
Если я хочу удалить сотрудника, достаточно ли обновить компанию новым набором сотрудников? или я должен УДАЛИТЬ сотрудника?
Или скажите, что я хочу сменить сотрудника из одной компании в другую. В B теоретически я могу обновить сотрудника в поле компании и выполнить его. В A нет такого пути...
A имеют преимущество, когда они очень прямолинейны, как сделать действие - CRUD на URL-адрес сущности. B заставляет пользователя API останавливаться и задаться вопросом, какое действие он может сделать на каком URL.
Но в то же время B имеют преимущество в том, что изменение "родительского контроля" объекта проще (в тех случаях, когда оно имеет значение).
Validation
В A необходимо проверить соответствие URL-адресов, поскольку пользователь может указать идентификатор сотрудника не той компании или отделу.
В B такой проблемы нет.
Ответ 4
Итак, это может показаться немного сумасшедшим, но в HTTP/REST нет такой вещи, как "subresource".
В вашей модели домена отдел не может существовать без компании.
Теперь в вашем API вы обнаружите json-представление компании в /companies/{companyId}
и json-представление отдела в /companies/{companyId}/departments/{departmentId}
.
Это оба "ресурсы". Ресурс, в терминологии HTTP/REST, означает только то, на что указывает URL. Таким образом, "json представительство компании" не является самой компанией.
Дизайн URL-адреса сам по себе представляет собой тупиковый переулок сам по себе - сами URL-адреса могут выглядеть как НИЧЕГО, неважно, читаемы они или нет. Разработчики делают запросы, выбирая URL-адреса в зависимости от названий операций из документации *. Попытка добавить смысл к URL-адресам сама по себе быстро запуталась и на самом деле может добавить путаницу с течением времени.
Лучше тратить время на документацию, вместо того, чтобы пытаться позволить людям сделать вывод о поведении домена.
* или гипермедиа (например, https://github.com/kevinswiber/siren)