Cómo enviar una solicitud HTTP POST en Delphi usando la API de WinInet
Pregunta
Estoy tratando de hacer solicitudes HTTP desde Delphi usando las funciones de WinInet.
Hasta ahora tengo:
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;
Pero esto no hace nada (estoy olfateando el tráfico http de la red para ver si funciona). He utilizado con éxito InternetOpenURL pero también necesito enviar una solicitud POST y esa función no lo hace.
¿Podría alguien mostrarme un ejemplo simple? El resultado que quiero es obtener la página de respuesta http en una var como cadena.
Solución
Tengo toda la parte de url / nombre de archivo desordenada con el código anterior. Estoy usando este de Jeff DeVore ahora y funciona bien:
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 es una función que divide una URL en " nombre de host / nombre de archivo " y TStringArray es una matriz de cadenas. Todavía tengo que revisar el código mañana, pero se ve bien y en mi sniffer vi que se enviaban los datos de la publicación y los encabezados.
Otros consejos
Personalmente prefiero usar la synapse para todos mis TCP / IP trabajo. Por ejemplo, una publicación HTTP simple se puede codificar como:
uses
httpsend;
function testpost;
begin
stm := tStringstream.create('param=value');
try
HttpPostBinary('http://example.com',Stm);
finally
stm.free;
end;
end;
La biblioteca está bien escrita y es muy fácil de modificar para satisfacer sus requisitos específicos. La última versión de Subversion funciona sin problemas para Delphi 2009 y Delphi 2010. Este marco no está basado en componentes, sino que es una serie de clases y procedimientos que funcionan bien en un entorno de subprocesos múltiples.
El tercer parámetro (lpszObjectName) para HttpOpenRequest
debe ser la URL que desea solicitar. Es por eso que la documentación describe el quinto parámetro (lpszReferer) como " puntero a una cadena terminada en nulo que especifica la URL del documento del que se obtuvo la URL en la solicitud (lpszObjectName). & Quot;
Los datos publicados se envían con HttpSendRequest
; El parámetro lpOptional se describe así:
Puntero a un búfer que contiene datos opcionales que se enviarán inmediatamente después de los encabezados de solicitud. Este parámetro se usa generalmente para operaciones POST y PUT. Los datos opcionales pueden ser el recurso o la información que se publica en el servidor. Este parámetro puede ser NULL si no hay datos opcionales para enviar.
El segundo parámetro para InternetOpen
debería ser solo el nombre del servidor ; No debe incluir el protocolo. El protocolo que especifique con el sexto parámetro.
Después de enviar la solicitud, puede leer la respuesta con < code> InternetReadFile y InternetQueryDataAvailable
.
No solo verifique si las funciones API devuelven cero y luego continúe en la siguiente línea. Si fallan, llame a GetLastError
para averiguar por qué. El código que ha publicado no generará excepciones, por lo que es inútil atrapar alguno. (Y de todos modos es una tontería "manejarlos" de la forma en que lo hace. No se dé cuenta de una excepción que aún no sabe cómo solucionar. Deje que todo lo demás llegue a la persona que llama, o la persona que llama, etc.)