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

我正在尝试使用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; 

但是这并没有做任何事情(我嗅探networkinghttpstream量,看看它是否工作)。 我已经成功地使用InternetOpenURL,但我也需要发送POST请求,该function不这样做。

有人能给我看一个简单的例子吗? 我想要的结果是得到一个var作为string的http响应页面。

我得到了所有的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是一个string数组。 我仍然必须明天检查代码,但看起来很好,在我的嗅探器中,我看到了发送的数据和头文件。

就我个人而言,我更喜欢为我所有的TCP / IP工作使用突触库。 例如,一个简单的HTTPpost可以被编码为:

 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都没有任何问题。这个框架不是基于组件的,而是一系列在multithreading环境下的类和过程。

HttpOpenRequest的第三个参数(lpszObjectName)应该是你希望请求的URL 。 这就是为什么文档将第五个参数(lpszReferer)描述为“指向空终止string的指针,该string指定了请求中的URL(lpszObjectName)从中获得的文档的URL。

发布的数据通过HttpSendRequest发送; lpOptional参数是这样描述的:

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

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

发送请求后,可以使用InternetReadFileInternetQueryDataAvailable读取响应。

不要只检查API函数是否返回零,然后继续下一行。 如果失败,请调用GetLastError来找出原因。 你发布的代码不会引发exception,所以捕捉任何代码都是徒劳的。 (而且,按照你这样做的方式“处理”它是愚蠢的,不要抓住你不知道如何解决的exception,让其他所有的东西去找调用者或者调用者的调用者等等。)