Вы используете неправильный HTTP-глагол для операции создания. RFC 2616 определяет семантику операций для POST
и PUT
.
Пункт 9.5:
POST
метод используется для запроса
что исходный сервер принимает
юридическое лицо, включенное в запрос как
новый подчиненный ресурса
идентифицируется Request-URI в строке запроса
Пункт 9,6
PUT
метод требует, чтобы
закрытая сущность должна храниться под
предоставлен URI запроса.
Есть тонкие детали этого поведения, например, PUT
может использоваться для создания нового ресурса по указанному URL, если он еще не существует. Однако POST
никогда не должен помещать новую сущность в URL запроса, а PUT
всегда должен помещать любую новую сущность в URL запроса. Это отношение к URL-адресу запроса определяет POST
как CREATE
и PUT
как UPDATE
.
Согласно этой семантике, если вы хотите использовать PUT
для создания нового человека, он должен быть создан в /CREATE_PERSON/{transaction_id}
. Другими словами, идентификатор транзакции, возвращаемый вашим первым запросом, должен быть ключом, который использовался для получения этой записи позже. Вы не должны PUT
запрашивать URL-адрес, который не будет конечным местоположением этой записи.
Еще лучше, тем не менее, вы можете сделать это как атомарную операцию, используя POST
до /CREATE_PERSON
. Это позволяет одним запросом создать новую запись о человеке и получить в ответ новый идентификатор (который также должен быть указан в заголовке HTTP Location
).
Между тем, в рекомендациях REST указано, что глаголы не должны быть частью URL ресурса. Таким образом, URL для создания нового человека должен совпадать с адресом для получения списка всех людей - /PERSONS
(я предпочитаю форму множественного числа: -)).
Таким образом, ваш REST API становится:
- чтобы получить всех людей -
GET /PERSONS
- на одного человека -
GET /PERSONS/{id}
- для создания нового человека -
POST /PERSONS
с телом, содержащим данные для новой записи
- для обновления существующего человека или создания нового человека с известным идентификатором -
PUT /PERSONS/{id}
с телом, содержащим данные для обновленной записи.
- чтобы удалить существующего человека -
DELETE /PERSONS/{id}
Примечание: я лично предпочитаю не использовать PUT для создания записей по двум причинам, если только мне не нужно создавать вспомогательную запись с таким же идентификатором, как у уже существующей записи из другого набора данных (также известный как «чужой бедняк» ключ ': -)).
Обновление: Вы правы, что POST
не идемпотентен, и это согласно спецификации HTTP. POST
будет всегда возвращать новый ресурс. В приведенном выше примере этот новый ресурс будет контекстом транзакции.
Однако, я хочу сказать, что вы хотите, чтобы PUT
использовался для создания нового ресурса (записи о человеке) и, согласно спецификации HTTP, этот новый ресурс сам должен располагаться по URL-адресу. В частности, когда ваш подход нарушается, URL, который вы используете с PUT
, является представлением транзакционного контекста, созданного POST, а не представлением самого нового ресурса. Другими словами, запись о человеке является побочным эффектом обновления записи о транзакции, а не ее непосредственный результат (обновленная запись о транзакции).
Конечно, при таком подходе запрос PUT
будет идемпотентным, поскольку после создания записи о человеке и завершения транзакции последующие запросы PUT
ничего не сделают. Но теперь у вас возникла другая проблема - чтобы действительно обновить эту запись о человеке, вам нужно будет сделать запрос PUT
на другой URL - тот, который представляет запись о человеке, а не транзакцию, в которой она была создана. Теперь у вас есть два отдельных URL-адреса, которые ваши клиенты API должны знать и делать запросы на манипулирование одним и тем же ресурсом.
Или же вы можете получить полное представление последнего состояния ресурса, скопированного в запись транзакции, а также чтобы обновления записей о персонале также просматривали URL транзакции для обновлений.Но на этом этапе URL транзакции равен для намерений и целей записи о человеке, что означает, что она была создана по запросу POST
на первом месте.