无法使用 wininet 获取 POST 请求响应
Unable to obtain POST request response using wininet
我需要向 API 发出 POST 请求以获取一些 XML 数据 (http://freecite.library.brown.edu/welcome/api_instructions)。这适用于 curl
:
curl -H "Accept: application/xml" --data "citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein, A.M., Pashos, C.L., Newhouse, J.P. and McNeil, B.J. Acute Myocardial Infarction in the Medicare population: process of care and clinical outcomes. Journal of the American Medical Association, 1992; 18:2530-2536. " http://freecite.library.brown.edu:80/citations/create
所以我正在尝试使用 Win32 SDK 做类似的事情。这是我的代码:
void LoadData()
{
wil::unique_hinternet hInternet(InternetOpen(L"Dummy", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0));
wil::unique_hinternet hConnect(InternetConnect(hInternet.get(), L"http://freecite.library.brown.edu", 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0));
wil::unique_hinternet hRequest(HttpOpenRequest(hConnect.get(), L"POST", L"/citations/create", NULL, NULL, NULL, NULL, NULL));
wstring data = L"citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein, A.M., Pashos, C.L., Newhouse, J.P. and McNeil, B.J. Acute Myocardial Infarction in the Medicare population: process of care and clinical outcomes. Journal of the American Medical Association, 1992; 18:2530-2536.";
PCWSTR szHeaders = L"Accept: application/xml";
HttpSendRequest(hRequest.get(), szHeaders, 0, (LPVOID)data.c_str(), static_cast<int>(data.length()));
DWORD availableBytes = 0;
InternetQueryDataAvailable(hRequest.get(), &availableBytes, 0, 0);
PBYTE outputBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, availableBytes);
PBYTE nextBytes = outputBuffer;
DWORD bytesUsed = 0; // number of used bytes.
while (availableBytes)
{
DWORD downloadedBytes;
InternetReadFile(hRequest.get(), nextBytes, availableBytes, &downloadedBytes);
bytesUsed = bytesUsed + downloadedBytes;
InternetQueryDataAvailable(hRequest.get(), &availableBytes, 0, 0);
if (availableBytes > 0)
{
// lazy buffer growth here. Only alloc for what we need. could be optimized if we end up with huge payloads (>10MB).
// otherwise, this is good enough.
outputBuffer = (PBYTE)HeapReAlloc(GetProcessHeap(), 0, outputBuffer, bytesUsed + availableBytes);
nextBytes = outputBuffer + bytesUsed; // careful, pointer might have moved! Update cursor.
}
}
// Convert outputed XML to wide char
int size_needed = MultiByteToWideChar(CP_UTF8, 0, (PCCH)outputBuffer, bytesUsed, NULL, 0);
std::wstring wstrTo(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, (PCCH)outputBuffer, bytesUsed, &wstrTo[0], size_needed);
wstring res = wstrTo;
}
问题是,在进入for
循环之前,即使在调用InternetQueryDataAvailable
之后,availableBytes
也是0。结果,我最终得到了一个空白字符串作为响应,而我期待的是 XML 响应。
谁能指出我做错了什么,以及如何解决?
InternetConnect
需要服务器名称或 IP 地址,因此不要在地址中包含 "http://"
。更改为:
InternetConnect(handle, L"freecite.library.brown.edu"...);
data
使用 UTF-8。 WinAPI 函数的其他参数正确使用 UTF-16,它们会自动进行必要的转换。
更改header:
std::wstring szHeaders = L"Content-Type: application/x-www-form-urlencoded\r\n";
accept
应通过 HttpOpenRequest
发送
const wchar_t *accept[] = { L"text/xml", NULL };
HINTERNET hrequest = HttpOpenRequest(hconnect, L"POST", L"/citations/create",
NULL, NULL, accept, 0, 0);
请注意,如果您不指定 accept
(使用 NULL
代替),那么结果可以是 html.
下面的例子应该return XML.
注意,为了简单起见,我把optional
作为ANSI字符串,但它应该是UTF8,然后你把它转换成UTF16woptional
并发送。 result
将是 UTF8 字符串,需要将其转换为 UTF16 以供 Windows 显示。
#include <iostream>
#include <string>
#include <Windows.h>
#include <WinINet.h>
#pragma comment(lib, "wininet.lib")//include WinINet library
int main()
{
std::string result;
std::wstring server = L"freecite.library.brown.edu";
std::wstring objectname = L"/citations/create"; //file in this case!
std::wstring header = L"Content-Type: application/x-www-form-urlencoded\r\n";
std::string optional = "citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein";
HINTERNET hsession = InternetOpen(L"appname",
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hconnect = InternetConnect(hsession, server.c_str(),
INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
const wchar_t* accept[] = { L"text/xml", NULL };
HINTERNET hrequest = HttpOpenRequest(hconnect, L"POST", objectname.c_str(),
NULL, NULL, accept, 0, 0);
if(HttpSendRequest(hrequest, header.c_str(), header.size(),
&optional[0], optional.size()))
{
DWORD blocksize = 4096;
DWORD received = 0;
std::string block(blocksize, 0);
while (InternetReadFile(hrequest, &block[0], blocksize, &received)
&& received)
{
block.resize(received);
result += block;
}
std::cout << result << std::endl;
}
if (hrequest) InternetCloseHandle(hrequest);
if (hconnect) InternetCloseHandle(hconnect);
if (hsession) InternetCloseHandle(hsession);
return 0;
}
我需要向 API 发出 POST 请求以获取一些 XML 数据 (http://freecite.library.brown.edu/welcome/api_instructions)。这适用于 curl
:
curl -H "Accept: application/xml" --data "citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein, A.M., Pashos, C.L., Newhouse, J.P. and McNeil, B.J. Acute Myocardial Infarction in the Medicare population: process of care and clinical outcomes. Journal of the American Medical Association, 1992; 18:2530-2536. " http://freecite.library.brown.edu:80/citations/create
所以我正在尝试使用 Win32 SDK 做类似的事情。这是我的代码:
void LoadData()
{
wil::unique_hinternet hInternet(InternetOpen(L"Dummy", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0));
wil::unique_hinternet hConnect(InternetConnect(hInternet.get(), L"http://freecite.library.brown.edu", 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0));
wil::unique_hinternet hRequest(HttpOpenRequest(hConnect.get(), L"POST", L"/citations/create", NULL, NULL, NULL, NULL, NULL));
wstring data = L"citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein, A.M., Pashos, C.L., Newhouse, J.P. and McNeil, B.J. Acute Myocardial Infarction in the Medicare population: process of care and clinical outcomes. Journal of the American Medical Association, 1992; 18:2530-2536.";
PCWSTR szHeaders = L"Accept: application/xml";
HttpSendRequest(hRequest.get(), szHeaders, 0, (LPVOID)data.c_str(), static_cast<int>(data.length()));
DWORD availableBytes = 0;
InternetQueryDataAvailable(hRequest.get(), &availableBytes, 0, 0);
PBYTE outputBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, availableBytes);
PBYTE nextBytes = outputBuffer;
DWORD bytesUsed = 0; // number of used bytes.
while (availableBytes)
{
DWORD downloadedBytes;
InternetReadFile(hRequest.get(), nextBytes, availableBytes, &downloadedBytes);
bytesUsed = bytesUsed + downloadedBytes;
InternetQueryDataAvailable(hRequest.get(), &availableBytes, 0, 0);
if (availableBytes > 0)
{
// lazy buffer growth here. Only alloc for what we need. could be optimized if we end up with huge payloads (>10MB).
// otherwise, this is good enough.
outputBuffer = (PBYTE)HeapReAlloc(GetProcessHeap(), 0, outputBuffer, bytesUsed + availableBytes);
nextBytes = outputBuffer + bytesUsed; // careful, pointer might have moved! Update cursor.
}
}
// Convert outputed XML to wide char
int size_needed = MultiByteToWideChar(CP_UTF8, 0, (PCCH)outputBuffer, bytesUsed, NULL, 0);
std::wstring wstrTo(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, (PCCH)outputBuffer, bytesUsed, &wstrTo[0], size_needed);
wstring res = wstrTo;
}
问题是,在进入for
循环之前,即使在调用InternetQueryDataAvailable
之后,availableBytes
也是0。结果,我最终得到了一个空白字符串作为响应,而我期待的是 XML 响应。
谁能指出我做错了什么,以及如何解决?
InternetConnect
需要服务器名称或 IP 地址,因此不要在地址中包含 "http://"
。更改为:
InternetConnect(handle, L"freecite.library.brown.edu"...);
data
使用 UTF-8。 WinAPI 函数的其他参数正确使用 UTF-16,它们会自动进行必要的转换。
更改header:
std::wstring szHeaders = L"Content-Type: application/x-www-form-urlencoded\r\n";
accept
应通过 HttpOpenRequest
const wchar_t *accept[] = { L"text/xml", NULL };
HINTERNET hrequest = HttpOpenRequest(hconnect, L"POST", L"/citations/create",
NULL, NULL, accept, 0, 0);
请注意,如果您不指定 accept
(使用 NULL
代替),那么结果可以是 html.
下面的例子应该return XML.
注意,为了简单起见,我把optional
作为ANSI字符串,但它应该是UTF8,然后你把它转换成UTF16woptional
并发送。 result
将是 UTF8 字符串,需要将其转换为 UTF16 以供 Windows 显示。
#include <iostream>
#include <string>
#include <Windows.h>
#include <WinINet.h>
#pragma comment(lib, "wininet.lib")//include WinINet library
int main()
{
std::string result;
std::wstring server = L"freecite.library.brown.edu";
std::wstring objectname = L"/citations/create"; //file in this case!
std::wstring header = L"Content-Type: application/x-www-form-urlencoded\r\n";
std::string optional = "citation=Udvarhelyi, I.S., Gatsonis, C.A., Epstein";
HINTERNET hsession = InternetOpen(L"appname",
INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hconnect = InternetConnect(hsession, server.c_str(),
INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
const wchar_t* accept[] = { L"text/xml", NULL };
HINTERNET hrequest = HttpOpenRequest(hconnect, L"POST", objectname.c_str(),
NULL, NULL, accept, 0, 0);
if(HttpSendRequest(hrequest, header.c_str(), header.size(),
&optional[0], optional.size()))
{
DWORD blocksize = 4096;
DWORD received = 0;
std::string block(blocksize, 0);
while (InternetReadFile(hrequest, &block[0], blocksize, &received)
&& received)
{
block.resize(received);
result += block;
}
std::cout << result << std::endl;
}
if (hrequest) InternetCloseHandle(hrequest);
if (hconnect) InternetCloseHandle(hconnect);
if (hsession) InternetCloseHandle(hsession);
return 0;
}