【问题标题】:What's the simplest way to call Http GET url using Delphi?使用 Delphi 调用 Http GET url 的最简单方法是什么?
【发布时间】:2010-09-23 00:38:12
【问题描述】:

我想在我的应用程序中调用一个 Web 服务,我可以通过导入 WSDL 来使用它,或者只使用带有 URL 和参数的“HTTP GET”,所以我更喜欢后者,因为它很简单。

我知道我可以使用 indy idhttp.get 来完成这项工作,但这是非常简单的事情,我不想将复杂的 indy 代码添加到我的应用程序中。

更新:对不起,如果我不清楚,我的意思是“不要添加复杂的 indy 代码”,我不想为这个简单的任务添加 indy 组件,并且更喜欢更轻量级方法。

【问题讨论】:

  • 你认为仅仅使用 HTTP GET 就足以调用 web 服务函数了吗?
  • 如果它是一个 RESTful Web 服务。
  • 我不知道 REST 也可以有 wsdl
  • AhmetCiftci,正如我之前所说,服务有“HTTP GET” url 来调用它,所以可以使用 WSDL 或只请求“HTTP GET”

标签: delphi http network-programming


【解决方案1】:

使用 Indy 调用 RESTful Web 服务非常简单。

将 IdHTTP 添加到您的 uses 子句中。请记住,IdHTTP 需要 URL 上的“HTTP://”前缀。

function GetURLAsString(const aURL: string): string;
var
  lHTTP: TIdHTTP;
begin
  lHTTP := TIdHTTP.Create;
  try
    Result := lHTTP.Get(aURL);
  finally
    lHTTP.Free;
  end;
end;

【讨论】:

  • 问题是不要添加复杂的 Indy 代码。代码并不复杂。
  • 您的“最轻”选项是使用外部代码,无论是 Indy、Synapse 还是 WinINet。如果您不能或不会使用 Delphi 组件或类,请使用 Delphi 围绕 WinINet 的包装函数。也是外部的,但至少 DLL 是随 Windows 一起安装的。
  • 在其辩护中,Indy 附带 Delphi,但 WinInet 版本和行为随着安装的 IE 版本而改变,这就像 DLL 地狱。
  • @Toby:一个空的控制台应用程序在 D7 中为 40K,在 DXE 中为 83K。将 IdHttp 添加到 uses 子句中,它们将分别达到 139K 和 739K。我不确定那 2MB 是从哪里来的。听起来它可能是在 Debug 模式下编译的更高版本的 Delphi。
  • TIdHTTP.Create(nil); 可以使用 Indy 10.6 缩短为 TIdHTTP.Create;
【解决方案2】:

您可以像这样使用WinINet API

uses WinInet;

function GetUrlContent(const Url: string): string;
var
  NetHandle: HINTERNET;
  UrlHandle: HINTERNET;
  Buffer: array[0..1024] of Char;
  BytesRead: dWord;
begin
  Result := '';
  NetHandle := InternetOpen('Delphi 5.x', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

  if Assigned(NetHandle) then 
  begin
    UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);

    if Assigned(UrlHandle) then
      { UrlHandle valid? Proceed with download }
    begin
      FillChar(Buffer, SizeOf(Buffer), 0);
      repeat
        Result := Result + Buffer;
        FillChar(Buffer, SizeOf(Buffer), 0);
        InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
      until BytesRead = 0;
      InternetCloseHandle(UrlHandle);
    end
    else
      { UrlHandle is not valid. Raise an exception. }
      raise Exception.CreateFmt('Cannot open URL %s', [Url]);

    InternetCloseHandle(NetHandle);
  end
  else
    { NetHandle is not valid. Raise an exception }
    raise Exception.Create('Unable to initialize Wininet');
end;

来源:http://www.scalabium.com/faq/dct0080.htm

WinINet API 使用与 InternetExplorer 相同的东西,因此您还可以免费获得 InternetExplorer 设置的任何连接和代理设置。

【讨论】:

  • 正如 bruce 所说,例如,如果您的 IE 配置错误以使用已关闭的代理设置,那么您也将陷入困境。如果它发生了,那么晦涩难懂,令人沮丧。
  • 我从艰苦的经验中了解到,您还可以免费获得安装在您客户端系统上的 IE 的 wininet.dll 中的所有错误,包括可怕的 TIMEOUT 错误。请注意。
  • @Warren:那些在安全更新和服务包中得到修复,而您自己的代码或其他任何人的代码并没有得到这么多眼球的同样严格审查。
  • 请注意,WinInet 不应在服务中使用。
  • Indy 的 TIdHTTP 组件在将响应正文作为 string 返回时为您处理这些转换。
【解决方案3】:

实际上接受答案中的代码对我不起作用。所以我稍微修改了一下,让它实际上返回 String 并在执行后优雅地关闭所有内容。示例将检索到的数据作为 UTF8String 返回,因此它适用于 ASCII 和 UTF8 页面。

uses WinInet;

function GetUrlContent(const Url: string): UTF8String;
var
  NetHandle: HINTERNET;
  UrlHandle: HINTERNET;
  Buffer: array[0..1023] of byte;
  BytesRead: dWord;
  StrBuffer: UTF8String;
begin
  Result := '';
  NetHandle := InternetOpen('Delphi 2009', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(NetHandle) then
    try
      UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
      if Assigned(UrlHandle) then
        try
          repeat
            InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
            SetString(StrBuffer, PAnsiChar(@Buffer[0]), BytesRead);
            Result := Result + StrBuffer;
          until BytesRead = 0;
        finally
          InternetCloseHandle(UrlHandle);
        end
      else
        raise Exception.CreateFmt('Cannot open URL %s', [Url]);
    finally
      InternetCloseHandle(NetHandle);
    end
  else
    raise Exception.Create('Unable to initialize Wininet');
end;

希望它对像我这样正在寻找如何在 Delphi 中检索页面内容的简单代码的人有所帮助。 干杯,阿尔迪斯 :)

【讨论】:

  • 只有当响应实际上是用 ASCII 或 UTF-8 编码时才有效,这不是保证。
【解决方案4】:

在较新的 Delphi 版本中,最好使用来自 System.Net.HttpClient 单元的 THTTPClient,因为它是标准的和跨平台的。简单的例子是

function GetURL(const AURL: string): string;
var
  HttpClient: THttpClient;
  HttpResponse: IHttpResponse;
begin
  HttpClient := THTTPClient.Create;
  try
    HttpResponse := HttpClient.Get(AURL);
    Result := HttpResponse.ContentAsString();
  finally
    HttpClient.Free;
  end;
end;

【讨论】:

    【解决方案5】:

    如果可以下载到文件,您可以使用 ExtActns 单元中的 TDownloadURL。比直接使用 WinInet 简单得多。

    procedure TMainForm.DownloadFile(URL: string; Dest: string);
    var
      dl: TDownloadURL;
    begin
      dl := TDownloadURL.Create(self);
      try
        dl.URL := URL;
        dl.FileName := Dest;
        dl.ExecuteTarget(nil); //this downloads the file
      finally
        dl.Free;
      end;
    end;
    

    使用它时也可以获得进度通知。只需将事件处理程序分配给 TDownloadURL 的 OnDownloadProgress 事件。

    【讨论】:

      【解决方案6】:

      使用 Windows HTTP API 可能也很容易。

      procedure TForm1.Button1Click(Sender: TObject);
      var http: variant;
      begin
       http:=createoleobject('WinHttp.WinHttpRequest.5.1');
       http.open('GET', 'http://lazarus.freepascal.org', false);
       http.send;
       showmessage(http.responsetext);
      end;
      

      在上面的代码中,我暗示 COM 已经为 VCL 主线程初始化。据报道,简单的应用程序或 LCL 应用程序可能并非总是如此。此外,异步(多线程)工作绝对不是这种情况。

      下面是运行真实代码的 sn-p。注意 - 功能是额外的。它不需要工作。因此,当我发出请求时,我并不关心他们的结果,结果会被忽略并转储。

      procedure TfmHaspList.YieldBlinkHTTP(const LED: boolean; const Key_Hardware_ID: cardinal);
      var URL: WideString;
      begin
        URL := 'http://127.0.0.1:1947/action.html?blink' +
          IfThen( LED, 'on', 'off') + '=' + IntToStr(Key_Hardware_ID);
      
        TThread.CreateAnonymousThread(
          procedure
          var Request: OleVariant;
          begin
            // COM library initialization for the current thread
            CoInitialize(nil);
            try
              // create the WinHttpRequest object instance
              Request := CreateOleObject('WinHttp.WinHttpRequest.5.1');
              // open HTTP connection with GET method in synchronous mode
              Request.Open('GET', URL, False);
              // set the User-Agent header value
      //        Request.SetRequestHeader('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0');
              // sends the HTTP request to the server, the Send method does not return
              // until WinHTTP completely receives the response (synchronous mode)
              Request.Send;
      //        // store the response into the field for synchronization
      //        FResponseText := Request.ResponseText;
      //        // execute the SynchronizeResult method within the main thread context
      //        Synchronize(SynchronizeResult);
            finally
              // release the WinHttpRequest object instance
              Request := Unassigned;
              // uninitialize COM library with all resources
              CoUninitialize;
            end;
          end
        ).Start;
      end;
      

      【讨论】:

      • 是的,这是一个出色而简单的解决方案,但不要忘记分别在开始和结束时调用 CoInitialize(nil);CoUninitialize
      • 在异步线程案例的链接中提到了这一点。然而,在这个特定的例子中,对象在主 VCL 线程中运行,其中 COM 已经被初始化。我想(虽然没有检查)同样适用于 Windows 上的 LCL
      • 嗨,是的。我提到它是因为我的应用程序在我使用它时给了我“Com 未初始化或出现错误”(带有按钮的标准 TForm)。同样由于某种原因,当我关闭应用程序时,它在使用 CoInitializeCoUninitialize 方法后崩溃了。仅供参考。
      • 你的“http”变量是本地的吗?难道是你在 CoUnInitialize 之前没有销毁/清除变量,所以它在 COM 子系统关闭后保留了指向 COM 对象的指针?
      • 在我的具体情况下,我需要调用 localhost 服务,因此它不会阻塞 GUI,我根本不需要任何结果,甚至不需要成功/错误状态。所以我一直在生成 TThread.Anonymous 工作人员,它们在自己内部初始化并关闭了 COM。像魅力一样工作。
      【解决方案7】:

      在 HTTPSEND 单元 (HTTPGetText, HTTPGetBinary) 中使用 Synapse TCP/IP 函数。它将为您执行 HTTP 拉取,并且不需要除 Winsock 之外的任何外部 DLL。最新的 SVN 版本在 Delphi 2009 中运行良好。它使用阻塞函数调用,因此无需编程事件。

      更新:这些单元很轻,不是基于组件的。 SVN 的最新版本在 Delphi XE4 中也运行良好。

      【讨论】:

      • Indy 中的 HTTP 调用也被阻塞了。
      【解决方案8】:

      如果您的应用程序仅适用于 Windows,我建议使用 WinSock。它很简单,允许执行任何 HTTP 请求,可以同步和异步工作(使用带有回调的非阻塞 WSASend/WSARecv 或在专用线程中使用旧的发送/接收)。

      【讨论】:

      • 虽然Winsock 可以直接用于此,但不推荐。 HTTP 是一个复杂的野兽,需要从头开始手动实现。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-13
      相关资源
      最近更新 更多