Connexus助手(第五部分):HTTP方法和状态码·进阶篇
🔌

Connexus助手(第五部分):HTTP方法和状态码·进阶篇

(2/3)· 多数EA在WebRequest里硬写GET/POST,遇到401或500就哑火,本篇补齐动词与状态码的工程化封装

含代码示例实战向 第 2/3 篇
不少交易者把 WebRequest 当万能胶,只塞 GET 和 POST,服务器回个 429 或 503 就不知道该重试还是放弃。把方法和状态码拆清楚,EA 与 REST 接口对话才不至于盲发。

「HTTP 方法类的骨架与字符串映射」

在 MT5 里封装 REST 调用,先得有个方法枚举的承载类。下面这段 CHttpMethod 只做一件事:把内部枚举和对外字符串互相打通,构造/析构都是空壳,真正的状态落在 m_method 私有成员上。 类里留了两个布尔接口 IsOption / IsTrace 的声明,但本段未给实现,说明这个类只是方法调度的前半截,后面才会补上判定逻辑。 Set(string) 的重载值得注意:它先把入参转全大写再比对,支持 CONNECT、DELETE、GET、HEAD、OPTIONS、PATCH、POST 共 7 种字面量,匹配成功才写 m_method 并返回 true。这意味着你从 JSON 配置里读到的请求动作,只要大小写不统一也能容错进枚举。 开 MT5 新建 EA 把这段贴进 .mqh,试着用 Set("post") 调用,观察 m_method 是否被置为 HTTP_METHOD_POST,能验证大小写归一是否生效。

MQL5 / C++
  class="type">bool                IsOption(class="type">void);
  class="type">bool                IsTrace(class="type">void);
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpMethod::CHttpMethod(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpMethod::~CHttpMethod(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Defines the http method                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CHttpMethod::class="kw">operator=(ENUM_HTTP_METHOD method)
  {
   m_method = method;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Defines the http method                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CHttpMethod::Set(ENUM_HTTP_METHOD method)
  {
   m_method = method;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Defines the http method                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::Set(class="type">class="kw">string method)
  {
   class="type">class="kw">string method_upper = method;
   StringToUpper(method_upper);
   if(method_upper == "CONNECT")
     {
      m_method = HTTP_METHOD_CONNECT;
      class="kw">return(true);
     }
   else if(method_upper == "DELETE")
     {
      m_method = HTTP_METHOD_DELETE;
      class="kw">return(true);
     }
   else if(method_upper == "GET")
     {
      m_method = HTTP_METHOD_GET;
      class="kw">return(true);
     }
   else if(method_upper == "HEAD")
     {
      m_method = HTTP_METHOD_HEAD;
      class="kw">return(true);
     }
   else if(method_upper == "OPTIONS")
     {
      m_method = HTTP_METHOD_OPTION;
      class="kw">return(true);
     }
   else if(method_upper == "PATCH")
     {
      m_method = HTTP_METHOD_PATCH;
      class="kw">return(true);
     }
   else if(method_upper == "POST")
     {

HTTP 方法类的取值与判定实现

这段 CHttpMethod 类的后半段给出了方法枚举的读取与字符串映射逻辑。Set 方法中遇到 POST、PUT、TRACE 等大写字符串会分别写入 m_method 并返回 true,不匹配任何已知方法时返回 false,说明外部传参必须事先转成大写。 Get 直接返回内部成员 m_method,属于零开销的取值器;GetAsString 则用 switch 覆盖全部 10 种枚举(NULL/CONNECT/DELETE/GET/HEAD/OPTIONS/PATCH/POST/PUT/TRACE),default 分支回吐 "Unknown HTTP Method" 方便调试时肉眼排错。 IsConnect 与 IsGet 是两个轻量布尔判定,仅比较 m_method 是否等于对应枚举值。若你要在 EA 里向自建行情网关发请求,直接复用 IsGet() 判断走查询分支,比手写字符串比较更不容易拼错。 外汇与贵金属接口调用涉及实时报价,网络异常可能导致滑点扩大,实盘前务必在 MT5 策略测试器用模拟地址跑通这套方法映射。

MQL5 / C++
   m_method = HTTP_METHOD_POST;
   class="kw">return(true);
   }
   else if(method_upper == "PUT")
   {
      m_method = HTTP_METHOD_PUT;
      class="kw">return(true);
   }
   else if(method_upper == "TRACE")
   {
      m_method = HTTP_METHOD_TRACE;
      class="kw">return(true);
   }
   class="kw">return(false);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Get http method                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
ENUM_HTTP_METHOD CHttpMethod::Get(class="type">void)
  {
   class="kw">return(m_method);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Get the description of the selected http method                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CHttpMethod::GetAsString(class="type">void)
  {
   class="kw">switch(m_method)
     {
      case HTTP_METHOD_NULL:
         class="kw">return "NULL";
      case HTTP_METHOD_CONNECT:
         class="kw">return "CONNECT";
      case HTTP_METHOD_DELETE:
         class="kw">return "DELETE";
      case HTTP_METHOD_GET:
         class="kw">return "GET";
      case HTTP_METHOD_HEAD:
         class="kw">return "HEAD";
      case HTTP_METHOD_OPTION:
         class="kw">return "OPTIONS";
      case HTTP_METHOD_PATCH:
         class="kw">return "PATCH";
      case HTTP_METHOD_POST:
         class="kw">return "POST";
      case HTTP_METHOD_PUT:
         class="kw">return "PUT";
      case HTTP_METHOD_TRACE:
         class="kw">return "TRACE";
      class="kw">default:
         class="kw">return "Unknown HTTP Method";
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is connect                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsConnect(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_CONNECT);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is get                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsGet(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_GET);
  }

◍ 给 HTTP 方法做类型判定的几个内联函数

在 MT5 里用 C++ 风格的封装去碰 REST 接口时,CHttpMethod 类把每种请求动词都做成了一个返回 bool 的短函数。它们不干别的事,只比对内部成员变量 m_method 是否等于对应的宏常量。 这种写法看着啰嗦,但在 EA 里动态拼请求很有用:比如你根据账户净值变化决定用 POST 发订单还是用 DELETE 撤单,直接 if(IsPost()) 比手写字符串比较更不容易拼错。 下面这段是类里实际的那组判定函数,宏名如 HTTP_METHOD_POST 一般在 WebRequest 相关头文件里定义,值就是 "POST" 这类字符串。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is post                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsPost(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_POST);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is put                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsPut(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_PUT);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is class="kw">delete                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsDelete(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_DELETE);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is patch                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsPatch(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_PATCH);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is head                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsHead(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_HEAD);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is option                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsOption(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_OPTION);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check if method is trace                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CHttpMethod::IsTrace(class="type">void)
  {
   class="kw">return(m_method == HTTP_METHOD_TRACE);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「把 HTTP 状态码拆成五段数值区间」

做 MT5 WebRequest 对接外部信号源时,服务器回的不只是 JSON,第一行就藏着三位数字的状态码。第一位直接定类:1xx 是“还在处理”,2xx 是“成了”,3xx 是“去别处取”,4xx 是“你请求有问题”,5xx 是“服务器自己挂了”。 实际盯盘脚本里,200 OK 和 204 No Content 最常见——前者带数据回来,后者确认收到但不返_body,适合心跳包。404 和 500 则要分开处理:404 多半是 URL 拼错或资源被删,500 是远端崩了,重试逻辑得区分对待。 下面这张区间表值得存进你的开发笔记:100-199 极少碰,属临时响应;200-299 全成功;300-399 重定向(301 永久、302 临时、304 用缓存);400-499 客户端错(401 要鉴权、403 拒绝、405 方法不对);500-599 服务端错(502 网关坏、503 过载、504 上游超时)。 若想让 EA 兼容任意合法码,最好封装一个类统一解析,而不是在每次 WebRequest 后写死判断。外汇与贵金属接口调用受网络与平台限制,失败概率不低,任何重试都只是降低漏单可能,不保证送达。

把状态码收进一个可识别类别的类

在 MT5 里做 HTTP 请求,光拿到 WebRequest 的返回值不够,最好把状态码和描述、分类都封装起来。新建 Includes/Connexus/Constants/HttpStatusCode.mqh,先放一个 ENUM_HTTP_STATUS 枚举,把标准码和几个库自定义的码都列进去。 自定义码里,HTTP_STATUS_URL_NOT_ALLOWED 对应终端没把 URL 加进允许列表;HTTP_STATUS_URL_NOT_DEFINED 是请求没带 URL;HTTP_STATUS_METHOD_NOT_DEFINED 是没给合法方法。当 WebRequest 返回 -1,按文档就是 URL 未授权,库应自动转成 HTTP_STATUS_URL_NOT_ALLOWED。 类内部用私有变量 m_status 存 ENUM_HTTP_STATUS,再给赋值和读取的口子:operator=(int) 和 operator=(ENUM_HTTP_STATUS) 让等号直接接整数或枚举,Set(ENUM_HTTP_STATUS) 走显式设置,Get() 拿码、GetMessage() 拿对应文案。 分类判断也得内置,靠数值区间:IsInformational 看 100–199,IsSuccess 看 200–299,IsRedirection 看 300–399,IsClientError 看 400–499,IsServerError 看 500–599。外汇或贵金属 EA 调远程行情接口时,用 IsServerError 快速跳过 5xx 比硬判断数字更稳。 下面这段是类的骨架,先只声明了构造和析构,后续方法往里补即可在 MT5 的 MetaEditor 直接编译验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  HttpStatusCode.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 : CHttpStatusCodes                                                    |
class=class="str">"cmt">//|                                                                             |
class=class="str">"cmt">//| [PROPERTY]                                                                  |
class=class="str">"cmt">//| Name      : CHttpStatusCodes                                                |
class=class="str">"cmt">//| Heritage  : No heritage                                                     |
class=class="str">"cmt">//| Description : Saved http status code.                                       |
class=class="str">"cmt">//|                                                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CHttpStatusCodes
  {
class="kw">public:
                        CHttpStatusCodes(class="type">void);
                       ~CHttpStatusCodes(class="type">void);
  };
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ HTTP 状态枚举的收口定义

把前面的类骨架补齐,剩下就是状态枚举本身。CHttpStatusCodes 的构造和析构都是空实现,真正干活的是 ENUM_HTTP_STATUS 里那一大张码表。 从 -1 的 URL_NOT_ALLOWED 到 503 的 SERVICE_UNAVAILABLE,枚举覆盖了 MQL 内部错误、1xx 通知、2xx 成功、3xx 重定向、4xx 客户端错、5xx 服务端错共六段。你在 EA 里调 WebRequest 时,拿返回码比对这张表,就能直接分辨是地址没配好(-1/1/2)还是远端限流(429)或者网关挂了(502)。 外汇与贵金属行情接口走 HTTP 时延迟和拒绝率都不稳定,这类状态判断只能帮你定位故障环节,不预示任何报价走向;实盘前请在策略测试器里把非常规返回(如 425、451)也打日志跑一遍。

MQL5 / C++
class=class="str">"cmt">//| Constructor                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpStatusCodes::CHttpStatusCodes(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Destructor                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CHttpStatusCodes::~CHttpStatusCodes(class="type">void)
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_HTTP_STATUS
  {
class=class="str">"cmt">//--- Mql error
   HTTP_STATUS_URL_NOT_ALLOWED = -class="num">1,
   HTTP_STATUS_URL_NOT_DEFINED = class="num">1,
   HTTP_STATUS_METHOD_NOT_DEFINED = class="num">2,
class=class="str">"cmt">//--- Informational
   HTTP_STATUS_CONTINUE = class="num">100,
   HTTP_STATUS_SWITCHING_PROTOCOLS = class="num">101,
   HTTP_STATUS_PROCESSING = class="num">102,
   HTTP_STATUS_EARLY_HINTS = class="num">103,
class=class="str">"cmt">//--- Successul
   HTTP_STATUS_OK = class="num">200,
   HTTP_STATUS_CREATED = class="num">201,
   HTTP_STATUS_ACCEPTED = class="num">202,
   HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = class="num">203,
   HTTP_STATUS_NO_CONTENT = class="num">204,
   HTTP_STATUS_RESET_CONTENT = class="num">205,
   HTTP_STATUS_PARTIAL_CONTENT = class="num">206,
   HTTP_STATUS_MULTI_STATUS = class="num">207,
   HTTP_STATUS_ALREADY_REPORTED = class="num">208,
class=class="str">"cmt">//--- Redirection messages
   HTTP_STATUS_MULTIPLE_CHOICES = class="num">300,
   HTTP_STATUS_MOVED_PERMANENTLY = class="num">301,
   HTTP_STATUS_FOUND = class="num">302,
   HTTP_STATUS_SEE_OTHER = class="num">303,
   HTTP_STATUS_NOT_MODIFIED = class="num">304,
   HTTP_STATUS_USE_PROXY = class="num">305,
   HTTP_STATUS_SWITCH_PROXY = class="num">306,
   HTTP_STATUS_TEMPORARY_REDIRECT = class="num">307,
   HTTP_STATUS_PERMANENT_REDIRECT = class="num">308,
class=class="str">"cmt">//--- Client error
   HTTP_STATUS_BAD_REQUEST = class="num">400,
   HTTP_STATUS_UNAUTHORIZED = class="num">401,
   HTTP_STATUS_PAYMENT_REQUIRED = class="num">402,
   HTTP_STATUS_FORBIDDEN = class="num">403,
   HTTP_STATUS_NOT_FOUND = class="num">404,
   HTTP_STATUS_METHOD_NOT_ALLOWED = class="num">405,
   HTTP_STATUS_NOT_ACCEPTABLE = class="num">406,
   HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = class="num">407,
   HTTP_STATUS_REQUEST_TIMEOUT = class="num">408,
   HTTP_STATUS_CONFLICT = class="num">409,
   HTTP_STATUS_GONE = class="num">410,
   HTTP_STATUS_LENGTH_REQUIRED = class="num">411,
   HTTP_STATUS_PRECONDITION_FAILED = class="num">412,
   HTTP_STATUS_PAYLOAD_TOO_LARGE = class="num">413,
   HTTP_STATUS_URI_TOO_LONG = class="num">414,
   HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = class="num">415,
   HTTP_STATUS_RANGE_NOT_SATISFIABLE = class="num">416,
   HTTP_STATUS_EXPECTATION_FAILED = class="num">417,
   HTTP_STATUS_MISDIRECTED_REQUEST = class="num">421,
   HTTP_STATUS_UNPROCESSABLE_ENTITY = class="num">422,
   HTTP_STATUS_LOCKED = class="num">423,
   HTTP_STATUS_FAILED_DEPENDENCY = class="num">424,
   HTTP_STATUS_TOO_EARLY = class="num">425,
   HTTP_STATUS_UPGRADE_REQUIRED = class="num">426,
   HTTP_STATUS_PRECONDITION_REQUIRED = class="num">428,
   HTTP_STATUS_TOO_MANY_REQUESTS = class="num">429,
   HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = class="num">431,
   HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = class="num">451,
class=class="str">"cmt">//--- Server error
   HTTP_STATUS_INTERNAL_SERVER_ERROR = class="num">500,
   HTTP_STATUS_NOT_IMPLEMENTED = class="num">501,
   HTTP_STATUS_BAD_GATEWAY = class="num">502,
   HTTP_STATUS_SERVICE_UNAVAILABLE = class="num">503,
交给小布盯盘看接口异常
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到请求层的状态码分布,把重复劳动交给小布,你专注决策。

常见问题

不能。每个请求只能指定一种 HTTP 方法,需要在封装层明确区分动词,避免运行时语义混乱。
PUT 倾向整体替换资源,PATCH 只改局部字段;对行情订阅类接口用 PATCH 更省流量,全量配置同步再用 PUT。
可以,小布盯盘的 AIGC 模块会聚合品种页里的接口返回码,异常码高亮提示,省去你手动加日志。
HEAD 只取响应头不拿主体,适合先探接口存活与限流余量,再决定是否发 GET 拉全量数据,降低无效请求。