将 WinHttp 转换为 WinInet API POST 请求
Converting WinHttp to WinInet API POST request
我正在尝试将某些 HTTP 请求代码从使用 WinHttp COM 接口转换为使用来自 <wininet.h>
的 lower-level WinInet 调用。 COM 版本正在运行,但我无法将调用转换为 WinInet API。
这段代码工作正常,并得到对 POST 请求的正确错误响应(因为请求数据为空):
#import <winhttpcom.dll>
#include <iostream>
#include <string>
int main()
{
HRESULT hr = CoInitialize(NULL);
using namespace WinHttp;
IWinHttpRequestPtr pReq = NULL;
hr = pReq.CreateInstance(__uuidof(WinHttpRequest));
const char* pszReq = "";
if (SUCCEEDED(hr))
{
_bstr_t bstrMethod("POST");
_bstr_t bstrUrl("https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb9.asmx");
hr = pReq->Open(bstrMethod, bstrUrl);
pReq->SetRequestHeader(_bstr_t("Content-Type"), _bstr_t("text/*"));
_variant_t vReq(pszReq);
hr = pReq->Send(vReq);
if (SUCCEEDED(hr))
{
_bstr_t bstrResp;
hr = pReq->get_ResponseText(&bstrResp.GetBSTR());
if (SUCCEEDED(hr))
{
std::cout << std::string(bstrResp) << "\n";
}
}
}
CoUninitialize();
}
将输出保存为 html,给出响应的呈现(这是我所期望的,因为我没有提供任何请求数据,通常包括访问令牌)。
这是我用来尝试使用 wininet.h 和 low-level Win32 调用复制此结果的代码(在下面的评论后进行了修改,并且应该是可重现的)(我意识到我还没有'关闭手柄)。
#include <windows.h>
#include <WinInet.h>
#include <iostream>
int main()
{
const char* pszReq = "";
const char* pszUrl = "https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb9.asmx";
char szHostName[256];
char szPath[256];
URL_COMPONENTSA comps = {};
comps.dwStructSize = sizeof(comps);
comps.lpszHostName = szHostName;
comps.dwHostNameLength = sizeof(szHostName);
comps.lpszUrlPath = szPath;
comps.dwUrlPathLength = sizeof(szPath);
if (!InternetCrackUrlA(pszUrl, strlen(pszUrl), 0, &comps)) return 1;
HINTERNET hOpen = InternetOpenA("XYZ",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
if (!hOpen) return 1;
HINTERNET hConnect = InternetConnectA(hOpen,szHostName,comps.nPort,
NULL,NULL,INTERNET_SERVICE_HTTP,0,NULL);
if (!hConnect) return 1;
const char * rgpszAcceptTypes[] = { "text/*", NULL };
HINTERNET hOpenReq = HttpOpenRequestA(hConnect,"POST",szPath,NULL, NULL,
rgpszAcceptTypes, 0,NULL);
if (!hOpenReq) return 1;
const char* pszHeader = "Content-Type: text/xml;charset=UTF-8";
//*** This line returns FALSE ***
BOOL bRet = HttpSendRequestA(hOpenReq, pszHeader, strlen(pszHeader), (LPVOID)pszReq, strlen(pszReq));
//*** LastError is ERROR_HTTP_INVALID_SERVER_RESPONSE
DWORD dwErr = GetLastError();
return 0;
}
所有 WinInet 句柄都是 non-zero,表明调用正常,但最后一个 HttpSendRequestA()
立即返回 FALSE,LastError 设置为 ERROR_HTTP_INVALID_SERVER_RESPONSE.
显然 COM 路由隐藏了很多中间工作,并且可能某些常量默认为特定值。我想它可能还会添加其他 header 信息。
也许有人可以指出我哪里出错了?
您的 WinInet 代码中有一些错误:
pszServerName
值本身需要只是主机名,而不是完整的 URL。如果你有一个 URL 作为输入,你可以使用 InternetCrackUrlA()
.
将它解析成它的组成部分
HttpOpenRequestA()
的第三个参数是请求的资源相对于pszServerName
。因此,在您的示例中,您需要使用 "/"
来请求根资源。
HttpSendRequestA()
的第一个参数需要是hOpenReq
,而不是hOpen
。此外,您不应在缓冲区大小中包含 null-terminators。
如果您还没有这样做,您应该看看 HTTP Sessions 上的 WinInet 文档。
话虽如此,试试这个:
#include <windows.h>
#include <WinInet.h>
#include <iostream>
const char * pszUrl = "https://someUrl";
const char * pszReq = "A string of request data";
const char* pszHeader = "Content-Type: text/xml;charset=UTF-8";
char szHostName[256];
char szPath[256];
URL_COMPONENTSA comps = {};
comps.dwStructSize = sizeof(comps);
comps.lpszHostName = szHostName;
comps.dwHostNameLength = sizeof(szHostName);
comps.lpszUrlPath = szPath;
comps.dwUrlPathLength = sizeof(szPath);
BOOL bRet = InternetCrackUrlA(pszUrl, strlen(pszUrl), 0, &comps);
if (!bRet) ...
HINTERNET hOpen = InternetOpenA("XYZ", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hOpen) ...
HINTERNET hConnect = InternetConnectA(hOpen, szHostName, comps.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
if (!hConnect) ...
HINTERNET hOpenReq = HttpOpenRequestA(hConnect, "POST", szPath, NULL, NULL, NULL, comps.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0, NULL);
if (!hOpenReq) ...
bRet = HttpSendRequestA(hOpenReq, pszHeader, strlen(pszHeader), pszReq, strlen(pszReq));
if (!bRet) ...
...
我正在尝试将某些 HTTP 请求代码从使用 WinHttp COM 接口转换为使用来自 <wininet.h>
的 lower-level WinInet 调用。 COM 版本正在运行,但我无法将调用转换为 WinInet API。
这段代码工作正常,并得到对 POST 请求的正确错误响应(因为请求数据为空):
#import <winhttpcom.dll>
#include <iostream>
#include <string>
int main()
{
HRESULT hr = CoInitialize(NULL);
using namespace WinHttp;
IWinHttpRequestPtr pReq = NULL;
hr = pReq.CreateInstance(__uuidof(WinHttpRequest));
const char* pszReq = "";
if (SUCCEEDED(hr))
{
_bstr_t bstrMethod("POST");
_bstr_t bstrUrl("https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb9.asmx");
hr = pReq->Open(bstrMethod, bstrUrl);
pReq->SetRequestHeader(_bstr_t("Content-Type"), _bstr_t("text/*"));
_variant_t vReq(pszReq);
hr = pReq->Send(vReq);
if (SUCCEEDED(hr))
{
_bstr_t bstrResp;
hr = pReq->get_ResponseText(&bstrResp.GetBSTR());
if (SUCCEEDED(hr))
{
std::cout << std::string(bstrResp) << "\n";
}
}
}
CoUninitialize();
}
将输出保存为 html,给出响应的呈现(这是我所期望的,因为我没有提供任何请求数据,通常包括访问令牌)。
这是我用来尝试使用 wininet.h 和 low-level Win32 调用复制此结果的代码(在下面的评论后进行了修改,并且应该是可重现的)(我意识到我还没有'关闭手柄)。
#include <windows.h>
#include <WinInet.h>
#include <iostream>
int main()
{
const char* pszReq = "";
const char* pszUrl = "https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb9.asmx";
char szHostName[256];
char szPath[256];
URL_COMPONENTSA comps = {};
comps.dwStructSize = sizeof(comps);
comps.lpszHostName = szHostName;
comps.dwHostNameLength = sizeof(szHostName);
comps.lpszUrlPath = szPath;
comps.dwUrlPathLength = sizeof(szPath);
if (!InternetCrackUrlA(pszUrl, strlen(pszUrl), 0, &comps)) return 1;
HINTERNET hOpen = InternetOpenA("XYZ",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
if (!hOpen) return 1;
HINTERNET hConnect = InternetConnectA(hOpen,szHostName,comps.nPort,
NULL,NULL,INTERNET_SERVICE_HTTP,0,NULL);
if (!hConnect) return 1;
const char * rgpszAcceptTypes[] = { "text/*", NULL };
HINTERNET hOpenReq = HttpOpenRequestA(hConnect,"POST",szPath,NULL, NULL,
rgpszAcceptTypes, 0,NULL);
if (!hOpenReq) return 1;
const char* pszHeader = "Content-Type: text/xml;charset=UTF-8";
//*** This line returns FALSE ***
BOOL bRet = HttpSendRequestA(hOpenReq, pszHeader, strlen(pszHeader), (LPVOID)pszReq, strlen(pszReq));
//*** LastError is ERROR_HTTP_INVALID_SERVER_RESPONSE
DWORD dwErr = GetLastError();
return 0;
}
所有 WinInet 句柄都是 non-zero,表明调用正常,但最后一个 HttpSendRequestA()
立即返回 FALSE,LastError 设置为 ERROR_HTTP_INVALID_SERVER_RESPONSE.
显然 COM 路由隐藏了很多中间工作,并且可能某些常量默认为特定值。我想它可能还会添加其他 header 信息。
也许有人可以指出我哪里出错了?
您的 WinInet 代码中有一些错误:
将它解析成它的组成部分pszServerName
值本身需要只是主机名,而不是完整的 URL。如果你有一个 URL 作为输入,你可以使用InternetCrackUrlA()
.HttpOpenRequestA()
的第三个参数是请求的资源相对于pszServerName
。因此,在您的示例中,您需要使用"/"
来请求根资源。HttpSendRequestA()
的第一个参数需要是hOpenReq
,而不是hOpen
。此外,您不应在缓冲区大小中包含 null-terminators。
如果您还没有这样做,您应该看看 HTTP Sessions 上的 WinInet 文档。
话虽如此,试试这个:
#include <windows.h>
#include <WinInet.h>
#include <iostream>
const char * pszUrl = "https://someUrl";
const char * pszReq = "A string of request data";
const char* pszHeader = "Content-Type: text/xml;charset=UTF-8";
char szHostName[256];
char szPath[256];
URL_COMPONENTSA comps = {};
comps.dwStructSize = sizeof(comps);
comps.lpszHostName = szHostName;
comps.dwHostNameLength = sizeof(szHostName);
comps.lpszUrlPath = szPath;
comps.dwUrlPathLength = sizeof(szPath);
BOOL bRet = InternetCrackUrlA(pszUrl, strlen(pszUrl), 0, &comps);
if (!bRet) ...
HINTERNET hOpen = InternetOpenA("XYZ", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (!hOpen) ...
HINTERNET hConnect = InternetConnectA(hOpen, szHostName, comps.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
if (!hConnect) ...
HINTERNET hOpenReq = HttpOpenRequestA(hConnect, "POST", szPath, NULL, NULL, NULL, comps.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_FLAG_SECURE : 0, NULL);
if (!hOpenReq) ...
bRet = HttpSendRequestA(hOpenReq, pszHeader, strlen(pszHeader), pszReq, strlen(pszReq));
if (!bRet) ...
...