2009-12-01 41 views
13

Estoy tratando de realizar solicitudes HTTP desde Delphi utilizando las funciones de WinInet.Cómo enviar una solicitud HTTP POST en Delphi usando la API 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 oliendo el tráfico http 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.

Respuesta

9

Obtuve toda la parte de url/nombre de archivo en mal estado con el código anterior. Estoy usando este from Jeff DeVore ahora y está funcionando muy 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 dirección 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.

+3

Encontré la fuente de su código para que pueda citar al autor original. Cambié el código para eliminar las declaraciones de caso booleanas mal concebidas, y cambié el referente para que no mienta y pretenda ser Firefox 3.0.10. –

+1

No puedo encontrar qué unidad contiene ParseURL, o tal vez es una rutina personalizada. El código en: http://delphi.pastebin.com/f1ea3a752 es algo diferente y no usa ParseURL. – lkessler

+0

¿Cómo agregaría un valor de encabezado personalizado a la solicitud de publicación? – hikari

1

El tercer parámetro (lpszObjectName) a HttpOpenRequest debe ser el 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 a InternetOpen debe ser sólo el nombre del servidor; no debe incluir el protocolo. El protocolo que especifique con el sexto parámetro.

Después de haber enviado la solicitud, se puede leer la respuesta con InternetReadFile y InternetQueryDataAvailable.

No solo verifique si las funciones de la API devuelven cero y luego continúe en la línea siguiente. Si fallan, llame al 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. .)

2

Personalmente, prefiero usar la biblioteca synapse para todos mis trabajos de TCP/IP. Por ejemplo, un simple mensaje HTTP puede ser codificada 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 escrito y muy fácil de modificar para satisfacer 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.

Cuestiones relacionadas