Перейти к содержанию

Коды состояния ответа

Методы показывают намерение клиента, а коды состояния — результат обработки. В предыдущем уроке создание, чтение и удаление имели разные успешные ответы. Теперь превратим эти различия в понятный договор: интерфейс должен понимать, что произошло, прежде чем читать подробности тела. Один статус 200 для всех ситуаций лишает клиент этой общей опоры.

Будем разбирать исходы операций с тем же каталогом. Начальный снимок снова содержит c001–c004. Реального обмена мы не выполняем: листинги являются ожидаемыми сообщениями спроектированного API. Результат урока — умение выбирать код по факту обработки, не смешивая транспортный успех, предметный отказ и отсутствие сетевого ответа.

Разные формы успешного результата

Для чтения существующего курса используем 200 OK и его JSON. Для POST, создавшего c005, выбираем 201 Created, адрес в Location и представление новой записи. Клиент может добавить её в интерфейс или перейти по полученному адресу. Ему не приходится угадывать, какой из отправленных заголовков превратился в серверный идентификатор.

Для успешного удаления выбираем 204 No Content. Так выражаем завершённое действие без возвращаемого содержимого. Тело не должно содержать даже объект с сообщением «готово». Если приложение обещает клиенту JSON с подробностями, нужно выбрать другой допустимый успешный ответ и явно описать его форму.

DELETE /api/courses/c004 HTTP/1.1
Host: catalog.example
HTTP/1.1 204 No Content

Вторая схема полностью показывает смысловое отсутствие тела. Это существенная часть результата, а не недописанный листинг. В обычном клиентском коде следует сначала учитывать статус, затем выбирать способ чтения. Универсальный вызов JSON-разбора после любого успеха ошибётся на таком ответе, хотя сервер выполнил договор.

202 Accepted имеет иной смысл: запрос принят к обработке, но ещё не завершён. Его нельзя использовать для уже готового создания только потому, что сервер работает быстро. Если позже добавим построение архива курса в фоне, понадобится отдельный адрес задачи и состояния выполнения. Сейчас все три операции считаются завершёнными при отправке успешного ответа.

Ошибка формы и отсутствие цели

Некорректный синтаксис запроса относится к 400 Bad Request. Например, тело с незакрытой JSON-строкой не удаётся превратить в объект. Сервер ещё не получил корректную структуру, которую можно проверять по предметным правилам. Для такой ошибки не имеет смысла обсуждать допустимое количество уроков.

Если путь корректен, но запись c999 отсутствует, используем 404 Not Found. Это говорит об отсутствии доступного ресурса по цели. В нашем публичном чтении оно соответствует отсутствующему курсу; в иных системах сервер может также использовать такой ответ, чтобы не раскрывать существование закрытой записи. Поэтому клиент не должен превращать 404 в доказательство устройства базы.

Пустой список после фильтра — другой случай. Запрос к существующей коллекции выполнен успешно, просто подходящих курсов нет. Мы возвращаем 200 и пустой массив в принятой оболочке:

{"items":[],"total":0,"nextCursor":null,"snapshot":"s1"}

Ожидаемый интерфейс объясняет отсутствие подходящих материалов и позволяет изменить фильтр. Он не показывает «страница не существует». Различие получается из цели операции: чтение коллекции и чтение одного неизвестного объекта отвечают на разные вопросы. Количество найденных элементов само по себе не выбирает класс статуса.

Содержательные данные могут быть неприемлемыми

Входной JSON способен быть синтаксически правильным и при этом нарушать контракт. Рассмотрим полный объект создания с отрицательным числом уроков. Он разбирается, но правило требует целое число от одного до тысячи.

{"title":"HTTP и API","topic":"backend","lessons":-3}

В нашем сервисе такой отказ обозначается 422 Unprocessable Content. Другие API могут документировать 400 для части предметной валидации; важно выбрать последовательный договор. Здесь отделение синтаксиса от приемлемости помогает клиенту показать ошибку конкретного поля. До сохранения запись не создаётся.

415 Unsupported Media Type используем, если входное представление имеет неподдерживаемый тип, например XML вместо объявленного JSON. 406 Not Acceptable относится к другой стороне обмена: клиент не принимает ни один предлагаемый формат ответа. Разница будет особенно видна в уроке о согласовании. Изменение одного заголовка способно поменять причину отказа при прежнем пути и теле.

409 Conflict описывает конфликт с текущим состоянием ресурса. Позже один ключ повторяемой операции с разными данными даст именно такой ответ. Это не замена любой ошибки проверки: отрицательное число уроков неприемлемо независимо от состояния каталога. Полезно спросить себя, помогло бы изменение предметного состояния сервера; если нет, конфликт обычно не объясняет причину.

Метод, личность и право

Отказ из-за метода и отказ из-за доступа также различаются. Ответ 405 сообщает, что целевой ресурс не поддерживает эту операцию. Он включает Allow. Если PUT предусмотрен, но конкретный пользователь не вправе редактировать курс, ошибка доступа не превращается в отсутствие поддержки метода. Позже схема авторизации определит соответствующее поведение сервиса.

Общие статусы 401 и 403 тоже нельзя выбирать по слову «ошибка» в интерфейсе. Первый связан с требованием аутентификации и применимым механизмом запроса учётных данных, второй — с отказом выполнить понятное обращение. В нашей первой части личность редактора предполагается установленной, поэтому не добавляем мнимый ответ 401 без согласованной схемы и требуемых метаданных.

Это полезное ограничение учебной модели: код должен иметь причину, а причина — реалистичный путь исправления. Пользователь не исправит неподдерживаемый метод повторным входом в систему, а разработчик не устранит отсутствие прав изменением Accept. Даже одинаковая красная область интерфейса должна опираться на разные состояния программы.

При ручном проектировании записывайте не только числовой код, но и следующее допустимое действие. Для 404 человек проверяет адрес, для 422 исправляет данные, для временной недоступности учитывает возможность повтора. Такое сопоставление обнаруживает ложные статусы ещё до реализации.

Ошибка сервиса и неопределённость

Класс 5xx обозначает, что сервер не смог выполнить кажущийся допустимым запрос. 500 подходит для неожиданного внутреннего сбоя, а 503 — для временной недоступности сервиса. Это не удобный способ наказать клиента за пустое название. Если клиент может исправить известное поле, сервер обязан дать предусмотренный предметный отказ.

Предполагаемая временная недоступность может сопровождаться указанием задержки повторения:

HTTP/1.1 503 Service Unavailable
Retry-After: 30
Content-Type: application/problem+json

{"type":"https://catalog.example/problems/service-unavailable","title":"Сервис временно недоступен","status":503}

Retry-After в этом варианте означает число секунд. Но клиент всё равно учитывает безопасность повтора операции. Для чтения повтор проще, чем для POST, который мог завершиться до сбоя доставки ответа. Полученный код и известное состояние операции вместе определяют следующее действие; одной задержки недостаточно.

Сетевой обрыв не является статусом 500. Если клиент не получил HTTP-ответ, он не знает код. Сервер мог не увидеть запрос, начать обработку или уже сохранить запись. Интерфейс должен объяснять неопределённость и не заявлять, что создание точно отменено. Позже предусмотрим ключ для одного намерения, чтобы безопаснее работать с таким случаем.

Код не обязан перечислять всю причину. Общий результат читают программы и инфраструктура, подробности — клиентский обработчик. Сообщение человеку можно перевести, не меняя код. Нельзя прятать отказ внутри успешного тела {"ok":false} и ожидать, что все посредники самостоятельно его расшифруют. Классы и назначение статусов.

Теперь возьмите одну операцию POST и отдельно опишите четыре исхода: создано, неверный синтаксис, неприемлемое поле и потерянный ответ. Только первые три имеют известное сообщение сервера. Такое разложение позволяет проектировать интерфейс по состояниям и помогает следующему уроку: заголовки уточнят, какой именно результат и представление пришли вместе с кодом.

Оглавление курса