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

HTTP-запрос и ответ

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

В этом уроке вы научитесь читать один обмен по частям и отличать сведения протокола от предметных данных. Примеры составлены для объяснения, они не являются записью сетевого трафика ProfessorWeb. Домен catalog.example служит обозначением учебного сервиса. Отправлять запросы на него не нужно. Наш каталог существует как модель из четырёх записей и сохраняется вместе с исходниками уроков.

Адрес и действие

Ресурсом назовём то, с чем работает API: например, коллекцию курсов или один конкретный курс. У коллекции будет путь /api/courses, у курса JavaScript — /api/courses/c001. Идентификатор c001 остаётся прежним при переименовании курса. Это удобно клиенту: сохранённая ссылка обозначает ту же запись, даже если её заголовок изменится.

Полный адрес включает схему, узел и путь. В нашем договоре это https://catalog.example/api/courses/c001. HTTPS указывает на защищённый транспорт HTTP; само наличие шифрования ещё не сообщает, вправе ли пользователь редактировать запись. Пока читаем общедоступные данные. Способ установления личности и прав будет отдельным этапом серии.

Первый листинг — учебное текстовое представление запроса. Используем привычную форму HTTP/1.1, чтобы видеть смысловые части. Пустая строка завершает поля заголовков; тела у этого GET нет.

GET /api/courses/c001 HTTP/1.1
Host: catalog.example
Accept: application/json

Метод GET выражает чтение представления. Путь выбирает цель, Host обозначает узел, а Accept сообщает, какой формат ответа умеет принимать клиент. Это разные решения: адрес не определяет автоматически формат, а формат не превращает чтение в сохранение. Важный первый навык — задавать к каждой строке отдельный вопрос: действие, цель или свойство сообщения?

Запись не показывает установку соединения, TLS и вычисление длины тела. В HTTP/2 и HTTP/3 сообщения кодируются иначе, поэтому их обмен не состоит из такой буквальной стартовой строки. Рассматриваемые методы, статусы и поля сохраняют смысл. Эта граница позволяет обсуждать проектирование API, не смешивая его с устройством конкретной версии транспорта. Модель сообщений HTTP.

Что возвращает сервер

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

HTTP/1.1 200 OK
Content-Type: application/json

{"id":"c001","title":"Современный JavaScript","topic":"frontend","lessons":40}

Число 200 относится к результату обработки запроса. Объект после пустой строки описывает курс: его идентификатор, название, направление и количество уроков. Content-Type сообщает клиенту способ интерпретации этих байтов. Если вместо JSON сервис выдаст HTML, прежний клиент не сможет безусловно применить JSON-разбор, даже когда ответ имеет статус успеха.

Число уроков является учебным исходным значением, а не результатом текущего запроса к опубликованному сайту. В снимке s1 JavaScript содержит 40 уроков, HTML/CSS — 32, производительность — 24, Markdown — 12. Во всех последующих самостоятельных сценариях начинаем с этих записей заново, если явно не указано изменение внутри сценария. Поэтому новое создание в одном уроке не заставляет вручную переделывать другой.

Ответ не обязан быть точной копией строки базы данных. Сервер может хранить служебные отметки, внутренние ключи и права доступа. Клиент получает только согласованное представление. Это позволяет изменить внутреннее хранение без изменения публичного договора, пока поля ответа сохраняют смысл. Мы ещё подробно разберём различие ресурса и его представления; сейчас достаточно увидеть, что наружу передаётся определённая форма данных.

Существование и отсутствие

Изменим только идентификатор в первом запросе: вместо c001 подставим c999. Метод, узел и ожидаемый формат останутся прежними. В начальном снимке такой записи нет. Ожидаемый статус — 404, а не объект с пустым названием и количеством уроков ноль.

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{"type":"https://catalog.example/problems/course-not-found","title":"Курс не найден","status":404,"detail":"Курс c999 отсутствует."}

Это отдельное представление ошибки. Поле status внутри него помогает сохранить сведения при передаче объекта между частями программы, но не заменяет настоящий статус HTTP. Клиент сначала узнаёт, что чтение завершилось отказом, затем использует описание для сообщения человеку. Форму Problem Details изучим позже; сейчас она показывает, что и неуспешный ответ может содержать полезные данные.

Отсутствие курса нельзя смешивать с отсутствием ответа. При сетевом обрыве клиент вообще может не получить статус. При 404 сообщение пришло, и сервер сообщил конкретный результат. Для интерфейса это разные состояния: можно объяснить, что ссылка ведёт к отсутствующему курсу, либо предложить повторить действие после проблемы связи. Если спрятать оба случая под «ничего не найдено», читатель получит неверное объяснение.

Страница и её отдельные запросы

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

Наш JSON курса не содержит команды загрузить уроки автоматически. Получение объекта лишь даёт программе данные. Клиент сам решает, как показать карточку и нужно ли затем обратиться к странице курса. В ответе нет скрытого обещания, что все связанные материалы уже доставлены. Это помогает отличить представление каталога от целого пользовательского сценария.

Статус 200 также не доказывает, что программа клиента успешно показала интерфейс. Сервер мог передать корректный JSON, а клиент обратиться к несуществующему полю или ошибиться в обработчике. Разбирая проблему, полезно сначала отделить получение сообщения от его последующего использования. В первом случае ищем статус, тип и тело; во втором — действия программы с уже полученными данными.

Например, карточка без названия при наличии title в ответе означает иной класс ошибки, чем 404 при чтении курса. Одинаковый пустой экран не является достаточным объяснением причины. Смысловой разбор HTTP-обмена даёт точку опоры, с которой можно переходить к разбору приложения.

Где заканчивается один обмен

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

Соединение и операция также не совпадают. По одному соединению может пройти несколько сообщений; современная версия протокола допускает несколько параллельных потоков. Если пользователь дважды нажал обновление, это два намерения чтения, даже когда транспорт сохранился. Позже такое различие поможет объяснить повторы и неопределённость после обрыва.

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

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