Что произойдет, если несколько раз вызвать метод PUT на один и тот же эндпоинт?

«Что произойдет, если несколько раз вызвать метод PUT на один и тот же эндпоинт?» — вопрос из категории Сети, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Согласно спецификации HTTP, метод PUT является идемпотентным. Это означает, что несколько идентичных запросов PUT должны оказывать на сервер тот же эффект, что и один запрос.

Типичное поведение REST API:

  1. Первый вызов PUT на /resource/{id}: Если ресурс с таким id существует, он полностью заменяется телом запроса. Если не существует — создается (если серверная логика это позволяет).
  2. Последующие идентичные вызовы: Состояние ресурса после первого успешного вызова не меняется. Сервер должен возвращать тот же успешный статус (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.