Ресурс RESTful - принимает список объектов

Я создаю коллекцию ресурсов RESTful, которые работают следующим образом: (Я буду использовать "людей" в качестве примера):

    GET /people/{key}
      - returns a person object (JSON)
    GET /people?first_name=Bob
      - returns a list of person objects who "first_name" is "Bob" (JSON)
    PUT /people/{key}
      - expects a person object in the payload (JSON), updates the person in the 
        datastore with the {key} found in the URL parameter to match the payload.
        If it is a new object, the client specifies the key of the new object.

Я чувствую себя довольно комфортно с дизайном до сих пор (хотя любой вход/критика приветствуется).

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

    PUT /people
      - expects a list of objects in JSON form with keys included in the object
        ("key":"32948").  Updates all of the corresponding objects in the datastore.

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

Кто-то упомянул об использовании запроса "PATCH" в более раннем вопросе, который у меня был: ресурс REST со свойством List

"PATCH" звучит фантастически, но я не хочу использовать его, потому что он еще не широко используется и не совместим с множеством программ и API.

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

Есть ли у кого-нибудь комментарии/предложения?

Последующий:

Пока я не решался использовать POST, потому что он, по-видимому, является наименее общим знаменателем, можно сказать, что все операции RESTful и многое другое можно сказать об этой операции (в частности, это идемпотент), PUT не может использоваться, потому что его требования слишком узкие. В частности: ресурс не полностью переписан и эквивалентный ресурс не отправляется обратно из запроса GET на тот же ресурс. Использование PUT со свойствами вне его спецификации могут вызвать проблемы, когда приложения, api и/или программисты пытаются работать с ресурсом и встречаются с неожиданным поведением с ресурсом.

В дополнение к принятому ответу, Даррел Миллер имел отличное предложение, если операция абсолютно должна была быть PUT, и это должно было добавить UUID в конец пути ресурса, так что эквивалентный запрос GET вернет эквивалентный ресурс.

Ответы

Ответ 1

POST указывает общее действие, отличное от GET, PUT и DELETE (общие действия хэш-таблицы). Поскольку общие действия хэш-таблицы не подходят, используйте POST. Семантика POST определяется ресурсом, для которого объект POST ed. Это не похоже на семантику общепринятых методов хэш-таблицы, которые хорошо известны.

POST /people/add-many HTTP/1.1
Host: example.com
Content-Type: application/json

[
  { "name": "Bob" },
  { "name": "Robert" }
]

Ответ 2

Использование PUT, безусловно, является неправильным глаголом в этом случае. POST предназначен для того, чтобы делать именно то, что вы просите. Из HTTP-спецификация:

Основное различие между запросами POST и PUT отражается в различном значении Request-URI. URI в запросе POST идентифицирует ресурс, который будет обрабатывать заключенный объект. Этот ресурс может быть процессом принятия данных, шлюзом к другому протоколу или отдельным объектом, который принимает аннотации. Напротив, URI в запросе PUT идентифицирует объект, заключенный с запросом - пользовательский агент знает, что такое URI, и сервер НЕ ДОЛЖЕН пытаться применить запрос к другому ресурсу...

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

Просто нужно, чтобы PUT был идемпотентным, а POST - нет, не означает, что POST не может быть идемпотентным. Ваш выбор HTTP-глагола не должен основываться на этом, но основываться на зависимости запрашиваемого ресурса и ресурса. Если ваше приложение напрямую обрабатывает запрошенный ресурс, используйте PUT. Если он действует на какой-то другой ресурс (или ресурсы, как в вашем случае), используйте POST.

Ответ 3

Я действительно не вижу никакого простого способа использовать PUT для создания произвольного набора людей. Если вы не готовы, клиент должен сгенерировать GUID и сделать что-то вроде

PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}

На стороне сервера вы можете взять людей из списка и добавить их в ресурс /People.

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

<link rel="AddList" href="/PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}"/>

в ресурсе People. Клиенту необходимо знать, что ему нужно перечислить список людей в ссылку AddList. Серверу необходимо убедиться, что каждый раз, когда он отображает ресурс /People, он создает новый url для ссылки AddList.

Ответ 4

Относительно предложения Даррена Миллера использовать PUT для GUID (я не могу комментировать...), точка использования PUT заключалась бы в достижении идемпотентности для операции. Тест лакмусовой бумажки, если идемпотент - это разговор между клиентом и сервером:

  • PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}
  • 204 NO CONTENT (указывает, что все прошло хорошо)
  • клиент теряет соединение и не видит 204
  • клиент автоматически повторяет попытку
  • PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}

Как сервер будет дифференцировать два? Если GUID "используется", так сказать, сервер должен будет ответить 404 или 410. Это вводит крошечный бит диалогового состояния на сервере, чтобы запомнить все используемые GUID.

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

Я думаю, что разумным решением является использование POST для создания (первоначально пустой, недолговечной) области хранения для ресурса, к которому вы можете ПОЗВОЛИТЬ, то есть клиентам необходимо ПОСТ, чтобы создать ресурс GUID, а не открывать его по ссылке

  • POST /PeopleList/CreateHoldingArea
  • 201 CREATED и Location: /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}
  • PUT /PeopleList/{1E8157D6-3BDC-43b7-817D-C3DA285DD606}

Это означало бы, что потерянная идемпотентность не привела бы к большим накладным расходам; клиенты просто создают новые GUID (посредством POSTing), если они не видят начального ответа 201 CREATED. "Маленький бит диалогового состояния" теперь будет только созданными, но еще не используемыми областями хранения.

Идеальное решение, конечно, не требовало бы никакого диалогового состояния на сервере, но оно ускользает от меня.