2009-12-01 5 views
13

J'essaie d'effectuer des requêtes HTTP à partir de Delphi en utilisant les fonctions WinInet.Comment envoyer une requête HTTP POST dans Delphi à l'aide de WinInet api

Jusqu'à présent, j'ai:

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; 

Mais cela ne fait rien (je renifler le trafic réseau http pour voir si cela fonctionne). J'ai utilisé InternetOpenURL avec succès mais j'ai aussi besoin d'envoyer une requête POST et cette fonction ne le fait pas.

Quelqu'un pourrait-il me montrer un exemple simple? Le résultat que je veux est d'obtenir la page de réponse http dans un var en tant que chaîne.

Répondre

9

J'ai eu toute la partie url/nom de fichier foiré avec le code précédent. J'utilise ce from Jeff DeVore maintenant et il fonctionne très 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 est une fonction qui divise une URL dans « nom d'hôte/nom de fichier » et TStringArray est un tableau de chaînes. Je dois encore passer en revue le code demain mais il semble bien et dans mon sniffer j'ai vu les données de poste et les en-têtes envoyés.

+3

J'ai trouvé la source de votre code pour vous afin que vous puissiez citer l'auteur original. J'ai changé le code pour supprimer les déclarations de cas booléennes mal conçues, et j'ai changé le referrer afin qu'il ne mens pas et prétendre être Firefox 3.0.10. –

+1

Je ne peux pas trouver quelle unité contient ParseURL, ou c'est peut-être une routine personnalisée. Le code à: http://delphi.pastebin.com/f1ea3a752 est quelque peu différent et n'utilise pas ParseURL. – lkessler

+0

Comment ajouter une valeur d'en-tête personnalisée à la demande de publication? – hikari

1

Le troisième paramètre (lpszObjectName) à HttpOpenRequest doit être l'URL que vous souhaitez demander. C'est pourquoi la documentation décrit le cinquième paramètre (lpszReferer) comme "un pointeur vers une chaîne terminée par zéro qui spécifie l'URL du document à partir duquel l'URL de la requête (lpszObjectName) a été obtenue."

Les données envoyées sont envoyées avec HttpSendRequest; Le paramètre lpOptional est décrit comme suit:

Pointeur vers un tampon contenant des données facultatives à envoyer immédiatement après les en-têtes de la requête. Ce paramètre est généralement utilisé pour les opérations POST et PUT. Les données facultatives peuvent être la ressource ou l'information en cours de publication sur le serveur. Ce paramètre peut être NULL s'il n'y a pas de données facultatives à envoyer.

Le second paramètre à InternetOpen doit être juste le nom du serveur; il ne devrait pas inclure le protocole. Le protocole que vous spécifiez avec le sixième paramètre. Après avoir envoyé la demande, vous pouvez lire la réponse avec InternetReadFile et InternetQueryDataAvailable.

Ne vérifiez pas simplement si les fonctions de l'API renvoient zéro et passez à la ligne suivante. En cas d'échec, appelez le GetLastError pour savoir pourquoi. Le code que vous avez publié ne déclenchera pas d'exceptions, il est donc inutile d'en prendre. (Et c'est idiot de les "manipuler" de la façon dont vous le faites de toute façon.) N'attrapez pas une exception que vous ne savez pas comment résoudre: laissez tout le reste aller à l'appelant, à l'appelant, etc.

2

Personnellement, je préfère utiliser la bibliothèque synapse pour tous mes travaux TCP/IP. Par exemple, un message HTTP simple peut être codé:

uses 
    httpsend; 

function testpost; 
begin 
    stm := tStringstream.create('param=value'); 
    try 
    HttpPostBinary('http://example.com',Stm); 
    finally 
    stm.free; 
    end; 
end; 

La bibliothèque est bien écrit et très facile à modifier en fonction de vos besoins spécifiques. La dernière version de subversion fonctionne sans aucun problème pour Delphi 2009 et Delphi 2010. Ce framework n'est pas basé sur des composants, mais plutôt sur une série de classes et de procédures qui s'intègrent bien dans un environnement multithread.

Questions connexes