REST API-массовое создание или обновление в одном запросе


предположим, что есть два ресурса Binder и Doc с ассоциативными отношениями означает, что Doc и Binder стоять самостоятельно. Doc может принадлежать или не принадлежать Binder и Binder может быть пустым.

если я хочу создать REST API, который позволяет пользователю отправлять коллекцию Docs,В ОДНОМ ЗАПРОСЕ следующим образом:

{
  "docs": [
    {"doc_number": 1, "binder": 1}, 
    {"doc_number": 5, "binder": 8},
    {"doc_number": 6, "binder": 3}
  ]
}

и для каждого дока в docs,

  • если doc существует, затем назначьте его Binder
  • если doc не существует, создайте его, а затем назначьте его

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

  • какой метод HTTP использовать?
  • какой код ответа должен быть возвращен?
  • это вообще подходит для отдыха?
  • как будет выглядеть URI? /binders/docs?
  • обработка массового запроса, что делать, если несколько элементов поднять ошибка, но другие проходят. Какой код ответа должен быть возвращен? Должна ли массовая операция быть атомной?
4 54

4 ответа:

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

  • С помощью POST метод обычно используется для добавления элемента при использовании в списке ресурсов, но вы также можете поддерживать несколько действий для этого метода. Смотрите этот ответ:Как обновить коллекцию ресурсов REST. Вы также можете поддерживать различные форматы представления для ввода (если они соответствуют массиву или a один элемент.)

    в этом случае нет необходимости определять формат для описания обновления.

  • С помощью PATCH метод также подходит, так как соответствующие запросы соответствуют частичному обновлению. Согласно RFC5789 (http://tools.ietf.org/html/rfc5789):

    несколько приложений, расширяющих протокол передачи гипертекста (HTTP), требуют, чтобы функция выполняла частичный ресурс модификация. Существующий метод HTTP PUT позволяет только полную замену документа. Это предложение добавляет новый метод HTTP, PATCH, чтобы изменить существующий ресурс HTTP.

    в этом случае вы должны определить свой формат для описания частичного обновления.

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

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

вы можете иметь два варианта относительно путей ресурсов.

  • используя путь к ресурсу для списка документов

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

вот пример маршрута для этого /docs.

содержание такого подхода может быть для метода POST:

[
    { "doc_number": 1, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 2, "binder": 4, (other fields in the case of creation) },
    { "doc_number": 3, "binder": 5, (other fields in the case of creation) },
    (...)
]
  • используя путь подресурсов связующего элемента

кроме того, вы также можете использовать подмаршруты для описания связи между документами и привязками. Намеки относительно связь между документом и связующим теперь не нужно указывать в содержании запроса.

вот пример маршрута для этого /binder/{binderId}/docs. В этом случае отправка списка документов с помощью метода POST или PATCH прикрепит документы к папке с идентификатором binderId после создания документа, если он не существует.

содержание такого подхода может быть для метода POST:

[
    { "doc_number": 1, (other fields in the case of creation) },
    { "doc_number": 2, (other fields in the case of creation) },
    { "doc_number": 3, (other fields in the case of creation) },
    (...)
]

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

  • Atomic

в этом случае вы можете использовать статус HTTP. Если все пойдет хорошо, вы получите статус 200. Если нет, то другой статус, как 400 если предоставленные данные не правильно (например, идентификатор связующего недопустим) или что-то еще.

  • атомные номера

в этом случае, статус 200 будет возвращен, и это до представления ответа, чтобы описать, что было сделано и где ошибки в конечном итоге происходят. ElasticSearch имеет конечную точку в своем REST API для массового обновления. Это может дать вам некоторые идеи на этом уровне: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html.

  • асинхронные

вы также можете реализовать асинхронную обработку для обработки предоставленных данных. В этом случае статус HTTP возвращает значение 202. Клиент должен вытащить дополнительный ресурс, чтобы увидеть, что происходит.

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

следующая ссылка также может помочь вам:https://templth.wordpress.com/2014/12/15/designing-a-web-api/.

надеюсь, что это поможет вам, Тьерри

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

делаешь PATCH /docs Это определенно допустимый вариант. Вы можете найти использование стандартных форматов патчей сложным для вашего конкретного сценария. Не уверен насчет этого.

вы могли бы использовать 200. Вы также можете использовать 207 - Мульти Статус

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

Если вы используете метод патча, я бы подумал, что ваша операция должна быть атомарной. т. е. я бы не использовал код состояния 207, а затем сообщал об успехах и неудачах в теле ответа. Если вы используете операцию POST, то подход 207 жизнеспособен. Вам нужно будет создать свой собственный орган ответа для обмена информацией о том, какие операции завершились успешно, а какие-нет. Я не знаю стандартизированный один.

поставить ing

PUT /binders/{id}/docs создать или обновить и связать один документ со связующим

например:

PUT /binders/1/docs HTTP/1.1
{
  "docNumber" : 1
}

патч ing

PATCH /docs создать документы, если они не существуют и связать их с привязками

например:

PATCH /docs HTTP/1.1
[
    { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } },
    { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } },
    { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } }
] 

я включу дополнительные идеи позже, но в то же время, если вы хотите, посмотрите на RFC 5789,RFC 6902 и Уильяма Дюрана пожалуйста. Не патч, как идиот запись в блоге.

в проекте, над которым я работал, мы решили эту проблему, реализовав то, что мы назвали "пакетными" запросами. Мы определили путь /batch где мы приняли JSON в следующем формате:

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 5,
         binder: 8
      }
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      }
   },
]

ответ имеет код состояния 207 (мульти-статус) и выглядит так:

[  
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 1,
         binder: 1
      }
      status: 200
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         error: {
            msg: 'A document with doc_number 5 already exists'
            ...
         }
      },
      status: 409
   },
   {
      path: '/docs',
      method: 'post',
      body: {
         doc_number: 6,
         binder: 3
      },
      status: 200
   },
]

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

Facebook и Google имеют похожие implementations:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

когда вы хотите создать или обновить ресурс с тем же вызовом, я бы использовал либо POST, либо PUT в зависимости от случая. Если документ уже существует, вы хотите, чтобы весь документ быть:

  1. заменяется документом, который вы отправляете (т. е. отсутствующие свойства в запросе будут удалены и уже существующие перезаписаны)?
  2. слияние с документом, который вы отправляете (т. е. отсутствующие свойства в запросе не будут удалены, а уже существующие свойства будут перезаписаны)?

в случае, если вы хотите поведение из Альтернативы 1 Вы должны использовать сообщение и в случае, если вы хотите поведение из Альтернативы 2 вы должны использовать КЛАСТЬ.

http://restcookbook.com/HTTP%20Methods/put-vs-post/

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