使用 TRESTClient 与 TRESTRequest 作为 HTTP Client

在 Delphi XE 推出以前的年代,Delphi的展方向是笔直朝向库连结Windows 用程式个目不断前的,从Delphi 1开始,到Delphi 7,Delphi奠定了VB Killer的外号,主要依靠的就是与接功能超越其他开工具,而且超越的距离不只一个世代。

在 .NET开始展,Delphi 8, Delphi .NET 不断延候,与库连接功能的方便性,仍然让许多ERP厂商、硬体厂商持续爱用 Delphi.

直到 Web 开与 App 开超越了 Windows 用程式的需求,VC, VB, Delphi 也开始随着波潮流,渐渐不再像 1990年代那么广受戴了。

在1990到 2010年之,Delphi的网路连线功能,主要是借由第三方元件来提供的,其中知名度最高,全球使用人数也最多的,应该就是 Indy 套元件了。

套元件在 2000 年前,叫做 WinShose,从第八版之后才改名Indy,全球投入套元件开的开,前后超40人,从最基的 TCP/IP 功能到各种定的Client与Server 端元件,笔者从中得益非常多,也开了当中的DNS Server元件,讯协定的深入了解,Indy团队是我不可或缺的师长

随着Delphi 工具走入了多平台开域,Indy的局限性也在两三年凸了出来,主要是在各个作上面于SSL与加密功能的支援无法合到作内建的功能所致。

由于个局限性,Delphi XE6开始,REST Client系列元件渐渐开始成 Delphi 团队的重点开发项目之一,所以我从 Delphi XE6, Delphi XE7 之后的版本,可以发现到,使用 TRESTClient, TRESTRequest, TRESTResponse 系列合的用程式越来越多了,原厂也不断鼓励大家使用套元件来提供 REST API 的连线功能。

REST API 的基是 HTTP 定,大多以 HTTP 的 POST 方法把 JSON 编码形式的参数传递到 Server,而 Server 再以 JSON 形式的参数回

作法也会稍有化,例如以 POST 方法把 Web-Form 编码形式的参数传递给 Server,Server 再以 JSON 形式把料回

形式不一而足,但相同的是 HTTP 定,最常用的也是以 POST 方法把参数传给 Server 端。

今天要跟大家分享的主是如何『使用 TRESTClient 与 TRESTRequest 作 HTTP Client』。

 

前面已提到,在没有 TRESTClient 整元件以前,我通常用的是 Indy 系列的元件来提供网路传输的功能,而在有了 TRESTClient 整元件,我在行平台上面就可以不需要另外配置函式,也能直接使用 https 与 server 连线了,在勒索病毒泛的今天,使用 https 会使用者比安心。

POST业说

在 HTTP 的 POST 作当中,参数跟 GET 作,Client端需要以 name=value&name2=value2 种形式行字串接,再送到 Server 端去。Get 跟 POST的差异,在于 Get 方法是把所有参数当做 URL 的一部分,送 HTTP GET 指令的候,参数同 URL  一起送。

而 POST 作业则送完 POST 指令后,把所有的参数与料随之送。依照 HTTP 型定的范,GET 作的 URL 是无法加密的,而且度也有限制。因此,当需要传递料比多,或者有机敏性,透 HTTPS 送,就是最直接,也最方便,更是目前最通用的料保方法。

POST 传递的参数,除了字串以外,常常包含了档案传递。我很常看到在网上面以按提供使用者选择要上的档案,也常看提供以拖拉的方式把档案上端系,尤其网页邮件系最常种作法。

去以 TIdHTTP 元件的 POST 方法送参数,呼叫方式如下:

 1 var
 2     httpClient : TIdHTTP;
 3     url, params, httpResultStr : string;
 4 begin
 5     url := 'http://mytestURL.com/test.php';
 6     params := 'name=我的名字&test=测试';
 7 
 8     httpClient := TIdHTTP.Create(self); 
 9     try
10          httpResultStr := httpClient.Post(url, params);
11          showMessage(httpResultStr);
12     finally
13         httpClient.Free; 
14     end; 
15 end;

这样就可以把 params 字串的众参数到 server 去了。理上是这样,但事情并没有简单,在 HTTP 定当中要参数 Server,如果些字串包含了特殊字元,要先经过编码,而编码,是我一生都需要与之抗的繁复程序。

在 HTTP GET 方法当中,所有的参数除了要以 name=value 每一个参数做描述,以及需要用 & 来接各参数,所有的 value 都需要以 url encode 来脱 URL 保留字元的纠缠。name 是否需要编码呢?笔者建,name 就乖乖的用英文吧,可以省下很多问题,以及些可避免的问题所需要的时间

那么同的功能,以 TRESTClient 跟 TRESTRequest 要怎么达成呢? 也很容易,作法如下:

1. 在 form 里面放上 TRESTClient 跟 TRESTRequest 元件各一。

2. 把要传递的参数加到 TRESTRequest 体的 params 属性里面去,个属性的型是 TArray,所以可以存放多参数。

3. 定 TRESTClient 要送参数的URL,注意,URL 是定在TRESTClient 哦!

4. 定 TRESTRequest 要使用的传输方法,要POST(因正在介的是POST方法,按照您的需求整)

5. 呼叫 TRESTRequest 体的 execute 方法,就可以把料送去 server 了。

 

写成 Delphi 的程式,会像以下这样:

self.RESTClient1.BaseURL :=
          'http://我的网址/acceptNewCard.php';
self.RESTRequest1.Params.Clear;
self.RESTRequest1.Method := rmPOST;

self.RESTRequest1.AddParameter('test', self.EditCardNo.Text);
self.RESTRequest1.AddParameter('name', self.EditName.Text);

是不是很容易呢?的确很容易,里问题等下再深入探,先来看 server 端要怎么接收些个参数,我用 PHP 当范例,需要用 C#,JSP的者朋友们请自行转译喔⋯⋯

 

PHP Server 端接收 POST 参数的方法

从 1994 年开始,笔者就陆续撰文明 HTTP POST 方法如何接参数,包含了CGI 用C,perl等作,也包含 ISAPI 以 Delphi 作,近几年比流行的是 PHP,JSP,C#,但 PHP 程式码读起来比较简洁易懂,所以我就选择 PHP 来做范例了。

在 PHP 里面,透GET 跟 POST 方法传递的参数,会被分存放在 $_GET 跟 $_POST 两个数里面,如果要偷懒,不想区分 GET 或 POST 方法,也可以从 $_REQUEST 取,当中有些安全性考量,最好勤一点,把它区分开来。

 

刚刚的例子来看,我们传了一个名 name,以及一个名 test 的字串,用的是 POST 方法,所以我得用以下两个数来存取两个字串:

•  $_POST['name']  数可以取得 Client 端送出来的 name 

•  $_POST['test']  数可以取得 Client 端送出来的 test

所以在 server 端,我可以这样写,来抓到两个料:

$name = $_POST["name"];

$test = $_POST["test"];

 

这样写会不会出问题呢? 答案是不会!如果使用者不入中文的

 

中文料的编码处

Delphi的开员绝大多数都是英美系的人,我推因此系的文字示与传输没有法完整的测试,但于我以中文的人来,从电脑诞生的那个年代,中文的示在每个操作、每种通讯协定的设计都比英文来的困

以上面的例子来看,如果我直接拿个例子来测试,笔者写的范例程式,传输资,Server 所抓到的文字并不是正确的中文字,如下所示:              

  

可以看得出来,到 server 的候,server 是不到资讯的。是怎么回事呢?笔者属于不认输的好奇宝宝,使出了身解数,于解决了问题

 

Web 程式的一定可以立刻推出来,这绝对是文字编码问题了,然而,是什么地方出问题?可能出问题的点我列出来跟大家分享:

•  HTTP Client 的 charset

•  HTTP Request 里面的文字编码问题

检查的方向也是从两个关点出,第一点的检测很容易,从Object Inspector检查一下 RESTRequest1的定:

AcceptCharset 确定是 UTF-8,没,所以定不是问题

接着,就要从 Client 端出去的料下手了。有者或『你怎么不怀疑Server端程式写了?』问题很好,之所以排除了问题,是因同一个 Server 端的 PHP 程式,我用了 Postman 做对测试,回果是正确的,因此判定是 Client 端程式的问题

接着笔者从 TRESTRequest.AddParameter 的各种多形式来尝试,AddParameter 个方法有以下几种多的形式:

procedure AddParameter(const AName, AValue: string); overload;
procedure AddParameter(const AName: string; AJsonObject: TJSONObject; AFreeJson: boolean = true); overload;
procedure AddParameter(const AName, AValue: string; const AKind: TRESTRequestParameterKind); overload;

三种形式我都测试过,从 AddParameter 的行中 trace 去看各个可能性,由于 TRESTRequest 的参数中,Get 跟 Post 的加入方法是混用的,在程式里面编码又会有点不同。

在 REST.Client.pas 里面,我曾经怀过编码错误,所以也在各个数都察,最后,找到了原因与解法,至于程,就不多了,花了我两天咧。

原因:编码错误

用HTTP传递中文的候,必用UTF-8编码,但一定要得,中文字在作中,都是UCS32编码象在Windows里面如此,在Android里面如此,在iOS跟Mac我不确定,但理方法是一的。

直接以 AddParameter('name', '中文测试'); 把参数加 TRESTRequest 候,REST.Client.pas 的程式是把 '中文测试' 个字串直接抓 Ord 的料来做编码的,然而,个作法,是的!!!!!!!!

在 HTTP 传递 UTF-8 料的候,我传递的是 UTF-8 文字的二料,但直接把 '中文测试' 个字串直接拿来成二位? 当时编码并不是 UTF-8 啊,当然怎么编码送到 server 都是的!!!!

解法:AddParameter之前先做 UTF-8 转换

个解法,笔者第一天就已想到,只是很想像以前改 Indy 程式一,直接改好 REST.Client.pas 之后,回馈给原厂使用,所以花了不少时间找方法,最后发现这个方法不用到 REST.Client.pas,又能正确理,就直接么跟大家分享了,写成 Delphi 程式如下:

var
     nameStr : String;
begin
   ...
   nameStr := TIdURI.ParamsEncode(nameStr, IndyTextEncoding_UTF8);
   self.RESTRequest1.AddParameter('name', nameStr,      
             TRESTRequestParameterKind.pkGETorPOST,     
             [TRESTRequestParameterOption.poDoNotEncode]);
   ... 
end;

在把字串透 AddParameter 加入参数列之前,我先把字串做个 UTF-8 转换,在里用的是 TIdURI 的类别方法 ParamsEncode,个方法只有两个参数,第一个参数是字串内容,第二个参数是要文字编码的种,在里我选择了 UTF8,写法就是上面范例程式的第一行。

接着,在呼叫 AddParameter 的候,我使用了多形式当中的第三种,要求 AddParameter 料的候不要再我的编码,因我已经处理好了。

这么修改过之后,在各个作业系统当中,执行结果都是正确的,上面两个图以 Windows 作业系统为例,现在我们拿 Android 截图来做为例子:


者可以看到右的截里面,server 回料已是正确的中文字了。

最后,我把 PHP 程式也附上来大家参考:

 1 <?php
 2 date_default_timezone_set("Asia/Taipei");
 3 header('Content-Type: charset=utf-8');
 4 
 5 include("public/DBClassPDO.php");
 6 $objDBPDO = new DBClassPDO();
 7 
 8 $cardNum = $_POST["cardNum"];
 9 $floorIdx = $_POST["floorIdx"];
10 $name = $_POST["name"];
11 $houseNum = $_POST["houseNum"];
12 
13 $params = array();
14 $params['name'] = $name;
15 $params['cardno'] = $cardNum;
16 $params['houseNum'] = $houseNum;
17 $params['floorIdx'] = $floorIdx;
18 $params['picFilename'] = $fileSaveName;
19 $params['created'] = 0;
20 
21 $result["resultCode"] = "0";
22 $result["result"] = "成功"; 
23 $result["sqlcmd"] = $name;
24 
25 $jsonStr = json_encode($result);
26 echo $jsonStr;
27 ?>

是最基本的传递字串,下次再大家示范怎么档案,透 TRESTRequest来做也是很简单的,TRESTRequest 跟 TRESTClient 的确是取代 TIdHTTP 的好工具。

posted @ 2017-06-09 01:23  DenniesChang  阅读(8195)  评论(2编辑  收藏  举报