сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Тестирование API

HTTP: методы и коды ответов

Методы и коды ответа

Я поднял маленький сервис заказов и отправил один и тот же запрос трижды. POST /orders три раза - получилось три заказа с номерами 1, 2 и 3. PUT /orders/1 три раза - состояние после каждого одинаковое. DELETE второй раз вернул 404 вместо 204, но ничего не сломал. Вот из этого замера и растёт всё, что спрашивают про методы.

Стержень: повтор GET, PUT и DELETE безопасен, повтор POST создаёт дубль, а код ответа обязан отражать исход, а не прятать ошибку в теле.

// Формулировки: «чем 401 отличается от 403?», «какие методы идемпотентны?», «что вернёшь на создание ресурса?».

Идемпотентность: замер повторов

Сначала словарь. Метод - это глагол запроса, он говорит серверу, что сделать: GET читает, POST создаёт или запускает действие, PUT заменяет объект целиком, PATCH меняет часть полей, DELETE удаляет. Идемпотентность - свойство запроса, при котором повтор не добавляет ничего сверх первого раза.

Мой замер показал ровно это. Три POST дали три разных заказа - метод не идемпотентен. Три PUT дали одно и то же состояние, потому что PUT задаёт объект целиком, а не прибавляет. Второй DELETE вернул 404, потому что удалять уже нечего, - но состояние системы после первого и после второго одинаковое, значит свойство соблюдено.

// Почему это важнее, чем кажется. Обрыв связи не говорит, дошёл ли запрос. Идемпотентный запрос можно спокойно повторить. Повторить POST на оплату - это второе списание у живого человека. Поэтому у платёжных сервисов есть ключ идемпотентности: клиент шлёт свой уникальный ключ в заголовке, и сервер по нему узнаёт повтор. Я проверил: три POST с одним ключом создали ОДИН заказ, первый ответил 201, следующие два вернули 200 с тем же номером.

идемпотентность
повтор запроса не меняет результат сверх первого раза
ключ идемпотентности
уникальная метка запроса, по которой сервер узнаёт повтор

Коды: что означает каждая сотня

Код ответа - трёхзначное число, которым сервер сообщает исход. Первая цифра задаёт класс. 2xx - получилось: 200 просто «ок», 201 «создано» (и рядом заголовок Location с адресом созданного объекта - мой сервис вернул /orders/1), 204 «сделано, отвечать нечем» (тело пустое, его и вернул DELETE).

3xx - «сходи в другое место или возьми из кэша». Самый полезный для тестировщика - 304 «не изменилось». Замер: первый запрос отчёта отдал 200 и 200 байт тела, повтор с меткой версии вернул 304 и ноль байт. Экономия трафика полная, но у этой же механики есть обратная сторона: «мы починили, а у пользователя всё по-старому» часто оказывается старым ответом из кэша - сохранённой копии, которую отдают вместо похода на сервер.

// 4xx - виноват клиент: 400 запрос кривой, 401 непонятно кто, 403 понятно кто, но нельзя, 404 такого нет, 409 конфликт с текущим состоянием (например, отменяем уже отменённый заказ), 422 не прошла проверка полей, 429 слишком часто. 5xx - виноват сервер: 500 внутренняя ошибка, 502 и 504 проблемы за посредником (это программа, стоящая перед сервисом и передающая ему запросы), 503 перегрузка. Разница практическая: 4xx повторять бессмысленно, запрос не станет правильным, а 5xx повторить с паузой имеет смысл.

201 и Location
создано; заголовок сразу говорит адрес созданного объекта
304
ресурс не изменился - тело не передаётся, берётся из кэша

401, 403 и ошибка, спрятанная в теле

Замер на том же сервисе. Токен - это строка-пропуск, которую сервер выдаёт после входа и которую клиент прикладывает к каждому запросу. Запрос без токена - 401 и текст «кто ты? токена нет». Запрос с токеном, но с обычной ролью - 403 «знаю кто ты, но сюда нельзя». Запрос с ролью администратора - 200. Разница в одном: 401 про аутентификацию (кто ты вообще), 403 про авторизацию (что тебе можно, когда уже понятно, кто ты).

Практический смысл разницы - в поведении клиента. На 401 клиент идёт обновлять токен или отправляет человека логиниться. На 403 повторять бесполезно: прав от повтора не появится. Если сервер путает коды и отдаёт 403 вместо 401, клиент попадает в бесконечный цикл обновления токена.

// А теперь антипаттерн, который я тоже воспроизвёл. Сервис вернул код 200 и тело {"error": "недостаточно средств"}. Обычная клиентская проверка «код в диапазоне 200-299 - значит успех» сочла это успехом. То есть слепнут все разом: клиент, мониторинг и автотест, который проверяет только статус. Код обязан отражать исход операции, иначе ошибку никто не увидит.

аутентификация / авторизация
кто ты (401) / что тебе можно (403)
ошибка под кодом 200
исход спрятан в теле - клиент и мониторинг видят успех

Как отвечать: «В чём разница между 401 и 403?»

Оба кода про доступ, но отвечают на разные вопросы. 401 - про аутентификацию, про «кто ты»: сервер не понял, кто делает запрос. Токена нет, он просрочен, битый или отозван - всё это 401. 403 - про авторизацию, про «что тебе можно»: сервер прекрасно знает, кто ты, но конкретно на это действие прав нет. Я это проверял на живом сервисе: без токена приходит 401, с токеном и обычной ролью - 403, с ролью администратора - 200. Практическая разница важнее формулировки: на 401 клиент обновляет токен и повторяет запрос, а на 403 повторять бессмысленно, права от повтора не появятся. Если сервер путает коды и отдаёт 403 там, где нужен 401, клиент уходит в бесконечный цикл обновления токена, а если наоборот - человека выкидывает на форму входа, хотя он уже вошёл. Как тестировщик я проверяю оба случая отдельно: без токена, с просроченным, с чужим, и обязательно с валидным токеном, но недостаточными правами - именно там путаница и вылезает.

Почему это сильный ответ: оси разведены (аутентификация против авторизации), подкреплены наблюдаемым поведением, показана практическая разница для клиента и названы конкретные проверки.

На чём валят

  • Повторять POST при обрыве без ключа идемпотентности: три попытки создали три заказа.
  • Отдавать 200 с текстом ошибки в теле - проверка «код 2xx значит успех» пропускает это насквозь.
  • Путать 401 и 403 - клиент либо бесконечно обновляет токен, либо выкидывает вошедшего.
  • Реализовать PUT как частичное обновление: честный клиент пришлёт объект целиком и потеряет поля.
  • Забыть про кэш: «починили, а у пользователя по-старому» - ответ взят из кэша по старой метке.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 11, остальные разбираются в тренажёре.

  1. #http_methods_codes1 / 5
    Что кодируют группы HTTP-статусов 2xx, 4xx и 5xx?
    A)2xx — ошибка клиента, 4xx — успех, 5xx — перенаправление на другой адрес
    B)2xx — предупреждение, 4xx — информационный ответ, 5xx — успешное завершение
    C)2xx — успех, 4xx — ошибка клиента (запрос), 5xx — ошибка сервера
    D)Группы различаются скоростью ответа: 2xx быстрые, 5xx самые медленные
    показать ответ и разбор
    +C)2xx — успех, 4xx — ошибка клиента (запрос), 5xx — ошибка сервера

    // разбор: Первая цифра статуса задаёт класс ответа. 2xx — запрос успешно обработан (200, 201, 204). 3xx — перенаправление. 4xx — клиент прислал что-то не то: неверный запрос, нет прав, ресурс не найден (400, 401, 403, 404). 5xx — сервер не смог обработать корректный запрос: внутренняя ошибка, недоступность (500, 502, 503). Разделение важно для диагностики: 4xx — чинить запрос/права, 5xx — искать проблему на сервере.

  2. #http_methods_codes2 / 5
    Чем различаются коды 200, 201 и 204 при успешном ответе?
    A)200 — ресурс создан, 201 — ресурс удалён, 204 — ресурс не найден на сервере
    B)Все три кода означают одно и то же и выбираются сервером произвольно при успехе
    C)200 применяют для GET, 201 для ошибок клиента, 204 для ошибок сервера
    D)200 — успех с телом, 201 — создан ресурс, 204 — успех без тела
    показать ответ и разбор
    +D)200 — успех с телом, 201 — создан ресурс, 204 — успех без тела

    // разбор: Все три — успех (2xx), но с разным смыслом. 200 OK — стандартный успех, в теле обычно есть данные (результат GET или обновления). 201 Created — запрос создал новый ресурс, часто в заголовке Location указан его адрес (типичный ответ на POST). 204 No Content — операция прошла успешно, но возвращать нечего (типично для DELETE или PUT без тела). Проверять надо не просто «2xx», а именно ожидаемый код: 201 вместо 200 подтверждает, что ресурс действительно создан.

  3. #http_methods_codes3 / 5
    Чем отличается статус 401 от 403?
    A)401 — не аутентифицирован (кто ты неизвестно); 403 — опознан, но прав нет
    B)401 — доступ запрещён по правам, 403 — не пройдена аутентификация пользователя
    C)401 — ресурс не найден, 403 — сервер временно недоступен для запросов клиента
    D)401 и 403 идентичны, сервер выдаёт один из них при отказе в доступе на выбор
    показать ответ и разбор
    +A)401 — не аутентифицирован (кто ты неизвестно); 403 — опознан, но прав нет

    // разбор: Оба относятся к отказу в доступе, но по разной причине. 401 Unauthorized — запрос не прошёл аутентификацию: нет токена, он невалиден или истёк; система не смогла установить личность. 403 Forbidden — личность установлена, но у этого пользователя нет прав на запрошенное действие/ресурс (обычный юзер лезет в админку). Практический тест: без токена ждём 401, с валидным токеном обычного юзера на чужой ресурс — 403. Путаница этих кодов — частый баг авторизации.

  4. #http_methods_codes4 / 5
    Чем PUT отличается от PATCH?
    A)PUT меняет отдельные поля, а PATCH заменяет ресурс целиком со всеми его полями
    B)PUT заменяет ресурс целиком (все поля), PATCH частично меняет только переданные поля
    C)PUT создаёт ресурс, а PATCH удаляет его; для изменения существующего берут POST
    D)PUT и PATCH — одно и то же, PATCH просто более новое название того же метода
    показать ответ и разбор
    +B)PUT заменяет ресурс целиком (все поля), PATCH частично меняет только переданные поля

    // разбор: PUT предполагает передачу полного представления ресурса и его замену: не переданные поля обычно затираются/сбрасываются. PATCH передаёт только изменяемые поля и правит их, остальное оставляя как есть. Для тестировщика разница критична: если по ошибке использовать PUT с неполным телом там, где нужен PATCH, часть полей ресурса обнулится. Проверяют оба сценария — что PUT заменяет всё, а PATCH не трогает непереданное.

  5. #http_methods_codes5 / 5
    Почему код 200 в ответе — ещё не гарантия, что операция прошла успешно?
    A)200 обеспечивает и корректность данных в теле ответа, поэтому тело можно не проверять
    B)Код 200 выдаётся сервером случайно, и ориентироваться на него при тестировании не получится
    C)200 подтверждает лишь состоявшийся HTTP-обмен; тело ответа может нести бизнес-ошибку
    D)200 означает, что запрос ещё обрабатывается, а результат придёт отдельным ответом
    показать ответ и разбор
    +C)200 подтверждает лишь состоявшийся HTTP-обмен; тело ответа может нести бизнес-ошибку

    // разбор: Код статуса отражает исход на уровне HTTP-транспорта, а не обязательно бизнес-логики. Некоторые API возвращают 200 даже при логической ошибке, кладя признак в тело: {"status": "error", "message": "..."}. Если тест смотрит только на код, он посчитает такой ответ успехом и пропустит дефект. Поэтому проверяют и код, и содержимое тела (флаги успеха, наличие ожидаемых полей). В идеале API само возвращает корректный 4xx/5xx, но полагаться на это без проверки тела нельзя.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.