Подтвердить что ты не робот

Последствия POST, не являющиеся идемпотентными (RESTful API)

Мне интересно, имеет ли смысл мой нынешний подход или есть лучший способ сделать это.

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

В результате я решил разделить все это на двухэтапный процесс. Сначала отправляем запрос POST для создания нового объекта, который возвращает URI нового объекта в заголовке Location. Во-вторых, выполнение идемпотентного запроса PUT в предоставленное местоположение для заполнения нового объекта данными. Если новый объект не заселен в течение 24 часов, сервер может удалить его через какое-то пакетное задание.

Звучит ли это разумно или есть лучший подход?

Спасибо

4b9b3361

Ответ 1

Единственное преимущество создания POST над PUT-созданием - генерация идентификаторов серверов. Я не думаю, что стоит недостаток идемпотентности (а затем необходимость удаления дубликатов или пустых объектов).

Вместо этого я буду использовать PUT с UUID в URL-адресе. Благодаря генераторам UUID вы почти уверены, что идентификатор, создаваемый на стороне клиента, будет уникальным сервером.

Ответ 2

Ну, все зависит от того, чтобы начать с вас, нужно больше говорить о URI, ресурсах и представлениях и не беспокоиться об объектах.

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

на POST данных формы в /some _collection/

normalize the natural key of your data (Eg. "lowercase" the Title field for a blog post)
calculate a suitable hash value (Eg. simplest case is your normalized field value)
lookup resource by hash value
if none then
    generate a server identity, create resource
        Respond =>  "201 Created", "Location": "/some_collection/<new_id>" 
if found but no updates should be carried out due to app logic
        Respond => 302 Found/Moved Temporarily or 303 See Other 
        (client will need to GET that resource which might include fields required for updates, like version_numbers)
if found but updates may occur
   Respond => 307 Moved Temporarily, Location: /some_collection/<id> 
   (like a 302, but the client should use original http method and might do automatically) 

Подходящая хеш-функция может быть такой же простой, как некоторые конкатенированные поля, или для больших полей или значений можно использовать усеченную функцию md5. См. [Хеш-функция] для более подробной информации 2.

Я предположил, что вы:

  • требуется другое значение идентификатора, чем значение хэша
  • используемые поля данных для идентичности нельзя изменить.

Ответ 3

Но теперь у вас есть два запроса, которые могут быть потеряны? И POST можно повторить, создав еще один экземпляр ресурса. Не переусердствуйте. Просто попросите пакетный процесс обмануть. Возможно, у вас есть статистика подсчета "доступа" к вашим ресурсам, чтобы узнать, кто из кандидатов-дубликатов был результатом оставленного сообщения.

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

Ответ 4

Независимо от того, какой HTTP-метод вы используете, теоретически невозможно сделать идемпотентный запрос без создания клиентской части уникального идентификатора, временно (как часть некоторой системы проверки запроса) или как идентификатор постоянного сервера. Утерянный HTTP-запрос не создает дубликат, хотя есть опасения, что запрос может получить доступ к серверу, но ответ не возвращает его клиенту.

Если конечный клиент может легко удалять дубликаты, и они не приводят к конфликтам со встроенными данными, вероятно, это не является достаточно большой проблемой для разработки системы предотвращения дублирования ad-hoc. Используйте POST для запроса и отправьте клиенту обратно статус 201 в HTTP-заголовке и уникальный в сервере уникальный идентификатор в теле ответа. Если у вас есть данные, которые показывают, что дублирование является частым явлением или какой-либо дубликат вызывает серьезные проблемы, я бы использовал PUT и создал уникальную идентификационную клиентскую сторону. Использовать идентификатор клиента в качестве идентификатора базы данных - нет никакого преимущества для создания дополнительного уникального идентификатора на сервере.

Ответ 5

Ваш метод генерации идентификаторов на сервере, в приложении, в специальном запросе-ответе, является очень хорошим! Уникальность очень важна, но клиенты, такие как женихи, будут продолжать повторять запрос до тех пор, пока они не добьются успеха, или пока они не получат отказ, который они готовы принять (маловероятно). Поэтому вам нужно получить уникальность где-то, и у вас есть только два варианта. Либо клиент, либо GUID, как предлагает Aurélien, или сервер, как вы предлагаете. Мне нравится вариант сервера. Столбцы семян в реляционных БД являются доступным источником уникальности с нулевым риском столкновений. В 2000 году я прочитал статью, защищающую это решение, называемое "Простая надежная передача сообщений с HTTP", поэтому это установленный подход к реальной проблеме.

Чтение вещей REST, вы могли бы быть прощены за то, что куча подростков только что унаследовала особняк Элвиса. Они взволнованно обсуждают, как переставить мебель, и они истеричны в идее, которую им может понадобиться, чтобы что-то принести из дома. Рекомендуется использовать POST, потому что он там, без каких-либо проблем с не-идемпотентными запросами.

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

Относительно бесплодная дискуссия о том, что означает идемпотент, я говорю, что это должно быть все. Последовательные запросы не должны генерировать никаких дополнительных эффектов и должны получать тот же ответ, что и первый обработанный запрос. Чтобы реализовать это, вы захотите сохранить все ответы сервера, чтобы их можно было воспроизвести, и ваши идентификаторы будут определять действия, а не только ресурсы. Вы будете выгнаны из особняка Элвиса, но у вас будет бомбоубеживающий апи.

Ответ 6

Я думаю, вы могли бы также свернуть запрос создания и обновления только на один запрос (upsert). Чтобы создать новый ресурс, клиентский POST-ресурс "factory", расположенный, например, в / factory -url-name. И затем сервер возвращает URI для нового ресурса.