无法使用 wininet 获取 POST 请求响应

Posted

技术标签:

【中文标题】无法使用 wininet 获取 POST 请求响应【英文标题】:Unable to obtain POST request response using wininet 【发布时间】:2017-12-19 18:06:26 【问题描述】:

我需要向 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 响应。

谁能指出我做错了什么,以及如何解决它?

【问题讨论】:

您没有对任何 API 调用进行任何错误处理。其中任何一个都可能失败,而您不会知道。另外,您可以使用HttpQueryInfo() 获取服务器的响应代码,以确保在读取响应数据之前请求确实成功。 同意,我确实添加了一个GetLastError() 调用,发现它在发送请求本身时失败了。 【参考方案1】:

InternetConnect 需要服务器名称或 IP 地址,因此请不要在地址中包含 "http://"。改为:

InternetConnect(handle, L"freecite.library.brown.edu"...);

data 使用 UTF-8。 WinAPI 函数的其他参数正确使用 UTF-16,它们会自动进行必要的转换。

更改标题:

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

下面的例子应该返回 XML。

注意,为简单起见,我将optional 设为ANSI 字符串,但它应该是UTF8,然后将其转换为UTF16 woptional 并发送。 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;

【讨论】:

工作就像一个绝对的魅力!非常感谢!你能解释一下在开头添加http://有什么问题吗? InternetConnect 需要主机名或 IP 地址。而InternetOpen 需要完整的网址。 啊。知道了。谢谢!

以上是关于无法使用 wininet 获取 POST 请求响应的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中使用 Wininet 发送 POST 请求时的编码问题

wininet 或 winhttp,这是 POST 请求的首选

使用wininet的异步请求

在 vc++ 代码中使用 wininet Api 发出 json 发布请求时得到空白响应

Python请求 - 无法获得POST表单响应(仅返回表单)

无法从 axios POST 请求中获得响应(React)