How to send a POST with a JSON in a WebRequest() call using MQL4?
Asked Answered
A

5

13

I would like to send a POST from MQL4-script, using a JSON-format to a Node-server.

I've tried the webRequest() standard function in MQL4, based on the following documentation, but it did NOT success.

From MQL4 Documentation:

Sending simple requests of type "key=value" using the header `Content-Type: application/x-www-form-urlencoded`.    

    int  WebRequest( const string  method,           // HTTP method  
                     const string  url,              // URL 
                     const string  cookie,           // cookie 
                     const string  referer,          // referer 
                     int           timeout,          // timeout 
                     const char   &data[],           // the array of the HTTP message body 
                     int           data_size,        // data[] array size in bytes 
                     char         &result[],         // an array containing server response data 
                      string      &result_headers    // headers of server response
                     );

and

Sending a request of any type specifying the custom set of headers for a more flexible interaction with various Web services.

    int  WebRequest( const string  method,           // HTTP method 
                     const string  url,              // URL 
                     const string  headers,          // headers  
                     int           timeout,          // timeout 
                     const char   &data[],           // the array of the HTTP message body 
                     char         &result[],         // an array containing server response data 
                     string       &result_headers    // headers of server response 
                     );


Parameters

    method          [in]  HTTP method.
    url             [in]  URL.
    headers         [in]  Request headers of type "key: value", separated by a line break "\r\n".
    cookie          [in]  Cookie value.
    referer         [in]  Value of the Referer header of the HTTP request.
    timeout         [in]  Timeout in milliseconds.
    data[]          [in]  Data array of the HTTP message body.
    data_size       [in]  Size of the data[] array.
    result[]       [out]  An array containing server response data.
    result_headers [out]  Server response headers. 

Returned value:
    HTTP server response code or -1 for an error.

Does anyone know how to perfom it?


UPDATE:


Here is the code on the MQL4-script side :

#include <Json\hash.mqh>
#include <Json\KoulJSONMgmt.mqh>
void OnStart()

    {
       string strParam = StringConcatenate("{","\"currency\"",":","\"",Symbol(),"\"",",","\"timeframe\"",":","\"",IntegerToString(Period()),"\"",",","\"ticktime\"",":","\"",TimeToString(TimeLocal(),TIME_DATE|TIME_SECONDS),"\"",",","\"bid\"",":",DoubleToString(MarketInfo(Symbol(),MODE_BID),4),",","\"ask\"",":",DoubleToString(MarketInfo(Symbol(),MODE_ASK),4),",","\"spread\"",":",DoubleToString(MarketInfo(Symbol(),MODE_SPREAD),0),"}");


        JSONParser *parser = new JSONParser();
        JSONValue *jv = parser.parse(strParam);
        string strJson = jv.toString();
        if (jv == NULL) {
            Print("error:"+(string)parser.getErrorCode()+parser.getErrorMessage());
        } else {
            Print("PARSED:"+strJson);
    //Example of json String :
    //EURUSD,M15: PARSED:{"bid" : 1.1152,"ask" : 1.1154,"spread" : 13,"ticktime" : "2016.10.10 16:24:01","currency" : "EURUSD","timeframe" : "15"}
        }

          string cookie=NULL,headers;
          char post[],result[];
          int res;
          string strResult,result_header;

          headers = "application/json";
          prmUrl=StringConcatenate("http://localhost/api"+"/"+"ticks");
          //--- Reset the last error code
          ResetLastError();
          int timeout=1000; //--- Timeout below 1000 (1 sec.) is not enough for slow Internet connection

          int intHostNameLength=StringLen(prmParameter);
          StringToCharArray(prmParameter,post,0,intHostNameLength);

          res=WebRequest("POST",prmUrl,headers,timeout,post,result,result_header);

          //--- Checking errors
          if(res==-1)
            {
             Print("Error in WebRequest. Error code  =",GetLastError());
             //--- Perhaps the URL is not listed, display a message about the necessity to add the address
             Print("Add the address '"+prmUrl+"' in the list of allowed URLs on tab 'Expert Advisors'","Error",MB_ICONINFORMATION);
            }
          else
           {
               for(int i=0;i<ArraySize(result);i++)
               {

                   if( (result[i] == 10) || (result[i] == 13)) {
                      continue;
                   } else {
                      strResult += CharToStr(result[i]);
                   }
               }
               ArrayCopy(strResult,result,0,0,WHOLE_ARRAY);

           }
           Print(strResult);

    }

And the Node side is :

server.js

//Create new Tick
app.post('/api/ticks', function(req, res) {
    console.log('Inserting New Tick');

    var tick = req.body;
    console.log('>'+JSON.stringify(tick,null,4));

    Tick.addTick(tick, function(err, tick){
        if(err) res.json(err);
        res.json(tick);
    });
});

and in model ticks.js

var mongoose = require('mongoose');


// User Schema
var TickSchema = mongoose.Schema({
    currency:{
        type: String
    },
    timeframe: {
        type: String
    },
    ticktime: {
        type: Date
    },
    bid: {
        type: Number
    },
    ask: {
        type: Number
    },
    spread: {
        type: Number
    },
    createddate :{
        type: Date,
        default:  Date.now
    }
}, {collection : 'fxTicks'});

var Tick = module.exports = mongoose.model('Tick', TickSchema);

//Create New Tick
module.exports.addTick = function(tick, callback){
    Tick.create(tick, callback);
};

// Get Ticks
module.exports.getTicks = function(callback, limit){
    Tick.find(callback).limit(limit);
};
Allottee answered 10/10, 2016 at 8:28 Comment(9)
would you mind to post the complete code to reproduce the issue? The posted MQL4 code inherits a JSONParser from some external resource ( LIB, DLL, etc. ), without a properly documented mechanism thereof. Thanks for re-considering to post a full M-inimum C-omplete V-erifiable E-xample of code ( and data, if relevant ) so as to allow to reproduce the objected behaviour in-vitro. Anyway, enjoy the hacking Wild Worlds of MQL4 & happy Trading.Brochure
prmUrl = StringConcatenate"http://localhost/api"+"/"+"ticks"); is missing an opening parenthesis in a call for **StringConcatenate()Brochure
If the code is realistic, it does not headbang in this particular example, but anyone shall beware a hidden trap in StringConcatenate() as MQL4-implementation cannot handle more than 63 items in this call and JSON-decorated structures could be pretty "fat" and "JSON-format-syntax-composition-lengthy"Brochure
Don't worry about it StringConcatenate line , it is NOT my main purpose. I ll correct it .Allottee
Sure, it will not compile :o) + How about the main MCVE-point .. the missing inheritance-trails of the JSONParser() ?Brochure
#include "json.mqh" directive to load external resources from ydrol?Brochure
The point here is not neither JSONParser , I have an example of the output of that external function (see in the code). Again I would to know if someone knows the right webrequest parameters in order to send my Json Object to my node.Allottee
Oh yes, I remember your last years question >>> https://mcmap.net/q/904627/-executing-an-url-from-mql4 where you got the full check-list of steps to follow to isolate the root-cause of the problem. The same here. JSON-string is not the issue, the proper setup for a use of WebRequest() is.Brochure
That was a different topic. With default setup it's working, using a simple http request (using the same WebRequest) with content-Type: application/json it's NOT the case, that's why I need your help.Allottee
B
10

So, back to the square No.1:

In the last-year's post, there was a step by step methodology to proceed with a MCVE-based approach to the problem isolation.

Repeating the same steps here, inside MQL4-code,
adding a python-based mock-up WebSERVER, to diagnose the actual working client/server http-protocol exchange & handshaking, ( not the WebSERVER-side interpretation of the delivered POST-request, which is the same, as if one have launched the URL from a WebBROWSER, for all related details ref: BaseHTTPServer.BaseHTTPRequestHandler )

>>> import BaseHTTPServer
>>> server_class  = BaseHTTPServer.HTTPServer
>>> handler_class = BaseHTTPServer.BaseHTTPRequestHandler
>>> httpd         = server_class( ( '', 8765 ), handler_class )
>>> httpd.handle_request()
127.0.0.1 - - [10/Oct/2016 09:46:45] code 501, message Unsupported method ('GET')
127.0.0.1 - - [10/Oct/2016 09:46:45] "GET /?test=123_from_Chrome HTTP/1.1" 501 -
>>> httpd.handle_request()
127.0.0.1 - - [10/Oct/2016 09:47:23] code 501, message Unsupported method ('GET')
127.0.0.1 - - [10/Oct/2016 09:47:23] "GET /favicon.ico HTTP/1.1" 501 -
>>>
>>>
>>>
>>> httpd = server_class( ( '', 80 ), handler_class )
>>> httpd.handle_request()
127.0.0.1 - - [10/Oct/2016 10:22:05] code 501, message Unsupported method ('GET')
127.0.0.1 - - [10/Oct/2016 10:22:05] "GET /?test=123_from_Chrome_on_port_80 HTTP/1.1" 501 -
>>> httpd.handle_request()
127.0.0.1 - - [10/Oct/2016 10:22:31] code 501, message Unsupported method ('GET')
127.0.0.1 - - [10/Oct/2016 10:22:31] "GET /?test=123_from_Chrome_on_port_80_again HTTP/1.1" 501 -
>>> httpd.handle_request()
127.0.0.1 - - [10/Oct/2016 10:22:34] code 501, message Unsupported method ('GET')
127.0.0.1 - - [10/Oct/2016 10:22:34] "GET /favicon.ico HTTP/1.1" 501 -
>>> httpd.handle_request()
127.0.0.1 - - [10/Oct/2016 11:25:56] code 501, message Unsupported method ('GET')
127.0.0.1 - - [10/Oct/2016 11:26:12] "GET /?test=123_from_Chrome_on_port_80_another_call HTTP/1.1" 501 -
>>>
>>>
127.0.0.1 - - [10/Oct/2016 12:03:03] code 501, message Unsupported method ('POST')
127.0.0.1 - - [10/Oct/2016 12:03:03] "POST / HTTP/1.1" 501 -
>>>

the output is
providing an evidence that the last pair of rows were produced by an MQL4-side WebRequest() that was setup correctly and works fine there and back
[ MetaTrader Terminal 4 ]-Log reads:

2016.10.10 12:03:03.921 ___StackOverflow_WebRequest_DEMO XAUUSD,H1:

           DATA:: <head><title>Error response</title></head>
                  <body>
                  <h1>Error response</h1><p>Error code 501.<p>
                  Message: Unsupported method ('POST').<p>
                  Error code explanation: 501 = Server does not support this operation.
                  </body>

2016.10.10 12:03:03.921 ___StackOverflow_WebRequest_DEMO XAUUSD,H1:

           HDRs:: HTTP/1.0 501 Unsupported method ('POST')
                  Server: BaseHTTP/0.3 Python/2.7.6
                  Date: Mon, 10 Oct 2016 20:03:03 GMT
                  Content-Type: text/html
                  Connection: close

A raw MQL4-snippet BUT use at one's own risk!

( strongly encourage NOT to use any BLOCKING WebRequest() calls
in any Production-grade code...for NON-BLOCKING tools see my other posts and how to or read into internal details on high-performance, low-latency, non-blocking integration tools for distributed heterogeneous systems processing alike ZeroMQ or nanomsg )

All have been warned, so:

Last years setup picture is still valid: enter image description here

The mock-up WebSERVER had inside the dotted form-field input of:

http://localhost/

One shall also bear in mind, that trying to set a specific port designation in the URL will violate the MetaQuotes Inc. design rule, that a port is being derived from the protocol pragma at the beginning of the URL declaration, so:

http://localhost:8765/

will not work, as MQL4 WebRequest() CANNOT use other port but either of { 80 | 443 }, given by protocol pragma stated in URL: { http: | https: }

Thus for any port-numbering gymnastics, one has to setup and tune a proper set of port-forwarding services, that would leave MetaTrader Terminal 4 live inside this design-cage, using just either of { 80 | 443 }.

The simplest MQL4-script OnStart() demonstrator looks this way:

//+------------------------------------------------------------------+
//|                             ___StackOverflow_WebRequest_DEMO.mq4 |
//|                                       Copyright © 1987-2016 [MS] |
//|                                                       nowhere.no |
//+------------------------------------------------------------------+ >>> https://mcmap.net/q/856831/-how-to-send-a-post-with-a-json-in-a-webrequest-call-using-mql4
#property copyright "Copyright © 1987-2016 [MS]"
#property link      "nowhere.no"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void  OnStart(){

      /* A BRIGHTER WAY:

         string JSON_string = StringFormat( "{\"currency\": \"%s\", \"timeframe\": \"%d\", \"ticktime\": \"%s\", \"bid\": %f, \"ask\": %f, \"spread\": %s }",  _Symbol,
                                                                                                                                                               Period(),
                                                                                                                                                               TimeToString( TimeLocal(), TIME_DATE | TIME_SECONDS ),
                                                                                                                                                               MarketInfo( _Symbol, MODE_BID ),
                                                                                                                                                               MarketInfo( _Symbol, MODE_ASK ),
                                                                                                                                                               MarketInfo( _Symbol, MODE_SPREAD )
                                                                                                                                                               );
      // A SMARTER WAY & THE FASTEST PROCESSING TIMES:
      // --------------

#define MQL4_COMPILE_TIME_JSON_TEMPLATE     "{\"currency\": \"%s\", \"timeframe\": \"%d\", \"ticktime\": \"%s\", \"bid\": %f, \"ask\": %f, \"spread\": %s }"   // CONSTANT TEMPLATE TO FILL-IN AD-HOC VALUES:
      // +
         string JSON_string = StringFormat( MQL4_COMPILE_TIME_JSON_TEMPLATE",                                                                                  _Symbol,
                                                                                                                                                               Period(),
                                                                                                                                                               TimeToString( TimeLocal(), TIME_DATE | TIME_SECONDS ),
                                                                                                                                                               MarketInfo( _Symbol, MODE_BID ),
                                                                                                                                                               MarketInfo( _Symbol, MODE_ASK ),
                                                                                                                                                               MarketInfo( _Symbol, MODE_SPREAD )
                                                                                                                                                               );
         */
      string JSON_string = StringConcatenate( "{",                                                    // **** MQL4 can concat max 63 items
                                              "\"currency\"",
                                              ":",
                                              "\"",
                                              Symbol(),
                                              "\"",
                                              ",",
                                              "\"timeframe\"",
                                              ":",
                                              "\"",
                                              IntegerToString( Period() ),
                                              "\"",
                                              ",",
                                              "\"ticktime\"",
                                              ":",
                                              "\"",
                                              TimeToString( TimeLocal(), TIME_DATE | TIME_SECONDS ),
                                              "\"",
                                              ",",
                                              "\"bid\"",
                                              ":",
                                              DoubleToString( MarketInfo( Symbol(), MODE_BID ), 4 ),
                                              ",",
                                              "\"ask\"",
                                              ":",
                                              DoubleToString( MarketInfo( Symbol(), MODE_ASK ), 4 ),
                                              ",",
                                              "\"spread\"",
                                              ":",
                                              DoubleToString( MarketInfo( Symbol(), MODE_SPREAD ), 0 ),
                                              "}"
                                              );
   // */
   /* off-topic: a JSON-string VALIDATOR -----------------------------------------------------------------------------------------------------------------------------------

#include <Json\hash.mqh>
#include <Json\KoulJSONMgmt.mqh>

        JSONParser *parser = new JSONParser();
        JSONValue  *jv     =         parser.parse(strParam);

        string strJson = jv.toString();

        if ( jv == NULL ) Print( "ERROR:"  + (string) parser.getErrorCode()
                                                    + parser.getErrorMessage()
                                 );
        else              Print( "PARSED:" + strJson );

                          // Example of a journalled Print() for an above setup JSON String :
                          // EURUSD,M15: PARSED:{"bid" : 1.1152,"ask" : 1.1154,"spread" : 13,"ticktime" : "2016.10.10 16:24:01","currency" : "EURUSD","timeframe" : "15"}
*/ // off-topic: a JSON-string VALIDATOR -----------------------------------------------------------------------------------------------------------------------------------

       // string  ReqSERVER_URL = "http://localhost:8765/",         // **** MQL4 WebRequest CANNOT use other port but either of { 80 | 443 } given by protocol pragma stated in URL: { http: | https: }
          string  ReqSERVER_URL = "http://localhost/",              // ---- MQL4 WebRequst
                  ReqCOOKIE     =  NULL,
               // ReqHEADERs    =               "application/json"; // **** MQL4 WebRequest MUST   use [in]  Request headers of type "key: value", separated by a line break "\r\n".
                  ReqHEADERs    = "Content-Type: application/json\r\n";
          int     ReqTIMEOUT    =  5000;                            // ---- MQL4 WebRequest SHALL  use [in]  Timeouts below 1000 (1 sec.) are not enough for slow Internet connection;
               // ================================================= // ~~~~ MQL4 WebRequest SHALL be AVOIDED as an un-control-able BLOCKING-SHOW-STOPPER, any professional need shall use NON-BLOCKING tools
          char    POSTed_DATA[],
                  result_RECVed_DATA_FromSERVER[];
          int     result_RetCODE;
          string  result_DecodedFromSERVER,
                  result_RECVed_HDRs_FromSERVER;

       // int     intHostNameLength                       = StringLen(  ReqSERVER_URL );
       // StringToCharArray( ReqSERVER_URL, POSTed_DATA, 0, StringLen(  ReqSERVER_URL ) );
       // StringToCharArray( prmParameter,  post,        0, intHostNameLength );
          StringToCharArray( JSON_string,   POSTed_DATA, 0, StringLen(  JSON_string   ) );

          ResetLastError();

          result_RetCODE = WebRequest( "POST",
                                       ReqSERVER_URL,
                                       ReqHEADERs,
                                       ReqTIMEOUT,
                                       POSTed_DATA,
                                       result_RECVed_DATA_FromSERVER,
                                       result_RECVed_HDRs_FromSERVER
                                       );
          if (  result_RetCODE == -1 ) Print( "Error in WebRequest. Error code  =", GetLastError() ); // returns error 4060 – "Function is not allowed for call" unless permitted -- ref. Picture in >>> https://mcmap.net/q/856831/-how-to-send-a-post-with-a-json-in-a-webrequest-call-using-mql4
          else {
                for (  int i = 0; i < ArraySize( result_RECVed_DATA_FromSERVER ); i++ ) {
                       if (  ( result_RECVed_DATA_FromSERVER[i] == 10 ) // == '\n'  // <LF>
                          || ( result_RECVed_DATA_FromSERVER[i] == 13 ) // == '\r'  // <CR>
                          ) 
                          continue;
                       else     result_DecodedFromSERVER += CharToStr( result_RECVed_DATA_FromSERVER[i] );
                }
                Print( "DATA:: ", result_DecodedFromSERVER );
                Print( "HDRs:: ", result_RECVed_HDRs_FromSERVER );
          }
      }
//+------------------------------------------------------------------+

Deviations from documented steps are easily visible in the source and were left for clarity.


Epilogue:

If documentation says something, it is worth keeping that advice ( with some tests, sure ).

If a sponsored Community advice says something, it is worth giving it at least a try, before asking for more.

Brochure answered 10/10, 2016 at 20:40 Comment(3)
Thank you for your explanation about blocking and non-blocking calls. For now I m okay with WebRequest because myWebServer reside on the same box, however I was and still interesting on jumping on nanomsg way. May be the next question will be about it. Thank you again the code is working I can receive everything on my Node side.Allottee
@Allottee Can you share you EA with me?Genarogendarme
the sample code works welll.......Mozza
R
10

It works well for me, I have someone like this :

string response = SendResquest("POST", "GetPrediction", "[4, 7]", "Content-Type: application/json", 5000);

string SendResquest(string httpType, string methodName, string bodyData = "", string headers = "", int timeout)
{
    uchar bodyDataCharArray[];
    ArrayResize(bodyDataCharArray, StringToCharArray(bodyData, bodyDataCharArray)-1);

    int response = WebRequest(httpType, this.address+methodName, headers, timeout, bodyDataCharArray, this.resultDataCharArray, this.resultHeader);

    string result = CharArrayToString(this.resultDataCharArray); 

    if(response == 200)
        return result;
    Print("Error when trying to call API : ", response);
    return "";
}
Rebuff answered 4/12, 2018 at 10:37 Comment(1)
default argument is needed: int timeout = 5000Polyphone
M
3

Have you tried setting the headers like this:

headers = "Content-Type: application/json\r\n";

?

Misdemeanor answered 25/3, 2018 at 13:48 Comment(1)
Perfect It worked for meFarmergeneral
K
2

Allow your [ MetaTrader Terminal 4 ] to communicate with URL via menu:

Tools -> Options -> Expert Advisors ->

1. mark a check-box [ X ] in front of 'Allow WebReq....'
&
2. type the URL name below the check-box, using the green (+) icon, inside the form.

If this doesn't help - try to add Print() statements to see the possible errors ( incorrect MQL4 code or incorrect JSON-format file ).

Kheda answered 10/10, 2016 at 9:43 Comment(2)
I do receive a request on the Node side and it executes the function but the json object is Empty. the function does only an insert into a db, and into my db I can see only the id and created date of it but NOT the rest of my json object.Allottee
Then show your code, without it no idea. If you send char[] data correctly -it should workKheda
F
0

A few Things to keep in mind :

  1. If you are sending post request make sure your data parameter is Converted to string from json. You can use Online Json to STring Converter.
  2. Also make sure your header is headers = "Content-Type: application/json\r\n";

Here is a function that might help:

void sendTelegramMsg(){

 string BotToken = "YOUR TOKEN";
 string ChatID = "-YOUR CHAT ID";

    string url = "https://api.telegram.org/bot" + BotToken + "/sendMessage";
string  message = "from mql5";

    string json_data ="{\"chat_id\":-4289184834,\"text\":\"Hello from mql5\"}";

    char post[];
    StringToCharArray(json_data, post);
    Print(CharArrayToString(post));

    char result[];
    string headers = "Content-Type: application/json\r\n";
    string result_headers;
    int timeout = 5000;

    int res = WebRequest("POST", url, headers, timeout, post, result, result_headers);

    if (res == 200)
    {
        Print("Message sent successfully!");
    }
    else
    {
        Print("Error in sending message. Error code: ", GetLastError()," ; res: ",res," ; Result: ",CharArrayToString(result));
    }
}
Farmergeneral answered 16/8 at 9:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.