I have been trying for the past few days to send an HTTP POST
request to my SpringBoot application with the Win32 API, but I'm always receiving the same error. The request is a multipart
consisting of a binary file and a JSON. Sending the request via Postman works with no problems, and I'm able to receive the file and the JSON correctly.
I have looked at almost every post and question regarding how to construct an HTTP request with WinHTTP, and it seems they all do exactly what I did, but for some reason I'm getting a weirdly constructed request, as seen in the WireShark image below.
It seems as if the request is not recognized as several parts, but rather as one chunk of data.
Looking in WireShark at the correct request which was sent with postman, shows that the request consists of all parts, just as it supposed to.
Here is my code:
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
Here is the error I receive in my SpringBoot app log:
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]
I would really appreciate any help here solving this mystery!
The MIME boundaries in your postdata1
and postdata2
strings are incomplete, which is why WireShark and the SpringBoot app are not parsing your data correctly.
Every MIME boundary in the body data must start with a leading --
, followed by the value you specified in the Content-Type
's boundary
attribute, followed by a trailing --
in the final termination boundary.
Let's look at an simpler example that doesn't use any -
in the boundary
value at all, this should make it clearer to you:
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--
As you can see above, and in Postman's data, the leading --
is present on each boundary, but is missing in your data:
Postman's boundary
attribute declares a value with 26 leading -
s, and each boundary in the body data begins with 28 leading -
s.
Your boundary
attribute declares a value with 28 leading -
s, and each boundary in the body data also begins with 28 leading -
s.
Hence, the leading --
is missing from each boundary in your data.
Simply remove 2 -
s from the value in your Content-Type
's boundary
attribute, and then you should be fine.