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]
如果能帮我解开这个谜团,我将不胜感激!
您的 postdata1
和 postdata2
字符串中的 MIME 边界不完整,这就是 WireShark 和 SpringBoot 应用程序无法正确解析您的数据的原因。
正文数据中的每个 MIME 边界必须开始,前导 --
,后跟 您在Content-Type
的 boundary
属性, 后跟 尾随 --
在最终终止边界。
让我们看一个更简单的例子,它根本没有在 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-Type
的 boundary
属性中的值中删除 2 -
s,然后就可以了。
过去几天我一直在尝试使用 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]
如果能帮我解开这个谜团,我将不胜感激!
您的 postdata1
和 postdata2
字符串中的 MIME 边界不完整,这就是 WireShark 和 SpringBoot 应用程序无法正确解析您的数据的原因。
正文数据中的每个 MIME 边界必须开始,前导 --
,后跟 您在Content-Type
的 boundary
属性, 后跟 尾随 --
在最终终止边界。
让我们看一个更简单的例子,它根本没有在 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-Type
的 boundary
属性中的值中删除 2 -
s,然后就可以了。