如何使用 WinInet api 在 Delphi 中发送 HTTP POST 请求

问题描述 投票:0回答:4

我正在尝试使用 WinInet 函数从 Delphi 发出 HTTP 请求。

到目前为止我已经:

function request:string;
var
  hNet,hURL,hRequest: HINTERNET;
begin
  hNet := InternetOpen(PChar('User Agent'),INTERNET_OPEN_TYPE_PRECONFIG or INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(hNet) then 
  begin
  try
    hURL := InternetConnect(hNet,PChar('http://example.com'),INTERNET_DEFAULT_HTTP_PORT,nil,nil,INTERNET_SERVICE_HTTP,0,DWORD(0));
    if(hURL<>nil) then
      hRequest := HttpOpenRequest(hURL, 'POST', PChar('param=value'),'HTTP/1.0',PChar(''), nil, INTERNET_FLAG_RELOAD or INTERNET_FLAG_PRAGMA_NOCACHE,0);
    if(hRequest<>nil) then
      HttpSendRequest(hRequest, nil, 0, nil, 0);
    InternetCloseHandle(hNet);
  except
    on E : Exception do
      ShowMessage(E.ClassName+' error raised, with message : '+E.Message);
  end;
  end
end;

但这没有任何作用(我正在嗅探网络http流量以查看它是否有效)。 我已成功使用 InternetOpenURL,但我还需要发送 POST 请求,而该函数无法执行此操作。

有人可以给我看一个简单的例子吗?我想要的结果是在 var 中获取 http 响应页面作为字符串。

delphi http post request wininet
4个回答
9
投票

我把之前的代码中的所有 url/文件名部分弄乱了。我现在正在使用 Jeff DeVore 的这个 ,效果很好:

function request(const AUrl, AData: AnsiString; blnSSL: Boolean = True): AnsiString; var aBuffer : Array[0..4096] of Char; Header : TStringStream; BufStream : TMemoryStream; sMethod : AnsiString; BytesRead : Cardinal; pSession : HINTERNET; pConnection : HINTERNET; pRequest : HINTERNET; parsedURL : TStringArray; port : Integer; flags : DWord; begin ParsedUrl := ParseUrl(AUrl); Result := ''; pSession := InternetOpen(nil, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); if Assigned(pSession) then try if blnSSL then Port := INTERNET_DEFAULT_HTTPS_PORT else Port := INTERNET_DEFAULT_HTTP_PORT; pConnection := InternetConnect(pSession, PChar(ParsedUrl[0]), port, nil, nil, INTERNET_SERVICE_HTTP, 0, 0); if Assigned(pConnection) then try if (AData = '') then sMethod := 'GET' else sMethod := 'POST'; if blnSSL then flags := INTERNET_FLAG_SECURE or INTERNET_FLAG_KEEP_CONNECTION else flags := INTERNET_SERVICE_HTTP; pRequest := HTTPOpenRequest(pConnection, PChar(sMethod), PChar(ParsedUrl[1]), nil, nil, nil, flags, 0); if Assigned(pRequest) then try Header := TStringStream.Create(''); try with Header do begin WriteString('Host: ' + ParsedUrl[0] + sLineBreak); WriteString('User-Agent: Custom program 1.0'+SLineBreak); WriteString('Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'+SLineBreak); WriteString('Accept-Language: en-us,en;q=0.5' + SLineBreak); WriteString('Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7'+SLineBreak); WriteString('Keep-Alive: 300'+ SLineBreak); WriteString('Connection: keep-alive'+ SlineBreak+SLineBreak); end; HttpAddRequestHeaders(pRequest, PChar(Header.DataString), Length(Header.DataString), HTTP_ADDREQ_FLAG_ADD); if HTTPSendRequest(pRequest, nil, 0, Pointer(AData), Length(AData)) then begin BufStream := TMemoryStream.Create; try while InternetReadFile(pRequest, @aBuffer, SizeOf(aBuffer), BytesRead) do begin if (BytesRead = 0) then Break; BufStream.Write(aBuffer, BytesRead); end; aBuffer[0] := #0; BufStream.Write(aBuffer, 1); Result := PChar(BufStream.Memory); finally BufStream.Free; end; end; finally Header.Free; end; finally InternetCloseHandle(pRequest); end; finally InternetCloseHandle(pConnection); end; finally InternetCloseHandle(pSession); end; end;

ParseUrl 是一个将 URL 拆分为“主机名/文件名”的函数,TStringArray 是一个字符串数组。明天我仍然需要检查代码,但它看起来很好,并且在我的嗅探器中我看到了正在发送的帖子数据和标头。


2
投票
我个人更喜欢使用

synapse 库来完成我的所有 TCP/IP 工作。例如,一个简单的 HTTP post 可以编码为:

uses httpsend; function testpost; begin stm := tStringstream.create('param=value'); try HttpPostBinary('http://example.com',Stm); finally stm.free; end; end;

该库编写得很好,并且很容易修改以满足您的特定要求。最新的 subversion 版本在 Delphi 2009 和 Delphi 2010 上都可以正常工作。该框架不是基于组件的,而是一系列在多线程环境中运行良好的类和过程。


1
投票

HttpOpenRequest

的第三个参数(lpszObjectName)应该是您要请求的URL。这就是为什么文档将第五个参数 (lpszReferer) 描述为“指向以 null 结尾的字符串的指针,该字符串指定从中获取请求 (lpszObjectName) 中的 URL 的文档的 URL。”

发布的数据通过

HttpSendRequest

 发送; lpOptional参数的描述如下:

指向缓冲区的指针,其中包含要在请求标头之后立即发送的任何可选数据。该参数一般用于POST和PUT操作。可选数据可以是发布到服务器的资源或信息。如果没有可选数据要发送,则此参数可以为 NULL。

InternetOpen

的第二个参数应该是只是服务器名称;它不应该包括协议。您使用第六个参数指定的协议。

发送请求后,您可以使用

InternetReadFile

InternetQueryDataAvailable
 阅读回复。

不要只检查 API 函数是否返回零,然后继续下一行。如果失败,请致电

GetLastError

 了解原因。您发布的代码不会引发异常,因此捕获任何异常都是徒劳的。 (无论如何,按照你这样做的方式“处理”它们是愚蠢的。不要捕获你还不知道如何修复的异常。让其他一切都交给调用者,或者调用者的调用者,等等.)


0
投票
您可以使用带有 OleObject 和 OleVariants 的后期绑定:WinHttp.WinHttpRequest

https://api-ninjas.com/api/textsimilarity

function TextSimilarity_API6(AURL, url_name, aApikey: string): string; var httpReq,hr: Olevariant; JPostdatabody: string; jo: TJSON; begin Result:= ''; JPostDataBody:= '{' + '"text_1": "gpt-3.5-turbo-instruct",'+ //'"prompt": "%s",'+ '"text_2": "gpt-3.5-turbo-instruct777"'+ '}'; // Use JSON for REST API calls and set API KEY via requestheader httpReq:= CreateOleObject('WinHttp.WinHttpRequest.5.1'); jo:= TJSON.Create(); try hr:= httpReq.Open('POST', AURL, false); httpReq.setRequestheader('user-agent',SUSERAGENT); // Select HTTPS POST method, set POST data, specify endpoint URL httpReq.setRequestheader('content-type','application/json'); httpReq.setRequestheader('X-Api-Key',aApikey); if hr= S_OK then HttpReq.Send(JPostDataBody); If HttpReq.Status = 200 Then result:= HttpReq.responseText Else result:= 'Failed getresponse:'+itoa(HttpReq.Status)+HttpReq.responseText; // writeln('debug response '+HttpReq.GetAllResponseHeaders); // Process returned JSON when request was successful jo.parse(result) result:='Answer2similarity: '+jo.values['similarity'].asstring; finally jo.Free; httpreq:= unassigned; end; end;
    
© www.soinside.com 2019 - 2024. All rights reserved.