← Назад к вопросам

Какие HTTP-методы являются идемпотентными

1.0 Junior🔥 181 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Идемпотентные HTTP-методы

Идемпотентность в HTTP — это свойство метода, при котором одно и то же действие выполняется много раз с одинаковым результатом. Другими словами, повторные запросы не должны изменять состояние сервера сверх первого запроса.

Какие методы идемпотентны?

GET — Безопасный и идемпотентный

  • Не изменяет состояние сервера
  • Множественные GET запросы возвращают одинаковый результат
// GET — безопасно идемпотентен
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users/123"))
    .GET()
    .build();
    
// Выполни 100 раз — результат идентичный
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

HEAD — Идемпотентен

  • Работает как GET, но без тела ответа
  • Используется для проверки доступности ресурса

OPTIONS — Идемпотентен

  • Запрашивает доступные методы для ресурса
  • Не изменяет состояние

PUT — Идемпотентен (при условии)

  • Заменяет ресурс целиком
  • При повторном запросе с одинаковыми данными результат остаётся прежним
// PUT — идемпотентный
String updatedUser = """
{
  "id": "123",
  "name": "John Doe",
  "email": "john@example.com"
}
""";

HttpRequest putRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users/123"))
    .method("PUT", HttpRequest.BodyPublishers.ofString(updatedUser))
    .header("Content-Type", "application/json")
    .build();

// Выполни дважды — ресурс будет в одинаковом состоянии
client.send(putRequest, HttpResponse.BodyHandlers.ofString());
client.send(putRequest, HttpResponse.BodyHandlers.ofString());

DELETE — Идемпотентен

  • Удаляет ресурс
  • При повторном запросе: ресурс уже удалён, но логически результат тот же (ресурса нет)
// DELETE — идемпотентный
HttpRequest deleteRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users/123"))
    .DELETE()
    .build();

// Можешь выполнить дважды безопасно
client.send(deleteRequest, HttpResponse.BodyHandlers.ofString());  // 200 или 204
client.send(deleteRequest, HttpResponse.BodyHandlers.ofString());  // 404, но это нормально

TRACE и CONNECT — Идемпотентны

  • Редко используются в практике
  • Используются для отладки и установления туннелей

Какие методы НЕ идемпотентны?

POST — Не идемпотентен

  • Создаёт новый ресурс
  • Каждый запрос может привести к созданию нового объекта
// POST — НЕ идемпотентный
String newUser = """
{
  "name": "Jane Doe",
  "email": "jane@example.com"
}
""";

HttpRequest postRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .method("POST", HttpRequest.BodyPublishers.ofString(newUser))
    .header("Content-Type", "application/json")
    .build();

// Выполни дважды — создастся ДВА разных пользователя!
client.send(postRequest, HttpResponse.BodyHandlers.ofString());  // Creates user #1
client.send(postRequest, HttpResponse.BodyHandlers.ofString());  // Creates user #2

PATCH — Обычно не идемпотентен

  • Частичное обновление
  • В зависимости от операции может быть или не быть идемпотентным
// PATCH — может быть не идемпотентным
String patchData = """
{
  "op": "add",
  "path": "/tags",
  "value": "important"
}
""";

// Каждый запрос добавляет новый тег — не идемпотентно!

Практическое применение

В микросервисах идемпотентность критична для обработки сбоев:

// Клиент с retry логикой
public <T> T executeWithRetry(HttpRequest request, int maxRetries) throws Exception {
    for (int i = 0; i < maxRetries; i++) {
        try {
            return client.send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException e) {
            if (i == maxRetries - 1) throw e;
            Thread.sleep(1000 * (i + 1));  // Exponential backoff
        }
    }
}

// Безопасно использовать для GET и DELETE
executeWithRetry(getRequest, 3);
executeWithRetry(deleteRequest, 3);

// Опасно для POST без дополнительных гарантий
executeWithRetry(postRequest, 3);  // Может создать несколько ресурсов!

Ключевые выводы

  • Идемпотентные: GET, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT
  • Не идемпотентные: POST, PATCH (условно)
  • Идемпотентность позволяет безопасно повторять запросы
  • Используй PUT для полного обновления и DELETE для удаления
  • POST используй только когда нужно создать новый ресурс
  • При реализации retry-логики учитывай идемпотентность методов