
Заголовок в Connexus (Часть 3): Освоение использования HTTP-заголовков для запросов
Введение
Настоящая статья является продолжением серии статей, в которых мы будем создавать библиотеку под названием Connexus. В первой статье мы разобрались с основами работы функции WebRequest, разобрались с каждым из ее параметров, а также создали пример кода, который демонстрирует использование данной функции и ее трудности. В данной статье мы рассмотрим важность и полезность заголовков в HTTP-коммуникации, а также то, как эти элементы используются для различных целей в современном Интернете.
Структура HTTP-сообщения, будь то ответ или запрос, состоит из двух основных элементов, которые мы рассмотрим подробнее: заголовков и тела. Каждый из них играет свою роль в процессе коммуникации, обеспечивая организованную, эффективную и безопасную передачу данных.
Для начала вкратце рассмотрим, как работает структура HTTP-запросов и ответов.
Структура HTTP-запроса
HTTP-запрос обычно выполняется в следующем формате:
HTTP Method | URL | HTTP Version Headers Body (Optional)
- Метод HTTP: Определяет цель запроса (GET, POST, PUT, DELETE и т.д.).
- URL: Идентифицирует запрашиваемый ресурс. (Мы обсуждали это более подробно в предыдущей статье)
- Версия HTTP: Указывает используемую версию протокола.
- Заголовки: Запрашивайте метаданные, такие как тип контента, аутентификация и т.д.
- Тело: Содержание запроса, обычно присутствующее в таких методах, как POST или PUT
Структура HTTP-ответа
HTTP-ответ имеет аналогичную структуру:
HTTP Version | Status Code | Status Message Headers Body (Optional)
- Код состояния: Указывает результат ((200 OK, 404 Not Found, 500 Internal Server Error, и т.д.).
- Заголовки: Информация о возвращаемом содержимом, такая как размер, тип данных и т.д.
- Тело: Фактическое содержимое ответа, например HTML-код страницы или данные в формате JSON. В нашем случае чаще всего будет использоваться получение данных в формате JSON, но имейте в виду, что некоторые API могут возвращать HTML.
В прошлой статье мы глубже изучили формат URL-адреса, разобрав каждый элемент по отдельности и сгруппировав их вместе, чтобы сформировать полный адрес. В настоящей статье мы подробнее рассмотрим заголовок HTTP-запроса. Для чего он нужен? Как его использовать? Каковы возможные значения и т.д.
Заголовки
Во-первых, давайте разберемся, что такое заголовки. В протоколе HTTP заголовок - это набор дополнительных данных, отправляемых вместе с запросом или ответом. HTTP-заголовки являются важными функциями при взаимодействии клиент-сервер. Их основная цель - предоставить подробную информацию о запросе, которая не является непосредственно частью URL-адреса или текста сообщения. Они помогают контролировать поток коммуникации и обеспечивают контекст для обеспечения корректной интерпретации и обработки данных сервером. Эти заголовки помогают как серверу, так и клиенту лучше понять контекст запроса или ответа, например, тип контента, кэширование, аутентификация и многое другое. Другими словами, они действуют как метаданные, которые информируют сервер о том, как запрос может быть обработан, а клиента - о том, как следует интерпретировать ответ.
Давайте упомянем некоторые из основных функций HTTP-заголовков:
- Аутентификация: Одним из наиболее распространенных способов использования заголовков является аутентификация клиента, чтобы сервер знал кто отправляет запрос и есть ли у него доступ к информации. Например, заголовок Authorization отправляет токен или учетные данные, которые сервер получает и может использовать для проверки клиента перед обработкой запроса.
- Управление кэшем: Заголовки, подобные Cache-Control, позволяют клиенту и серверу настраивать способ кэширования данных, что может быть полезно для предотвращения еще одного ненужного запроса от клиента к серверу или от сервера к какой-либо другой службе. Кэшированные данные могут храниться на клиенте, прокси-серверах или других промежуточных узлах.
- Указание типа контента: Заголовок Content-Type позволяет клиенту информировать сервер о том, какой тип данных отправляется или принимается. Как правило, используются такие форматы, как JSON , XML или HTML. Это гарантирует, что обе стороны коммуникации знают, как корректно интерпретировать данные.
- Согласование содержания: Клиент может использовать заголовок Accept, чтобы сообщить серверу, какие форматы ответов являются приемлемыми, такие как application/json (сервер отправит данные в формате json) или text/html (сервер отправит их в формате html). Это позволяет серверу отправлять ответ в формате, который клиент готов получить. Наиболее часто используемым форматом является JSON, и мы сосредоточимся на нем здесь, но поддерживаются и другие форматы.
- Безопасность: Такие заголовки, как Strict-Transport-Security, помогают усилить использование HTTPS (HTTPS - это то же самое, что HTTP, но он содержит дополнительный уровень веб-безопасности. Запрос, ответ, заголовки, тело, URL и другие форматы в точности совпадают, поэтому рекомендуется всегда использовать HTTPS). Другие заголовки, такие как CORS (совместное использование ресурсов между разными источниками (Cross Origin Resource Sharing)), определяют, какие домены могут получать доступ к ресурсам API, повышая безопасность. Таким образом, сервер фильтрует, кому он отправляет информацию, отправляя ее только в заранее определенный домен, чтобы кроме этого домена ни один домен не мог получить доступ к данным.
- Ограничение количества запросов: Некоторые службы возвращают заголовки, такие как X-RateLimit-Limit и X-RateLimit-Remaining, чтобы сообщить клиенту, сколько запросов разрешено за данный период времени. Это предотвращает перегрузку сервера большим количеством запросов.
Таким образом, заголовки играют решающую роль в HTTP-коммуникации, обеспечивая контроль, ясность и безопасность в отношении того, как следует обрабатывать запрос и ответ.
Знание некоторых возможных значений
Давайте посмотрим, каковы возможные значения наиболее распространенных заголовков. HTTP-заголовки очень универсальны и могут быть настроены по мере необходимости для связи между клиентом и сервером. Имейте в виду, что значения, которые может содержать заголовок, зависят от контекста сообщения. Ниже приведены некоторые примеры наиболее распространенных заголовков и их возможных значений:
- Авторизация: Используется для отправки учетных данных или токенов аутентификации от клиента на сервер, обеспечивая доступ к защищенным ресурсам. Может принимать различные значения в зависимости от используемого метода аутентификации. Существует несколько методов аутентификации, давайте рассмотрим наиболее часто используемые из них:
-
Токен на предъявителя (Bearer Token): Один из наиболее распространенных форматов, особенно в современных API, использующих аутентификацию на основе токенов. Это значение обычно представляет собой токен JWT (JSON Web Token), получаемый клиентом после входа в систему или аутентификации на сервере аутентификации.
Authorization: Bearer <my_token_jwt>
Значение <my_token_jwt> - это сам токен аутентификации, обычно представляющий собой длинную строку символов в кодировке Base64. Этот токен содержит информацию об авторизации пользователя и имеет ограниченный срок действия.
-
Basic: Это значение использует базовую аутентификацию HTTP, где имя пользователя и пароль кодируются в формате Base64 и отправляются непосредственно в заголовке. Этот метод менее безопасен, так как учетные данные могут быть легко расшифрованы, и его не следует использовать без HTTPS.
Authorization: Basic base64(username:password)
Значение base64(username:password) - это кодировка Base64 для пары имя username:password. Хотя этот метод прост, он уязвим для атак, если не использовать его по зашифрованным соединениям.
-
Digest: Более безопасный метод, чем Basic, Digest вместо прямой отправки учетных данных использует хэш учетных данных пользователя. Хотя сегодня, с появлением OAuth и JWT, он стал менее распространенным, он все еще встречается в некоторых API.
Authorization: Digest username="admin", realm="example", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", response="6629fae49393a05397450978507c4ef1"
Здесь значение содержит несколько полей, таких как имя пользователя, одноразовый номер (число, используемое один раз), область (область проверки подлинности) и хэшированный зашифрованный ответ.
-
- Content-Type: Данный заголовок определяет тип содержимого, присутствующего в теле запроса или ответа. Рассмотрим некоторые из наиболее часто используемых возможных значений:
-
application/json: Это наиболее распространенное значение при работе с API, поскольку JSON - это легкий и понятный для чтения и записи формат для передачи данных. Пример использования:
Content-Type: application/json
-
application/xml: XML (расширяемый язык разметки (Extensible Markup Language)) широко использовался до появления JSON и до сих пор используется в некоторых устаревших системах или старых API. Однако большинство современных API поддерживают формат JSON, так что пока об XML можно не беспокоиться.
Content-Type: application/xml
-
multipart/form-data: Это значение используется для отправки смешанных данных, особенно когда клиенту необходимо загрузить файлы или данные формы. Сейчас это не является предметом нашего внимания, но приятно осознавать, что такая возможность существует.
-
text/html: Используется, когда отправляемый или получаемый контент является HTML. Это значение часто используется в запросах и ответах на веб-страницах.
Content-Type: text/html
-
- User-Agent: Этот заголовок используется клиентом для самоидентификации. Обычно он содержит данные о типе устройства, браузере и операционной системе, с которой выполняется запрос. Конкретного правила для значений нет, но используются некоторые стандарты:
-
Веб-браузеры: Значение, отправляемое браузерами, обычно содержит подробную информацию о названии и версии браузера, а также об операционной системе.
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
-
API или Приложения: Приложения могут использовать пользовательское значение, например:
User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)
Это позволяет серверу узнать, что запрос поступает из определенного приложения, что упрощает его отладку или мониторинг в случае необходимости.
-
- Accept: Используется клиентом для информирования о том, какой контент он принимает в качестве ответа. Возможные значения для этого заголовка аналогичны Content-Type, давайте рассмотрим некоторые из них:
-
application/json, application/xml, text/html: Вы уже знаете их, мы видели их в разделе "Content-Type".
-
image/png, image/jpeg, image/webp: Эти значения используются, когда клиент ожидает получить изображение. Например, в API-интерфейсах, предоставляющих графические данные, такие как миниатюры или сгенерированные диаграммы. Или просто изображения, такие как аватар пользователя, логотип веб-сайта и т.д.
Accept: image/png, image/jpeg
Здесь клиент сообщает, что принимает изображения как в формате PNG, так и в формате JPEG.
-
*/*: Это значение указывает на то, что клиент принимает любой тип контента в качестве ответа. Используется, когда клиент не отдает предпочтения определенному формату или готов иметь дело с любым типом ответа.
-
Как организовать заголовок?
Теперь, когда мы поняли, для чего нужны заголовки и некоторые возможные значения, давайте разберемся, как заголовки устроены. Заголовки организованы в виде набора пар ключ-значение и внедрены в запросы. Хотя некоторые заголовки являются обязательными для некоторых операций, большинство из них являются опциональными и зависят от требований приложения. Пример базового заголовка для HTTP-запроса:
GET /api/resource HTTP/1.1 Host: example.com Authorization: Bearer token123 Content-Type: application/json User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)
Как показано в этом примере, у нас есть 3 заголовка:
- Authorization: Предоставляет наш токен доступа “token123”.
- Content-Type: Предоставляет информацию о том, как передаются данные, мы используем JSON
- User-Agent: Предоставляет информацию серверу, чтобы тот знал, как обработать запрос. В данном пример мы используем “Connexus/1.0 (MetaTrader 5 Terminal)”
Эти заголовки содержат информацию, позволяющую серверу знать, как обработать запрос. Это всего лишь простой пример, и вскоре мы более подробно рассмотрим, какие наборы используются чаще всего, возможные значения и для чего предназначен каждый из них.
Рука на коде
Мы уже понимаем, как работают заголовки, для чего они нужны и как их использовать, теперь перейдем к практической части. Помните httpbin? Это бесплатный сервис, который просто работает как зеркало: все, что мы отправляем, возвращается к нам снова. Мы будем использовать его, чтобы проверить, какие заголовки мы отправляем, и есть ли какие-либо заголовки, которые сам терминал добавляет автоматически. Для этого создадим файл под именем TestHeader.mq5 in the Experts/Connexus/Test/TestHeader.mq5 folder. Давайте создадим POST-запрос, не отправляя ничего в заголовке, и посмотрим, что он нам ответит:
#include <Connexus2/Data/Json.mqh> #include <Connexus2/URL/URL.mqh> //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- URL CURL url; url.Parse("https://httpbin.org"); url.Path("post"); //--- Data to be sent string method = "POST"; char body_send[]; string headers_send; //--- Data that will be received char body_receive[]; string headers_receive; //--- Send request int status_code = WebRequest(method,url.FullUrl(),headers_send,5000,body_send,body_receive,headers_receive); //--- Show response Print("Respose: ",CharArrayToString(body_receive)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
При выполнении этого кода в терминале мы получим следующий ответ:
Respose: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Accept-Language": "pt,en;q=0.5", "Content-Length": "0", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "MetaTrader 5 Terminal/5.4518 (Windows NT 11.0.22631; x64)", "X-Amzn-Trace-Id": "Root=1-66feb3d9-50de44d019af8b0c1058436b" }, "json": null, "origin": "189.74.63.39", "url": "https://httpbin.org/post" }
Обратите внимание на значение “headers”, оно содержит другой объект json с некоторыми значениями заголовка, которые автоматически определяются терминалом. Помните, что в запросе мы не отправляем какие-либо данные, ни в заголовке, ни в теле. Давайте разберемся с каждым из этих заголовков, которые отправляются автоматически:
-
Accept: Как мы видели ранее, он сообщает серверу, какие типы контента клиент готов принять в качестве ответа. В этом случае значение */*, как показано ранее, означает, что клиент принимает любой тип контента в качестве ответа.
-
Accept-Encoding: определяет типы кодировки контента, которые клиент может принять. Кодировки используются для сжатия данных с целью экономии пропускной способности сети.
- gzip: Это формат сжатия, используемый для уменьшения размера отправляемого ответа.
- deflate: Это еще одна форма сжатия данных, похожая на gzip, но с некоторыми техническими отличиями в алгоритме.
-
Accept-Language: Этот заголовок сообщает серверу, какие языки предпочитает клиент, но сервер должен поддерживать несколько языков. Он помогает серверу предоставлять контент на языке, наиболее подходящем для пользователя.
- pt: Клиент предпочитает получать ответ на португальском языке.
- en;q=0.5: Здесь en обозначает английский, а q=0,5 - это "коэффициент качества" (от 0 до 1), указывающий на относительный приоритет языка. Значение q=1,0 будет максимальным предпочтением, в то время как q=0,5 означает, что клиент принимает английский, но предпочитает португальский.
-
Content-Length: указывает размер тела запроса в байтах. В данном случае значение равно 0, что означает отсутствие содержимого, как уже было сказано, мы не отправляем данные в теле запроса.
-
Content-Type: информирует сервер о типе данных, отправляемых на сервер. В этом случае значение application/x-www-form-urlencoded означает, что данные отправлены в формате URL-кодированной формы. Я не совсем понимаю, почему MetaTrader 5 устанавливает этот формат по умолчанию.
-
Host: указывает имя сервера, на который отправляется запрос. Это необходимо для того, чтобы сервер знал, какой домен или IP запрашивается, особенно если он обслуживает множество доменов по одному IP-адресу.
-
User-Agent: это строка, идентифицирующая HTTP-клиента (в данном случае MetaTrader 5), отправляющий запрос. В ней содержатся подробные сведения об используемом программном обеспечении и операционной системе.
-
X-Amzn-Trace-Id: Заголовок X-Amzn-Trace-Id используется службами AWS (Amazon Web Services) для отслеживания запросов в их распределенной инфраструктуре. Это помогает выявлять и устранять проблемы в приложениях, работающих в облаке Amazon cloud.
- Root=1-66feb3d9-50de44d019af8b0c1058436b: Это значение представляет собой уникальный идентификатор отслеживания, который может быть использован для идентификации конкретной транзакции. Оно может использоваться для диагностики и мониторинга работы.
- Meaning: Данный идентификатор автоматически присваивается системой AWS и может использоваться для отслеживания пути запроса при его прохождении через различные службы инфраструктуры Amazon.
- Common usage: Этот заголовок используется внутри служб AWS для сбора информации об отслеживании и мониторинге. Он особенно удобен в архитектурах микросервисов для диагностики узких мест и проблем с задержкой.
Я полагаю, что MetaTrader 5 добавляет его автоматически, чтобы иметь возможность отслеживать запросы терминала на диагностику, например, платформы, или помочь исправить ошибку.
Теперь, когда мы увидели, какие заголовки добавляются MetaTrader 5 по умолчанию, давайте изменим некоторые из них. Помните, что заголовки не могут иметь два одинаковых ключа, то есть вы не можете использовать Content-Type: application/json и Content-Type: application/x-www-form-urlencoded . Следовательно, если мы определим значение Content-Type, он перезапишет старое значение. Давайте изменим некоторые данные, определив другие значения для следующих заголовков:
- Content-Type: Content-Type: application/json
- User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)
Давайте добавим эти значения в переменную “headers_send”, которая представляет собой строку. Не забудьте добавить “\n”, чтобы отделить заголовки друг от друга. Измененный код представлен ниже:
#include <Connexus2/Data/Json.mqh> #include <Connexus2/URL/URL.mqh> //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- URL CURL url; url.Parse("https://httpbin.org"); url.Path("post"); //--- Data to be sent string method = "POST"; char body_send[]; //--- Headers that will be sent separated by "\n" string headers_send = "User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)\nContent-Type: application/json"; //--- Data that will be received char body_receive[]; string headers_receive; //--- Send request int status_code = WebRequest(method,url.FullUrl(),headers_send,5000,body_send,body_receive,headers_receive); //--- Show response Print("Respose: ",CharArrayToString(body_receive)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
При выполнении кода мы получим такой результат:
Respose: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Accept-Language": "pt,en;q=0.5", "Content-Length": "0", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "Connexus/1.0 (MetaTrader 5 Terminal)", "X-Amzn-Trace-Id": "Root=1-66feb90f-037374b15f220d3e28e1cb32" }, "json": null, "origin": "189.74.63.39", "url": "https://httpbin.org/post" }
Обратите внимание, что мы изменили значения User-Agent и Content-Type на те, которые мы определили. Теперь, когда у нас есть простой пример запроса, отправляющего несколько пользовательских заголовков, давайте добавим эту функцию заголовков в нашу библиотеку. Наша цель - создать класс для работы с заголовками. Этот класс должен быть прост в использовании, иметь простой и интуитивно понятный интерфейс и возможность легко добавлять, удалять или обновлять заголовки.
Создание класса HttpHeaders
Создадим новую папку внутри папки Connexus, которая находится внутри Includes. Эта новая папка будет называться Headers, а внутри этой новой папки мы создадим новый файл с именем HttpHeaders.mqh. В итоге он будет выглядеть так:
//+------------------------------------------------------------------+ //| Header.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| class : CHttpHeader | //| | //| [PROPERTY] | //| Name : CHttpHeader | //| Heritage : No heritage | //| Description : Responsible for organizing and storing the headers | //| of a request. | //| | //+------------------------------------------------------------------+ class CHttpHeader { public: CHttpHeader(void); ~CHttpHeader(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CHttpHeader::CHttpHeader(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CHttpHeader::~CHttpHeader(void) { } //+------------------------------------------------------------------+
Для хранения этих заголовков мы будем использовать объект json, где ключ json будет ключом заголовка, и точно так же значение json будет значением заголовка. Для этого мы импортируем класс json и создадим внутри него новый экземпляр с именем m_headers.
//+------------------------------------------------------------------+ //| Include the file CJson class | //+------------------------------------------------------------------+ #include "../Data/Json.mqh" //+------------------------------------------------------------------+ //| class : CHttpHeader | //| | //| [PROPERTY] | //| Name : CHttpHeader | //| Heritage : No heritage | //| Description : Responsible for organizing and storing the headers | //| of a request. | //| | //+------------------------------------------------------------------+ class CHttpHeader { private: CJson m_headers; public: CHttpHeader(void); ~CHttpHeader(void); }; //+------------------------------------------------------------------+
Когда объект json готов к хранению данных, следующим шагом будет определение того, какие методы должны быть у этого класса. Изначально мы создадим следующие методы:
- Add(string key, string value) : Добавляет новый заголовок к HTTP-запросу или обновляет его, если он уже существует
- Get(string key) : Возвращает значение определенного заголовка с указанием его имени.
- Remove(string key) : Удаляет определенный заголовок.
- Has(string key) : Проверяет, присутствует ли заголовок с указанным ключом.
- Clear() : Удаляет все заголовки из запроса.
- Count() : Возвращает количество заголовков.
Добавим в класс эти более простые методы
//+------------------------------------------------------------------+ //| class : CHttpHeader | //| | //| [PROPERTY] | //| Name : CHttpHeader | //| Heritage : No heritage | //| Description : Responsible for organizing and storing the headers | //| of a request. | //| | //+------------------------------------------------------------------+ class CHttpHeader { private: CJson m_headers; public: CHttpHeader(void); ~CHttpHeader(void); //--- Functions to manage headers void Add(string key, string value); // Adds a new header to the HTTP request or updates it if it already exists string Get(string key); // Returns the value of a specific header, given its name. void Remove(string key); // Removes a specific header. bool Has(string key); // Checks whether a header with the specified key is present. void Clear(void); // Removes all headers from the request. int Count(void); // Returns the number of headers. }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CHttpHeader::CHttpHeader(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CHttpHeader::~CHttpHeader(void) { } //+------------------------------------------------------------------+ //| Adds a new header to the HTTP request or updates it if it already| //| exists | //+------------------------------------------------------------------+ void CHttpHeader::Add(string key,string value) { m_headers[key] = value; } //+------------------------------------------------------------------+ //| Returns the value of a specific header, given its name. | //+------------------------------------------------------------------+ string CHttpHeader::Get(string key) { return(m_headers[key].ToString()); } //+------------------------------------------------------------------+ //| Removes a specific header. | //+------------------------------------------------------------------+ void CHttpHeader::Remove(string key) { m_headers.Remove(key); } //+------------------------------------------------------------------+ //| Checks whether a header with the specified key is present. | //+------------------------------------------------------------------+ bool CHttpHeader::Has(string key) { return(m_headers.FindKey(key) != NULL); } //+------------------------------------------------------------------+ //| Removes all headers from the request. | //+------------------------------------------------------------------+ void CHttpHeader::Clear(void) { m_headers.Clear(); } //+------------------------------------------------------------------+ //| Returns the number of headers. | //+------------------------------------------------------------------+ int CHttpHeader::Count(void) { return(m_headers.Size()); } //+------------------------------------------------------------------+
Теперь, когда мы добавили простейшие методы, давайте добавим последние два метода, которые являются сердцем класса, это:
- Serialize() : Возвращает все заголовки в строковом формате, готовые к отправке в HTTP-запросе.
- Parse(string headers) : Преобразует строку, содержащую заголовки (обычно получаемые в HTTP-ответе), в формат, используемый в классе.
//+------------------------------------------------------------------+ //| class : CHttpHeader | //| | //| [PROPERTY] | //| Name : CHttpHeader | //| Heritage : No heritage | //| Description : Responsible for organizing and storing the headers | //| of a request. | //| | //+------------------------------------------------------------------+ class CHttpHeader { private: CJson m_headers; public: CHttpHeader(void); ~CHttpHeader(void); //--- Auxiliary methods string Serialize(void); // Returns all headers in string format, ready to be sent in an HTTP request. bool Parse(string headers); // Converts a string containing headers (usually received in an HTTP response) into a format usable by the class. }; //+------------------------------------------------------------------+ //| Returns all headers in string format, ready to be sent in an HTTP| //| request. | //+------------------------------------------------------------------+ string CHttpHeader::Serialize(void) { //--- String with the result string headers; //--- Get size int size = this.Count(); for(int i=0;i<size;i++) { //--- Adds the header to the string in the format: "key: value" headers += m_headers[i].m_key + ": " + m_headers[i].ToString(); //--- If it's not the last time it adds "\n" at the end of the string if(i != size -1) { headers += "\n"; } } //--- Return result return(headers); } //+------------------------------------------------------------------+ //| Converts a string containing headers (usually received in an HTTP| //| response) into a format usable by the class. | //+------------------------------------------------------------------+ bool CHttpHeader::Parse(string headers) { //--- Array to store the key value sets string params[]; //--- Separate the string, using the "\n" character as a separator int size = StringSplit(headers,StringGetCharacter("\n",0),params); for(int i=0;i<size;i++) { //--- With the header separated using ": " int pos = StringFind(params[i],": "); if(pos >= 0) { //--- Get key and value string key = StringSubstr(params[i],0,pos); string value = StringSubstr(params[i],pos+2); //--- Clear value StringTrimRight(value); //--- Add in json this.Add(key,value); } } return(true); } //+------------------------------------------------------------------+
Тесты
Для выполнения тестов класса я буду использовать тот же файл, который мы создали в начале статьи, TestHeader.mq5. Импортируем файл HttpHeader, затем создадим экземпляр класса CHttpHeader, передав данные в этот класс. Затем я использую функцию Serialize(), чтобы отформатировать его в виде строки.
#include <Connexus/Data/Json.mqh> #include <Connexus/URL/URL.mqh> #include <Connexus/Header/HttpHeader.mqh> //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- URL CURL url; url.Parse("https://httpbin.org"); url.Path("post"); //--- Data to be sent string method = "POST"; char body_send[]; //--- Headers CHttpHeader headers_send; headers_send.Add("User-Agent","Connexus/1.0 (MetaTrader 5 Terminal)"); headers_send.Add("Content-Type","application/json"); //--- Data that will be received char body_receive[]; string headers_receive; //--- Send request int status_code = WebRequest(method,url.FullUrl(),headers_send.Serialize(),5000,body_send,body_receive,headers_receive); //--- Show response Print("Respose: ",CharArrayToString(body_receive)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
При выполнении этого кода в терминале мы получим такой же ответ:
Respose: { "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Accept-Language": "pt,en;q=0.5", "Content-Length": "0", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "Connexus/1.0 (MetaTrader 5 Terminal)", "X-Amzn-Trace-Id": "Root=1-66fed891-0d6adb5334becd71123795c9" }, "json": null, "origin": "189.74.63.39", "url": "https://httpbin.org/post" }
Заключение
Вкратце, HTTP-заголовки подобны небольшим запискам, которые вы передаете во время урока так, чтобы сервер знал, что делать с вашим запросом. Они могут проходить аутентификацию, устанавливать тип контента, давать инструкции по кэшированию и многое другое. Без них общение по протоколу HTTP было бы таким же хаотичным, как попытка заказать кофе без указания размера, количества сахара или типа молока. В настоящее время диаграмма классов, которую мы создали, выглядит следующим образом:
Теперь, когда вы разобрались с заголовками и тем, насколько они важны, пришло время заняться чем-то еще более интересным: телом запроса. В следующей статье рассмотрим суть HTTP—взаимодействия - фактический контент, который надо отправлять на сервер. В конце концов, нет смысла отправлять записку без содержания, верно?
Приготовьтесь, в следующей главе мы рассмотрим, как элегантно и эффективно упаковать эту информацию в основной текст. До встречи!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16043





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования