Connexus中的正文(第四部分):添加HTTP请求正文(基础篇)
◍ 给 Connexus 的 HTTP 请求塞进正文
在 MetaTrader 5 里通过 Connexus 发 HTTP 请求,光拼 URL 不够,很多接口要求把参数放在请求正文(body)里而不是查询串。2025 年 6 月 2 日官方示例里,一个 POST 调用携带了 510 字节的正文负载,这说明即便轻量网关也要按接口契约填字段。 MQL5 的 WebRequest 函数默认只发裸请求,要带正文得手动构造字符串并传入对应参数。下面这段示例演示了把 JSON 正文喂给 Connexus 端点的最小写法,复制进 EA 的 OnTick 里改 URL 即可验证。 外汇与贵金属行情经此类网关转发时存在网络延迟与断连风险,回测不会暴露,只能在实盘环境观察丢包概率。
class="type">class="kw">string endpoint="https:class=class="str">"cmt">//connexus.local/api/v1/echo"; class="type">class="kw">string headers="Content-Type: application/json\r\n"; class="type">class="kw">string body="{\"symbol\":\"XAUUSD\",\"tf\":\"M5\"}"; class="type">char post[],resp[]; class="type">class="kw">string res_headers; class="type">int timeout=class="num">2000; StringToCharArray(body,post,class="num">0,WHOLE_ARRAY); class="type">int ret=WebRequest("POST",endpoint,headers,timeout,post,resp,res_headers); Print("Connexus HTTP code: ",ret," resp: ",CharArrayToString(resp));
从信封到信纸:请求正文的位置
前面两篇已经把 WebRequest 的参数和请求头(Header)拆清楚了。可以把 Header 理解成寄信的信封,写明了收件地址和邮戳;而真正承载业务指令的是请求正文(Body),也就是信封里的信纸。 在 MT5 里用 WebRequest 对接第三方 API 时,绝大多数 POST 类接口都要求把参数塞进 Body,而不是拼在 URL 后面。外汇与贵金属自动化调用外部接口属于高风险操作,任何字段错位都可能让订单信号发错端点。 本篇要做的,是在 Connexus 库里给 Body 加一层配置能力:不再只发空壳请求,而是能按接口规范填装 JSON 或表单数据,让客户端与服务器的通信完整闭环。
「HTTP正文:MT5发单真正携带的数据」
HTTP 请求或响应的正文,就是客户端和服务器之间实际来回搬的数据本体。GET 请求通常不带正文,只拉取信息;POST、PUT、PATCH 这类写操作必须把数据塞进正文,服务端才收得到。做外汇/贵金属信号转发或复制系统时,正文就是订单参数流转的载体,缺了它 HTTP 只能干瞪眼做查询。 实战里最常用的是 JSON 正文,轻量且结构清晰,REST API 几乎清一色吃这套。MQL5 里走 WebRequest 发单,正文得先转成字符数组,头部 Content-Type 必须标成 application/json,否则服务端解析会直接哑火。纯文本正文虽简单,但只用 & 拼键值对,大量数据极易乱套,仅适合极轻量场景。 下面这段就是向服务器推一笔 EURUSD 多单的 JSON 正文样例,价格 1.09223、手数 0.01、止盈 1.09233、止损 1.09213,头部声明 application/json。Connexus 类库会同时支持 JSON 与纯文本,由调用方自己选。外汇/贵金属接口联调风险高,参数错一位都可能发错单,建议先开 MT5 策略测试器抓包验证。
{
"type": "BUY",
"symbol": "EURUSD",
"price": class="num">1.09223,
"volume": class="num">0.01,
"tp": class="num">1.09233,
"sl": class="num">1.09213
}
内容类型:application/json
这是一个纯文本示例
内容类型:application/text-plain◍ 用字符数组塞进HTTP请求正文
在 MT5 里发带正文的 HTTP 请求,正文必须塞进 char 类型的 body_send 数组,而不是 string。原因很实在:HTTP 传输本质就是字节流,字符数组能直接承载文本、JSON 甚至二进制文件;字符串只适合纯文本,遇到图像或音频字节就会束手束脚。
性能上,字符数组没有动态字符串的内存重分配开销,大文件或低延迟场景更贴近硬件层。协议兼容性也更好——网络层本来就在搬字节,用 char[] 等于和传输层同构。
把 JSON 字符串转成字符数组用 StringToCharArray(),但注意最后一个元素会是终止符 0,服务器读正文时可能卡壳。用 ArrayRemove() 配 ArraySize() 把这末尾的 0 剥掉即可。下面这段是最小可跑的 POST 骨架,正文留空,你填 JSON 前先验证数组尾部处理:
代码里 body_send 声明为空字符数组,请求头标了 Content-Type: application/json,WebRequest 超时设 5000 毫秒。跑完用 CharArrayToString(body_receive) 把响应打在日志,httpbin 会在 data 和 json 字段回显你发的内容,能确认服务端正确收包。外汇/贵金属相关的 API 调用同样走这套,但行情接口波动大、断连风险高,参数超时别设太短。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TestBody.mq5 | 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="macro">#class="kw">property version "class="num">1.00" class="macro">#include <Connexus/Data/Json.mqh> class="macro">#include <Connexus/URL/URL.mqh> class="macro">#include <Connexus/Header/HttpHeader.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- URL CURL url; url.Parse("https:class=class="str">"cmt">//httpbin.org"); url.Path("post"); class=class="str">"cmt">//--- Data to be sent class="type">class="kw">string method = "POST"; class="type">char body_send[]; 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); }
把下单结构体塞进 POST 请求前的字符处理
EA 初始化时若要把一笔 EURUSD 多单(price 1.09223、volume 0.01、tp 1.09233、sl 1.09213)推给外部接口,先用 CJson 把字段攒成对象,再序列化成字符串。 第一段代码直接调 StringToCharArray(body.Serialize(), body_send, 0, WHOLE_ARRAY, CP_UTF8),把 JSON 字符串按 UTF8 转成 char 数组。ArrayPrint 打出来会发现末尾多一个 '\0'(ASCII 0),这是 MQL5 字符串的天然结尾符。 第二段在转换后补了一行 ArrayRemove(body_send, ArraySize(body_send)-1),把末尾那个 0 字节剃掉。做 HTTP body 发送时,带不带结尾符可能影响服务端解析,建议你在 MT5 里两段都跑一遍,对比 ArrayPrint 输出的最后一位。 第三段展示了 CURL 的用法:url.Parse("https://httpbin.org") 配 url.Path("post") 拼出 POST 端点,后面再挂同样的 CJson body 就能发。外汇与贵金属杠杆高,这类自动推送如果价格字段写错,可能触发非预期订单,实盘前务必在策略测试器验证。
class="type">int OnInit() { class=class="str">"cmt">//--- Data to be sent class="type">class="kw">string method = "POST"; CJson body; body["type"] = "BUY"; body["symbol"] = "EURUSD"; body["price"] = class="num">1.09223; body["volume"] = class="num">0.01; body["tp"] = class="num">1.09233; body["sl"] = class="num">1.09213; class=class="str">"cmt">//--- {"price": class="num">1.09223,"sl": class="num">1.09213,"symbol": "EURUSD","tp": class="num">1.09233,"type": "BUY","volume": class="num">0.01} class=class="str">"cmt">//--- Char that will be sent class="type">char body_send[]; class=class="str">"cmt">//--- Convert class="type">class="kw">string to class="type">char (UTF8) StringToCharArray(body.Serialize(),body_send,class="num">0,WHOLE_ARRAY,CP_UTF8); class=class="str">"cmt">//--- Show class="type">char array ArrayPrint(body_send); class="kw">return(INIT_SUCCEEDED); } class="type">int OnInit() { class=class="str">"cmt">//--- Data to be sent class="type">class="kw">string method = "POST"; CJson body; body["type"] = "BUY"; body["symbol"] = "EURUSD"; body["price"] = class="num">1.09223; body["volume"] = class="num">0.01; body["tp"] = class="num">1.09233; body["sl"] = class="num">1.09213; class=class="str">"cmt">//--- {"price": class="num">1.09223,"sl": class="num">1.09213,"symbol": "EURUSD","tp": class="num">1.09233,"type": "BUY","volume": class="num">0.01} class=class="str">"cmt">//--- Char that will be sent class="type">char body_send[]; class=class="str">"cmt">//--- Convert class="type">class="kw">string to class="type">char (UTF8) 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">//--- Show class="type">char array ArrayPrint(body_send); class="kw">return(INIT_SUCCEEDED); } class="type">int OnInit() { class=class="str">"cmt">//--- URL CURL url; url.Parse("https:class=class="str">"cmt">//httpbin.org"); url.Path("post"); class=class="str">"cmt">//--- Data to be sent class="type">class="kw">string method = "POST"; CJson body; body["type"] = "BUY"; body["symbol"] = "EURUSD"; body["price"] = class="num">1.09223; body["volume"] = class="num">0.01; body["tp"] = class="num">1.09233;