2009-12-01 192 views
13

我想使用WinInet函數從Delphi發出HTTP請求。如何使用WinInet API在Delphi中發送HTTP POST請求

到目前爲止,我有:

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響應頁面。

回答

9

我把所有的URL /文件名部分搞砸了與以前的代碼。現在,我使用這個from 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是一個字符串數組。我仍然必須明天檢查代碼,但看起來很好,在我的嗅探器中,我看到了發送的數據和標題。

+3

我發現你的代碼的來源給你們,你們可以舉出原作者。我更改了代碼以刪除佈景錯誤的布爾語句,並且我更改了引用者,所以它不會撒謊並聲稱是Firefox 3.0.10。 – 2009-12-01 19:11:39

+1

我找不到包含ParseURL的單元,或者它可能是一個自定義例程。代碼位於:http://delphi.pastebin.com/f1ea3a752有些不同,不會使用ParseURL。 – lkessler 2010-02-14 23:18:40

+0

如何將自定義標題值添加到發佈請求中? – hikari 2016-04-12 23:24:31

1

第三個參數(lpszObjectName)到HttpOpenRequest應該是您要請求的網址。這就是爲什麼文檔將第五個參數(lpszReferer)描述爲「一個指向以null結尾的字符串的指針,該字符串指定從中獲取請求中的URL(lpszObjectName)的文檔的URL。」

張貼的資料與HttpSendRequest發送; lpOptional參數如下所述:

指向緩衝區的指針,該緩衝區包含要在請求標頭後立即發送的任何可選數據。該參數通常用於POST和PUT操作。可選數據可以是發佈到服務器的資源或信息。如果沒有可選數據要發送,則此參數可以爲NULL。

InternetOpen的第二個參數應該是只是服務器名稱;它不應該包括協議。您使用第六個參數指定的協議。

你發送的請求後,可以讀取InternetReadFileInternetQueryDataAvailable響應。

不要只檢查API函數是否回零,然後繼續下一行。如果失敗,請致電GetLastError查明原因。你發佈的代碼不會引發異常,因此捕捉任何代碼都是徒勞的。 (不管怎麼樣,不管怎麼樣,「處理」它們的方式都是愚蠢的,不要發現你還不知道如何解決的異常,讓所有其他事情去找調用者或調用者的調用者等。 。)

2

我個人更喜歡使用synapse庫我所有的TCP/IP的工作。

uses 
    httpsend; 

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

圖書館是寫得很好,很容易修改,以滿足您的具體要求:例如,一個簡單的HTTP後可作爲進行編碼。最新的Subversion版本對Delphi 2009和Delphi 2010都沒有任何問題。這個框架不是基於組件的,而是一系列在多線程環境中的類和過程。