Cómo enviar una solicitud HTTP POST en Delphi usando la API de WinInet
request (3)
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.
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 "un puntero a una cadena terminada en nulo que especifica la URL del documento desde el que se obtuvo la URL en la solicitud (lpszObjectName)".
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 generalmente se usa 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
debe 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 InternetReadFile
e 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 has publicado no generará excepciones, por lo que es inútil detectarlo. (Y es tonto "manejarlos" de la manera en que lo haces de todos modos. No tomes una excepción que no sepas cómo arreglar. Deja que todo lo demás pase a la persona que llama, a la persona que llama, etc. .)
Obtuve toda la parte de URL / nombre de archivo en mal estado con el código anterior. Estoy usando esto de Jeff DeVore ahora y está funcionando 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 rastreador vi que se enviaban los datos de las publicaciones y los encabezados.
Personalmente prefiero usar la biblioteca de sinapsis para todo mi trabajo de TCP / IP. Por ejemplo, una simple publicación HTTP puede codificarse 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 adaptarse a sus requisitos específicos. La última versión de subversión funciona sin problemas tanto para Delphi 2009 como para Delphi 2010. Este marco no se basa en componentes, sino que es una serie de clases y procedimientos que funcionan bien en un entorno de subprocesos múltiples.