Connexus中的正文(第四部分):添加HTTP请求正文·进阶篇
📨

Connexus中的正文(第四部分):添加HTTP请求正文·进阶篇

(2/3)· 头部只是信封,正文才是信件——本篇带你把POST/PUT的真实数据塞进Connexus

新手友好 第 2/3 篇

不少人在MQL5里调WebRequest时,只配了URL和头部就等服务器回数据,POST出去却收到400。问题往往出在正文:要么没传,要么格式不对,服务器直接拒收。把正文当可选附件,是这套通信里最隐蔽的坑。

◍ 用 WebRequest 把订单结构推到外部接口

在 MT5 里想让 EA 把下单意图发给外部系统,核心就是先把结构体序列化成 char 数组再走 WebRequest。下面这段实测向 httpbin.org/post 发了一个 EURUSD 的 BUY 请求,返回里能看到服务端正确解析出了 price=1.09223、sl=1.09213、tp=1.09233、volume=0.01。 注意序列化后尾部多一个字节,代码里用 ArrayRemove 把最后一位干掉,否则 Content-Length 会变成 103 且服务端可能收脏数据。Header 里 User-Agent 和 Content-Type 必须显式 Add,不然默认头可能让外部接口拒收。 WebRequest 的超时参数给的是 5000 毫秒,回测环境不支持网络调用,只能在实盘或模拟盘终端里验证。外汇和贵金属品种波动剧烈,这类外发动作仅作信号中转,不预示任何方向,实盘推送失败可能造成漏单,属高风险操作。

MQL5 / C++
  body["sl"] = class="num">1.09213;
  class="type">char body_send[];
  StringToCharArray(body.Serialize(),body_send,class="num">0,WHOLE_ARRAY,CP_UTF8);
  ArrayRemove(body_send,ArraySize(body_send)-class="num">1);
  
  class=class="str">"cmt">//--- Headers that will be sent separated by "\n"
  CHttpHeader headers_send;
  headers_send.Add("User-Agent","Connexus/class="num">1.0 (MetaTrader class="num">5 Terminal)");
  headers_send.Add("Content-Type","application/json");
  
  class=class="str">"cmt">//--- Data that will be received
  class="type">char body_receive[];
  class="type">class="kw">string headers_receive;
  
  class=class="str">"cmt">//--- Send request
  class="type">int status_code = WebRequest(method,url.FullUrl(),headers_send.Serialize(),class="num">5000,body_send,body_receive,headers_receive);
  
  class=class="str">"cmt">//--- Show response
  Print("Respose: ",CharArrayToString(body_receive));
  class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
Respose: {
  "args": {},
  "data": "{\"type\":\"BUY\",\"symbol\":\"EURUSD\",\"price\":class="num">1.09223000,\"volume\":class="num">0.01000000,\"tp\":class="num">1.09233000,\"sl\":class="num">1.09213000}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "pt,en;q=class="num">0.5",
    "Content-Length": "class="num">103",
    "Content-Type": "text/plain",
    "Host": "httpbin.org",
    "User-Agent": "Connexus/class="num">1.0 (MetaTrader class="num">5 Terminal)",
    "X-Amzn-Trace-Id": "Root=class="num">1-67081b9c-3def4b1527d04edc1511cc6b"
  },
  "json": {
    "price": class="num">1.09223,
    "sl": class="num">1.09213,
    "symbol": "EURUSD",
    "tp": class="num">1.09233,
    "type": "BUY",
    "volume": class="num">0.01
  },
  "origin": "class="num">189.74.class="num">63.39",
  "url": "https:class=class="str">"cmt">//httpbin.org/post"
}

「给请求正文写个收纳类」

在 Connexus 库里处理 HTTP 请求正文,最省事的做法是单独封一个 CHttpBody 类,文件落在 Include/Connexus/Header/HttpBody.mqh。它内部只靠两个私有成员撑起来:m_body 用字符数组存正文本体,m_codepage 记编码,默认就是 UTF-8。 对外暴露的方法覆盖增删查全链路。写数据有 AddString 塞文本、AddJson 接 CJson 对象(内部转格式化串)、AddBinary 直接灌字节数组;Clear 一键清空重来;读的时候能 GetAsString、GetAsJson 或 GetAsChar 按场景拿。大小与编码用 GetSize、GetCodePage、SetCodePage 控。 这些方法最长的也没超过三行,但别小看这点代码量。实际接进库里之后,调用层能砍掉一大截重复拼装逻辑,后期维护也只盯这一处。 下面这段是类骨架刚建好时的样子,只挂了构造和析构,具体方法体后续往里填。注意版权头里的链接只是原文件自带标记,开 MT5 自己写的时候可删。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 HttpBody.mqh |
class=class="str">"cmt">//|                                                     Copyright class="num">2024, MetaQuotes Ltd. |
class=class="str">"cmt">//|                                                       [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| class : CHttpBody                                                          |
class=class="str">"cmt">//|                                                                            |
class=class="str">"cmt">//| [PROPERTY]                                                                 |
class=class="str">"cmt">//| Name       : CHttpBody                                                     |
class=class="str">"cmt">//| Heritage   : No heritage                                                   |
class=class="str">"cmt">//| Description : Responsible for organizing and storing the body of           |
class=class="str">"cmt">//|               a request.                                                   |
class=class="str">"cmt">//|                                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CHttpBody
  {
class="kw">public:
                  CHttpBody(class="type">void);
                 ~CHttpBody(class="type">void);
  };
class=class="str">"cmt">//+------------------------------------------------------------------+

请求体容器的空壳与字节数组

在 MT5 的 EA 或脚本里做 HTTP 请求时,先把请求体当成一个纯粹的字节容器来管理会更干净。下面这段类定义只搭了骨架:构造和析构函数都是空的,说明对象生成和销毁阶段不分配也不释放任何资源,真正的负载留给后续方法往 m_body 里塞数据。 类内部有两个私有成员:m_body 是 char 类型动态数组,负责以字节形式存请求体原文;m_codepage 是 uint,记着编码页(比如 CP_UTF8=65001),发请求前不显式设编码,服务端解析就可能乱码。 它用 #include "../Data/Json.mqh" 把 JSON 辅助类拉进来,倾向说明这个 body 主要装序列化后的 JSON 字符串。外汇和贵金属接口调用大多要求严格 UTF-8,实操前务必在 MT5 里确认 codepage 默认值,否则报文的 body 可能直接被网关拒掉。

MQL5 / C++
class=class="str">"cmt">//| Constructor                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpBody::CHttpBody(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpBody::~CHttpBody(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include the file CJson class                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "../Data/Json.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| class : CHttpBody                                                                 |
class=class="str">"cmt">//|                                                                 |
class=class="str">"cmt">//| [PROPERTY]                                                                 |
class=class="str">"cmt">//| Name      : CHttpBody                                                                 |
class=class="str">"cmt">//| Heritage  : No heritage                                                                 |
class=class="str">"cmt">//| Description : Responsible for organizing and storing the body of |
class=class="str">"cmt">//|       a request.                                                                 |
class=class="str">"cmt">//|                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CHttpBody
  {
class="kw">private:

   class="type">char            m_body[];          class=class="str">"cmt">// Will store the request body as a class="type">char array
   class="type">uint            m_codepage;        class=class="str">"cmt">// Used to store the defined encoding
   };;
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ HTTP 请求体的本地封装思路

在 MT5 里拼外接 API 请求时,请求体(body)不能随手用 string 硬塞,尤其涉及中文注释或 JSON 载荷,编码错乱会直接让服务端拒收。下面这个类把 body 统一收成 char 数组,并在构造时把码页锁死为 UTF-8(宏 CP_UTF8),从根上避开多字节乱码。 私有成员只有两个:m_body[] 负责存原始字节流,m_codepage 记当前编码。公开接口给了三种灌数据的方式——AddString 吃文本、AddJson 吃 CJson 引用、AddBinary 吃 char 数组引用,覆盖表单、JSON-RPC、以及图片/压缩包这类二进制场景。 实际写 EA 调用时,若你往 body 里丢含中文的订单备注,没显式走 UTF-8 通道,MT5 终端默认 ANSI 可能把字截成问号;用这套封装后,服务端收到字节流解码失败的概率会明显下降。外汇与贵金属接口对接属高风险操作,参数错一处就可能发错单,务必先在策略测试器里用模拟域名跑通。

MQL5 / C++
class=class="str">"cmt">//| Constructor                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpBody::CHttpBody(class="type">void)
  {
   m_codepage = CP_UTF8;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| class : CHttpBody                                                                |
class=class="str">"cmt">//|                                                                                  |
class=class="str">"cmt">//| [PROPERTY]                                                                       |
class=class="str">"cmt">//| Name      : CHttpBody                                                            |
class=class="str">"cmt">//| Heritage  : No heritage                                                          |
class=class="str">"cmt">//| Description : Responsible for organizing and storing the body of                |
class=class="str">"cmt">//|               a request.                                                         |
class=class="str">"cmt">//|                                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CHttpBody
  {
class="kw">private:

   class="type">char            m_body[];          class=class="str">"cmt">// Will store the request body as a class="type">char array
   class="type">uint            m_codepage;        class=class="str">"cmt">// Used to store the defined encoding

class="kw">public:
                     CHttpBody(class="type">void);
                    ~CHttpBody(class="type">void);
   class=class="str">"cmt">//--- Add data to the body
   class="type">void            AddString(class="type">class="kw">string data);        class=class="str">"cmt">// Adds a text class="type">class="kw">string to the request body
   class="type">void            AddJson(CJson &data);          class=class="str">"cmt">// Adds data in JSON format to the body
   class="type">void            AddBinary(class="type">char &data[]);       class=class="str">"cmt">// Allows you to add binary data

   class=class="str">"cmt">//--- Clear the body

「HTTP 请求体的底层封装与编码细节」

在 MT5 里自己封装 HTTP 请求体时,最容易被忽略的是字节数组的结尾处理。下面这段 CHttpBody 实现把字符串、JSON、二进制三种载荷统一收进 char 数组,默认编码直接锁死 UTF-8(m_codepage = CP_UTF8),对外汇 EA 调 REST 接口能少踩一半乱码坑。 AddString 里先用 StringToCharArray 把文本按当前 codepage 写进 m_body,偏移量取 GetSize()-1;随后立刻 ArrayRemove 掉末尾那个位置。实测这一行决定你 POST 出去的 body 不会多带一个 0 终止符,Broker 侧解析失败率会明显下降。 GetAsBinary 返回 char[] 引用,适合直接塞入 MT5 的 WebRequest 二进制参数;GetAsJson 则把 body 反序列成 CJson 对象,做 AIGC 信号回传时比手搓字符串解析稳。 真要验证,开 MT5 新建一个 C++ 风格类文件粘进去,用 AddString("XAUUSD=2050.30") 然后 GetSize() 打印,长度应正好等于字符数而非字符数+1。贵金属与外汇接口调用本身属高风险操作,返回结构异常可能直接让持仓逻辑失效。

MQL5 / C++
class="type">void Clear(class="type">void);                      class=class="str">"cmt">// Remove all body content

class=class="str">"cmt">//--- Gets the body content
class="type">class="kw">string GetAsString(class="type">void);              class=class="str">"cmt">// Returns the request body as a class="type">class="kw">string
CJson GetAsJson(class="type">void);                 class=class="str">"cmt">// Converts the request body into a JSON object, useful when the body contains structured data
class="type">void GetAsBinary(class="type">char &body[]);        class=class="str">"cmt">// Returns the body as an array of bytes, useful for working with binary data

class=class="str">"cmt">//--- Size in bytes
class="type">int GetSize(class="type">void);                    class=class="str">"cmt">// Returns the size of the request body, usually in bytes

class=class="str">"cmt">//--- Codepage
class="type">uint GetCodePage(class="type">void);               class=class="str">"cmt">// Returns the defined codepage
class="type">void SetCodePage(class="type">uint codepage);      class=class="str">"cmt">// Defines the codepage to be used
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpBody::CHttpBody(class="type">void)
  {
   m_codepage = CP_UTF8;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpBody::~CHttpBody(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Adds a text class="type">class="kw">string to the request body                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CHttpBody::AddString(class="type">class="kw">string data)
  {
   StringToCharArray(data,m_body,this.GetSize()-class="num">1,WHOLE_ARRAY,m_codepage);
   ArrayRemove(m_body,this.GetSize()-class="num">1);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Adds data in JSON format to the body                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CHttpBody::AddJson(CJson &data)
  {
   this.AddString(data.Serialize());
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Allows you to add binary data                                    |
让小布替你校验请求体
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到请求正文的格式预警,把重复劳动交给小布,你专注决策。

常见问题

按HTTP规范,GET用于查询,通常不带正文;MetaTrader的WebRequest发GET时填了正文多数服务器会忽略或报错,建议把参数放URL查询串。
它把JSON、XML或二进制数据封装成字符数组,统一交给WebRequest,避免每处调用都手写转换和长度计算。
可以,小布在品种页内置了请求体格式诊断,能提示JSON结构残缺或编码异常,减少你手动抓包的次数。
可能服务端处理完不回数据,也可能你发的正文解析失败被静默丢弃;先用Connexus打日志确认发出字节数再排查。
二进制要走multipart或裸流,Content-Type和边界符与application/json完全不同,Connexus需切换body构造分支。