Как отправить HTTP-запрос POST в Delphi с помощью WinInet api - PullRequest
15 голосов
/ 01 декабря 2009

Я пытаюсь сделать HTTP-запросы от Delphi, используя функции WinInet.

Пока у меня есть:

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 в виде строки.

Ответы [ 3 ]

9 голосов
/ 01 декабря 2009

Я перепутал всю часть URL / имени файла с предыдущим кодом. Я использую это от Джеффа ДеВора сейчас, и оно отлично работает:

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-адрес на «hostname / filename», а TStringArray - это массив строк. Завтра мне еще предстоит пересмотреть код, но он выглядит нормально, и в своем анализаторе я увидел отправляемые данные и заголовки.

2 голосов
/ 01 декабря 2009

Лично я предпочитаю использовать библиотеку 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. Эта инфраструктура не основана на компонентах, а представляет собой серию классов и процедур, которые хорошо работают в многопоточной среде.

1 голос
/ 01 декабря 2009

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

Отправленные данные отправляются с HttpSendRequest; Параметр lpOptional описывается так:

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...