基于WinINet实现的网络库中有趣知识点收集(2) - 请求头部 Content-Type 与请求数据主体 lpOptional 的兼容性

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. 常见的编码方式

  1. application/x-www-form-urlencoded: 这是默认的编码方式,通常用于发送表单数据。数据会被 URL 编码,以键值对的形式传递。

  2. multipart/form-data: 用于上传文件或二进制数据。数据被分成多个部分,每个部分都包含数据以及相关的元数据。

  3. application/json: 如果你要发送 JSON 数据,可以使用这种编码方式。

  4. text/plain: 纯文本方式,适用于发送普通文本数据。

  5. 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 并比较一下如果没有将传入的双字节数据转换为单字节的结果会如何?

图3-1 表单提交数据设置为单字节效果
图3-2 lpszRequestData双字节内容转换为pUTF8URL单字节内容
其中 0x00848DF0 对应 lpszRequestData 指向的内容,0x008BEED8 对应 pUTF8URL 指向的内容
​​
图3-3 表单数据不经过处理,直接是双字节的结果
肉眼可见 Fiddler 上的字符显示就已经每隔一个字符带了一个空格

 

4. 总结        

        可见如果没有按照约定的编码格式进行传输,服务器端显然就不知道该怎么解析请求数据。这类问题产生的原因非常简单,但在实际软件生产环境中却千变万化,隐藏很深,在研发和测试过程难以全部覆盖。根据笔者的经验,存在于产线中大量的前后端不兼容问题,包括我们经常见于前端的字符显示乱码问题,本质上基本都是由各模块间编解码和数据格式不兼容造成的。后续笔者将给出一些亲历的相关案例,包括解决方法、测试方法以及如何从源头尽量杜绝的策略,供大家参考。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值