Como enviar uma solicitação HTTP POST em Delphi usando WinInet API
Pergunta
Eu estou tentando fazer solicitações HTTP de Delphi utilizando as funções de WinInet.
Até agora eu tenho:
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;
Mas isso não faz nada (estou cheirando o tráfego http rede para ver se ele funciona). Eu tenho usado com sucesso InternetOpenURL mas eu também preciso enviar solicitação POST e que a função não faz isso.
Alguém poderia me mostrar um exemplo simples? O resultado que eu quero é fazer com que a página de resposta http em um var como string.
Solução
Eu tenho toda a parte a url / filename confuso com o código anterior. Estou usando este de Jeff DeVore e agora é trabalhar bem:
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 é uma função que divide um URL em "hostname / filename" e TStringArray é um array de strings. Eu ainda tenho que rever o código de amanhã, mas ele parece estar bom e no meu sniffer eu vi os dados POST e cabeçalhos sendo enviado.
Outras dicas
Pessoalmente eu prefiro usar o href="http://synapse.ararat.cz/doku.php/download" rel="nofollow noreferrer"> sinapse biblioteca para toda a minha TCP / IP trabalhos. Por exemplo, um post HTTP simples pode ser codificado como:
uses
httpsend;
function testpost;
begin
stm := tStringstream.create('param=value');
try
HttpPostBinary('http://example.com',Stm);
finally
stm.free;
end;
end;
A biblioteca é bem escrito e muito fácil de modificar para atender às suas necessidades específicas. O lançamento mais recente subversão funciona sem problemas, tanto para Delphi 2009 e Delphi 2010. Este quadro não é baseado em componentes, mas sim uma série de aulas e procedimentos que bem em um ambiente multi-threaded.
O terceiro parâmetro (lpszObjectName) para HttpOpenRequest
deve ser o URL você deseja solicitar. É por isso que a documentação descreve o quinto parâmetro (lpszReferer) como "um ponteiro para uma string terminada em nulo que especifica o URL do documento a partir do qual foi obtido o URL no pedido (lpszObjectName)."
Os dados publicado é enviado com HttpSendRequest
; o parâmetro lpOptional é descrito assim:
Ponteiro para um tampão contendo quaisquer dados opcionais para ser enviada imediatamente após os cabeçalhos de solicitação. Este parâmetro é geralmente usado para operações POST e PUT. Os dados opcionais podem ser o ser recurso ou informação enviada para o servidor. Este parâmetro pode ser NULL se não houver dados opcionais para enviar.
O segundo parâmetro para InternetOpen
deve ser apenas o nome do servidor ; ele não deve incluir o protocolo. O protocolo especificado com o sexto parâmetro.
Depois de ter enviado o pedido, você pode ler a resposta com InternetReadFile
e InternetQueryDataAvailable
.
Não basta verificar se as funções da API retornar zero e então prosseguir na próxima linha. Se eles falharem, GetLastError
chamada para descobrir o porquê. O código que você postou não vai levantar exceções, por isso é inútil para pegar qualquer. (E é tolice "alça"-los da maneira que você está fazendo isso de qualquer maneira. Não capturar uma exceção que você ainda não sabe como consertar. Deixe tudo ir até o chamador, ou chamador do chamador, etc .)