English Deutsch 日本語
preview
Заголовок в Connexus (Часть 3): Освоение использования HTTP-заголовков для запросов

Заголовок в Connexus (Часть 3): Освоение использования HTTP-заголовков для запросов

MetaTrader 5Примеры | 21 мая 2025, 08:27
129 0
joaopedrodev
joaopedrodev

Введение

Настоящая статья является продолжением серии статей, в которых мы будем создавать библиотеку под названием 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-заголовков:

  1. Аутентификация: Одним из наиболее распространенных способов использования заголовков является аутентификация клиента, чтобы сервер знал кто отправляет запрос и есть ли у него доступ к информации. Например, заголовок Authorization отправляет токен или учетные данные, которые сервер получает и может использовать для проверки клиента перед обработкой запроса.
  2. Управление кэшем: Заголовки, подобные Cache-Control, позволяют клиенту и серверу настраивать способ кэширования данных, что может быть полезно для предотвращения еще одного ненужного запроса от клиента к серверу или от сервера к какой-либо другой службе. Кэшированные данные могут храниться на клиенте, прокси-серверах или других промежуточных узлах.
  3. Указание типа контента: Заголовок Content-Type позволяет клиенту информировать сервер о том, какой тип данных отправляется или принимается. Как правило, используются такие форматы, как JSON , XML или HTML. Это гарантирует, что обе стороны коммуникации знают, как корректно интерпретировать данные.
  4. Согласование содержания: Клиент может использовать заголовок Accept, чтобы сообщить серверу, какие форматы ответов являются приемлемыми, такие как application/json (сервер отправит данные в формате json) или text/html (сервер отправит их в формате html). Это позволяет серверу отправлять ответ в формате, который клиент готов получить. Наиболее часто используемым форматом является JSON, и мы сосредоточимся на нем здесь, но поддерживаются и другие форматы.
  5. Безопасность: Такие заголовки, как Strict-Transport-Security, помогают усилить использование HTTPS (HTTPS - это то же самое, что HTTP, но он содержит дополнительный уровень веб-безопасности. Запрос, ответ, заголовки, тело, URL и другие форматы в точности совпадают, поэтому рекомендуется всегда использовать HTTPS). Другие заголовки, такие как CORS (совместное использование ресурсов между разными источниками (Cross Origin Resource Sharing)), определяют, какие домены могут получать доступ к ресурсам API, повышая безопасность. Таким образом, сервер фильтрует, кому он отправляет информацию, отправляя ее только в заранее определенный домен, чтобы кроме этого домена ни один домен не мог получить доступ к данным.
  6. Ограничение количества запросов: Некоторые службы возвращают заголовки, такие как X-RateLimit-Limit и X-RateLimit-Remaining, чтобы сообщить клиенту, сколько запросов разрешено за данный период времени. Это предотвращает перегрузку сервера большим количеством запросов.

Таким образом, заголовки играют решающую роль в HTTP-коммуникации, обеспечивая контроль, ясность и безопасность в отношении того, как следует обрабатывать запрос и ответ.


Знание некоторых возможных значений

Давайте посмотрим, каковы возможные значения наиболее распространенных заголовков. HTTP-заголовки очень универсальны и могут быть настроены по мере необходимости для связи между клиентом и сервером. Имейте в виду, что значения, которые может содержать заголовок, зависят от контекста сообщения. Ниже приведены некоторые примеры наиболее распространенных заголовков и их возможных значений:

  1. Авторизация: Используется для отправки учетных данных или токенов аутентификации от клиента на сервер, обеспечивая доступ к защищенным ресурсам. Может принимать различные значения в зависимости от используемого метода аутентификации. Существует несколько методов аутентификации, давайте рассмотрим наиболее часто используемые из них:
    • Токен на предъявителя (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"

      Здесь значение содержит несколько полей, таких как имя пользователя, одноразовый номер (число, используемое один раз), область (область проверки подлинности) и хэшированный зашифрованный ответ.

  1. 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

  1. 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)

      Это позволяет серверу узнать, что запрос поступает из определенного приложения, что упрощает его отладку или мониторинг в случае необходимости.

  1. 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. В итоге он будет выглядеть так:

path

Файл должен содержать пустой класс, аналогичный этому. Я добавил несколько комментариев:
//+------------------------------------------------------------------+
//|                                                       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 было бы таким же хаотичным, как попытка заказать кофе без указания размера, количества сахара или типа молока. В настоящее время диаграмма классов, которую мы создали, выглядит следующим образом:

diagram connexus

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

Приготовьтесь, в следующей главе мы рассмотрим, как элегантно и эффективно упаковать эту информацию в основной текст. До встречи!

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16043

Прикрепленные файлы |
Фильтр Калмана для возвратных стратегий на рынке Форекс Фильтр Калмана для возвратных стратегий на рынке Форекс
Фильтр Калмана представляет собой рекурсивный алгоритм, применяемый в алготрейдинге для оценки истинного состояния финансового временного ряда посредством фильтрации шума из движения цен. Он динамически обновляет прогнозы на основе новых рыночных данных, что делает его ценным для таких адаптивных стратегий, как возвратные. В этой статье впервые представлен фильтр Калмана, а также рассмотрены его расчет и реализация. Кроме того, в качестве примера мы применим этот фильтр к классической возвратной форекс-стратегии. Наконец, проведем различные виды статистического анализа, сравнивая фильтр со скользящей средней на различных валютных парах.
Матричная модель прогнозирования на марковской цепи Матричная модель прогнозирования на марковской цепи
Создаем матричную модель прогнозирования на марковской цепи. Что такое марковские цепи, и как можно использовать марковскую цепь для трейдинга на Форекс.
Самообучающийся советник с нейросетью на матрице состояний Самообучающийся советник с нейросетью на матрице состояний
Самообучающийся советник с нейросетью на матрице состояний. Совмещаем марковские цепи с многослойной нейросетью MLP, написанной на библиотеке ALGLIB MQL5. Как могут быть совмещены для прогнозирования Форекс марковские цепи и нейросети?
Интеграция MQL5 с пакетами обработки данных (Часть 3): Улучшенная визуализация данных Интеграция MQL5 с пакетами обработки данных (Часть 3): Улучшенная визуализация данных
В этой статье мы рассмотрим расширенную визуализацию данных, включая такие функции, как интерактивность, многослойные данные и динамические элементы, позволяющие трейдерам более эффективно изучать тренды, закономерности и корреляции.