【问题标题】:How to send a HTTP POST Request in Delphi using WinInet api如何使用 WinInet api 在 Delphi 中发送 HTTP POST 请求
【发布时间】:2010-12-21 20:47:52
【问题描述】:

我正在尝试使用 WinInet 函数从 Delphi 发出 HTTP 请求。

到目前为止我有:

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 请求,而该功能不这样做。

谁能给我一个简单的例子?我想要的结果是将 var 中的 http 响应页面作为字符串获取。

【问题讨论】:

    标签: delphi http post request wininet


    【解决方案1】:

    我把所有的 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 是一个字符串数组。我明天仍然需要查看代码,但它看起来很好,在我的嗅探器中我看到了正在发送的帖子数据和标题。

    【讨论】:

    • 我为您找到了代码的来源,因此您可以引用原作者。我更改了代码以删除考虑不周的布尔 case 语句,并更改了引用者,因此它不会撒谎并声称是 Firefox 3.0.10。
    • 我找不到包含 ParseURL 的单元,或者它可能是自定义例程。 delphi.pastebin.com/f1ea3a752 的代码有些不同,不使用 ParseURL。
    • 如何向发布请求添加自定义标头值?
    【解决方案2】:

    就我个人而言,我更喜欢将synapse 库用于我所有的 TCP/IP 工作。例如,一个简单的 HTTP post 可以编码为:

    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 上都没有任何问题。这个框架不是基于组件的,而是一系列在多线程环境中运行良好的类和过程。

    【讨论】:

      【解决方案3】:

      HttpOpenRequest 的第三个参数 (lpszObjectName) 应该是您希望请求的 URL。这就是为什么文档将第五个参数 (lpszReferer) 描述为“一个指向以 null 结尾的字符串的指针,该字符串指定从其获取请求中的 URL (lpszObjectName) 的文档的 URL。”

      发布的数据通过HttpSendRequest 发送; lpOptional 参数是这样描述的:

      指向缓冲区的指针,其中包含要在请求标头之后立即发送的任何可选数据。该参数一般用于 POST 和 PUT 操作。可选数据可以是发布到服务器的资源或信息。如果没有可选数据要发送,此参数可以为 NULL。

      InternetOpen 的第二个参数应该是只是服务器名称;它不应该包括协议。您使用第六个参数指定的协议。

      发送请求后,您可以使用InternetReadFileInternetQueryDataAvailable 阅读响应。

      不要只检查 API 函数是否返回零,然后继续下一行。如果失败,请致电GetLastError 了解原因。您发布的代码不会引发异常,因此捕获任何异常都是徒劳的。 (无论如何,以你这样做的方式“处理”它们是愚蠢的。不要捕获你还不知道如何修复的异常。让其他一切都交给调用者或调用者的调用者等.)

      【讨论】:

        猜你喜欢
        • 2017-06-01
        • 2011-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多