Является ли HTTP-метод GET идемпотентным?

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

Ответ

Да, согласно спецификации HTTP/1.1 (RFC 7231), метод GET является идемпотентным, как и методы HEAD, PUT и DELETE.

Что такое идемпотентность в контексте HTTP? Метод считается идемпотентным, если многократное выполнение одного и того же запроса с одинаковыми параметрами оказывает тот же эффект на состояние сервера, что и однократное выполнение.

Практические следствия для GET:

  1. Безопасность (Safe): GET также является «безопасным» (safe) методом. Это означает, он предназначен только для получения данных и не должен иметь побочных эффектов, изменяющих состояние сервера (не должен создавать, изменять или удалять ресурсы).
  2. Предсказуемость: Клиенты (браузеры, прокси, CDN) могут безопасно повторять GET-запросы при таймаутах или для кэширования, не опасаясь нежелательных изменений.
  3. Результат vs Эффект: Идемпотентность относится к побочному эффекту на сервере, а не обязательно к идентичности ответа. Данные ресурса могут измениться между запросами (например, другим PUT-запросом), поэтому ответы могут различаться, но сам GET-запрос не является причиной этих изменений.

Пример идемпотентного GET:

[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
    [HttpGet("{id}")] // GET /api/users/5
    public ActionResult<UserDto> GetUser(int id)
    {
        // Чтение из репозитория/БД. Не изменяет состояние.
        var user = _userService.GetById(id);
        if (user == null) return NotFound();
        return Ok(user);
    }
}
// 10 одинаковых вызовов `GET /api/users/5`:
// - Не создадут новых пользователей.
// - Не изменят данные пользователя с id=5.
// - Вернут один и тот же результат (если данные не менялись извне).

Нарушение идемпотентности GET (антипаттерн):

// НЕПРАВИЛЬНО: GET с побочным эффектом
[HttpGet("increment-counter")]
public int GetAndIncrementCounter()
{
    // Запрос изменяет состояние сервера! Это нарушение контракта.
    _counter++;
    return _counter;
}
// Каждый вызов этого GET изменит состояние (_counter++).
// Так делать нельзя. Для изменения состояния используйте POST или PUT.

Итог: Идемпотентность GET — это фундаментальное свойство протокола HTTP, которое обеспечивает предсказуемость, надёжность и позволяет эффективно использовать кэширование. Разработчик должен соблюдать этот контракт и не вносить изменяющие состояние операции в обработчики GET-запросов.