Come inviare una richiesta POST HTTP in Delphi usando WinInet api
Domanda
Sto provando a fare richieste HTTP da Delphi usando le funzioni WinInet.
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 (sto annusando il traffico http di rete per vedere se funziona). Ho usato con successo InternetOpenURL ma devo anche inviare una richiesta POST e questa 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.
Soluzione
Ho inserito tutta la parte url / nomefile con il codice precedente. Sto usando questo di Jeff DeVore ora e funziona bene:
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 " nomehost / nomefile " e TStringArray è un array di stringhe. Domani devo ancora rivedere il codice ma sembra a posto e nel mio sniffer ho visto i dati dei post e le intestazioni inviate.
Altri suggerimenti
Personalmente preferisco utilizzare la synapse per tutto il mio TCP / IP lavoro. 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 libreria è ben scritta e molto facile da modificare in base alle proprie esigenze specifiche. L'ultima versione di Subversion funziona senza problemi sia per Delphi 2009 che per Delphi 2010. Questo framework non è basato su componenti, ma piuttosto è una serie di classi e procedure che si adattano bene in un ambiente multi-thread.
Il terzo parametro (lpszObjectName) su HttpOpenRequest
dovrebbe essere l ' URL che desideri richiedere. Ecco perché la documentazione descrive il quinto parametro (lpszReferer) come " un puntatore a una stringa con terminazione nulla che specifica l'URL del documento da cui è stato ottenuto l'URL nella richiesta (lpszObjectName). & Quot;
I dati pubblicati 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 che vengono inviate al server. Questo parametro può essere NULL se non ci sono dati opzionali da inviare.
Il secondo parametro di InternetOpen
dovrebbe essere solo il nome del server ; non dovrebbe includere il protocollo. Il protocollo specificato con il sesto parametro.
Dopo aver inviato la richiesta, puoi leggere la risposta con < code> InternetReadFile e InternetQueryDataAvailable
.
Non limitarti a verificare se le funzioni API restituiscono zero e quindi procedi alla riga successiva. Se falliscono, chiama GetLastError
per scoprire perché. Il codice che hai pubblicato non genererà eccezioni, quindi è inutile prenderne uno. (Ed è sciocco "gestirli" nel modo in cui lo fai comunque. Non cogliere un'eccezione che non sai già come risolvere. Lascia che tutto il resto salga al chiamante o al chiamante, ecc.)