Примеры запросов

> [HTML Version](httpex.html)

В Simple-Scada можно выполнять [HTTP POST/GET/PUT запросы](tmhttp.md) к сторонним серверам для чтения данных с них. 

**Примеры скриптов**

**(./images/tog_code_1.png) 1. GET-запрос к серверу ipstack.com для получения информации о IP-адресе [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget1)**

````
var  
  aHTTP: TM\_HTTP;  
  aQuery: string;  
begin  
  // формируем текст запроса  
  aQuery := 'http://api.ipstack.com/16.112.69.253?access\_key=' +  
    'здесь должен быть ваш API ключ доступа' +  // \!\!\!  
    '\&format=1';  
  
  aHTTP := RequestHTTP;      // создаем экземпляр запроса  
  aHTTP.Get(aQuery);         // выполняем GET запрос  
end.

````
После получения ответа, будет вызван скрипт с типом события "[Выполнен HTTP запрос](postgetcompleted.md)" с параметром Response, который содержит данные ответа.

**(./images/tog_code_1.png) 2. POST-запрос к произвольному сайту [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget2)**

````
var  
  aHTTP: TM\_HTTP;  
  aPOST: TM\_HTTPPost;  
begin  
  aPost := TM\_HTTPPost.Create;        // создаем данные для отправки  
  aPost.Add('username=admin');        // добавляем первый параметр  
  aPost.Add('password=my\_password');  // добавляем второй параметр  
  
  aHTTP := RequestHTTP;               // создаем экземпляр запроса  
  aHTTP.Post('http://mysite.com/index.html', aPost); // вызываем POST  
end.
````

````
**(./images/tog_code_1.png) 3. GET-запрос с сохранением результата в файл [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget3)**

Допустим, требуется запросить с сервера какие-то двоичные данные (например .pdf-файл или документ word/excel), а затем сохранить полученные данные в файл. Тогда сначала нужно отправить запрос на сервер:

````
var  
  aHTTP: TM\_HTTP;  
begin  
  aHTTP := RequestHTTP;  // создаем экземпляр запроса  
  aHTTP.GetStream('https://myserver.org/api/documents/pdf', 1);  // отправляем запрос на сервер, отмечая его тегом 1  
end.

````
Далее, [создадим скрипт](via-script-editor.md) с типом события "[Выполнен HTTP запрос](postgetcompleted.md)" cо следующим кодом:

````
begin  
  if (Response.Tag = 1) then                 // если выполнен запрос отмеченный тегом 1  
    Response.SaveToFile('D:\\Response.pdf');  // то сохраняем его результат в .pdf-файл  
end.
````

````
**(./images/tog_code_1.png) 4. POST-запрос с данными в формате JSON [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget4)**

Допустим, требуется передать серверу POST запрос с JSON-данными и указать заголовок "ContentType" как "application/json". Для этого, при создании POST-данных нужно использовать константу hptJSON и добавить JSON данные в виде строки. Чтобы задать ContentType нужно использовать метод SetHeader. 

````
var  
  aHTTP: TM\_HTTP;  
  aPOST: TM\_HTTPPost;  
begin  
  // создаём POST с JSON-данными  
  aPost := TM\_HTTPPost.Create(hptJSON);  
  aPost.Add('\{"login":"my\_login","password":"my\_pass"\}');  
  
  aHTTP := RequestHTTP;  
  aHTTP.SetHeader('ContentType', 'application/json');  
  aHTTP.Post('https://myserver.ru/auth/', aPost, 1); // отмечаем запрос тегом = 1  
end;
````

````
**(./images/tog_code_1.png) 5. POST-запрос с JSON-данными и авторизацией по токену [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget5)**

Допустим, на сервере нужно авторизоваться по токену. Т.е., сначала нужно выполнить запрос и получить от сервера токен авторизации, а затем использовать этот токен в последующих запросах. Для решения задачи удобнее всего создать в скриптах глобальный модуль и описать в нем нужные процедуры, а затем вызывать их в других скриптах. 

[Создадим новый скрипт](via-script-editor.md) с типом события "[Глобальный модуль](globalmodule.md)" с таким кодом (см. комментарии чтобы понять принцип работы):

````
interface  
  
var  
  AuthToken: string = '';  // в эту глобальную переменную будет получен токен  
  
procedure DoAuthOpen;  
procedure DoMyTestRequest;  
  
implementation  
  
// процедура аутентификации, после её выполнения сервер вернёт  
// токен. Значение токена будет записано в переменную AuthToken  
procedure DoAuthOpen;  
var  
  aHTTP: TM\_HTTP;  
  aPOST: TM\_HTTPPost;  
begin  
  AuthToken := '';  
  aPost := TM\_HTTPPost.Create(hptJSON);  
  aPost.Add('\{"login":"my\_login","password":"my\_password"\}');  
  
  aHTTP := RequestHTTP;  
  aHTTP.SetHeader('ContentType', 'application/json');  
  aHTTP.Post('https://api.myserver.ru/auth/open', aPost, 1); // отмечаем запрос тегом = 1  
end;  
  
// эта процедура отправляет тестовый запрос используя полученный ранее токен авторизации  
procedure DoMyTestRequest;  
var  
  aHTTP: TM\_HTTP;  
  aPOST: TM\_HTTPPost;  
begin  
  // прерываем выполнение, если токен ещё не был получен  
  if AuthToken = '' then   
    Exit;  
  
  // создаём данные для отправки серверу  
  aPost := TM\_HTTPPost.Create(hptJSON);  
  aPost.Add('\{"field1":"value1", "field2":"value2"\}');  
  
  aHTTP := RequestHTTP;  
  aHTTP.SetHeader('ContentType', 'application/json');  
  aHTTP.SetHeader('Authorization', 'Bearer ' + AuthToken); // указываем токен авторизации   
  aHTTP.Post('https://api.myserver.ru/test/', aPost, 2);   // отмечаем запрос тегом = 2  
end;  
  
end.

````
Далее, [создадим новый скрипт](via-script-editor.md) с типом события "[Выполнен HTTP запрос](postgetcompleted.md)" с таким кодом:

````
var  
  aNode: TM\_JSONNode;  
begin  
  case Response.Tag of  
    1: // Tag равен 1, значит выполнена процедура аутентификации  
    if Response.Code = 200 then             // сервер вернул ответ с кодом 200 (OK)  
    begin  
      aNode := Response.NodesStr\['token'\];  // извлекаем поле "token"  
      if Assigned(aNode) then               // если такое поле существует  
        AuthToken := aNode.AsStr;           // то сохраняем значение токена в глоб. переменную AuthToken  
    end else  
      Log\_Add('запрос на аутентификацию вернул ошибку\!');  
  
    2: // Tag равен 2, значит выполнен тестовый запрос  
    if Response.Code = 200 then             // сервер вернул ответ с кодом 200 (OK)  
    begin                                     
      Text1.Text := Response.Text;  // выводим ответ сервера в компонент Text1  
    end else  
      Log\_Add('сервер вернул ошибку в ответ на тестовый запрос\!');  
  end;  
end.

````
Теперь остается вызвать процедуру DoAuthOpen и дождаться получения токена от сервера:

````
begin  
  DoAuthOpen;  
end.

````
Когда сервер вернет ответ, будет запущен скрипт "[Выполнен HTTP запрос](postgetcompleted.md)" и значение токена запишется в глобальную переменную "AuthToken". Затем можно выполнить тестовый запрос с использованием токена:

````
begin  
  DoMyTestRequest;  
end.
````

````
**(./images/tog_code_1.png) 6. GET-запрос с аутентификацией с использованием HMAC [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget6)**

Допустим, есть некий сервис, который после регистрации выдал нам: 

- **публичный ключ**: 9621a10905f1e50777ba4c59d6313d4d940f0ac8845ddd95ed98114515e9b5a2

- **закрытый ключ**: 2f589f22d1495f2b7532e424d4370547d3eb3b09be14e527dede76abbaea7b83

Заголовок аутентификации должен быть передан в следующем формате: 

````
Authentication: "hmac \{public-key\}:\{unixtime\}:\{nonce\}:\{signature\}"

````
, где:

- **public-key** - публичный ключ,

- **unixtime** - текущее время в формате Unix,

- **nonce** - случайное целое число,

- **signature** - закодированная подпись.

Подпись "signature" должна представлять собой строку вида "\{method\}+\{path\}+\{unixtime\}+\{nonce\}" закодированную механизмом HMAC с использованием криптографического алгоритма SHA-256 и закрытого ключа. Т.е., формулу для вычисления подписи можно записать так: 

````
hash\_hmac("sha256", "private key", "\{method\}+\{path\}+\{unixtime\}+\{nonce\}")

````
, где: 

- **method** - метод HTTP (GET или POST),

- **path** - запрашиваемый путь,

- **unixtime** - текущее время в формате Unix,

- **nonce** - случайное целое число.

Все вышеописанное можно легко сформировать в Simple-Scada с помощью встроенных функций и отправить в запросе серверу. Ниже пример скрипта с подробными комментариями:

````
var  
  aHTTP: TM\_HTTP;  
  aTimestamp, aNonce: string;  
  aSignContent, aSignature: string;  
  aRequest, aPublic, aPrivate: string;  
begin  
  // задаём публичный и закрытый ключ  
  aPublic := '9621a10905f1e50777ba4c59d6313d4d940f0ac8845ddd95ed98114515e9b5a2';  
  aPrivate := '2f589f22d1495f2b7532e424d4370547d3eb3b09be14e527dede76abbaea7b83';  
    
  // путь который будем запрашивать с сервера  
  aRequest := '/api/v1/station/';  
  
  // генерируем случайное целое число  
  aNonce := IntToStr(Random(2147483647));  
    
  // берём текущее время в формате UNIX  
  aTimestamp := IntToStr(DateTimeToUnix(Now));  
    
  // формируем строку подписи, которую будем кодировать HMAC  
  aSignContent := 'GET+' + aRequest + '+' + aTimestamp + '+' + aNonce;  
    
  // кодируем строку подписи используя HMAC с алгоритмом SHA-256 и закрытым ключом  
  aSignature := GetHMAC(hmacSHA256, aSignContent, aPrivate);  
  
  // формируем HTTP запрос с соответствующими заголовками  
  aHTTP := RequestHTTP;  
  aHTTP.SetHeader('Accept', 'application/json');  
  aHTTP.SetHeader('Authentication', 'hmac ' + aPublic + ':' + aTimestamp + ':' + aNonce + ':' + aSignature);  
    
  // выполняем запрос методом GET  
  aHTTP.Get('http://myserver.com' + aRequest, 1);  
end.
````

````
**(./images/tog_code_1.png) 7. Сервер вернул ответ в формате JSON. Как его разобрать? [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget7)**

Допустим, сервер в ответ на HTTP запрос вернул в скаду данные в формате JSON, следующего вида:

````
\{    
  "status": "success",  
    
  "data": \[  
    \{"id":967, "external\_id":867459044641280, "name":"object\_1", "description":"object one"\},  
    \{"id":846, "external\_id":867459049502594, "name":"object\_2", "description":"object two"\},  
    \{"id":885, "external\_id":867459044641298, "name":"object\_3", "description":"object three"\},  
    \{"id":172, "external\_id":202350642234324, "name":"object\_4", "description":"object four"\}  
  \],  
  
  "totals": \[\{"total\_count":4\}\]  
\}

````
В ответе видно массив "data", который может содержать произвольное количество данных (в данном примере он содержит 4 объекта). Нам нужно пройти в цикле по всем элементам массива "data" и для каждого элемента вывести поля "id", "name" и "description" на мнемосхему в компонент Text1. Когда в скаду приходит ответ от HTTP-сервера, сервер скады автоматически вызывает скрипты с типом события "Выполнен HTTP запрос" и в параметр Response записываются все данные ответа. Поэтому код для разбора ответа нужно написать в скрипте "[Выполнен HTTP запрос](postgetcompleted.md)":

````
var  
  I: Integer;  
  aResult: string;  
  aData, aNode: TM\_JSONNode;  
begin  
  aResult := '';  
  aData := Response\['data'\]; // извлекаем массив "data" в aData  
  // проходим по всем элементам массива  
  for I := 0 to aData.Count - 1 do  
  begin  
    aNode := aData.Nodes\[I\]; // извлекаем очередной элемент массива в aNode  
      
    // в переменную результата добавляем значения свойств id, name и description  
    aResult := aResult +  
      'id = ' + aNode\['id'\].AsStr + ', ' +  
      'name = ' + aNode\['name'\].AsStr + ', ' +  
      'description = ' + aNode\['description'\].AsStr +   
      #13#10;  // добавляем символ переноса строки  
  end;  
  
  // выводим полученный результат в компонент Text1  
  Text1.Text := aResult;  
end.

````
После выполнения данного кода в компоненте Text1 отобразится следующий текст:

````
id = 967, name = object\_1, description = object one  
id = 846, name = object\_2, description = object two  
id = 885, name = object\_3, description = object three  
id = 172, name = object\_4, description = object four

````


Теперь рассмотрим другой ответ сервера. Пусть сервер вернул следующие данные:

````
\{  
    "dataType": "Current",  
    "dateTime": "2021-05-01T18:00:00.000",  
    "attributes": \[\],  
    "values": \[\{  
        "dataParameter": "Par1",  
        "value": 95,  
        "isBad": false  
    \}, \{  
        "dataParameter": "Par2",  
        "value": 24,  
        "isBad": false  
    \}, \{  
        "dataParameter": "Par3",  
        "value": 5,  
        "isBad": false  
    \}\]  
\}

````
Нам нужно пройти по каждому элементу массива "values" и извлечь из них значение свойства "dataParameter". Пример кода для данной задачи:

````
var  
  I: Integer;  
  aValues, aElem: TM\_JSONNode;  
begin  
​  aValues := Response\['values'\]; // извлекаем массив "values" в aValues  
​  // проходим по всем элементам массива  
​  for I := 0 to aValues.Count - 1 do  
​  begin  
​    aElem := aValues.Nodes\[I\]; // извлекаем очередной элемент из aValues в aElem  
​    // здесь можем что-то сделать с элементом  
​    // например получить значение свойства "dataParameter" так: aElem\['dataParameter'\].AsStr  
​  end;  
end.
````

````
**(./images/tog_code_1.png) 8. POST-запрос с заданной кодировкой [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget8)**

По-умолчанию, при создании POST-запросов данные кодируются в UTF8. При необходимости, можно изменить [кодировку](tm_filecodepage.md), указав ее при создании запроса:

````
var  
  aHTTP: TM\_HTTP;  
  aPOST: TM\_HTTPPost;  
begin  
  // создаем данные для отправки в формате JSON с кодировкой windows-1251  
  aPost := TM\_HTTPPost.Create(hptJSON, fcpCyrillic\_windows1251);  
  aPost.Add('\{"login":"my\_login","password":"my\_pass"\}');  
  
  aHTTP := RequestHTTP;  
  aHTTP.SetHeader('ContentType', 'application/json');  
  aHTTP.Post('https://myserver.ru/auth/', aPost, 1);   
end.
````

````
**(./images/tog_code_1.png) 9. PUT-запрос с данными в формате JSON [(./images/link.png)](https://simple-scada.com/help/script/httpex.html?anchor=postget9)**

Допустим, требуется передать серверу PUT запрос с данными в формате JSON и указать заголовок "ContentType" как "application/json". Пример скприпта:

````
var  
  aHTTP: TM\_HTTP;  
  aJSONData: string;  
begin  
  // формируем данные, которые будем передавать  
  aJSONData := '\{"login":"my\_login","password":"my\_pass"\}';  
  
  // создаем экземпляр запроса  
  aHTTP := RequestHTTP;  
  // устанавливаем ContentType  
  aHTTP.SetHeader('ContentType', 'application/json');  
  // отправлляем PUT запрос c JSON данными в кодировке UTF8  
  aHTTP.Put('https://postman-echo.com/put', aJSONData, fcpUTF8);  
end.
````

````
****