REST коллекции коллекций, продвижение и демонстрация - PullRequest
0 голосов
/ 15 мая 2019

У нас есть ресурс, который называется задачи.Со следующими конечными точками мы можем перечислить все задачи и создать задачи:

GET:  /tasks
POST: /tasks

Проблема состоит в том, что задачи могут содержать подзадачи, и мы хотим встроить функциональность в обе поддержки, чтобы продвигать подзадачи всвои собственные задачи и понижение задач на подзадачи другой задачи.

Наивный подход должен состоять в том, чтобы удалить подзадачу и снова создать ее как задачу, и наоборот, но мы находим это слишком наивным.

Второй вариант, который мы получилиup with - это поддержка конечных точек, таких как следующие, где {id} - это идентификатор задачи, а {sid} - это идентификатор подзадачи:

POST: /tasks/{id}/add/{sid}
POST: /tasks/{id}/upgrade/{sid}

Первая конечная точка должна добавить задачу к другой задаче, тем самым удалив первую задачу и добавив копию как подзадачу ко второй задаче.Вторая конечная точка должна обновить подзадачу до задачи из другой задачи, тем самым удалив подзадачу и добавив копию в качестве задачи.

Таким образом, наш вопрос, будет ли этосчитается хорошей практикой, или есть ли другие варианты, которые мы не рассмотрели?

Ответы [ 3 ]

3 голосов
/ 15 мая 2019

Важно подчеркнуть, что REST вообще не заботится о написании URI. Однако, если центральным элементом архитектурного стиля REST является ресурс , то имеет смысл, что в URI используются существительные вместо глаголов .

Архитектурный стиль REST не зависит от протокола, но обычно он реализован поверх протокола HTTP. В этом подходе метод HTTP предназначен для выражения операции , которая будет выполняться над ресурсом, идентифицированным данным URI.


В вашем вопросе не указано, как выглядит ваш домен. Но давайте рассмотрим, что у вас есть что-то вроде:

+-------------------+
|       Task        |
+-------------------+
|- id: Long         |
|- title: String    |
|- parent: Task     |
|- children: Task[] |
+-------------------+

Создание задачи можно выразить с помощью запроса POST:

POST /tasks
Host: example.org
Content-Type: application/json

{
  "title": "Prepare dinner"
}
HTTP/1.1 201 Created
Location: /tasks/1

Создание подзадачи может быть выражено с помощью запроса POST, указывающего parentId в полезной нагрузке:

POST /tasks
Host: example.org
Content-Type: application/json

{
  "parentId": 1
  "title": "Order a pizza"
}
HTTP/1.1 201 Created
Location: /tasks/2

Содействие подзадаче в задаче может быть достигнуто путем установки parentId в null с использованием PATCH запроса:

PATCH /tasks/2
Host: example.org
Content-Type: application/json-patch+json

[
  { 
    "op": "replace", "path": "/parentId", "value": null
  }
]
HTTP/1.1 204 No Content

Обновление задачи, чтобы стать подзадачей, может быть достигнуто путем установки parentId с использованием запроса PATCH:

PATCH /tasks/2
Host: example.org
Content-Type: application/json-patch+json

[
  { 
    "op": "replace", "path": "/parentId", "value": 1 
  }
]
HTTP/1.1 204 No Content

Приведенные выше примеры используют JSON Patch (application/json-patch+json) в качестве полезной нагрузки для PATCH. В качестве альтернативы вы можете рассмотреть JSON Merge Patch (application/merge-patch+json). Я не буду вдаваться в различия таких форматов, потому что это сделает этот ответ слишком длинным. Но вы можете перейти по ссылкам выше и проверить их самостоятельно.

Об обработке ошибок см. Раздел Обработка ошибок в RFC 5789 , документ, определяющий метод PATCH.


Я также признателен за то, что некоторые API избегают использования PATCH по ряду причин, которые выходят за рамки этого ответа. Если это так, вы можете рассмотреть PUT. При таком подходе состояние ресурса будет заменено на состояние, определенное в представлении, отправленном в полезной нагрузке запроса.

В качестве альтернативы вы можете иметь конечную точку, такую ​​как /tasks/{id}/parent, поддерживающую PUT. При таком подходе клиенту просто нужно отправить представление родителя (например, идентификатор) вместо полного представления задачи.

Выберите подход, который подходит вам лучше всего.

2 голосов
/ 15 мая 2019

REST - это аббревиатура для передачи текущего состояния ресурса в формате представления, поддерживаемом клиентом. Поскольку REST является обобщением Всемирной паутины, те же понятия, которые вы используете для Интернета, также применимы к приложениям, следующим модели архитектуры REST. Таким образом, основной вопрос решается вокруг: Как бы вы спроектировали свою систему для работы на веб-страницах и применили те же шаги к вашему дизайну.

Как Кассио уже упоминал, что написание URI не имеет значения для клиентов, так как URI остается URI, и вы не можете определить из URI, является ли система «RESTful» или нет. На самом деле, нет такой вещи, как «RESTful» или «RESTless» URI, поскольку URI, как указано выше, остается URI. Вероятно, лучше рассматривать URI как ключ, используемый для кэширования ответов в локальном или промежуточном кэше. Филдинг сделал поддержку кеширования даже с ограничением , а не просто с опцией.

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

Я не согласен с user991710 в одном из его комментариев о том, что REST нельзя использовать для представления процессов, однако я согласен с тем, что REST также не должен пытаться создавать новые глаголы. Как упоминалось ранее, REST предназначен для передачи текущего состояния ресурсов в поддерживаемом формате представления. Если задача может быть представлена ​​в виде потока данных, то она также может быть представлена ​​клиенту. Самоинформативность сообщений, т. Е. Использование типа носителя, который определяет правила обработки полезных данных, гарантирует, что клиент сможет разобраться в данных. То есть Ваш браузер может отображать изображения, воспроизводить видео, показывать текст и тому подобное, поскольку он знает, как интерпретировать поток данных соответствующим образом. Поддержка таких полей может быть добавлена ​​с помощью специальных дополнений или плагинов, которые могут загружаться динамически во время выполнения даже без перезапуска приложения.

Если бы мне приходилось разрабатывать задачи для веб-страниц, я бы изначально возвращал отображаемое по страницам представление существующих задач, возможно, отображаемых в таблице, со ссылкой на страницу, которая может создавать новые задачи, или ссылкой в ​​каждой строке. обновить или удалить существующую задачу. Страницы create-new и update могут использовать одну и ту же HTML-форму для ввода или обновления информации о задачах. Если задача должна быть назначена в качестве подзадачи для другой задачи, вы можете выбрать родительскую задачу из заданного набора задач, например, в раскрывающемся текстовом поле, или ввести URI родительского элемента в поле. выделенное поле. При отправке задачи будет использоваться HTTP-метод POST, который будет выполнять операцию на основе собственной семантики сервера, поэтому создается новый ресурс или обновляется один или несколько из них на сервере. Суть в том, что все, что может сделать клиент, преподается сервером. Форма для добавления новых или обновления существующих задач просто сообщает клиенту, какие поля поддерживаются (или ожидаются) сервером. На самом деле для выполнения запроса клиенту не требуется никаких внешних внеполосных знаний. На сервере все еще есть возможность отклонять неполные полезные нагрузки или полезные нагрузки, которые нарушают определенные ограничения, и клиент узнает об этом, получив соответствующее сообщение об ошибке.

Поскольку клиенты не должны анализировать или интерпретировать URI, они будут использовать определенный текст, описывающий, что делают URI. В браузере изображение мусорной корзины может использоваться в качестве символа для удаления, в то время как карандаш может использоваться в качестве символа для обновления существующей записи (или тому подобного). Как люди, мы быстро понимаем, для чего предназначены эти ссылки, без необходимости читать символы фактического URI.

Последние два абзаца просто суммировали, как может выглядеть модель взаимодействия на обычной веб-странице. Те же понятия должны быть использованы и в архитектуре REST. Контент, которым вы обмениваетесь, может отличаться в большей степени, в идеале с стандартизированными форматами представления , по сравнению со старшим братом, Интернетом, хотя концепции ссылок используются для ссылки с одного ресурса на другие ресурсы, и сервер учит клиентов тому, что это необходимо применить здесь. Однако по сравнению с HTML вы можете использовать больше HTTP-методов, чем просто POST и GET. Удаление представления ресурса, вероятно, более целесообразно при использовании метода DELETE, чем при использовании метода POST. Кроме того, для обновлений может иметь смысл PUT или PATCH, в зависимости от ситуации. В отличие от использования разумных картинок для намека пользователей на то, для чего может пригодиться эта ссылка, следует использовать имена отношений ссылок, которые намекают клиентам о назначении ссылки. Такие имена связей должны быть стандартизированными или, по крайней мере, выражать здравый смысл, такой как выраженный через специальные онтологии , или использовать абсолютные URI в качестве механизма расширения .

Вы можете добавить выделенные ссылки, чтобы показать коллекцию всех задач на основе определенных фильтров, то есть всех задач или только родительских задач, а что нет, чтобы клиент мог выполнять итерацию по интересующей его задаче. При выборе задачи вы можете добавьте ссылки на все подзадачи, которые может вызвать клиент, чтобы узнать, что это за подзадачи, если это необходимо. Здесь URI задач могут оставаться неизменными, что поддерживает кэширование по умолчанию. Обратите внимание, что я ничего не упомянул относительно того, как вы должны хранить данные в своем бэкэнде, так как это всего лишь некоторые детали реализации, которые обычно не интересуют клиента. Это просто идеальный случай, когда доменная модель не обязательно должна быть похожим на представление состояния ресурсов.

Относительно того, какая операция HTTP для выполнения повышения или понижения задачи является в основном неким выбором дизайна, который также может зависеть от формата представления, на который обмениваются полезные данные. Поскольку HTTP поддерживает только POST и GET, такое изменение может быть запрошено через POST, другие типы носителей могут поддерживать другие HTTP-методы , такие как PUT, которые, согласно его * Спецификация 1044 * (последний абзац, стр. 27) может иметь побочные эффекты, или PATCH, который необходимо применять атомарно - либо полностью, либо вообще не применять. PATCH на самом деле похоже на исправление программного обеспечения, где набор инструкций должен быть применен к некоторому целевому коду. Уильям Дюран резюмировал эту концепцию в довольно цитируемом блоге . Однако позже он обновил свой пост в блоге, отметив, что с помощью application/merge-patch+json можно использовать более естественный и интуитивно понятный способ «обновления» ресурсов. Что касается поддержки формы, существует пара проектов, таких как hal-формы , halo-json (halform) , Ion или hydra , которые предлагают такое определение, но в настоящее время не имеют более широкой библиотечной поддержки или окончательного стандарта. Так что немного работы еще предстоит внести в принятый стандарт.

Чтобы завершить этот пост, вы должны разработать свой API так, как будто бы вы разрабатывали свою систему для работы с веб-страницами, применять те же концепции, которые вы используете для взаимодействия с типичными веб-страницами, такими как ссылки и формы, и использовать их в ваши ответы. Какая операция HTTP вы выполняете с помощью фактического продвижения или понижения, может зависеть от фактического типа носителя и от того, какие операции HTTP он поддерживает. POST должен работать во всех случаях, PUT, вероятно, более интуитивно понятен для типичных обновлений, выполняемых в веб-формах, в то время как для исправления потребуется, чтобы ваш клиент фактически рассчитал шаги, необходимые для преобразования представления текущего ресурса в желаемое (если вы используете application/json-patch+json в частности). application/merge-patch+json также может быть применимо, что значительно упростит исправление, поскольку текущие данные, содержащиеся в форме, могут быть просто отправлены на сервер, а правила по умолчанию будут определять, было ли удалено, добавлено или обновлено поле.

0 голосов
/ 15 мая 2019

Вы можете разделить свой маршрут на:

POST : /tasks => for create or add tasks 

PUT/PATCH : /tasks => for upgrading your tasks

или, если быть более точным, вы можете передать: id в params. Я думаю, что лучший подход - это когда вам нужно изменить удалить обновление или получить конкретный объект, который вы должны использовать: id в ваших параметрах

PUT/PATCH/GET : /tasks/:id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...