# HTTP 客户端适配

本文档介绍如何在 Emscripten 项目中集成微信小游戏 HTTP 客户端模块,适用于Cocos2dx、自研引擎项目。

# 概述

本模块提供完整的 HTTP 客户端实现,将 C++ 的 HTTP 请求映射到微信小游戏的 wx.requestwx.downloadFilewx.uploadFile API。

业务只需要:

  1. 创建 HTTP 客户端对象
  2. 配置请求参数
  3. 设置成功/失败回调
  4. 发送请求

# 功能特性

功能 说明
HTTP 请求 GET, POST, PUT, DELETE, HEAD, OPTIONS 等方法
文件下载 支持下载文件到本地路径
文件上传 支持上传本地文件到服务器
HTTPS 支持安全连接
请求头 自定义 HTTP 请求头
请求体 支持文本和二进制数据
响应处理 支持文本和二进制响应
超时控制 可配置请求超时时间
高级特性 HTTP/2、QUIC、HttpDNS、缓存等

# 快速开始

# 集成到项目

# CMakeLists.txt 配置

cmake_minimum_required(VERSION 3.10)
project(MyGame)

set(WXGAMESDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wxgamesdk")

add_executable(mygame main.cpp)

target_include_directories(mygame PRIVATE
    ${WXGAMESDK_DIR}/include
)

target_link_libraries(mygame
    ${WXGAMESDK_DIR}/lib/libwxgamesdk.a
)

set_target_properties(mygame PROPERTIES
    LINK_FLAGS "\
        -s WASM=1 \
        -s EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"stringToUTF8\", \"UTF8ToString\", \"lengthBytesUTF8\", \"allocateUTF8\"]' \
        -s EXPORTED_FUNCTIONS='[\
            \"_HttpRequestOnCookie\", \
            \"_HttpRequestOnSuccessStrData\", \
            \"_HttpRequestOnSuccessBinData\", \
            \"_HttpOnError\", \
            \"_main\", \"_free\", \"_malloc\"]' \
        --js-library ${WXGAMESDK_DIR}/jslib/libwxhttp_open.jslib \
        -Wl,--whole-archive ${WXGAMESDK_DIR}/lib/libwxgamesdk.a -Wl,--no-whole-archive"
)

# API 参考

# 头文件

#include "http/wx_http_interface.h"

# 命名空间

所有 HTTP 相关接口都在 WXSocket 命名空间下。

# 核心接口

# 1. HTTP 请求客户端

// 创建 HTTP 客户端
WXSocket::HttpClientInterface* CreateHttpClient(int* client_id);

// 发送请求
int SendHttpRequest(int client_id);

// 中止请求
int AbortHttpRequest(int client_id);

# 2. 文件下载客户端

// 创建下载客户端
WXSocket::HttpDownloadClientInterface* CreateHttpDownloadClient(int* client_id);

// 发送下载请求
int SendHttpDownloadRequest(int client_id);

// 中止下载请求
int AbortHttpDownloadRequest(int client_id);

# 3. 文件上传客户端

// 创建上传客户端
WXSocket::HttpUploadClientInterface* CreateHttpUploadClient(int* client_id);

// 发送上传请求
int SendHttpUploadRequest(int client_id);

// 中止上传请求
int AbortHttpUploadRequest(int client_id);

# 响应数据结构

namespace WXSocket {

// HTTP 响应信息
struct Response {
    std::string version;               // HTTP 版本
    int statusCode;                    // 状态码
    std::string reason;                // 状态描述
    Headers headers;                   // 响应头
    std::vector<std::string> cookies;  // Cookies
    bool useHttpDns;                   // 是否使用 HttpDNS
};

// 错误信息
struct Error {
    int errorCode;        // 错误码
    std::string errorMsg; // 错误信息
    bool useHttpDns;      // 是否使用 HttpDNS
};

// 进度信息
struct HttpRequestProgress {
    int progress;           // 进度百分比
    int totalBytesExpected; // 预期总字节数
    int totalBytes;         // 已传输字节数
};

}

# 使用示例

# 基本 HTTP 请求

#include "http/wx_http_interface.h"
#include <cstdio>

// 定义测试状态结构
struct HttpTest {
    std::string method;
    bool http_request_success = false;
    bool http_request_complete = false;
    bool http_request_fail = false;
    int client_id = 0;
    
    bool IsComplete() { return http_request_complete; }
    void CheckSuccess() { assert(http_request_success); }
};

// 成功回调
void OnSuccess(const WXSocket::Response& response, const void* data, size_t size, void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("HTTP 请求成功, id:%d, method:%s, data:%s\n", 
           test->client_id, test->method.c_str(), (const char*)data);
    test->http_request_success = true;
}

// 失败回调
void OnFail(const WXSocket::Error& error, void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("HTTP 请求失败, id:%d, method:%s, error:%d, msg:%s\n", 
           test->client_id, test->method.c_str(), error.errorCode, error.errorMsg.c_str());
    test->http_request_fail = true;
}

// 完成回调(无论成功或失败都会调用)
void OnComplete(void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("HTTP 请求完成, id:%d, method:%s\n", test->client_id, test->method.c_str());
    test->http_request_complete = true;
}

// 构造并发送 HTTP 请求
WXSocket::HttpClientInterface* ConstructHttpRequest(
    HttpTest* test, 
    const std::string& url, 
    const std::string& method,
    const std::string& body) {
    
    int client_id = 0;
    WXSocket::HttpClientInterface* httpclient = WXSocket::CreateHttpClient(&client_id);
    if (httpclient == nullptr) {
        return nullptr;
    }
    
    // 配置请求参数
    httpclient->SetUrl(url);
    httpclient->SetMethod(method);
    httpclient->SetTimeout(2000);  // 2秒超时
    httpclient->SetResponseType(WXSocket::HttpClientInterface::kResponseTypeString);
    httpclient->SetBody(WXSocket::HttpClientInterface::kDataTypeString, body.c_str(), body.size());
    httpclient->SetHeader("Content-Type", "application/json");
    
    // 保存客户端 ID
    test->client_id = client_id;
    test->method = method;
    
    // 设置回调函数
    httpclient->SetSuccessCallback(OnSuccess, test);
    httpclient->SetFailCallback(OnFail, test);
    httpclient->SetCompleteCallback(OnComplete, test);
    
    return httpclient;
}

// 使用示例
void TestHttpRequest() {
    static HttpTest httptest_get;
    static HttpTest httptest_post;
    
    // GET 请求
    auto* client = ConstructHttpRequest(&httptest_get, "https://api.example.com/users", "GET", "");
    if (client) {
        int ret = WXSocket::SendHttpRequest(httptest_get.client_id);
        assert(ret == 0);
    }
    
    // POST 请求
    std::string post_data = R"({"id": 3, "name": "tom", "email": "tom@example.com"})";
    client = ConstructHttpRequest(&httptest_post, "https://api.example.com/users", "POST", post_data);
    if (client) {
        int ret = WXSocket::SendHttpRequest(httptest_post.client_id);
        assert(ret == 0);
    }
}

# 文件下载

#include "http/wx_http_interface.h"
#include <cstdio>

struct HttpTest {
    int client_id = 0;
    bool http_request_success = false;
    bool http_request_complete = false;
};

// 下载成功回调
void OnDownloadSuccess(const WXSocket::HttpDownloadClientInterface::HttpDownloadResponse& response, void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("下载成功, id:%d, status_code:%d, tempFilePath:%s, filePath:%s\n",
           test->client_id, response.statusCode, 
           response.tempFilePath.c_str(), response.filePath.c_str());
    test->http_request_success = true;
    
    // 下载完成后可以读取文件
    FILE* fp = fopen(response.filePath.c_str(), "r");
    if (fp) {
        char buffer[1024];
        size_t readsize = fread(buffer, 1, sizeof(buffer), fp);
        printf("读取内容: %.*s\n", (int)readsize, buffer);
        fclose(fp);
    }
}

void OnDownloadFail(const WXSocket::Error& error, void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("下载失败, id:%d, error:%d, msg:%s\n", 
           test->client_id, error.errorCode, error.errorMsg.c_str());
}

void OnDownloadComplete(void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    test->http_request_complete = true;
    printf("下载完成, id:%d\n", test->client_id);
}

// 构造下载请求
WXSocket::HttpDownloadClientInterface* ConstructHttpDownloadRequest(
    HttpTest* test, 
    const std::string& url,
    const std::string& filePath) {
    
    int client_id = 0;
    WXSocket::HttpDownloadClientInterface* httpclient = WXSocket::CreateHttpDownloadClient(&client_id);
    if (httpclient == nullptr) {
        return nullptr;
    }
    
    httpclient->SetUrl(url);
    httpclient->SetFilePath(filePath);  // 本地保存路径
    httpclient->SetTimeout(60000);       // 60秒超时
    
    test->client_id = client_id;
    httpclient->SetSuccessCallback(OnDownloadSuccess, test);
    httpclient->SetFailCallback(OnDownloadFail, test);
    httpclient->SetCompleteCallback(OnDownloadComplete, test);
    
    return httpclient;
}

// 使用示例
void TestDownload() {
    static HttpTest httptest_download;
    
    // 下载文件到本地路径(必须以 /CustomWritablePath 开头)
    const std::string url = "https://example.com/config/settings.json";
    const std::string localPath = "/CustomWritablePath/Library/Application_Support/1.0.0/remoteSettings.json";
    
    auto* client = ConstructHttpDownloadRequest(&httptest_download, url, localPath);
    if (client) {
        int ret = WXSocket::SendHttpDownloadRequest(httptest_download.client_id);
        assert(ret == 0);
        printf("下载请求已发送\n");
    }
}

# 文件上传

#include "http/wx_http_interface.h"
#include <cstdio>

struct HttpTest {
    int client_id = 0;
    bool http_request_success = false;
};

// 上传成功回调
void OnUploadSuccess(const WXSocket::HttpUploadClientInterface::HttpUploadResponse& response, 
                     const char* data, void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("上传成功, id:%d, status_code:%d, response:%s\n", 
           test->client_id, response.statusCode, data ? data : "");
    test->http_request_success = true;
}

void OnUploadFail(const WXSocket::Error& error, void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("上传失败, id:%d, error:%d, msg:%s\n", 
           test->client_id, error.errorCode, error.errorMsg.c_str());
}

void OnUploadComplete(void* user_data) {
    HttpTest* test = (HttpTest*)user_data;
    printf("上传完成, id:%d\n", test->client_id);
}

// 构造上传请求
WXSocket::HttpUploadClientInterface* ConstructHttpUploadRequest(
    HttpTest* test, 
    const std::string& url,
    const std::string& filePath) {
    
    int client_id = 0;
    WXSocket::HttpUploadClientInterface* httpclient = WXSocket::CreateHttpUploadClient(&client_id);
    if (httpclient == nullptr) {
        return nullptr;
    }
    
    httpclient->SetUrl(url);
    httpclient->SetFilePath(filePath);  // 本地文件路径
    httpclient->SetFileName("file");     // 服务端接收的字段名
    httpclient->SetTimeout(30000);
    
    test->client_id = client_id;
    httpclient->SetSuccessCallback(OnUploadSuccess, test);
    httpclient->SetFailCallback(OnUploadFail, test);
    httpclient->SetCompleteCallback(OnUploadComplete, test);
    
    return httpclient;
}

// 使用示例
void TestUpload() {
    static HttpTest httptest_upload;
    
    // 先创建一个本地文件
    FILE* fp = fopen("/CustomWritablePath/upload_test.txt", "w");
    fprintf(fp, "hello world");
    fclose(fp);
    
    // 上传文件
    auto* client = ConstructHttpUploadRequest(
        &httptest_upload, 
        "https://api.example.com/upload",
        "/CustomWritablePath/upload_test.txt");
    
    if (client) {
        int ret = WXSocket::SendHttpUploadRequest(httptest_upload.client_id);
        assert(ret == 0);
    }
}

# 主循环中处理请求完成

#include <emscripten.h>

static HttpTest httptest_download;

void main_loop() {
    // 检查下载是否完成
    if (httptest_download.http_request_complete) {
        if (httptest_download.http_request_success) {
            printf("下载成功完成\n");
        }
        
        // 中止并释放客户端资源
        if (httptest_download.client_id != 0) {
            int ret = WXSocket::AbortHttpDownloadRequest(httptest_download.client_id);
            assert(ret == 0);
            httptest_download.client_id = 0;
        }
    }
}

int main() {
    // 发起下载请求
    auto* client = ConstructHttpDownloadRequest(
        &httptest_download,
        "https://example.com/config.json",
        "/CustomWritablePath/config.json");
    
    if (client) {
        WXSocket::SendHttpDownloadRequest(httptest_download.client_id);
    }
    
    // 启动主循环
    emscripten_set_main_loop(main_loop, 0, 0);
    return 0;
}

# HttpClientInterface 详细配置

# 必选配置

方法 说明
SetUrl(url) 设置请求 URL
SetMethod(method) 设置 HTTP 方法(GET/POST/PUT/DELETE 等)
SetSuccessCallback(callback, user_data) 设置成功回调
SetFailCallback(callback, user_data) 设置失败回调

# 常用配置

方法 说明
SetTimeout(ms) 设置超时时间(毫秒),默认 60000
SetHeader(key, value) 设置请求头
SetBody(type, data, size) 设置请求体(文本或二进制)
SetResponseType(type) 设置响应类型(kResponseTypeString / kResponseTypeBinary)
SetCompleteCallback(callback, user_data) 设置完成回调(可选)

# 高级配置

方法 说明
SetEnableHttp2(enable) 启用 HTTP/2 协议
SetEnableQuic(enable) 启用 QUIC/H3 协议
SetEnableHttpDNS(enable) 启用 HttpDNS
SetHttpDNSServiceId(serviceId) 设置 HttpDNS 服务商 ID
SetEnableCache(enable) 启用 HTTP 缓存
SetEnableChunked(enable) 启用分块传输
SetUseHighPerformanceMode(enable) 启用高性能模式
SetHeaderReceivedCallback(callback, user_data) 设置响应头接收回调
SetChunkReceivedCallback(callback, user_data) 设置分块数据接收回调

# HttpDownloadClientInterface 配置

方法 说明
SetUrl(url) 下载 URL
SetFilePath(path) 本地保存路径(必须以 /CustomWritablePath 开头)
SetHeader(key, value) 请求头
SetTimeout(ms) 超时时间
SetSuccessCallback 成功回调,返回 HttpDownloadResponse
SetFailCallback 失败回调
SetProgressUpdateCallback 进度回调(可选)

# HttpUploadClientInterface 配置

方法 说明
SetUrl(url) 上传服务器 URL
SetFilePath(path) 本地文件路径
SetFileName(name) 文件对应的 key(服务端用此 key 获取文件)
SetHeader(key, value) 请求头
SetFormData(data) 额外的表单数据
SetTimeout(ms) 超时时间
SetSuccessCallback 成功回调,返回 HttpUploadResponse
SetFailCallback 失败回调
SetProgressUpdateCallback 进度回调(可选)

# 错误处理

# 错误码说明

错误码 说明
0 成功
-1 通用错误 / 请求失败

# HTTP 状态码

状态码 说明
200 成功
201 创建成功
400 请求错误
401 未授权
403 禁止访问
404 未找到
500 服务器内部错误

# 注意事项

  1. 域名配置:生产环境需要在微信公众平台配置合法域名
  2. 本地路径:下载文件路径必须以 /CustomWritablePath 开头
  3. 资源释放:请求完成后,SDK 会自动清理资源;如需提前终止请求,调用对应的 Abort 方法
  4. 回调线程:回调函数在主线程中执行,避免在回调中执行耗时操作
  5. 并发限制:注意控制同时发起的请求数量

# 下一步

点击咨询小助手