2009-12-01 28 views
13

Sto cercando di fare richieste HTTP da Delphi utilizzando le funzioni WinInet.Come inviare una richiesta POST HTTP in Delphi utilizzando WinInet api

Finora ho:

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; 

Ma questo non fa nulla (mi sniffing il traffico http rete per vedere se funziona). Ho usato con successo InternetOpenURL ma ho anche bisogno di inviare la richiesta POST e quella funzione non lo fa.

Qualcuno potrebbe mostrarmi un semplice esempio? Il risultato che voglio è ottenere la pagina di risposta http in una var come stringa.

risposta

9

Ho ottenuto tutta la parte url/filename incasinata del codice precedente. Sto usando questo from Jeff DeVore ora e che sta funzionando benissimo:

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 è una funzione che divide un URL in "hostname/filename" e TStringArray è un array di stringhe. Devo ancora rivedere il codice domani ma sembra a posto e nel mio sniffer ho visto i dati e le intestazioni dei messaggi inviati.

+3

Ho trovato la fonte del codice per te in modo da poter citare l'autore originale. Ho cambiato il codice per rimuovere le false dichiarazioni booleane, e ho cambiato il referrer in modo da non mentire e affermare di essere Firefox 3.0.10. –

+1

Non riesco a trovare quale unità contiene ParseURL, o forse è una routine personalizzata. Il codice su http://delphi.pastebin.com/f1ea3a752 è leggermente diverso e non usa ParseURL. – lkessler

+0

Come aggiungerei un valore di intestazione personalizzato alla richiesta di post? – hikari

1

Il terzo parametro (lpszObjectName) per HttpOpenRequest dovrebbe essere il URL si desidera richiedere. Ecco perché la documentazione descrive il quinto parametro (lpszReferer) come "un puntatore a una stringa terminata da null che specifica l'URL del documento da cui è stato ottenuto l'URL nella richiesta (lpszObjectName)."

I dati inviati vengono inviati con HttpSendRequest; il parametro lpOptional è descritto in questo modo:

Puntatore a un buffer contenente dati facoltativi da inviare immediatamente dopo le intestazioni della richiesta. Questo parametro viene generalmente utilizzato per le operazioni POST e PUT. I dati facoltativi possono essere la risorsa o le informazioni inviate al server. Questo parametro può essere NULL se non ci sono dati opzionali da inviare.

Il secondo parametro dovrebbe essere InternetOpensolo il nome del server; non dovrebbe includere il protocollo. Il protocollo specificato con il sesto parametro.

Dopo aver inviato la richiesta, è possibile leggere la risposta con InternetReadFile e InternetQueryDataAvailable.

Non basta controllare se le funzioni API restituiscono zero e quindi procedere alla riga successiva. Se falliscono, chiama GetLastError per scoprire perché. Il codice che hai postato non solleverà eccezioni, quindi è inutile prenderne. (Ed è sciocco "gestirli" nel modo in cui lo fai comunque. Non rilevare un'eccezione che non sai già come risolvere. Lascia che tutto il resto salga al chiamante, al chiamante del chiamante, ecc. .)

2

Personalmente preferisco usare la libreria synapse per tutto il mio lavoro TCP/IP. Ad esempio, un semplice post HTTP può essere codificato come:

uses 
    httpsend; 

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

La biblioteca è ben scritto e molto facile da modificare in base alle proprie specifiche esigenze. L'ultima versione di subversion funziona senza problemi sia per Delphi 2009 che per Delphi 2010. Questo framework non è basato sui componenti, ma piuttosto una serie di classi e procedure che si trovano bene in un ambiente multi-thread.

Problemi correlati