Post multipart/form-data 使用 WinHTTP 的 HTTP 请求

Post a multipart/form-data HTTP request with WinHTTP

过去几天我一直在尝试使用 Win32 API 向我的 SpringBoot 应用程序发送 HTTP POST 请求,但我总是收到同样的错误。该请求是一个 multipart,由一个二进制文件和一个 JSON 组成。通过 Postman 发送请求没有问题,我能够正确接收文件和 JSON。

我已经查看了几乎所有 post 以及有关如何使用 WinHTTP 构造 HTTP 请求的问题,它们似乎都与我所做的完全相同,但出于某种原因,我得到了一个奇怪的构造请求,如下面的 WireShark 图片所示。

好像请求没有被识别为几个部分,而是一个数据块。

在 WireShark 中查看由 postman 发送的正确请求,显示请求由所有部分组成,正如它应该的那样。

这是我的代码:

LPCWSTR additionalHeaders = L"Accept: application/json\r\nContent-Type: multipart/form-data; boundary=----------------------------346435246262465368257857\r\n";

if (data->type == RequestType::MULTIPART) {
    size_t filesize = 0;
    char* fileData = fileToString("img.png", "rb", &filesize);

    WinHttpAddRequestHeaders(hRequest, additionalHeaders, -1L, WINHTTP_ADDREQ_FLAG_ADD);
      
    char postData1[] = 
        "----------------------------346435246262465368257857\r\n"
        "Content-Disposition: form-data; name=\"file\"; filename=\"img.png\"\r\n"
        "Content-Type: image/png\r\n\r\n";  
    char postData2[] = 
        "\r\n----------------------------346435246262465368257857\r\n"
        "Content-Disposition: form-data; name=\"newData\"\r\n"
        "Content-Type: application/json\r\n\r\n"
        "{\"dataType\":\"DEVICE_SETTINGS\"}"        
        "\r\n----------------------------346435246262465368257857--\r\n";

     
    if (hRequest)
        bResults = WinHttpSendRequest(hRequest,
            WINHTTP_NO_ADDITIONAL_HEADERS,
            0, WINHTTP_NO_REQUEST_DATA, 0,
            lstrlenA(postData1) + lstrlenA(postData2) + filesize, NULL);

    DWORD dwBytesWritten = 0; 
    if (bResults)
        bResults = WinHttpWriteData(hRequest, postData1, lstrlenA(postData1), &dwBytesWritten);
    if (bResults) 
        bResults = WinHttpWriteData(hRequest,(LPCVOID) fileData, filesize, &dwBytesWritten);
    if (bResults)
        bResults = WinHttpWriteData(hRequest, postData2, lstrlenA(postData2), &dwBytesWritten);
}

errorMessageID = ::GetLastError();

// End the request.
if (bResults)
    bResults = WinHttpReceiveResponse(hRequest, NULL);

Valid request sent with Postman

Invalid request sent with WinHTTP

Postman configuration 1

Postman configuration 2

SpringBoot controller

这是我在 SpringBoot 应用程序日志中收到的错误:

2022-03-04 14:48:34.520 WARN 25412 --- [nio-8010-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]

如果能帮我解开这个谜团,我将不胜感激!

您的 postdata1postdata2 字符串中的 MIME 边界不完整,这就是 WireShark 和 SpringBoot 应用程序无法正确解析您的数据的原因。

正文数据中的每个 MIME 边界必须开始,前导 --后跟 您在Content-Typeboundary 属性, 后跟 尾随 -- 在最终终止边界。

让我们看一个更简单的例子,它根本没有在 boundary 值中使用任何 -,这应该会让你更清楚:

POST /resource HTTP/1.1
Host: ...
Content-Type: multipart/form-data; boundary=myboundary\r\n";

--myboundary
Content-Disposition: form-data; name="file"; filename="img.png"
Content-Type: image/png

<file data>
--myboundary
Content-Disposition: form-data; name="newData"
Content-Type: application/json

<json data>
--myboundary--

正如您在上面看到的,在 Postman 的数据中,前导 -- 出现在每个边界上,但在您的数据中缺失:

  • Postman 的boundary 属性声明了一个以26 领先- 的值,正文数据中的每个边界都以28领先-s.

  • 您的 boundary 属性声明了一个以 28 前导 - 的值,正文数据中的每个边界也以28 领先 -s.

因此,您的数据中每个边界都缺少前导 --

只需从 Content-Typeboundary 属性中的值中删除 2 -s,然后就可以了。