1. 回顾 HttpSendRequest
之前我们曾介绍过 HttpSendRequest
是 Windows 中 wininet.h
头文件中定义的一个函数,用于向服务器发送 HTTP 请求,并接收服务器的响应。
BOOL HttpSendRequest(
HINTERNET hRequest, //指向已经通过 HttpOpenRequest 函数创建的 HTTP 请求句柄。
LPCTSTR lpszHeaders, //字符串,包含要发送的 HTTP 请求头部。
DWORD dwHeadersLength, //lpszHeaders 字符串的长度。
LPVOID lpOptional, //可选的请求主体数据,例如 POST 请求的内容。
DWORD dwOptionalLength //lpOptional 数据的长度。
);
lpOptional
参数本质上是一个指向数据缓冲区的指针。这些数据通常用于 POST 请求,用来传递表单数据、文件等内容。在使用这个参数时,需要确保正确设置缓冲区的内容以及大小(dwOptionalLength),以便正确发送所需的数据。在设置请求头部时(lpszHeaders),也需要设置相匹配的 Content-Type
头字段来指定传递的数据编码方式。这个字段告诉服务器请求体(正文)的编码方式,以便服务器正确解析请求的数据。总之,lpszHeaders 告诉服务器怎么读数据。lpOptional 告诉服务器读什么数据。
2. 常见的编码方式
-
application/x-www-form-urlencoded: 这是默认的编码方式,通常用于发送表单数据。数据会被 URL 编码,以键值对的形式传递。
-
multipart/form-data: 用于上传文件或二进制数据。数据被分成多个部分,每个部分都包含数据以及相关的元数据。
-
application/json: 如果你要发送 JSON 数据,可以使用这种编码方式。
-
text/plain: 纯文本方式,适用于发送普通文本数据。
-
application/xml: 如果你要发送 XML 数据,可以使用这种编码方式。
3. 如何发送 POST 请求
POST https://alpha.test.com/authorization/oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
User-Agent: Integration Http Test Agent - Win/23.8.21
Host: alpha.test.com
Content-Length: 118
Connection: Keep-Alive
Cookie: trackingSessionID=TESTWAS_A69B83C4CA724F6AADFAD06CB0160187
grant_type=refresh_token&client_id=de5e8736-5056-44ab-82be-c29acf2606b7&refresh_token=alpha_09uvDBXgxdDwt5XuO1C1P2cbDA
其中 Content-Type: application/x-www-form-urlencoded 就是由 lpszHeaders 参数传入的。application/x-www-form-urlencoded:这种类型要求使用单字节数据。它是将表单数据编码为键值对的形式,使用 URL 编码进行传输。其实更标准的格式应该是 application/x-www-form-urlencoded;charset=UTF-8,只不过大多数情况下服务器会默认使用 UTF-8 编码来解析 application/x-www-form-urlencoded 类型的请求数据。
void URLObject::SendRequest(LPCTSTR lpszRequestData)
{
LPCTSTR lpszHeader = _T("Content-Type: application/x-www-form-urlencoded\r\n");
char* pUTF8URL = NULL;
int nLen = 0;
if (lpszRequestData != NULL && lpszRequestData[0] != _T('\0'))
{
nLen = ::WideCharToMultiByte(m_uCodePage,
0, lpszRequestData, -1, NULL, 0, NULL, NULL);
if (nLen > 0)
{
pUTF8URL = new char[nLen];
if (NULL == pUTF8URL)
{
return;
}
SecureZeroMemory(pUTF8URL, sizeof(char) * nLen);
nLen = ::WideCharToMultiByte(m_uCodePage,
0, lpszRequestData, -1, pUTF8URL, nLen, NULL, NULL);
}
}
HttpSendRequest(m_hRequest,
lpszHeader,
_tcslen(lpszHeader),
(LPVOID)pUTF8URL,
nLen);
if (pUTF8URL != NULL)
{
delete[] pUTF8URL;
pUTF8URL = NULL;
}
return;
}
以上是一个封装了 HttpSendRequest 的函数举例,其中作为函数参数传入的 lpszRequestData 指向双字节数据。我们试着 debug 并比较一下如果没有将传入的双字节数据转换为单字节的结果会如何?
4. 总结
可见如果没有按照约定的编码格式进行传输,服务器端显然就不知道该怎么解析请求数据。这类问题产生的原因非常简单,但在实际软件生产环境中却千变万化,隐藏很深,在研发和测试过程难以全部覆盖。根据笔者的经验,存在于产线中大量的前后端不兼容问题,包括我们经常见于前端的字符显示乱码问题,本质上基本都是由各模块间编解码和数据格式不兼容造成的。后续笔者将给出一些亲历的相关案例,包括解决方法、测试方法以及如何从源头尽量杜绝的策略,供大家参考。