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, остальные разбираются в тренажёре.
- Что кодируют группы 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 — искать проблему на сервере.
- Чем различаются коды 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 подтверждает, что ресурс действительно создан.
- Чем отличается статус 401 от 403?A)401 — не аутентифицирован (кто ты неизвестно); 403 — опознан, но прав нетB)401 — доступ запрещён по правам, 403 — не пройдена аутентификация пользователяC)401 — ресурс не найден, 403 — сервер временно недоступен для запросов клиентаD)401 и 403 идентичны, сервер выдаёт один из них при отказе в доступе на выбор
показать ответ и разбор
+A)401 — не аутентифицирован (кто ты неизвестно); 403 — опознан, но прав нет// разбор: Оба относятся к отказу в доступе, но по разной причине. 401 Unauthorized — запрос не прошёл аутентификацию: нет токена, он невалиден или истёк; система не смогла установить личность. 403 Forbidden — личность установлена, но у этого пользователя нет прав на запрошенное действие/ресурс (обычный юзер лезет в админку). Практический тест: без токена ждём 401, с валидным токеном обычного юзера на чужой ресурс — 403. Путаница этих кодов — частый баг авторизации.
- Чем PUT отличается от PATCH?A)PUT меняет отдельные поля, а PATCH заменяет ресурс целиком со всеми его полямиB)PUT заменяет ресурс целиком (все поля), PATCH частично меняет только переданные поляC)PUT создаёт ресурс, а PATCH удаляет его; для изменения существующего берут POSTD)PUT и PATCH — одно и то же, PATCH просто более новое название того же метода
показать ответ и разбор
+B)PUT заменяет ресурс целиком (все поля), PATCH частично меняет только переданные поля// разбор: PUT предполагает передачу полного представления ресурса и его замену: не переданные поля обычно затираются/сбрасываются. PATCH передаёт только изменяемые поля и правит их, остальное оставляя как есть. Для тестировщика разница критична: если по ошибке использовать PUT с неполным телом там, где нужен PATCH, часть полей ресурса обнулится. Проверяют оба сценария — что PUT заменяет всё, а PATCH не трогает непереданное.
- Почему код 200 в ответе — ещё не гарантия, что операция прошла успешно?A)200 обеспечивает и корректность данных в теле ответа, поэтому тело можно не проверятьB)Код 200 выдаётся сервером случайно, и ориентироваться на него при тестировании не получитсяC)200 подтверждает лишь состоявшийся HTTP-обмен; тело ответа может нести бизнес-ошибкуD)200 означает, что запрос ещё обрабатывается, а результат придёт отдельным ответом
показать ответ и разбор
+C)200 подтверждает лишь состоявшийся HTTP-обмен; тело ответа может нести бизнес-ошибку// разбор: Код статуса отражает исход на уровне HTTP-транспорта, а не обязательно бизнес-логики. Некоторые API возвращают 200 даже при логической ошибке, кладя признак в тело: {"status": "error", "message": "..."}. Если тест смотрит только на код, он посчитает такой ответ успехом и пропустит дефект. Поэтому проверяют и код, и содержимое тела (флаги успеха, наличие ожидаемых полей). В идеале API само возвращает корректный 4xx/5xx, но полагаться на это без проверки тела нельзя.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.