HTTP-запросы

Общая информация

HTTP - это протокол передачи данных прикладного уровня (описан в стандарте RFC 2616). Основой HTTP является взаимодействие "клиент-сервер". В этом взаимодействии выделяются две стороны:
  • Клиент - некоторая система, которая инициирует соединение и посылает запрос.
  • Сервер - программное обеспечение, которое ожидает установку соединения от клиента, получает запрос, выполняет некоторые действия и возвращает ответ клиенту.

В основе HTTP-взаимодействия лежит понятие URI (или, в более простом варианте - URL) - универсального идентификатора ресурса, который описан к стандарте RFC 3986. Взаимодействие осуществляется с помощью запросов. Каждый запрос содержит несколько частей: стартовая строка (определяет тип сообщения), заголовки (определяет параметры запроса) и тело сообщения (дополнительные данные запроса, тело сообщения может быть пустым).

Стартовая строка имеет следующий вид:
метод uri HTTP/версия
. Рассмотрим, из чего состоит стартовая строка:
  • метод - это определение операции, которую необходимо выполнить с ресурсом, которую описан с помощью URI. Состоит из любых символов US-ASCII (кроме управляющих символов и разделителей). В принципе, название метода может быть любым, но рекомендуется придерживаться набора методов, который описан в спецификации HTTP 1.1 (RFC 2616).
  • uri - описывает путь до конкретного ресурса на том сервере, который будет выполнять запрос. Запрос будет выполняться над данными, которые адресуются указанным URI. Если метод, указанный в запросе, не требует указания какого-либо ресурса, вместо URI следует указать символ "*".
  • версия - указывается версия протокола HTTP. В настоящий момент это версия 1.1.
В ответ на запрос клиента, сервер возвращает ответ, который по структуре аналогичен запросу. Ответ сервера содержит стартовую строку ответа, заголовки и тело сообщения. Тело сообщения является необязательным для ответа. Стартовая строка ответа имеет следующий вид:
HTTP/версия код-состояния пояснение
. Рассмотрим подробнее содержимое стартовой строки ответа:
  • версия - номер версии HTTP-протокола, аналогично стартовой строки запроса. В настоящий момент это версия 1.1.
  • код-состояния - описывает результат выполнения запроса. Код состояния может сообщать о том, что запрос выполнен успешно, что во время запроса произошла какая-то ошибка или что запрос нужно повторить, изменив параметры запроса.
  • пояснение - текстовое пояснение результата выполнения запроса. Данный текст не содержит символов перевода строки и возврата каретки.

Заголовки запроса — это набор именованных параметров запроса. Каждый запрос может содержать различный набор таких параметров. Заголовки делятся на 4 большие части: общие заголовки (используются как в запросах, так и в ответах), заголовки запроса (используются только в запросах), заголовки ответа (используются только в ответах) и заголовки сущностей (сопровождают каждую сущность сообщений, используются как в запросах, так и в ответах). Имена заголовков не чувствительны к регистру символов. Запрос может содержать несколько заголовков с одинаковым именем. Такое допускается только в том случае, если несколько значений заголовка могут быть перечислены через символ «,», а также если порядок следования значений в заголовке не изменяет семантику заголовка.

Таким образом HTTP-взаимодействие выполняется следующим образом:

  • Клиент устанавливает соединение с сервером.
  • Клиент готовит запрос и отправляет этот запрос серверу.
  • Сервер принимает запрос и пытается выполнить действия, указанные в запросе.
  • Результат выполнения запроса сервер оформляет в виде ответа, где код состояния содержит суть ответа. Затем ответ отправляется обратно клиенту
  • Клиент получает ответ и анализирует код состояния. По результатам анализа клиент принимает решение о дальнейших действиях.

«1С:Исполнитель» может выступать только в качестве клиента для протокола HTTP.

Методы HTTP-запроса

Стандарт HTTP 1.1 (RFC 2616) описывает набор методов, которыми рекомендуется пользоваться для сохранения соответствия стандарту и понимания другими разработчиками.
Табл. 1. Методы HTTP-запроса
Метод Описание
CONNECT Устанавливает туннель к серверу, определенному URI из запроса
DELETE Удаляет данные, которые идентифицируются URI из запроса
GET Позволяет запросить содержимое какого-либо ресурса или выполнить какое-либо действие
HEAD Позволяет получить метаданные, проверить наличие какого-либо ресурса или узнать, что изменилось с момента последнего обращения. Ответ сервера на содержит тела
OPTIONS Используется для определения возможностей веб-сервера или параметров соединения для конкретного ресурса
PATCH Аналогичен запросу PUT, но применяется к фрагменту данных ресурса
POST Позволяет клиенту передать на сервер какие-то данные. Эти данные, скорее всего, будут обработаны сервером
PUT Позволяет клиенту передать на сервер какие-то данные. Эта данные, скорее всего, заменят те данные, которые сейчас адресуются URI из запроса
TRACE Возвращает полученный запрос обратно клиенту. Позволяет клиенту узнать, что добавляют или изменяют в оригинальном запросе промежуточные узлы (через которые проходит запрос)

Описание методов, приведенное в таблице, является рекомендуемым поведением. Фактическое поведение целиком и полностью определяется фактической реализацией сервера HTTP-запроса.

Коды состояния

Коды состояния группируются в 5 основных классов (RFC 2616).
Табл. 2. Классы кодов состояния
Код Класс Описание
1хх Информационный Информирование о процессе передачи запроса
2хх Успех Информирование о том, что запрос успешно выполнился
3хх Перенаправление Сообщает клиенту о том, что для выполнения запроса необходимо выполнить запрос с другими параметрами. Заголовки ответа содержат информацию о том, что надо изменить в запросе
4хх Ошибка клиента Указывает, что запрос клиента содержит ошибку
5хх Ошибка сервера Указывает, что во время выполнения запроса на сервере произошла ошибка

Кодирование URL адреса в ЗапросHttp

В адресе запроса могут содержаться символы, использование которых недопустимо в URI. Для успешного выполнения такого запроса недопустимые символы должны быть закодированы с использованием символа «%». Однако разработчик не всегда может гарантировать кодирование всех недопустимых символов. В результате в адресе запроса могут встречаться допустимые символы, закодированные недопустимые символы и недопустимые символы, которые не были закодированы.

Для того чтобы обеспечить успешное выполнение запроса в любом случае, ЗапросHttp во входящей строке сохраняет процент-кодированные представления символов, которые там уже есть, а все недопустимые символы кодирует. Входящая строка адреса обрабатывается следующим образом: если за символом процента (%) следует значение, допустимое для кода символа (два шестнадцатеричных значения), то считается, что это код символа. Иначе считается, что это знак процента, который будет закодирован (%25).

Входящий адрес URL (строка) разделяется на группы и каждая группа кодируется отдельно (например, путь, запрос и т. д.). Группы адреса соответствуют описанию стандарта RFC3986: схема (scheme), основание (authority), путь (path), запрос (query), фрагмент (fragment):

Допустимыми являются все разделители внутри сегментов, которые указаны в стандарте RFC 3986: «!», «&», «'», «+», «*», «$», «(», «)», «,», «;», «=». Также для сегментов запрос и фрагмент допустимы «/», «?».

Символы, которые являются разделителями сегментов, не меняются «:» / «/» / «?» / «#» / «[» / «]» / «@». Не меняются разрешенные к использованию во всех частях URL-адреса символы: «-» / «.» / «_» / «~».

Если во входящей строке представлены символы национальных алфавитов (то есть не латиница, в частности — русские буквы), то они также кодируются. Если такие символы присутствуют в доменной части строки, то выполняется IDN-кодирование. Если такие символы присутствуют в других частях URL, то выполняется процентное кодирование.

Примеры процентного кодирования:

Исходный адрес запроса

Адрес запроса после преобразований: пробел и обратный слеш были закодированы

https://yandex.ru/maps%2F%3Ftest1%2Ftest 2%5Ctest3\test4

https://yandex.ru/maps%2F%3Ftest1%2Ftest%202%5Ctest3%5Ctest4

Исходный адрес запроса

Адрес запроса после преобразований: текст не изменился, так как после процента допустимый код символа

http://test.ru/search=aa%2Faa

http://test.ru/search=aa%2Faa

Исходный адрес запроса

Адрес запроса после преобразований: процент был закодирован

http://test.ru/search=100%

http://test.ru/search=100%25

Пример IDN-кодирования:

Исходный адрес

Адрес после преобразования

http://яндекс.рф

http://xn--d1acpjx3f.xn--p1ai

Объектная модель

Теперь, когда мы знаем, как устроено клиент-серверное взаимодействие с использованием HTTP-запросов, можно наложить на эту схему объектную модель, которую предоставляет «1С:Исполнитель».

В качестве основы всей модели выступает тип КлиентHttp. Экземпляр этого типа может быть получен из свойства глобального контекста КлиентHttp. Он содержит базовые параметры HTTP-взаимодействия.

В тоже время, нам может потребоваться в один момент времени работать с несколькими серверами. Поэтому экземпляр типа КлиентHttp может создавать новые экземпляры типа КлиентHttp, которые будут получать все параметры из своего базового экземпляра, кроме некоторых параметров, которые можно изменить при создании копии. Другими словами - будет создавать измененная копия родительского экземпляра. Говоря про такую возможность (создание нового экземпляра на базе существующего) мы будет говорить, что существующий экземпляр выступает в роли фабрики. Экземпляр типа КлиентHttp, который предоставляет «1С:Исполнитель», будем называть базовым экземпляром.

Одним из ключевых свойств КлиентHttp выступает свойство БазовыйUrl. Это свойство указывает адрес сервера, к которому будут выполняться запросы нашего клиентского приложения. Но в базовом экземпляре данное свойство пустое. Поэтому для создания клиента, который сможет обратиться к какому-либо серверу, следует воспользоваться методом СБазовымUrl() и в параметре передать ему адрес сервера. Таким образом, создание экземпляра для работы с конкретным сервером будет выглядеть следующим образом:
пер МойКлиент = КлиентHttp.СБазовымUrl("http://example.com")

Теперь мы имеем экземпляр, который выступает в роли клиента и нам необходимо выполнить запрос к серверу, который мы указали при создании клиента. Для этого мы от экземпляра типа КлиентHttp получим экземпляр типа ЗапросHttp.

Тип ЗапросHttp служит для формирования запроса от клиента серверу и получения ответа от сервера. Ответ от сервера поступает в виде экземпляра типа ОтветHttp.

Если мы хотим выполнить запрос, описанный в стандарте HTTP 1.1, то можно использовать метод КлиентHttp.ЗапросGet() и аналогичные ему (все поддерживаемые имена HTTP-методов перечислены в таблице Методы HTTP-запроса). В этом случае в метод необходимо передать только URI на сервере, к которому будет применяться запрос.

Если мы хотим указать метод в качестве параметра или использовать нестандартный метод, следует использовать метод КлиентHttp.СоздатьЗапрос(). В этом случае следует передать и имя метода и URI. Таким образом, для создания запроса к серверу, например, GET-запроса, можно использовать одну из следующих записей:
пер Запрос = МойКлиент.ЗапросGet("/path/resource?param1=value1&param2=value")
пер Запрос = МойКлиент.СоздатьЗапрос("GET", "/path/resource?param1=value1&param2=value")

Получив экземпляр типа ЗапросHttp мы можем указать ему нужные заголовки (задав их явно или переопределив уже существующие). Для этого есть свойство ЗапросHttp.Заголовки, которое предоставляет доступ ко всей коллекции заголовков данного запроса. Свойство имеет тип ЗаголовкиHttp.

Также ЗапросHttp позволяет работать как с одним заголовком (методы ДобавитьЗаголовок()/УстановитьЗаголовок()/УдалитьЗаголовок()), так и с несколькими заголовками сразу (методы ДобавитьЗаголовки()/УстановитьЗаголовки()/ОчиститьЗаголовки()). С помощью методов добавления/установки заголовков нельзя изменять заголовки Content-Length и Transfer-Encoding. Для изменения заголовка Content-Length предназначен метод УстановитьТипСодержимого(). Для установки информации о клиентском приложении (заголовок User-Agent) предназначен метод УстановитьUserAgent().

Если тело запроса содержит много информации, если есть необходимость в отправке больших файлов, если тело запроса формируется предварительно отдельным алгоритмом — есть возможность установить его из строки, файла или потока. Для этого вы можете использовать четыре метода УстановитьТело():
  • УстановитьТело(Тело: Строка): ЗапросHttp — устанавливает тело запроса из строки.
  • УстановитьТело(Поток: ПотокЧтения): ЗапросHttp — в этом случае запрос будет выполнен (ЗапросHttp.Выполнить()) с использованием Chunked transfer encoding. Чтение переданного потока будет осуществляться только в момент вызова метода ЗапросHttp.Выполнить(). После выполнения отправки переданный поток будет закрыт.
  • УстановитьТело(Поток: ПотокЧтения, Размер: Число): ЗапросHttp — чтение переданного потока будет осуществляться только в момент вызова метода ЗапросHttp.Выполнить(). После выполнения отправки переданный поток будет закрыт.
  • УстановитьТело(Файл: Файл): ЗапросHttp — чтение переданного файла будет осуществляться только в момент вызова метода ЗапросHttp.Выполнить().

После того, как наш запрос готов, его необходимо выполнить. Для этого служит метод ЗапросHttp.Выполнить(). Вызов этого метода приведет к фактическому выполнению запроса. "Внутри" этого вызова будут выполнены все действия HTTP-взаимодействия. Результат вызова будет помещен в экземпляр типа ОтветHttp и этот экземпляр вернется для анализа и получения данных, которые вернул сервер.