RESTful способ создания нескольких элементов в одном запросе

122

Я работаю над небольшой клиент-серверной программой для сбора заказов. Я хочу сделать это "REST (полностью) способом".

Что я хочу сделать:

Соберите все строки заказа (продукт и количество) и отправьте полный заказ на сервер

На данный момент я вижу два варианта сделать это:

  1. Отправьте каждую строку заказа на сервер: POST qty и product_id

На самом деле я не хочу этого делать, потому что хочу ограничить количество запросов к серверу, поэтому вариант 2:

  1. Соберите все строки заказов и сразу отправьте их на сервер.

Как реализовать вариант 2? У меня есть пара идей: оберните все строки заказа в объект JSON и отправьте его на сервер или используйте массив для публикации строк заказа.

Является ли реализация варианта 2 хорошей идеей или хорошей практикой, и если да, то как мне это сделать.

Что такое хорошая практика?


источник

Ответы:

74

Я считаю, что еще один правильный подход - создать еще один ресурс, представляющий вашу коллекцию ресурсов. Например, представьте, что у нас есть такая конечная точка, /api/sheep/{id}и мы можем выполнить POST /api/sheepдля создания ресурса овцы.

Теперь, если мы хотим поддерживать массовое создание, мы должны рассмотреть новый ресурс стада /api/flock(или /api/<your-resource>-collectionесли у вас нет более значимого имени). Помните, что ресурсы не нужно сопоставлять с вашей базой данных или моделями приложений . Это распространенное заблуждение.

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

Я думаю, можно с уверенностью предположить, что использование ресурса может также означать создание нескольких других сущностей в качестве побочного эффекта.

miguelcobain
источник
Я согласен с этим. Вы должны абстрагироваться от концепции коллекции вашего ресурса и рассматривать ее как ресурс. Это также даст вам больше гибкости в будущем, когда вы захотите начать выполнять операции с этим и т. Д.
villy393
Это правильный подход. Это не нарушает запрос на сбор POST. Поскольку он используется для публикации одного объекта. Отправка массового запроса с «отдельным массовым объектом» - правильный подход.
Sorter
2
Мне так нравится, как вы называете конечную точку api с помощью овец и стад! С некоторой степенью абстракции это почти имеет библейское упоминание: «У меня есть другие овцы, которые не из этого загона; Я должен привести и их, и они услышат Мой голос; и станут одним стадом с одним пастырем». Иоанна 10:16.
Евгений
1
Интересно, что люди рекомендуют использовать форму множественного числа (коллекции) в URL-адресе, когда вы хотите создать один ресурс, например: отправьте POST в / api / books для создания книги. Но затем, когда вы хотите создать 100 книг (в одном запросе как json), по какому URL-адресу вы разместите коллекцию из 100 книг? вот где начинается беспокойство.
code4kix
@ code4kix, который вы могли бы использовать /api/book-group, /api/book-collectionили что-то подобное.
miguelcobain
47

Хотя массовые операции (например, пакетное создание) необходимы во многих системах, формально они не рассматриваются в архитектурном стиле RESTful.

Я обнаружил, что POSTing коллекции, как вы предложили, в основном работает, но проблемы возникают, когда вам нужно сообщить об ошибках в ответ на такой запрос. Такие проблемы усугубляются, когда происходит несколько сбоев по разным причинам или когда сервер не поддерживает транзакции. Я предлагаю вам, если нет проблем с производительностью, например, когда поставщик услуг находится в локальной сети (не в глобальной сети) или данные относительно малы, стоит отправить 100 запросов POST на сервер. Будьте проще, начните с отдельных запросов, а если у вас есть проблемы с производительностью, попробуйте оптимизировать.

LiorH
источник
3
Вы сами нашли решение ошибок при дозировании? Отправка 100 почтовых запросов по мобильному соединению для отображения стыков страницы - плохая идея.
Thomas Ahle
Я добавляю ошибки в массив, перенаправляю пользователя на страницу с ошибкой 419 Conflict (и возвращаю эту ошибку клиенту) и отображаю массив ошибок. См. Мой ответ ниже для более подробной информации.
Эрик Фуллер,
5
Это нонсенс. Речь идет об отправке заказа на многие товары, которые, как многие сказали, вы можете просто отправить в сущность одного запроса POST. Другое дело, как сервер это обрабатывает. В этом случае я не вижу проблем с созданием заказа, заполнением того, что вы можете для этого заказа, а также заполнением деталей, которые не могли быть выполнены. Таким образом, пользователь может увидеть свой заказ и увидеть, что все, кроме N товаров, были добавлены в заказ, но некоторые из них отсутствуют на складе или система не знает, что с ними делать. Еще один более простой, но менее удобный вариант - отклонить все,
thecoshman
2
@thecoshman сильно изменился за 3,25 года. Вероятно, вам стоит опубликовать полностью сформулированный ответ на вопрос.
dlamblin
3
@dlamblin да, мне, наверное, нужно сделать много вещей ... Возможно, я доберусь до этого на каком-то этапе ...
thecoshman
9

Facebook объясняет, как это сделать: https://developers.facebook.com/docs/graph-api/making-multiple-requests

Простые пакетные запросы

Пакетный API принимает массив логических HTTP-запросов, представленных в виде массивов JSON - каждый запрос имеет метод (соответствующий HTTP-методу GET / PUT / POST / DELETE и т. Д.), Relative_url (часть URL-адреса после graph.facebook. com), необязательный массив заголовков (соответствующий заголовкам HTTP) и необязательный текст (для запросов POST и PUT). Пакетный API возвращает массив логических HTTP-ответов, представленных в виде массивов JSON - каждый ответ имеет код состояния, необязательный массив заголовков и необязательное тело (которое представляет собой строку в кодировке JSON).

rwitzel
источник
1
Это очень интересная ссылка, предлагаемое решение мне кажется полезным. В любом случае, в StackOverflow предпочтительный ответ объясняет концепцию решения в теле ответа, поскольку ссылки могут измениться или исчезнуть.
Ян Влцинский
7
Это действительно способ сделать это Facebook, не обязательно RESTful, как попросил OP
0cd,
Я думаю, что пакетный API (от Google, Facebook и т. Д. - @PuneetArora) более полезен при группировании нескольких несвязанных запросов вместе. Создание запроса, который создает один элемент, а затем группирование всех этих запросов вместе для отправки коллекции элементов - это «безумие» (Эйнштейн). Просто создайте запрос, который передает коллекцию элементов.
tfmontague 01
8

Мне ваша идея кажется верной. Реализация - дело ваших предпочтений. Вы можете использовать JSON или просто параметры для этого (массив "order_lines []") и сделать

POST /orders

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

Милан Новота
источник
6

Думаю, лучше отправлять отдельные запросы в рамках одного соединения . Конечно, ваш веб-сервер должен это поддерживать.

zakovyrya
источник
5

Я действительно боролся с этим в последнее время, и вот над чем я работаю.

Если POST, который добавляет несколько ресурсов, завершается успешно, верните 200 OK (я рассматривал 201, но пользователь в конечном итоге не попадает на ресурс, который был создан) вместе со страницей, которая отображает все добавленные ресурсы, либо в режиме чтения -только или редактируемый мод. Например, пользователь может выбрать и отправить несколько изображений в галерею, используя форму, содержащую только один входной файл. Если запрос POST завершается успешно, пользователю предоставляется набор форм для каждого созданного представления ресурса изображения, что позволяет ему указать более подробную информацию о каждом (имя, описание и т. Д.).

Если не удается создать один или несколько ресурсов, обработчик POST прерывает всю обработку и добавляет каждое отдельное сообщение об ошибке в массив. Затем возвращается Конфликт 419, и пользователь перенаправляется на страницу ошибки 419 Конфликт, которая представляет содержимое массива ошибок, а также обратный путь к отправленной форме.

Эрик Фуллер
источник
-2

Вы не захотите отправлять заголовки HTTP для 100 строк заказа. Вы также не хотите создавать больше запросов, чем необходимо.

Отправьте весь заказ в одном объекте JSON на сервер: server / order или server / order / new. Верните то, что указывает на: server / order / order_id

Также рассмотрите возможность использования CREATE PUT вместо POST

бодрый
источник
Я полагаю, это метод HTTP POST. Не существует такой вещи, как метод CREATE HTTP.
Милан Новота, 04
Нет? Ой, подождите, не было. Вместо этого были PUT.
Cheery
22
Зачем вам использовать PUT для создания контента? Именно для этого и предназначен метод HTTP POST.
thecoshman
8
Вы используете PUT для создания ресурсов, когда хотите, чтобы клиент указывал URI ресурса, как в webdav. Я не согласен с использованием плаката PUT, но он имеет место в создании ресурсов, хотя это место может быть ограничено по объему.
user602525
2
Примечание: POST-запрос объекта должен привести к тому, что объект станет подчиненным по отношению к ресурсу, указанному в запросе, и не будет идемпотентным. PUT заменяет объект по адресу и является идемпотентным. Идемпотентность (слово?) - важное ожидание потребителей.
Люк Пуплетт 05