2009-12-01 3 views
13

Я пытаюсь выполнить HTTP-запросы из Delphi с помощью функций WinInet.Как отправить запрос HTTP POST в Delphi с помощью WinInet api

До сих пор у меня есть:

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; 

Но это ничего не делать (я нюхать сети HTTP трафик, чтобы увидеть, если он работает). Я успешно использовал InternetOpenURL, но мне также нужно отправить запрос POST, и эта функция не делает этого.

Может ли кто-нибудь показать мне простой пример? В результате я хочу получить страницу ответа http в переменной var.

ответ

9

Я получил всю часть url/filename, испорченную предыдущим кодом. Я использую этот from Jeff DeVore сейчас и это прекрасно работает:

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 это функция, которая разделяет URL в «имя хоста/файла» и TStringArray является массивом строк. Мне все равно придется пересматривать код завтра, но он выглядит отлично, и в моем сниффере я видел, как отправляются почтовые данные и заголовки.

+3

Я нашел для вас источник вашего кода, чтобы вы могли привести оригинального автора. Я изменил код, чтобы удалить непродуманные логические операторы case, и я изменил реферер, чтобы он не лгал и утверждал, что он Firefox 3.0.10. –

+1

Я не могу найти, какая единица содержит ParseURL, или, может быть, ее пользовательскую процедуру. Код по адресу: http://delphi.pastebin.com/f1ea3a752 несколько отличается и не использует ParseURL. – lkessler

+0

Как вы добавили пользовательское значение заголовка в запрос на отправку? – hikari

1

Третий параметр (lpszObjectName) до HttpOpenRequest должен быть URL, который вы хотите запросить. Вот почему документация описывает пятый параметр (lpszReferer) как «указатель на строку с нулевым завершением, которая указывает URL-адрес документа, из которого был получен URL-адрес запроса (lpszObjectName)».

Отправленные данные отправляются с HttpSendRequest; параметр lpOptional описывается следующим образом:

Указатель на буфер, содержащий любые дополнительные данные, которые должны быть отправлены сразу после заголовков запроса. Этот параметр обычно используется для операций POST и PUT. Необязательными данными могут быть ресурсы или информация, отправляемые на сервер. Этот параметр может быть NULL, если нет дополнительных данных для отправки.

Второй параметр InternetOpen должен быть только имя сервера; он не должен включать протокол. Протокол, который вы указываете с шестым параметром.

После того, как вы отправили запрос, вы можете прочитать ответ с InternetReadFile и InternetQueryDataAvailable.

Не просто проверять, возвращаются ли функции API нулю, а затем перейти к следующей строке. Если они не сработают, позвоните GetLastError, чтобы узнать, почему. Код, который вы опубликовали, не будет генерировать исключения, поэтому бесполезно поймать их. (И глупо «обрабатывать» их так, как вы это делаете. Не поймайте исключение, которое вы еще не знаете, как исправить. Пусть все остальное подходит к вызывающему абоненту или вызывающему абоненту и т. Д. .)

2

Лично я предпочитаю использовать библиотеку synapse для всех моих TCP/IP-операций. Например, простой HTTP сообщения могут быть закодированы как:

uses 
    httpsend; 

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

Библиотека написана хорошо и очень легко изменить в соответствии с вашими требованиями. Последняя версия subversion работает без проблем как для Delphi 2009, так и для Delphi 2010. Эта структура не основана на компонентах, а представляет собой ряд классов и процедур, которые хорошо подходят для многопоточной среды.

Смежные вопросы