Find-or-create idiom в дизайне REST API?

говорят, что у нас есть ресурс пользователя с уникальным ограничением на "имя". как бы вы проектировали REST API для работы с приложением для поиска или создания (по имени)? Я вижу следующие варианты:

вариант 1: несколько запросов

клиент:

POST /user
{"name":"bob"}

Сервер:

HTTP 409 //or something else

клиент:

GET /user?name=bob

Сервер:

HTTP 200 //returns existing user

вариант 2: один запрос, два кода ответа

клиент:

POST /user
{"name":"bob"}

сервер:

HTTP 200 //returns existing user

(в случае, если пользователь фактически создан, вместо этого верните HTTP 201)

вариант 3: запрос ошибок, но данные ответа содержат конфликтующий объект

клиент:

POST /user
{"name":"bob"}

Сервер:

HTTP 409 //as in option1, since no CREATE took place
{"id": 1, "name":"bob"} //existing user returned

Ответ 1

Я считаю, что "правильный" RESTful способ сделать это:

GET /user?name=bob
   200: entity contains user
   404: entity does not exist, so
        POST /user { "name" : "bob" }
           303: GET /user?name=bob
                200: entity contains user

Я также являюсь большим поклонником шаблона Post-Redirect-Get, который повлечет за собой передачу сервером перенаправления клиенту с помощью uri вновь созданного пользователя. Тогда ваш ответ в случае POST будет содержать объект в своем теле с кодом состояния 200.

Это означает, что 1 или 3 раунда поездки на сервер. Большим преимуществом PRG является защита клиента от повторной загрузки, когда происходит перезагрузка страницы, но вы должны прочитать больше об этом, чтобы решить, подходит ли это вам.

Если это слишком много назад и вперед с сервером, вы можете сделать вариант 2. Это не строго RESTful, когда я прочитал https://tools.ietf.org/html/rfc2616#section-9.5:

Действие, выполняемое методом POST, может не привести к ресурсу которые могут быть идентифицированы с помощью URI. В этом случае либо 200 (OK), либо 204 (Без содержимого) - соответствующий статус ответа, в зависимости от того, или не ответ включает объект, который описывает результат.

Если вы согласны с откатом от стандарта, и вы обеспокоены поездками в оба конца, тогда вариант 2 является разумным.

Ответ 2

Я бы пошел с Вариантом 2 по двум причинам:

Во-первых, код ответа HTTP, 2xx (например, 200 201), относится к успешной операции в отличие от 4xx. Таким образом, в обоих случаях, когда поиск или создание происходит, у вас успешная операция.

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

Ответ 3

Я использую версию опции 2. Я возвращаю 201, когда создается ресурс, и 303 ( "see other" ), когда он просто извлекается. Я решил сделать это, отчасти потому, что get_or_create, похоже, не является общей идиомой REST, а 303 - немного необычным кодом ответа.