在libcurl中复现特殊curl请求:部分参数大括号不编码
问题:libcurl复现特殊编码的curl请求
在命令行中,存在一个包含两个带大括号的查询参数的curl请求,其中一个参数必须URL编码才有效,另一个必须不编码:
有效请求示例(返回3个ID的数据)
curl -X GET "https://example.com/search?TIME=%7B%22TIME_TYPE%22%3A%22MESSAGE%22%2C%22MESSAGENUMBER%22%3A5%7D&P_ID={1,2,3}"
- TIME参数的大括号已URL编码(
%7B和%7D) - P_ID参数的大括号未编码
无效请求示例(忽略P_ID,返回所有用户数据)
curl -X GET "https://example.com/search?TIME=%7B%22TIME_TYPE%22%3A%22MESSAGE%22%2C%22MESSAGENUMBER%22%3A5%7D&P_ID=%7B1,2,3%7D"(P_ID大括号编码)curl -X GET "https://example.com/search?TIME=%7B%22TIME_TYPE%22%3A%22MESSAGE%22%2C%22MESSAGENUMBER%22%3A5%7D&P_ID=1&P_ID=2&P_ID=3"(多P_ID参数)curl -X GET "https://example.com/search?TIME={%22TIME_TYPE%22%3A%22MESSAGE%22%2C%22MESSAGENUMBER%22%3A5}&P_ID=1&P_ID=2&P_ID=3"(TIME大括号未编码)
尝试用libcurl的CURLUPART实现时出现错误,代码如下:
void *curlHandle; curl_easy_reset(curlHandle); CURLU *url = curl_url(); curl_url_set(url, CURLUPART_URL, "example.com", 0); curl_url_set(url, CURLUPART_QUERY, "P_ID={1,2,3}", CURLU_APPENDQUERY); curl_url_set(url, CURLUPART_QUERY, "TIME=%7B%22TIME_TYPE%22%3A%22MESSAGE%22%2C%22MESSAGENUMBER%22%3A5%7D", CURLU_APPENDQUERY); curl_url_set(url, CURLUPART_SCHEME, "https", 0); curl_url_set(url, CURLUPART_PATH, "/search/", 0); curl_easy_setopt(curlHandle, CURLOPT_CURLU, url); curl_easy_perform(curlHandle);
错误信息:
- No URL set
Curl failed with error 3: URL using bad/illegal format or missing URL. 0 retries remaining
解决方法
1. 修复CURLUPART代码错误
你的CURLUPART代码存在两个关键问题:
curlHandle未初始化,直接调用curl_easy_reset会导致未定义行为,需先通过curl_easy_init()创建句柄- URL设置顺序错误,应先指定scheme和主机,再配置其他部分
修正后的代码:
CURL *curlHandle = curl_easy_init(); // 初始化句柄 if(curlHandle) { CURLU *url = curl_url(); // 先设置scheme和主机 curl_url_set(url, CURLUPART_SCHEME, "https", 0); curl_url_set(url, CURLUPART_HOST, "example.com", 0); curl_url_set(url, CURLUPART_PATH, "/search", 0); // 添加查询参数,用CURLU_NON_SUPPORT_SCHEME避免自动编码 curl_url_set(url, CURLUPART_QUERY, "TIME=%7B%22TIME_TYPE%22%3A%22MESSAGE%22%2C%22MESSAGENUMBER%22%3A5%7D", CURLU_APPENDQUERY | CURLU_NON_SUPPORT_SCHEME); curl_url_set(url, CURLUPART_QUERY, "P_ID={1,2,3}", CURLU_APPENDQUERY | CURLU_NON_SUPPORT_SCHEME); curl_easy_setopt(curlHandle, CURLOPT_CURLU, url); CURLcode res = curl_easy_perform(curlHandle); if(res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } // 清理资源 curl_url_cleanup(url); curl_easy_cleanup(curlHandle); }
CURLU_NON_SUPPORT_SCHEME选项会让libcurl不对查询参数做自动编码,保留你传入的原始格式(包括未编码的P_ID大括号和已编码的TIME参数)
2. 直接构造完整URL并禁用自动编码
如果CURLUPART方式仍有问题,可以直接拼接符合要求的完整URL字符串,通过CURLOPT_URL设置,同时确保不触发自动编码:
CURL *curlHandle = curl_easy_init(); if(curlHandle) { const char *fullUrl = "https://example.com/search?TIME=%7B%22TIME_TYPE%22%3A%22MESSAGE%22%2C%22MESSAGENUMBER%22%3A5%7D&P_ID={1,2,3}"; curl_easy_setopt(curlHandle, CURLOPT_URL, fullUrl); // 禁用额外的内容编码转换,确保URL格式完全保留 curl_easy_setopt(curlHandle, CURLOPT_ENCODING, ""); CURLcode res = curl_easy_perform(curlHandle); if(res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } curl_easy_cleanup(curlHandle); }
注意:直接拼接URL时要确保字符串完全符合有效格式,避免包含其他非法字符。
额外问题解答
除已尝试的4种方法,还有其他方式吗?
- 可以用
curl_easy_escape手动编码TIME参数,再拼接P_ID的原始字符串,构造完整URL后设置给libcurl,这种方式更灵活,能精准控制每个参数的编码状态。 - 不推荐用
CURLOPT_POSTFIELDS模拟GET请求(将查询参数放在POSTFIELDS中,设置CURLOPT_CUSTOMREQUEST为"GET"),这种方式不符合HTTP规范,可能引发兼容性问题。
是否应该联系API所有者?
非常建议联系API所有者询问这种特殊编码要求的原因:
- 这种不一致的编码逻辑不符合URL标准(RFC 3986),大括号属于保留字符,理论上编码或不编码都应被正确解析,API的特殊处理大概率是实现bug或未公开的规则。
- 了解原因后,你可以确认是否存在更规范的调用方式,避免后续API更新导致当前的特殊调用方式失效。
内容的提问来源于stack exchange,提问作者rabbitdude
相关产品推荐
相关产品推荐

