HTTP-запросы и HTTP-сервисы - вероятно, главные инструменты интеграции 1С с другими системами на сегодняшний день. Однако при встрече с конкретной их реализацией, будь то в рабочих базах или сторонних решениях, зачастую складывается впечатление, будто механизмы для работы с данным протоколом представляют из себя не конкретные стройные функции, а фломастеры для хаотичного закрашивания клеток в "морском бое" против стороннего API или внешнего клиента. Победой же считается запихивание всех известных миру заголовков в свой запрос и перебор всех вариаций формирования тела запроса до тех пор, пока не вернется код 200
В этой статье не будет каких-то особенных срывов покровов, но мы постараемся окунуться немного глубже в понимание этого популярного стандарта, чтобы реализация обмена стала чуть менее хаотичной и чуть более осмысленной
Что нужно знать для понимания этой статьи: зачем используются HTTP-запросы и что запрос состоит из метода (GET, POST, PUT и пр.), кода состояния (404), заголовков и тела (необязательно). Подразумевается, что вы уже работали с HTTP в платформе 1С или где-то еще
HTTP — надстройка над TCP
Почему HTTP — это не волшебство? Потому что волшебство — это TCP
TCP (Transmission Control Protocol) — это протокол транспортного уровня, который отвечает за передачу данных между двумя узлами в сети. Его задача — установить соединение, передать данные и гарантировать их доставку в правильном порядке. Когда мы используем нечто вроде HTTPЗапрос и HTTPСоединение.ОтправитьДляОбработки(), то основная работа, а именно передача данных, выполняется на уровне TCP.
Нам не обязательно знать, как он работает в деталях — достаточно того, что это древнее как мир описание порядка действий по установке соединения и обмена пакетами для сетевого взаимодействия с обозначением, какой блок информации где расположен и сколько должен весить. Едва ли кому-то из нас придется погружаться на настолько низкий уровень абстракции и реализовывать его руками
Но что тогда такое HTTP? Это протокол прикладного (более высокого) уровня, который описывает, как клиент и сервер должны взаимодействовать при передаче данных. Т.е. он определяет формат сообщения, которое передается по TCP, а также как и в каком порядке его понимать при получении.
Если рассмотреть весь процесс целиком, то когда вы отправляете HTTP-запрос:
- HTTP формирует запрос (заголовки, тело и метод, например,
GET /index.html HTTP/1.1
). - TCP берет этот запрос и разбивает его на пакеты.
- Пакеты отправляются на сервер, где TCP собирает их обратно в единое сообщение.
- Сервер обрабатывает HTTP-запрос (исходя из того, что он построен по правилам HTTP) и формирует ответ.
- Ответ передается через TCP обратно клиенту.
TCP — это курьер, HTTP — посылка с инструкцией. Вариаций таких "посылок с инструкцией" - они же протоколы over TCP, вообще, существует огромное количество: AMQP (у RabbitMQ), FTP, SMTP, SSH, MQTT и пр. Все они отличаются форматом содержимого ("посылкой") и способом его разбора ("инструкцией"), но не способом доставки. Но так как речь у нас идет о работе с HTTP в 1С, а значит с уже реализованными внутри платформы готовыми функциями даже "инструкция" нас не интересует — отличие лишь в содержимом, а разбором занимается платформа
Важно лишь помнить, что такие понятия как "тело", "код состояния" и "заголовки" - это не более чем абстракции над единым блоком данных в TCP пакете. Просто кто-то подумал (вполне обоснованно), что это удобно иметь отдельно часть произвольных данных, часть, которая всегда ключ-значение и числовой показатель статуса в виде трехзначного числа. Но никакими особенными качествами или самостоятельными действиями они по отдельности не обладают. Но об этом далее
Сервер ждет лишь то, что прописано для него разработчиками
К чему нужен сказ про отношение HTTP к TCP? Главная идея заключается в том, что никаких волшебных преобразований в запросе и ответе, при добавлении туда информации или ее отправке не происходит
Когда вы добавляете в запрос, например, заголовки, то они просто "складываются в стопку" как часть содержимого и переправляются по TCP на принимающую сторону. HTTP-сервер (вроде Apache или IIS) может предварительно разобрать пакет на код состояния, тело и заголовки, так, как это описано в стандарте. Но то, как этот конкретный разобранный набор данных далее обрабатывается, определяется сугубо алгоритмом, написанным бэкендерами для конкретного API: если использование каких-либо данных, отправленных вами, не предусмотрено разработчиками сервиса, то они будут просто проигнорированы. Сам стандарт не обязывает к обязательному поиску и обработке конкретных данных или отдельных наименований заголовков. Есть буквально 2 из них, которые гипотетически считаются обязательными - это Host и Content-Length для запросов с телом, но они, как правило, вообще зашиваются внутрь функции работы c HTTP, а их отсутствие все равно не приводит к 100% ошибке при работе
По идее, требования к наличию тех или иных данных должны быть перечислены в документации к конкретному API. Опять же: все это - лишь соглашение между разработчиками клиента и разработчиками сервера, с поправкой на некоторый усредненный стандарт, чтобы не изобретать каждый раз велосипед. Вы можете использовать любые свои заголовки с любой информацией (при реализации сервера), если другая сторона согласна с ними работать. Однако есть несколько правил, которые, как и многое в HTTP, нестрогие и носят рекомендательный характер, а именно:
- Не создавать свои заголовки, если уже есть стандартные, подходящие по смыслу
- Начинать нестандартные заголовки с префикса "X-"
- Если заголовок не распознан, это не должно приводить к ошибкам
Подытожим к текущему моменту:
- Данные передаются по TCP, но нас это не волнует, так как это делает платформа
- Сообщение, которое передается по TCP, имеет формат, соответствующий стандарту HTTP. Сам стандарт HTTP описывает, как информация в сообщении должна быть собрана и как принимающая сторона должна его разобрать обратно в полезную информацию. Это тоже делает платформа, показывая нам лишь уже разобранный "полезный" объект типа HTTPЗапрос, HTTPОтвет или HTTPСервисОтвет
- Обработка полезных данных не имеет строгих регламентов — лишь соглашения и "правила хорошего тона"
*Неожиданная рекламная пауза*
А множество (19) уже готовых решений для работы с онлайн сервисами вы можете найти в Открытом пакете интеграций! Это расширение для 1С, пакет для OneScript и консольное приложение с кучей готовых методов интеграции с такими сервисами как Bitrix24, Telegram, VK, Viber, Google Drive, Sheet, Yandex Disk, S3 и многими другими. Открыто, бесплатно и с подробной документацией!
Инфостарт | Github (поставьте звездочку, пж :Р )
*Конец неожиданной рекламной паузы*
Соглашения по заголовкам
Как уже было рассмотрено, стандарт HTTP довольно мягкий по отношению к конкретным данным и не определяет большого количества строгих правил для разработчика, который работает поверх уже реализованного HTTP-клиента или сервера. Однако есть ряд мягких соглашений (рекомендаций), которые вероятно придется понимать для работы в качестве клиента и крайне желательно знать при реализации сервера, чтобы не причинять неудобства своим будущим пользователям
Во-первых, в стандарте есть перечень стандартных заголовков, которые желательно использовать именно так как это написано в стандарте. Это банально создает меньше путаницы — никто ведь не ожидает, что ему придется передавать пароль через заголовок, предназначенный для передачи размера содержимого
Во-вторых, так как для стандартных заголовков определено их назначение, большинство из них можно отнести к разным логическим категориям: заголовки запроса и заголовки ответа, заголовки клиентские и серверные и пр. Это важно, так как позволяет избежать добавления заголовков, которые априори не могут быть полезны в конкретной ситуации
Для лучшего понимания разберем часть заголовков, которые используются чаще всего и работают почти всегда, если сервис сделан с умом и по стандартам:
- Accept - это клиентский заголовок, который можно передать серверу для определения MIME типа, ожидаемого в ответе. Например, если сервер может возвращать данные в XML и JSON, то указание в запросе Accept:application/json скажет серверу, что вы хотите JSON. В свою очередь, вы можете реализовать обработку данного заголовка на своем сервере для предоставления подобного функционала
- Accept-Encoding - клиентский заголовок с указанием доступных алгоритмов сжатия. Если ваш клиент поддерживает прием сжатых данных (Коннектор, например, поддерживает gzip), а сервер может в сжатие данных, то размер трафика можно значительно снизить
- Authorization - стандартный заголовок запроса для передачи авторизационных данных: логинов:паролей, токенов и пр.
- Content-Type - универсальный заголовок, обозначающий, какой тип данных содержится в теле запроса или ответа
- Content-Length - заголовок, обозначающий размер тела запроса или ответа. Некоторые сервера вообще не умеют принимать запросы с телом без этого заголовка. Желательно его указывать вообще всегда (если есть тело)
- Location - стандартный заголовок ответа, если требуется перенаправление. Если вы обратились "немного не туда", но сервер понял, что вы хотели, то он может вернуть код 3хх и URL в это заголовке
Есть еще пачка популярных заголовков для работы с Web-приложениями, вроде Cookie, но для работы с API они не используются и мы их опустим. Повторюсь лишь еще раз, что описание этих заголовков в стандарте вовсе не означает, что они будут обработаны при работе с конкретным сервером. Также и ваш сервер не будет их обрабатывать, пока вы не пропишите этого в коде или не покрутите в настройках веб-сервера, вроде IIS, Apache, nginx (при наличии соответствующих настроек)
MDN — ваш лучший друг!
Если в процессе работы с HTTP вы встретились с незнакомым заголовком или вообще попали на что-то непонятное, то попробуйте найти это на MDN Web Docs. Это такой большой справочник от Mozilla по всему Web-у и по HTTP с HTML в частности
О реализации сервера
В случае с клиентом вам придется подстраиваться под сервер. В случае же с сервером свободы больше, но больше и шанс сделать больно своим потенциальным пользователям. Вот некоторые моменты, учтя которые вы можете реализовать свой API (http-сервис) более прогнозируемым и понятным:
- Код состояния - это важно. Теоретически, вы можете использовать в ответе код 200 на все случаи жизни, описывая ошибки в теле (в JSON например). Однако, как частый пользователь различных API, могу сказать - когда сервер так делает, это очень неудобно. Дело в том, что коды состояния — самые простые данные, получаемые из ответа и не требующие особенной обработки. Если я, например, не жду полезной нагрузки в ответе от сервера, то зачем мне проверять его содержимое при коде 200 (Успех)? Абсолютно незачем и многие HTTP-клиенты также опираются на код состояния как на важный элемент при обработке данных. Если же код не меняется в зависимости от ответа, то вы рискуете получить пользователя, свято уверенного, что все ОК, даже если все не ОК и текст ошибки возвращен в теле запроса или его заголовках
- Старайтесь добавлять общепринятые заголовки в свои ответы. Нет ничего сложного в том, чтобы посчитать и добавить размер ответа в Content-Length или кодировку и тип данных в Content-Type. Однако это сделает работу с вашим API гораздо проще и комфортнее, а также уменьшит шанс появления проблем у клиентов, которые опираются на эти данные при разборе. Например, 1С использует UTF-8 для строк по умолчанию. UTF-8 - популярная кодировка, но это вовсе не означает, что какой-нибудь клиент не может использовать по умолчанию другую. И только заголовок может дать ему понять, что вместо его "родной кодировки" надо использовать конкретно UTF-8
- По возможности избегайте собственных видов заголовков и не используйте заголовки, априори неподходящие под вид запроса. Добавление собственных видов заголовков — дело ответственное, по умолчанию подразумевающее наличие документации, где их смысл будет описан. Неподходящие же по виду запросы (клиентские в серверных ответах, заголовки ответа в запросах и пр.) в принципе не нужны ни для чего и просто являются мертвым грузом в ваших запросах — избегайте их (про виды запросов на MDN)
- Добавьте сжатие. Сжатие помогает сильно уменьшить нагрузку на сеть как для вас, так и для клиента. В 1С вполне реализуем и deflate и gzip, а большинство современных клиентов вполне умеют их распаковывать обратно. В любом случае, заголовки позволяют делать сжатие опциональным: клиент прислал Accept-Encoding — сжимаем, не прислал — не сжимаем
- Используйте JSON. Никто не любит XML
В заключение
HTTP является крайне гибким стандартом в вопросах передаваемых данных: как один из главных прикладных протоколов Интернета, будучи рассчитанным на максимальную универсальность, другим он быть просто не мог. Но обратная сторона подобной гибкости — возможность вольных интерпретаций, неограниченных обязательными условиями. И только соблюдение нестрогих, но рекомендованных решений позволяет создавать удобные и понятные сервисы, не вынуждающие пользователей для каждого нового API проходить все "как в первый раз". Не пренебрегайте ими
Спасибо за внимание!
Связанные статьи:
Глоссарий HTTP аутентификации: Basic, Bearer, OAuth и другие непонятные слова
Рано или поздно каждый 1С разработчик сталкивается с задачей автоматизации работы со сторонним сервисом посредством RestAPI. Одними из основных (а, как правило - и самыми запутанными) элементами любой подобной автоматизации являются процессы авторизации и аутентификации. Токены, подписи, шифрование - как не потеряться во всем этом? Поможет данное краткое руководство
Новые методы в составе Открытого пакета интеграций для работы по протоколу TCP (в качестве клиента) на основе Native API компоненты на Rust.