Connexus的头(第三部分):掌握HTTP请求头的使用方法·进阶篇
(2/3)· 不少EA卡在WebRequest返回401或乱码,根子常在请求头没拼对,而非URL写错
「请求头到底怎么摆」
HTTP 请求头本质是键值对,写进请求里告诉服务端「你是谁、带什么、发什么格式」。上面那段示例里塞了 3 个头:Authorization 带访问令牌 token123,Content-Type 标 JSON,User-Agent 自报是 Connexus/1.0 跑在 MT5 终端。 多数头是可选,只有少数接口强制要某几个。下面这段是最小可发结构,Host 加三个头就能让服务端认人、认格式、认来源。 在 MT5 里用 WebRequest 发请求时,把头按行拼进 string 数组即可;先拿这段原样测通,再按需加减字段,避免一上来堆满冗余头把鉴权搞错。外汇与贵金属接口调用涉及账户与密钥,属高风险操作,令牌泄露可能直接导致资金端暴露。
GET /api/resource HTTP/class="num">1.1 Host: example.com Authorization: Bearer token123 Content-Type: application/json User-Agent: Connexus/class="num">1.0 (MetaTrader class="num">5 Terminal)
◍ 用 httpbin 看清 MT5 自动塞了哪些头
想确认 MetaTrader 5 终端在发 HTTP 请求时到底偷偷带了什么头,最省事的办法是拿 httpbin.org 当镜子——你发过去什么,它原样回什么。在 Experts/Connexus/Test/ 下建一个 TestHeader.mq5,先发一个不带任何自定义头的 POST,看回显里的 headers 字段就够了。 实测回显里终端自动补了这几项:Accept 为 */*,Accept-Encoding 为 gzip, deflate,Accept-Language 为 pt,en;q=0.5,Content-Length 为 0,Content-Type 为 application/x-www-form-urlencoded,Host 为 httpbin.org,User-Agent 含 MetaTrader 5 Terminal/5.4518 及 Windows NT 11.0.22631 等系统信息,另外还有 AWS 的 X-Amzn-Trace-Id(例如 Root=1-66feb3d9-50de44d019af8b0c1058436b)。其中 Content-Length=0 印证了咱们没发 body;那个 Trace-Id 大概率是平台用来做请求诊断的。 头里不能有两个同名键,所以自定义时会直接覆盖默认值,而不是追加。比如把 Content-Type 改成 application/json、User-Agent 改成 Connexus/1.0 (MetaTrader 5 Terminal),拼进 headers_send 字符串并用 \n 分隔,再跑一次就能在回显里看到新值顶掉了旧的。 下面这段是最小可跑的裸请求示例,注意 WebRequest 的超时设的 5000 毫秒,返回体直接用 CharArrayToString 打印:
class="macro">#include <Connexus2/Data/Json.mqh> class="macro">#include <Connexus2/URL/URL.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="type">class="kw">string headers_send; 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,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">//+------------------------------------------------------------------+
正文
| <span class="comment">// | Expert initialization function | </span> |
|---|
<span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">int</span> <span class="functions">OnInit</span>() { <span class="comment">//--- URL</span> CURL url; url.Parse(<span class="string">"https://httpbin.org"</span>); url.Path(<span class="string">"post"</span>); <span class="comment">//--- Data to be sent</span> <span class="keyword">string</span> method = <span class="string">"POST"</span>; <span class="keyword">char</span> body_send[]; <span class="comment">//--- Headers that will be sent separated by "\n"</span> <span class="keyword">string</span> headers_send = <span class="string">"User-Agent: Connexus/1.0 (MetaTrader 5 Terminal)\nContent-Type: application/json"</span>; <span class="comment">//--- Data that will be received</span> <span class="keyword">char</span> body_receive[]; <span class="keyword">string</span> headers_receive; <span class="comment">//--- Send request</span> <span class="keywo
「搭一个存 HTTP 头部的类骨架」
在 MT5 的 Includes/Connexus 目录下新建 Headers 文件夹,里面放 HttpHeaders.mqh,这套目录结构能让后续多个 EA 共用同一套请求头逻辑,不用每次重写。 头部数据用 JSON 对象托管最省事:键是头部名、值是头部内容。类里声明一个 m_headers 实例,初始化即空 JSON,后面所有增删查都围绕它转。 先列清楚这个类要暴露的方法:Add(key,value) 新增或覆盖、Get(key) 取值、Remove(key) 删除、Has(key) 判断存在、Clear() 清空、Count() 计数,再加两个核心的 Serialize() 和 Parse(string)。 下面这段是类的空壳,只挂了构造和析构,注释里标了它专门管请求头的组织与存储。开 MT5 照这个建文件,编译能过就说明目录和类声明没写错。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Header.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">2023, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class : CHttpHeader | class=class="str">"cmt">//| | class=class="str">"cmt">//| [PROPERTY] | class=class="str">"cmt">//| Name : CHttpHeader | class=class="str">"cmt">//| Heritage : No heritage | class=class="str">"cmt">//| Description : Responsible for organizing and storing the headers | class=class="str">"cmt">//| of a request. | class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class CHttpHeader { class="kw">public: CHttpHeader(class="type">void); ~CHttpHeader(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 请求头容器的空壳与 JSON 依赖
在 MT5 里做 REST 请求封装时,CHttpHeader 这个类当前只是个空架子:构造和析构函数体均为空,没有实际的头部写入或解析逻辑。
它内部只持有一个私有成员 CJson m_headers,说明设计意图是把请求头以键值对形式塞进 JSON 对象来管理,而非用裸字符串数组。
代码里通过 #include "../Data/Json.mqh" 引入了 CJson 类,这意味着你编译前必须保证相对路径下有这个文件,否则会直接报找不到头文件。
开 MT5 验证时,可先新建一个 EA,把这段类声明贴进去并补上 Json.mqh 的本地路径,看编译器是否因缺失依赖而报错——这是排查请求模块能否跑起来的第一关。
CHttpHeader::CHttpHeader(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CHttpHeader::~CHttpHeader(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 : CHttpHeader | class=class="str">"cmt">//| | class=class="str">"cmt">//| [PROPERTY] | class=class="str">"cmt">//| Name : CHttpHeader | class=class="str">"cmt">//| Heritage : No heritage | class=class="str">"cmt">//| Description : Responsible for organizing and storing the headers | class=class="str">"cmt">//| of a request. | class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class CHttpHeader { class="kw">private: CJson m_headers; class="kw">public: CHttpHeader(class="type">void); ~CHttpHeader(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+
请求头对象的封装与接口
在 MT5 里做 REST 请求时,把 HTTP 头信息从字符串拼凑改成结构化对象,能少踩很多坑。下面这个类用私有成员 CJson m_headers 承载所有键值对,对外只暴露增删查的轻量接口。 Add(string key, string value) 负责写入或覆盖某个头;Get(string key) 按名取值,取不到返回空串;Has(string key) 用布尔值告诉你某个头是否存在。Remove 与 Clear 分别删单条和清空,Count 回传当前头数量——这几个方法足够覆盖绝大多数鉴权头(如 Authorization、Content-Type)的动态维护。 构造函数 CHttpHeader::CHttpHeader(void) 体为空,说明初始化成本极低,直接在 EA 或脚本里声明即用。外汇与贵金属行情接口调用受网络与平台限制,实盘前请在策略测试器或 demo 环境验证头字段是否被网关正确转发。
class=class="str">"cmt">//| [PROPERTY] | //| Name : CHttpHeader | //| Heritage : No heritage | //| Description : Responsible for organizing and storing the headers | //| of a request. | //| | //+------------------------------------------------------------------+ class CHttpHeader { class="kw">private: CJson m_headers; class="kw">public: CHttpHeader(class="type">void); ~CHttpHeader(class="type">void); class=class="str">"cmt">//--- Functions to manage headers class="type">void Add(class="type">class="kw">string key, class="type">class="kw">string value); class=class="str">"cmt">// Adds a new header to the HTTP request or updates it if it already exists class="type">class="kw">string Get(class="type">class="kw">string key); class=class="str">"cmt">// Returns the value of a specific header, given its name. class="type">void Remove(class="type">class="kw">string key); class=class="str">"cmt">// Removes a specific header. class="type">bool Has(class="type">class="kw">string key); class=class="str">"cmt">// Checks whether a header with the specified key is present. class="type">void Clear(class="type">void); class=class="str">"cmt">// Removes all headers from the request. class="type">int Count(class="type">void); class=class="str">"cmt">// Returns the number of headers. }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CHttpHeader::CHttpHeader(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------+