delphi http post request wininet

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.