Ответ
Согласно спецификации HTTP, метод PUT является идемпотентным. Это означает, что несколько идентичных запросов PUT должны оказывать на сервер тот же эффект, что и один запрос.
Типичное поведение REST API:
- Первый вызов PUT на
/resource/{id}: Если ресурс с такимidсуществует, он полностью заменяется телом запроса. Если не существует — создается (если серверная логика это позволяет). - Последующие идентичные вызовы: Состояние ресурса после первого успешного вызова не меняется. Сервер должен возвращать тот же успешный статус (200 OK или 204 No Content).
Пример на C#:
// Все три вызова приведут к одному и тому же состоянию ресурса
var userData = new { name = "Alice", role = "Admin" };
var content = new StringContent(JsonSerializer.Serialize(userData), Encoding.UTF8, "application/json");
await httpClient.PutAsync("/api/users/123", content); // Создает или заменяет
await httpClient.PutAsync("/api/users/123", content); // Ничего не меняет
await httpClient.PutAsync("/api/users/123", content); // Ничего не меняет
Критические уточнения:
- Идемпотентность ≠ атомарность: Между последовательными вызовами PUT другой клиент может изменить ресурс (например, через PATCH). Ваш последующий PUT перезапишет эти изменения, что может привести к потере данных. Для предотвращения используют оптимистическую блокировку (через заголовок
If-Matchили версию). - Побочные эффекты: Идемпотентность относится к состоянию целевого ресурса. Логирование, отправка уведомлений или списание баланса при каждом вызове — это побочные эффекты, нарушающие идемпотентность. Их следует избегать в обработчике PUT.