web.rest
下面的支持库最简单的用法就是作为一个 HTTP客户端使用,该客户端对象简化了get
,post
,put
,patch
,delete
等常用的 HTTP请求操作,并提供编码请求数据、解码返回数据的功能。
标准库中用于调用 REST API 的库:
web.rest.client
请求参数使用urlencode编码,服务器返回文本数据。web.rest.xmlClient
请求参数使用urlencode编码,服务器返回xml格式数据。web.rest.jsonLiteClient
请求参数使用urlencode编码,服务器返回JSON格式数据。web.rest.jsonClient
请求参数与服务器返回数据都使用JSON格式。
除了与服务器交互的数据格式不同以外, 这几个库的接口用法完全一样,可以看看这几个库的源码实际上他们都是调用 web.rest.client 这一个库。
基本的 HTTP 请求
web.rest下面的支持库最简单的用法就是作为一个HTTP客户端使用,该客户端对象简化了get
,post
,put
,patch
,delete
等常用的HTTP请求操作,并提供编码请求数据、解码返回数据的功能,下面是一个最简单的示例:
1 | import console; |
从上面的示例可以看出,我们上传参数的是aardio
中的对象,返回的数据也被自动解码为aardio
对象,虽然 HTTP传输使用的是 JSON
数据,但使用时不需要去管 JSON
的编解码等一系列的操作。
转换为 AArdio 函数
web.rest
不仅仅可以用来做上面这些简单的 HTTP请求、以及编解码的操作,他还可以将基本符合 REST风格的 Web API转换为aardio
中的函数对象,这非常有意思,REST本身不是一个严格的规范、更缺乏WebService
那样的WSDL
接口描述服务,但是aardio
设计了一种简单可行的声明语法,可以非常方便的把混乱的 Web API转换为统一的 aardio函数。
首先我们看一下 REST API的 URL 一般会是这种格式 http://主机/资源目录名/资源目录名/资源名aardio
的web.rest
库模块中的客户端对象提供一个 api 函数用于声明一个API接口,api 函数的定义如下:
var restApi = restClient.api("接口URL描述","默认HTTP请求动词")
其中接口URL描述可以直接指定一个web api的网址,在该网址中还可以使用变量,变量放在花括号中,例如:http://主机/{变量名}/资源目录名/资源名 aardio
并不关心变量名的内容是什么,只关心它们出现的前后顺序,当调用restApi
的成员函数时会使用函数名替换接口 URL 中的变量生成新的请求URL。
下面是一个简单的示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import console;
import web.rest.jsonClient;
// 创建REST客户端
var restClient = web.rest.jsonClient();
//声明一个API接口,第一个参数指定URL描述
var restApi = restClient.api("http://httpbin.org/api/{program}/{lang}")
/*
下面调用接口函数,
在请求时下面代码中的接口名"language"替换接口URL描述中的变量{program}
接口名"aardio"则替换接口URL描述中的变量{lang}
最后生成的请求URL为 http://httpbin.org/api/language/aardio
*/
var result = restApi.language.aardio()
console.log("请求的URL", restClient.lastRequestUrl)
restClient.lastResponse(); //输出服务端最后返回的数据
console.pause();
接口 URL中连接的变量名还可以合并为{...}
例如 http://httpbin.org/api/{program}/{lang}
可以简写为 http://httpbin.org/api/{...}
当 {...}
出现在尾部时还可以直接省略,例如 http://httpbin.org/api/
注意head,get,post,put,patch,delete等默认的HTTP请求操作作为函数名时不会被添加到生成的URL中。
这些默认的HTTP方法名在 web.rest.client._defaultMethod
中指定,例如使用 restApi.language.get()
显示的指定 HTTP请求动词为GET
。如果不指定 HTTP请求动词,则使用调用 restClient.api("接口URL描述","默认HTTP请求动词")
函数时第二个参数指定的 HTTP请求动词,不指定该参数时默认为POST
。
HTTP规定了九种动词(Verbs)
用于指定请求方法:GET
,HEAD
,POST
,DEBUG
,PUT
,DELETE
,PATCH
,OPTIONS
,
而在REST API中
用到的有五种 GET
,POST
,PUT
,DELETE
,PATCH
,他们的用途如下:
GET
:用于获取数据POST
: 用于创建数据PUT
: 用于替换数据、也可用于更新数据DELETE
: 用于删除数据PATCH
:用于更新数据
上传下载文件
如果一个REST API在请求时需要上传、下载文件,那么所有调用规则如前不变。
你仅仅需要做的是,在调用API以前指定接受、或发送文件的回调函数以获取上传、下载的进度。
上传文件示例:
1 | restClient.sendFile( "上传文件路径" |
下载文件示例:
1 | restClient.receiveFile( "上传文件路径" |
web.rest
也可以支持 multipart/form-data
编码上传文件,示例:
1 | import console; |
客户端对象的错误处理
web.rest
客户端对象的错误处理与inet.http
相同:
请求成功返回服务器数据,失败返回空值,错误信息,错误代码等。
注意下面为了演示所有的细节,代码写的比较长,实际开发中不必要写的这么细
1 | import console; |
当然上面的代码一般在调试故障时才需要,一般没必要把错误处理写的这么细,上面的代码也可以简化如下:
1 | import web.rest.jsonLiteClient; |
服务端接口设计原则
本文参考了 REST API的设计风格、但有部分规则有所变通。在实践中 REST API 完全符合REST规则的比较少见,我在设计 web.rest.client 试图编写一个尽可能通用的支持库、但是发现REST API的实现真是五花八门,而且也缺乏一个统一的接口描述规则。
如果你需要为你的aardio客户端程序设计自己的Web API,那么参考下面的几条原则去实现服务端接口 - 这可以让标准库里 web.rest.client 方便的支持该API( 请参考:使用 web.rest 调用 REST API ).
资源定位路径
URL应用于清晰的展现资源定位路径,目录应当使用清晰的资源名称,并可以使用统一的URL接口描述语法声明该API。
例如:http://主机/资源分类/资源目录/资源名/资源ID 原则上不应当把资源名放在URL参数里,
但是要使用这样的友好URL在现实中是用一定代价的,对于一般的Web服务器这可能需URL重写,有一定的性能负担。所以也可以将资源名放到URL参数里,例如 HTTP://主机/资源分类?资源目录=目录名&资源名=资源名&资源ID=资源ID 要注意这里的资源定位有关的参数应当直接放到URL的参数里也就是?号后面,要将资源定位的参数与HTTP提交的参数分开来( 如果你用过 web.rest.client
就知道为什么要这样做 ),并且要按资源定位关系决定参数出现的先后关系。
最后生成的URL要能使用以下的URL描述规则:
URL中的资源名应当能使用{模板变量}
代替、{模板变量}
的先后关系应当对应资源名的出现顺序。{模板变量}
包含在花括号里 - 可以使用多个数字或字母,数值的大小并不重要,URL描述仅关心资源出现的先后关系。可以使用 {...}
表示不定个数的模板变量。
http://主机/资源分类/资源目录/资源名/资源ID 使用URL描述语法转换结果就是这样: http://主机/{res}/{category}/{name/{id} 也可以使用 http://主机/{res}/{…} 表示。如果 {...}
出现在最后则可以省略
HTTP://主机/资源分类?资源目录=目录名&资源名=资源名&资源ID=资源ID
使用URL描述语法转换以后: HTTP://主机/{res}?资源目录={category}&资源名={name}&资源ID={id}
可以看到资源名是不是写到参数里都能清晰的展现资源定位,要注意 Web API 并不是浏览器,URL并不会出现在浏览器的地址栏,设计一个友好的 API URL 重要的是编程语言里能不能更好的理解并自动分析转换。 例如aardio
中的 web.rest.client
就按照这种 URL 描述语法自动的将 URL 描述转换为aardio
中的函数对象。
不包含动词
原则上URL不应当包含动词,使用HTTP协议的指令动词表示要执行的操作:
GET: 表示获取资源
POST: 表示新增数据
PUT: 表示替换数据
DELETE: 表示删除数据
PATCH: 表示更新数据
一般的Web服务器因为安全方面的考虑对HEAD、GET、POST之外的请求有所限制, 很多API用POST替代PUT,DELETE的功能,而又要做到URL中不出现动词,就背离了REST的初衷了。
因此建议可选在URL资源定位的最可选性的添加扩展的操作动词,例如:
http://host/group/user/userid/ 使用get读取用户信息
http://host/group/user/userid/password/change 使用扩展的change方法修改用户密码
如果按这种规则实现服务端的API,那么在aardio里用 web.rest.client 调用起来就很方便,示例:
1 | import web.rest.jsonClient; |
为什么不直接在每一个请求里写具体的URL呢?要考虑到实现一个API的扩展库,API服务端的地址可能发生变更,使用上面的方法就可以简单的维护一个声明URL参数即可。
不出现文件后缀名
Web API 的URL中不应出现文件后缀名:
例如: http://host/x/y.php 应当在服务器上移动到 http://host/x/y/index.php ,然后提供给客户端的API应隐藏默认的文档名,即 http://host/x/y/ 这样的好处是服务端变更实现会非常方便。
不出现 IP 地址
Web API 的URL中不应出现IP地址,即使是测试期间,也应尽可能的使用域名替代IP地址。