对接Royal Mail API V4:libcurl实现curl --data-urlencode功能遇错求助
尝试用C语言结合libcurl对接Royal Mail API V4获取令牌,已有可用的curl命令行脚本能通过--data-urlencode成功获取令牌:
curl -k --location 'https://authentication.proshipping.net/connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id=AAAAAAA' \ --data-urlencode 'client_secret=BBBBBBBBB' \ --data-urlencode 'grant_type=client_credentials'
但自行编写的libcurl代码返回{"error":"invalid_client"},代码如下:
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, FALSE); curl_easy_setopt(hnd, CURLOPT_URL, "https://authentication.proshipping.net/connect/token"); curl_easy_setopt(hnd, CURLOPT_POST, TRUE); headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded"); curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); postfields = curl_slist_append(postfields, "client_id=AAAAAAA"); postfields = curl_slist_append(postfields, "client_secret=BBBBBBBBB"); postfields = curl_slist_append(postfields, "grant_type=client_credentials"); curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, postfields); curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(hnd);
请问如何在libcurl中实现curl --data-urlencode的等价功能?
你的核心错误是误用了curl_slist链表给CURLOPT_POSTFIELDS赋值:CURLOPT_POSTFIELDS需要的是一个URL编码后的字符串(对应application/x-www-form-urlencoded格式),而curl_slist是用于构造multipart/form-data类型表单(对应CURLOPT_HTTPPOST选项)的。服务器收到错误格式的请求,因此返回invalid_client。
要实现--data-urlencode的等价功能,需按以下步骤操作:
对每个参数值进行URL编码:
使用curl_easy_escape函数对参数值进行URL编码,这对应--data-urlencode的编码逻辑,确保特殊字符(如&、=、空格等)被正确转义。拼接成标准的URL编码字符串:
将编码后的参数按key=encoded_value&key2=encoded_value2的格式拼接成完整的POST数据字符串。正确设置
CURLOPT_POSTFIELDS:
将拼接好的字符串传给CURLOPT_POSTFIELDS,而非链表。
修正后的代码片段
替换你原代码中构造postfields链表及设置CURLOPT_POSTFIELDS的部分:
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, FALSE); curl_easy_setopt(hnd, CURLOPT_URL, "https://authentication.proshipping.net/connect/token"); curl_easy_setopt(hnd, CURLOPT_POST, TRUE); // 设置Content-Type头(也可省略,libcurl会自动为urlencoded数据添加该头) headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded"); curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); // 对每个参数值进行URL编码 char *encoded_client_id = curl_easy_escape(hnd, "AAAAAAA", 0); char *encoded_client_secret = curl_easy_escape(hnd, "BBBBBBBBB", 0); char *encoded_grant_type = curl_easy_escape(hnd, "client_credentials", 0); // 拼接POST数据 char post_data[256]; snprintf(post_data, sizeof(post_data), "client_id=%s&client_secret=%s&grant_type=%s", encoded_client_id, encoded_client_secret, encoded_grant_type); // 释放编码后的内存(必须调用curl_free,不能用free) curl_free(encoded_client_id); curl_free(encoded_client_secret); curl_free(encoded_grant_type); // 设置POST数据 curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, post_data); curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(hnd);
关键说明
curl_easy_escape会自动处理参数值中的特殊字符,完全等价于--data-urlencode的编码行为。- 编码后的内存必须用
curl_free释放,不能用标准C的free,避免内存泄漏或错误。 - 如果参数值长度不确定,建议使用动态内存分配(如
asprintf)来拼接POST数据,避免栈溢出风险。
内容的提问来源于stack exchange,提问作者Ian

