Читая ответ на вопрос на идемпотентность HTTP PUT Я узнал об интересном аспекте условных запросов PUT, который приводит к следующему вопросу.
Спецификация условных запросов HTTP определяет в раздел 3.1:
Исходный сервер НЕ ДОЛЖЕН выполнять запрошенный метод, если полученное условие If-Match оценивается как ложное; вместо этого исходный сервер ДОЛЖЕН ответить либо а) кодом состояния 412 (сбой предварительного условия), либо б) одним из кодов состояния 2xx (успешно), если исходный сервер подтвердил, что запрашивается изменение состояния, а конечное состояние уже установлено. отражается в текущем состоянии целевого ресурса (т. е. изменение, запрошенное агентом пользователя, уже выполнено успешно, но агент пользователя может не знать об этом, возможно, из-за того, что предыдущий ответ был утерян или какое-то другое изменение было сделано совместимым изменением). пользовательский агент). В последнем случае исходный сервер НЕ ДОЛЖЕН отправлять поле заголовка валидатора в ответе, если только он не может убедиться, что запрос является дубликатом непосредственно предшествующего изменения, сделанного тем же пользовательским агентом.
Условные запросы PUT используются, чтобы избежать проблемы потери обновлений, когда несколько агентов действуют параллельно на одном и том же ресурсе (используя оптимистическую блокировку). Предположим, что стоимость ресурса
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "0"
{ "value": 0 }
Теперь два клиента одновременно хотят увеличить значение и отправить запрос PUT
с телом, содержащим новое значение, вместе с заголовком If-Match
.
PUT /value HTTP/1.1
Content-Type: application/json
If-Match: "0“
{ "value": 1 }
На первый запрос сервер отвечает со статусом 200
(OK
), а на второй со статусом 412
(Precondition Failed
), т.е. второй клиент должен повторить свою задачу (GET, обновить ресурс и PUT его с заголовком If-Match
с новым etag ценность).
Теперь давайте предположим, что оба клиента не получили ответа и, следовательно, оба снова отправляют запрос (очевидно, что они должны использовать один и тот же заголовок If-Match
). Согласно спецификации, сервер ДОЛЖЕН возвращать одинаковые коды состояния, то есть 200
для первого и 412
для второго. Если сервер вернет 200
обоим, обновление будет потеряно, а если сервер вернет 412
обоим, то оба клиента начнут заново, и в конце значение будет равно 3.
Мой вопрос в том, как это можно реализовать на стороне сервера. Я вижу необходимость в том, чтобы сервер хранил историю всех изменений состояния вместе с идентификацией клиента. Но как идентифицировать клиента? Будет ли достаточно комбинации client-ip и заголовка User-Agent?
Существуют ли (Java) библиотеки, которые реализуют это поведение из коробки? Также мне непонятно, какое поле заголовка валидатора нельзя отправлять клиенту.
{ "value": 1}
с новым etag 1 и без специальной обработки вернет412 Precondition Failed
на оба повторно отправленных запроса, в результате чего оба клиента начнут заново, получат текущее состояние с новым etag, изменят данные и обновят данные. Спецификация указывает, что в этом случае первому абоненту должно быть возвращено 200, а второму абоненту — 412, т.е. сервер должен хранить историю всех переходов вместе со ссылкой на клиента. Я правильно понял и как это реализовано? - person Dominik   schedule 20.03.2019