- Отправка Push-уведомлений
- Отправка уведомлений по электронной почте
- Отправка файлов на сервер FTP
- Обмен данными с веб-сервером по протоколу HTTP/HTTPS
- Установление и разрыв соединения сетевого сокета
- Проверка состояния сокета
- Настройка таймаутов передачи и приема данных сокетами
- Чтение, запись данных по незащищенному сокет-соединению
- Подготовка защищенного сокет-соединения
- Чтение и запись данных по защищенному сокет-соединению
Обмен данными с веб-сервером по протоколу HTTP/HTTPS
MQL5 позволяет интегрировать программы с веб-сервисами и запрашивать данные из Интернет. Отправка и получение данных по протоколам HTTP/HTTPS осуществляется функцией WebRequest, имеющей две версии: для упрощенного и для продвинутого взаимодействия с веб-серверами.
int WebRequest(const string method, const string url, const string cookie, const string referer,
int timeout, const char &data[], int size, char &result[], string &response)
int WebRequest(const string method, const string url, const string headers, int timeout,
const char &data[], char &result[], string &response)
Основное отличие между двумя функциями в том что, упрощенный вариант позволяет указать в запросе заголовки только двух типов: так называемые "куки" (cookie) и ссылку на адрес, откуда производится переход (referer, здесь нет опечатки, так исторически сложилось, что английское слово "referrer" пишут в HTTP-заголовках через одно 'r'). Расширенный вариант принимает обобщенный параметр headers для передачи произвольного набора заголовков. Заголовки запроса имеют вид "имя: значение" и соединяются переносом строки "\r\n", если их больше одного.
В частности, если предположить, что строка cookie должна содержать "name1=value1; name2=value2", а ссылка referer равна "google.com", то для вызова второго варианта функции с тем же эффектом, что и первого, следует в параметр headers добавить: "Cookie: name1=value1; name2=value2\r\nReferer: google.com".
В параметре method указывается один из методов протокола, "HEAD", "GET" или "POST". Адрес запрашиваемого ресурса или сервиса передается в параметре url. По спецификации HTTP длина сетевого идентификатора ресурса ограничена 2048 байтами, однако на момент написания книги в MQL5 было свое ограничение - 1024 байта.
Максимальная длительность запроса определяется таймаутом (timeout) в миллисекундах.
Оба варианта функции позволяют передать на сервер данные из массива data. Первый вариант дополнительно требует указания размера этого массива в байтах (size).
Для отправки простых запросов со значениями нескольких переменных можно их соединить в строку вида "имя1=значение1&имя2=значение2&..." и добавить в адрес GET-запроса, после символа-разделителя '?' или поместить в массив data для POST-запроса с использованием заголовка "Content-Type: application/x-www-form-urlencoded". В более сложных случаях, когда требуется передать, например, файлы, используйте POST-запрос и "Content-Type: multipart/form-data".
Приемный массив result получит тело ответа сервера (если оно есть). Заголовки ответа сервера помещаются в строку response.
Функция возвращает HTTP-код ответа сервера или -1 в случае системной ошибки (например, проблемы со связью, ошибка в параметрах). Возможные коды в _LastError:
- 5200 — ERR_WEBREQUEST_INVALID_ADDRESS — некорректный URL;
- 5201 — ERR_WEBREQUEST_CONNECT_FAILED — не удалось подключиться к указанному URL;
- 5202 — ERR_WEBREQUEST_TIMEOUT — превышен таймаут получения ответа от сервера;
- 5203 — ERR_WEBREQUEST_REQUEST_FAILED — иная ошибка в результате выполнения запроса.
Напомним, что даже если запрос выполнен без ошибок на уровне MQL5, прикладная ошибка может содержаться в HTTP-коде ответа сервера (например, требуется авторизация, неверный формат данных, страница не найдена и т.д.). При этом результат окажется пустым, а инструкции по разрешению ситуации, как правило, выясняются путем анализа полученных заголовков response.
Для использования функции WebRequest следует добавить адреса серверов в список разрешенных URL во вкладке Советники в настройках терминала. Порт сервера выбирается автоматически на основе указанного протокола: 80 для адресов "http://" и 443 — для "https://".
Функция WebRequest является синхронной, то есть она приостанавливает выполнение программы, пока ждет ответа от сервера. В связи с этим функция запрещена для вызова из индикаторов, поскольку они работают в общих потоках по каждому символу. Задержка выполнения одного индикатора приведет к остановке обновления всех графиков по данному символу.
При работе в тестере стратегий функция WebRequest не выполняется.
Начнем практику с простого скрипта WebRequestTest.mq5, выполняющего единичный запрос. Во входных параметрах предоставим выбор для метода (по умолчанию "GET"), адреса тестовой веб-страницы, дополнительных заголовков (по желанию), а также таймаута.
input string Method = "GET"; // Method (GET,POST)
|
Адрес вводится как в строке браузера: все символы, которые запрещены спецификацией HTTP напрямую использовать в адресах (в том числе, символы локального алфавита) функция WebRequest автоматически "маскирует" перед отправкой по алгоритму urlencode (точно так же делает и браузер, но мы это не видим, так как это представление предназначено для передачи по сетевой инфраструктуре, а не для людей).
Также добавим опцию DumpDataToFiles: когда она равна true, скрипт сохранит ответ сервера в отдельный файл, поскольку он может быть довольно большим. Значение false предписывает выводить данные напрямую в журнал.
input bool DumpDataToFiles = true; |
Сразу стоит сказать, что тестирование подобных скриптов требует наличия сервера. Желающие могут установить локальный веб-сервер, например, node.js, но это предполагает самостоятельную подготовку или установку серверных скриптов (в данном случае, с подключением модулей на JavaScript). Более простой способ заключается в использовании публичных тестовых веб-серверов, доступных в Интернет. Среди них, например, httpbin.org, httpbingo.org, webhook.site, putsreq.com, www.mockable.io, reqbin.com. Они предоставляют разный набор возможностей. Выберите или найдите для себя подходящий (удобный и понятный или максимально гибкий).
В параметре Address по умолчанию находится адрес "конечной точки" (endpoint) серверного API httpbin.org — эта динамическая "веб-страница" возвращает клиенту HTTP-заголовки его запроса (в формате JSON). Таким образом, мы сможем в своей программе увидеть, что именно пришло на веб-сервер из терминала.
Не забудьте добавить домен "httpbin.org" в список разрешенных в настройках терминала.
Кстати говоря, текстовый формат JSON является стандартом де-факто для веб-сервисов. Готовые реализации классов для разбора JSON можно найти на сайте mql5.com, но мы сейчас будем просто показывать JSON "как есть".
В обработчике OnStart вызовем WebRequest с заданными параметрами и обработаем результат, если код ошибки неотрицательный. Заголовки ответ сервера (response) всегда выводятся в журнал.
void OnStart()
|
Для формирования имени файла мы используем вспомогательный класс URL из заголовочного файла URL.mqh (здесь не будет описан полностью). Метод URL::parse разбирает переданную строку на составляющие URL согласно спецификации — общий вид URL всегда такой: "protocol://domain.com:port/path?query#hash", причем многие фрагменты опциональны. Результаты помещаются в приемный массив, индексы в котором соответствуют конкретным частям URL и описаны в перечислении URL_PARTS:
enum URL_PARTS
|
Таким образом, когда получаемые данные должны записаться в файл, скрипт создает его в папке по имени сервера (parts[URL_HOST]) и далее с сохранением иерархии пути в URL (parts[URL_PATH]): в простейшем случае это будет просто имя "конечной точки". Когда запрашивается главная страница сайта (путь содержит только наклонную черту '/'), файл получает название "_index_.htm".
Попробуем запустить скрипт с параметрами по умолчанию, не забыв предварительно разрешить данный сервер в настройках терминала. В журнале мы увидим следующие строки (HTTP-заголовки ответа сервера и сообщение об успешном сохранении файла):
WebRequest(Method,Address,Headers,Timeout,data,result,response)=200 / ok
|
Внутри файла httpbin.org/headers находятся заголовки нашего запроса, каким его увидел сервер (форматирование JSON сервер добавил сам при ответе нам).
{
|
Таким образом, терминал сообщает о том, что готов принять данные любого типа, с поддержкой сжатия конкретными методами и списком предпочтительных языков. Кроме того, он представляется в поле User-Agent как MetaTrader 5. Последнее может быть нежелательным при работе с некоторыми сайтами, которые оптимизированы для работы исключительно с браузерами. Тогда мы можем во входном параметре Headers указать фиктивное название, например, "User-Agent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36".
Некоторые из перечисленных выше тестовых сайтов позволяют организовать на сервере временное тестовое окружение со случайным именем под ваш личный эксперимент: для этого нужно зайти на сайт из браузера и получить уникальную ссылку, рабочую, как правило, в пределах 24 часов. Тогда вы сможете использовать эту ссылку в качестве адреса для запросов из MQL5 и отслеживать поведение запросов непосредственно из браузера. Там же вы сможете настраивать ответы сервера, в частности, пробовать отправку форм.
Слегка усложним пример. Сервер может потребовать от клиента дополнительных действий для выполнения запроса, в частности, авторизоваться, выполнить "редирект" (переход по другому адресу), снизить частоту запросов и т.д. Все подобные "сигналы" обозначаются особыми HTTP-кодами, которые возвращает функция WebRequest. Например, коды 301 и 302 — это редирект по разным причинам, и функция WebRequest выполняет его внутри автоматически, перезапрашивая страницу по указанному сервером адресу (поэтому коды редиректа никогда не попадают в код MQL-программы). А код 401 требует от клиента предоставить имя пользователя и пароль, и здесь вся ответственность лежит на нас. Способов послать эти данные существует множество. В новом скрипте WebRequestAuth.mq5 продемонстрирована обработка двух вариантов авторизации, которые сервер запрашивает с помощью ответных HTTP-заголовков: "WWW-Authenticate: Basic" или "WWW-Authenticate: Digest". В заголовках это может выглядеть так:
WWW-Authenticate:Basic realm="DemoBasicAuth" |
Или так:
WWW-Authenticate:Digest realm="DemoDigestAuth",qop="auth", » |
Первый из них — самый простой и небезопасный, а потому практически не используется: в книге он приведен из-за простоты изучения на первом этапе. Суть его работы в том, чтобы в ответ на требование сервера сформировать следующий HTTP-запрос, добавив специальный заголовок:
Authorization: Basic dXNlcjpwYXNzd29yZA== |
Здесь после ключевого слова "Basic" идет Base64-кодировка строки "user:password" с актуальным именем и паролем, а символ ':' вставляется здесь и далее "как есть" в качестве связующего звена. Более наглядно процесс взаимодействия представлен на изображении.
Схема простой авторизации на веб-сервере
Более продвинутой считается схема авторизации Digest. В этом случае сервер предоставляет некоторую дополнительную информацию в своем ответе:
- realm — название сайта (области сайта), куда производится вход;
- qop — разновидность метода Digest (мы затронем только "auth");
- nonce — случайная строка, которая будет использоваться для генерации данных авторизации;
- opaque — случайная строка, которую мы в своих заголовках передадим обратно "как есть";
- algorithm — опциональное название алгоритма хэширования, по умолчанию подразумевается MD5;
Для авторизации нужно выполнить следующие действия:
- Сгенерировать собственную случайную строку cnonce;
- Инициализировать или увеличить счетчик своих запросов nc;
- Вычислить hash1 = MD5(user:realm:password);
- Вычислить hash2 = MD5(method:uri), здесь uri — это путь и имя страницы;
- Вычислить response = MD5(hash1:nonce:nc:cnonce:qop:hash2).
После этого клиент может повторить запрос к серверу, добавив в свои заголовки строку вида:
Authorization: Digest username="user",realm="realm",nonce="...", » |
Поскольку сервер имеет ту же информацию, что и клиент, он сможет повторить вычисления и проверить совпадение хэшей.
В параметры скрипта добавим переменные для ввода имени пользователя и пароля. В параметре Address по умолчанию прописан адрес "конечной точки" digest-auth, которая умеет запрашивать авторизацию с параметрами qop ("auth"), логином ("test") и паролем ("pass") — это все задается на выбор в самом пути "конечной точки" (вы можете тестировать другие методы и реквизиты пользователя, например, так: "https://httpbin.org/digest-auth/auth-int/mql5client/mql5password").
const string Method = "GET";
|
В параметре Headers мы просто так указали фиктивное название браузера для демонстрации возможности.
В функции OnStart добавим обработку HTTP-кода 401. Если имя пользователя и пароль не предоставлены, мы не сможем продолжить.
void OnStart()
|
Далее следует проанализировать заголовки, полученные с сервера. Для удобства был написан класс HttpHeader (HttpHeader.mqh). В его конструктор передается полный текст, а также разделитель элементов (в данном случае, символ перевода строки '\n') и символ, используемый между именем и значением внутри каждого элемента (в данном случае, двоеточие ':'). В процессе своего создания объект "парсит" текст, и затем элементы становятся доступны через перегруженный оператор [], причем тип его аргумента — строка. В результате, мы можем проверить наличие требования авторизации по имени "WWW-Authenticate". Если такой элемент есть в тексте и равен "Basic", формируем ответный заголовок "Authorization: Basic " с закодированными в Base64 логином и паролем.
code = -1;
|
Для Digest-авторизации все немного сложнее, в соответствии с изложенным выше алгоритмом.
else if(StringFind(auth, "Digest ") == 0)
|
Статический метод HttpHeader::hash получает строку с шестнадцатеричным представлениям хэша (по умолчанию MD5) для всех требуемых составных строк. На основе этих данных формируется заголовок для очередного вызова WebRequest. Статический метод HttpHeader::unquote убирает обрамляющие кавычки.
Остальная часть скрипта осталась без изменений. Повторный HTTP-запрос может оказать успешным, и тогда мы получим содержимое защищенной страницы, либо в авторизации будет отказано, и сервер напишет что-то вроде "Access denied".
Поскольку параметры по умолчанию содержат правильные значения ("/digest-auth/auth/test/pass" соответствует пользователю "test" и паролю "pass"), мы должны получить следующий результат запуска скрипта (все основные этапы и данные выводятся в лог). Рассмотрим его по порядку.
WebRequest(Method,Address,Headers,Timeout,data,result,response)=401 / ok
|
Первый вызов WebRequest закончился с кодом 401, и среди ответных заголовков находится требование об авторизации ("WWW-Authenticate") с необходимыми параметрами. На их основе мы посчитали правильный ответ и подготовили заголовки для нового запроса.
Header=User-Agent: noname
|
Второй запрос уже возвращает код 200 и полезные данные, которые мы записываем в файл.
WebRequest(Method,Address,Header,Timeout,data,result,response)=200 / ok
|
Внутри файла MQL5/Files/httpbin.org/digest-auth/auth/test/pass можно найти "веб-страницу", а точнее статус успешной авторизации в формате JSON.
{
|
Если при запуске скрипта указать неправильный пароль, получим пустой ответ от сервера, и файл не будет записан.
При использовании WebRequest мы автоматически оказываемся в области распределенных программных систем, в которых правильная работа зависит не только от нашего клиентского MQL-кода, но и сервера (не говоря уже о промежуточных звеньях, вроде прокси). Поэтому нужно быть готовым к возникновению чужих ошибок. В частности, на момент написания книги в реализации "конечной точки" digest-auth на httpbin.org имелась проблема: введенное в запрос имя пользователя не участвует в проверке авторизации, и потому любой логин приводит к успешной авторизации при указании правильного пароля. Чтобы все-таки проверить наш скрипт воспользуйтесь другими сервисами, например, аналогичным httpbingo.org/digest-auth/auth/test/pass. Также вы можете настроить скрипт на адрес jigsaw.w3.org/HTTP/Digest/ — он ожидает логин/пароль "guest"/"guest".
На практике большинство сайтов реализует авторизацию с помощью форм, встроенных непосредственно в веб-страницы: внутри HTML-кода они представляют собой тег-контейнер form с набором полей ввода (теги input, select и другие, см. далее), которые заполняются пользователем и отправляются на сервер методом POST. В связи с этим имеет смысл разобрать пример с отправкой формы. Однако, прежде чем заняться этим вплотную, желательно осветить еще один технический прием.
Дело в том, что взаимодействие клиента и сервера обычно сопровождается изменением состояния как клиента, так и сервера. На примере авторизации это можно понять наиболее наглядно, так как до авторизации пользователь был для системы неизвестным, а после — система уже знает его логин, и может применить предпочтительные настройки для сайта (например, язык, цвет, способ отображения форума), а также разрешить доступ к тем страницам, куда неавторизованные посетители попасть не могут (сервер такие попытки пресекает, возвращая HTTP-статус 403, Forbidden).
Поддержка и синхронизация согласованного состояния клиентской и серверной частей распределенного веб-приложения обеспечивается с помощью механизма "печеньиц" (cookies) — поименованных переменных и их значений в HTTP-заголовках. Термин восходит к "печеньям с предсказаниями", так как cookie тоже содержат маленькие послания, невидимые пользователю.
Любая из сторон — сервер и клиент — может добавлять cookie в HTTP-заголовок. Сервер это делает с помощью строки вида:
Set-Cookie: имя=значение; ⌠Domain=домен; Path=путь; Expires=дата; Max-Age=количество_секунд ...⌡ᵒᵖᵗ |
Только имя и значение являются обязательными, а остальные атрибуты опциональны: здесь приведены основные — Domain, Path, Expires и Max-Age, но по факту их больше.
Получив такой заголовок (или несколько), клиент должен запомнить у себя имя и значение переменной и отсылать их на сервер во всех запросах, которые адресуются к соответствующему домену (Domain), пути (Path) внутри этого домена и пока не истечет срок (Expires или Max-Age).
В исходящем от клиента HTTP-запросе cookie передаются строкой вида:
Cookie: имя⁽№⁾=значение⁽№⁾ [; имя⁽ⁱ⁾=значение⁽ⁱ⁾ ...]ᵒᵖᵗ |
Здесь через точку с запятой с пробелом перечисляются все пары "имя=значение", установленные сервером и известные на данном клиенте, подходящие по домену и пути к текущему запросу, а также с неистекшим сроком жизни.
Сервер и клиент обмениваются всеми нужными куками при каждом HTTP-запросе — именно поэтому данный архитектурный стиль распределенных систем называется REST (Representational State Transfer или "передача самодостаточного состояния"). Например, после того как пользователь успешно авторизуется на сервере, последний установит (через заголовок "Set-Cookie:") специальную куку с идентификатором пользователя, после чего веб-браузер (или, в нашем случае, терминал с MQL-программой) будет отсылать её в следующих запросах (добавляя соответствующую строку в заголовок "Cookie:").
Функция WebRequest незаметно проделывает для нас всю эту работу: собирает "куки" из приходящих заголовков и добавляет подходящие куки в исходящие HTTP-запросы.
Куки хранятся терминалом и между сессиями, согласно их настройкам. Чтобы проверить это, достаточно дважды запросить веб-страницу с сайта, использующего куки.
Внимание, куки хранятся в привязке к сайту и потому незаметно подставляются в исходящие заголовки всех MQL-программ, которые используют WebRequest для того же сайта.
Для упрощения последовательных запросов имеет смысл формализовать популярные действия в специальном классе HTTPRequest (HTTPRequest.mqh). В нем будем хранить общие HTTP-заголовки, которые, скорее всего, понадобятся для всех запросов (например, поддерживаемые языки, инструкции для прокси и т.д.). Кроме того, такая настройка как таймаут также является общей. Обе настройки передаются в конструктор объекта.
class HTTPRequest: public HttpCookie
|
По умолчанию таймаут установлен в 5 секунд. Основной, в некотором смысле универсальный метод класса — request.
int request(const string method, const string address,
|
Опишем еще пару методов для запросов конкретных типов.
Запросы GET используют только заголовки, а тело документа (часто встречается термин payload, "нагрузка") у них пустое.
int GET(const string address, uchar &result[], string &response,
|
В запросах POST "нагрузка", как правило, есть.
int POST(const string address, const uchar &payload[],
|
Но отправка форм возможна в разных форматах. Наиболее простой из них "application/x-www-form-urlencoded". Он подразумевает, что полезная "нагрузка" будет представлять собой строку (может быть и очень длинную, так как спецификации не накладывают ограничений, и все зависит от настроек веб-серверов). Для таких форм предоставим более удобную перегрузку метода POST со строковым параметром "нагрузки".
int POST(const string address, const string payload,
|
Для проверки нашего клиентского веб-движка напишем простой скрипт WebRequestCookie.mq5. Его задачей будет запросить одну и ту же веб-страницу дважды: первый раз сервер, скорее всего, предложит установить свои куки, и тогда они будут автоматически подставлены во второй запрос. Во входных параметрах укажем адрес страницы для теста — пусть это будет сайт mql5.com. Также сымитируем заголовки по умолчанию — пусть это будет откорректированная строка "User-Agent".
input string Address = "https://www.mql5.com";
|
В основной функции скрипта опишем объект HTTPRequest и выполним в цикле два запроса GET.
Внимание! Данный тест работает в предположении, что MQL-программы еще не заходили на сайт www.mql5.com и не получали с него куки. После однократного запуска скрипта куки останутся в кэше терминала, и воспроизвести пример станет невозможно: на обеих итерациях цикла мы получим одинаковые записи в журнале.
Не забудьте добавить домен "www.mql5.com" в список разрешенных в настройках терминала.
void OnStart()
|
Первая итерация цикла породит такие записи в журнале (с сокращениями):
>>> Request:
|
Мы получили одну новую куку с именем sid. Чтобы убедиться в её эффективности, можно перейти к просмотру второй части журнала, для второй итерации цикла.
>>> Request:
|
К сожалению, нам здесь не видны полные исходящие заголовки, формируемые внутри WebRequest, но то, что кука отправилась на сервер с помощью заголовка "Cookie:", доказывает тот факт, что сервер в своем втором ответе уже не просит её установить.
В принципе, данная кука просто идентифицирует посетителя (так делает большинство сайтов), но не означает его авторизации. Поэтому вернемся к упражнению по отправке формы в общем виде, подразумевая на перспективу частную задачу ввода логина и пароля.
Напомним, что для отправки формы мы можем воспользоваться методом POST со строковым параметром payload. Принцип подготовки данных по стандарту "x-www-form-urlencoded" заключается в том, что именованные переменные и их значения записываются в одну сплошную строку (в чем-то похожую на куки).
имя⁽№⁾=значение⁽№⁾[&имя⁽ⁱ⁾=значение⁽ⁱ⁾...]ᵒᵖᵗ |
Имя и значение связаны знаком равно '=', а пары состыковываются с помощью символа амперсанда '&'. Значение может отсутствовать. Например,
Name=John&Age=33&Education=&Address= |
Важно отметить, что с технической точки зрения данная строка должна быть перед отправкой преобразована по алгоритму urlencode (именно отсюда и название формата), однако WebRequest сам выполняет это преобразование за нас.
Имена переменных обусловлены веб-формой (содержимым тега form в веб-странице) или логикой веб-приложения — в любом случае, имена и значения должен уметь трактовать веб-сервер. Поэтому для знакомства с технологией нам требуется тестовый сервер с формой.
Тестовая форма имеется по адресу https://httpbin.org/forms/post. Она представляет собой диалог для заказа пиццы.
Тестовая веб-форма
Её внутреннее устройство и поведение описывается следующим HTML-кодом. В нем нас в первую очередь интересуют теги input, которые и задают ожидаемые сервером переменные. Кроме того следует обратить внимание на атрибут action в самом теге form, так как он определяет адрес, на который должен отправляться POST-запрос, и в данном случае это "/post", что вместе с доменом дает строку "httpbin.org/post" — именно её будем использовать в MQL-программе.
<!DOCTYPE html>
|
В скрипте WebRequestForm.mq5 мы подготовили аналогичные входные переменные для указания пользователем перед отправкой на сервер.
input string Address = "https://httpbin.org/post";
|
Уже установленные строки приведены только для тестирования в одно нажатие: вы можете заменить их на собственные, но учтите, что внутри каждой строки следует редактировать только величину справа от '=', а имя слева от '=' нужно сохранить (неизвестные имена сервер проигнорирует).
В функции OnStart опишем HTTP-заголовок "Content-Type:" и подготовим объединенную строку со всеми переменными.
void OnStart()
|
Затем выполним метод POST и выведем ответ сервера в журнал. Вот пример результата.
>>> Request:
|
Тестовый сервер подтверждает получение данных в виде их копии в формате JSON. На практике сервер, конечно, не станет возвращать сами данные, а просто сообщит статус успеха и, возможно, перенаправит на другую веб-страницу, на которую эти данные оказали эффект (например, покажет номер заказа).
С помощью таких POST-запросов, но поменьше, обычно выполняется и авторизация. Правда, большинство веб-сервисов специально усложняет данный процесс в целях безопасности, и предварительно требует рассчитать несколько хэш-сумм от реквизитов пользователя. Публичные, специально разработанные API обычно имеют в документации описания всех необходимых алгоритмов. Но так бывает не всегда. В частности, у нас не получится авторизоваться с помощью WebRequest на mql5.com, потому что сайт не имеет открытого программного интерфейса.
При отправке запросов в веб-сервисы всегда придерживайтесь правила о непревышении частоты запросов: обычно каждый сервис указывает свои ограничения, и их нарушение приведет к последующей блокировке вашей клиентской программы, аккаунта или IP-адреса.