灵长科技API管理解决方案的核心技术是具备中美知识产权保护的,名为通用设备互联框架(CDIF)的云端应用开发管理软件框架。CDIF可为各类云端应用提供了统一的开发、管理和调用机制。客户端app可通过CDIF提供的统一JSON API调用接口访问由CDIF帮助管理的各类云端后台应用。 CDIF为云端应用提供的统一访问JSON API接口类似于经典的SOAP协议,本质上是HTTP上的RPC(远程过程调用)接口。其调用参数和返回结果全部在HTTP body中传输,安全性比普通的REST API更好,也更容易组织出复杂的API参数请求。同时,通过CDIF提供的JSON API接口,客户端app可获取一份描述某个云端应用包含的服务列表,以及每个服务下包含的所有API信息的JSON文档,该JSON文档由虚拟设备根据CDIF定义的API文档规范生成。文档中包含了所有关于如何调用虚拟设备API接口的信息。客户端app收到该文档后可对其解析、自动生成UI上的相应输入表单;并在收集到用户的输入信息后,调用CDIF的REST接口以访问该虚拟API设备。
CDIF框架代码用node.js书写,底层的云端应用代码全部使用node.js的npm包进行管理。在CDIF的定义中,云端应用使用“设备”这个名称作为应用的基本单位抽象。而每个设备都由各自的npm包中的代码管理。而这些代码的基本框架可以在灵长科技的网站上从上述的API规范自动生成,并在灵长科技网站上在线编辑和发布。云端应用及其提供的API的内部实现可以依赖于node.js丰富的生态中任意一个优秀的第三方包帮助实现,比如request, lodash, moment等等,因此非常容易开发和管理。在用户部署自己的云端应用时,灵长科技网站会自动将这些依赖包安装和部署到网站后台的CDIF应用管理框架中。云端应用可享受到一键部署升级等方便的能力。CDIF为每个虚拟设备生成唯一的device-UUID并进行管理,虚拟设备的device-UUID可通过API市场或者用户自己的私有设备列表查看,也可以由用户在控制台中,将API添加到我的API列表后,通过下面1.2.4节中描述的device-list接口获取。
在访问CDIF提供的API接口时,客户端需要在HTTP请求的header中填入自己在灵长科技网站注册后获得的appKey。此appKey作为客户接入的唯一凭证。其具体格式为:
X-Apemesh-Key: <用户appKey>
CDIF统一提供了以下三条REST接口用于访问灵长科技API市场中的任意一个API虚拟设备,并通过这三条REST接口帮助实现客户端app对API的手动或自动化接入:
以上的get-spec接口返回虚拟设备的API描述JSON文档,schema接口用于获取API参数的完整JSON schema定义,invoke-action则用于对虚拟设备发起API调用并获取返回结果。以下对其分别简要介绍。
调用虚拟设备的API接口。API调用的请求参数和返回结果包含在JSON格式的HTTP请求和响应body中。调用请求必须包含以下信息:
对invoke-action接口的调用必须在HTTP请求header上指定Content-Type: application/json。
invoke-action的调用参数和返回结果统一包含在HTTP请求body和返回结果的input和output JSON对象中。具体格式如下:
POST <APIENTRYADDRESS>/devices/<deviceID>/invoke-action
request body:
{
"serviceID": <id>,
"actionName": <name>,
"input": { ... }
}
API调用的返回结果统一包含在HTTP response body的output对象中,统一为以下格式:
response body:
{
"output": { ... }
}
以上input和outputJSON对象中的具体内容可参考灵长科技API市场中每个虚拟设备的API文档。
成功的invoke-action调用返回HTTP 200状态码。如果发生任何错误,统一返回HTTP 500 状态码,其JSON格式的HTTP response body中包含以下信息:
收到API调用请求后,CDIF将请求数据通过设备抽象层下发到虚拟设备中,由其处理后返回相应结果。
以获取好友最近发布的10条新浪微博的API为例,其接口调用curl范例如下:
curl -H "Content-Type: application/json" -H "X-Apemesh-Key: <用户appKey>" -X POST -d '{"serviceID":"urn:weibo-com:serviceID:微博","actionName":"关注微博","input":{"count": 10}}' <APIENTRYADDRESS>/devices/8014e6d8-0880-5d72-9ad2-b8703811098e/invoke-action
获取虚拟设备JSON格式的API文档。该JSON文档提供SOA,即面向服务架构风格的API描述,其中包含以下信息:
关于API文档格式的进一步说明可参考下面的API描述范例和API文档规范中的内容。
获取API参数的完整JSON schema定义。假设某虚拟设备的API描述文档中某个参数的JSON schema定义为 /foo/bar, 那么以下调用将返回该参数的完整JSON schema对象:
GET <APIENTRYADDRESS>/devices/<device-UUID>/schema/foo/bar
以上URL中schema关键字后跟随的 /foo/bar 字符串是一个JSON指针,其取值可通过上述的get-spec接口获取。举例而言,当API描述文档中某个参数类型的schema关键字取值为 /foo/bar 时,该参数的完整JSON schema定义可从上述接口中获取。具体可参考下一节的新浪微博API范例。
通过以上介绍的三个REST接口,客户端app可获取虚拟API设备提供的描述文档和其中包含的JSON schema定义,在集成react-schema-form,或者angular-schema-form这样的第三方组件后,将被CDIF框架管理的API动态地展现在app UI上供用户使用。schema form组件会根据JSON schema的定义将表单渲染在UI上,并帮助收集用户的输入到数据对象中,app只需将数据对象包装在input JSON对象中,以上述的格式发给invoke-action接口即可完成调用。同时,schema form规范提供了许多选项,可供客户端精确地控制表单的生成方式。
在此基础上,当虚拟设备的API发生变化时,比如新增API、已有API的参数定义发生变化,其API文档会随之发生变化并可从get-spec接口获取。用户可以通过客户端app实时地看到变化后的接口并发起新的访问请求。
我们提供了一个基于react-schema-form的开源范例,描述了如何通过CDIF将被其管理的REST API动态地展现在客户端app UI上供用户使用,并可在底层API接口发生变化时,将变化后的接口自动展示在app UI中。关于schema form和类似技术的更多信息可参考http://schemaform.io。
如果客户端app不希望采用schema form这样的技术自动生成app UI,也可以通过传统的手动方式,通过CDIF提供的REST接口将API集成进app中。灵长科技网站的API市场中对每个虚拟API设备有详细API说明文档,以帮助实现这一过程。此时,客户端app可根据灵长科技网站API市场中的API文档说明访问上述的invoke-action接口,而忽略对另两个接口的使用。
除上述的三条REST接口外,CDIF提供了以下接口帮助客户端app获取某个appKey添加过的所有虚拟设备列表:
该调用将返回用户某个appKey所有添加过的device UUID,以及他们的设备基本信息,调用具体格式如下:
GET <APIENTRYADDRESS>/device-list
response:
{
device-UUID1: {...}, device-UUID2: {...}
}
该调用永远返回HTTP 200 OK状态码
本节以一个简单的新浪微博API为例,介绍CDIF为客户端app提供的API规范。以下的JSON文档描述了一个获取关注用户最新微博的API接口定义。客户端app通过CDIF的get-spec接口获取该文档后,配合上述的schema接口即可获得所有关于如何调用该API的全部信息:
{
"specVersion": {
"major": 1,
"minor": 0
},
"device": {
"friendlyName": "Weibo",
"manufacturer": "Sina Inc.",
"manufacturerURL": "http://www.weibo.com",
"iconList": [
{
"mimetype": "image/png",
"width": 1000,
"height": 800,
"depth": 8,
"url": "https://dl6vjmn951u44.cloudfront.net/assets/d7213db550a384267fcb152ed7b57c2b/images/weibo.png"
}
],
"serviceList": {
"urn:weibo-com:serviceID:微博": {
"actionList": {
"关注微博": {
"argumentList": {
"input": {
"direction": "in",
"relatedStateVariable": "A_ARG_TYPE_friendsTimelineArgs"
},
"output": {
"direction": "out",
"relatedStateVariable": "A_ARG_TYPE_friendsTimelineResult"
}
}
}
},
"serviceStateTable": {
"A_ARG_TYPE_friendsTimelineArgs": {
"dataType": "object",
"schema": "/weibo/statuses/friendsTimelineArgs"
},
"A_ARG_TYPE_friendsTimelineResult": {
"dataType": "object",
"schema": "/weibo/statuses/friendsTimelineResult"
}
}
}
}
}
}
以上的JSON文档中提供的信息及其说明如下:
字段名称 | 取值范例 | 说明 |
---|---|---|
specVersion | { "major": 1, "minor": 0 } | CDIF规范版本号,取值必须是范例中的对象 |
friendlyName | 设备名称 | |
manufacturer | Sina Inc. | 厂商名称 |
manufacturerURL | http://www.weibo.com | 厂商网址 |
iconList |
{
"mimetype": "image/png",
"width": 1000,
"height": 800,
"depth": 8,
"url": "https://dl6vjmn951u44.cloudfront.net/assets/ d7213db550a384267fcb152ed7b57c2b/images/weibo.png" } |
API图标信息,以及图标所在URL |
serviceList | 略 | 服务列表。本字段以下可包含一个到多个服务名称及其内部的API定义 |
urn:weibo-com:serviceID:微博 | 略 | 服务名称 |
actionList | 略 | API列表。本字段下可包含一个到多个API名称及其参数定义 |
"关注微博" | 略 | API名称 |
argumentList | 略 | API参数列表,本字段下最多只能包含两个参数,一个名为input,表示输入参数,另一个名为output,表述返回结果 |
input | { "direction": "in", "relatedStateVariable": "A_ARG_TYPE_friendsTimelineArgs" } | 本字段下direction关键字取值为in时表示这是一个输入参数,其具体类型定义在下面serviceStateTable字段的A_ARG_TYPE_friendsTimelineArgs中定义 |
output | { "direction": "out", "relatedStateVariable": "A_ARG_TYPE_friendsTimelineResult" } | 本字段下direction关键字取值为out时表示这是一个返回结果,其具体类型定义在下面serviceStateTable字段的A_ARG_TYPE_friendsTimelineResult中定义 |
serviceStateTable | 略 | 本字段下包含API参数的具体类型定义 |
dataType | object | 本字段取值必须是object |
schema | /weibo/statuses/friendsTimelineArgs | 本字段提供参数的JSON schema定义。该字段的取值是一个JSON指针。在获取该指针的内容后,客户端可从以下的地址获取参数的完整JSON schema定义: GET <APIENTRYADDRESS>/devices/<device-UUID>/schema/weibo/statuses/friendsTimelineArgs |
在以上范例中,访问以下地址:
GET <APIENTRYADDRESS>/devices/8014e6d8-0880-5d72-9ad2-b8703811098e/schema/weibo/statuses/friendsTimelineArgs
将返回以下的参数JSON schema定义:
{
"type": "object",
"properties": {
"since_id": {
"type": "integer"
},
"count": {
"type": "integer"
},
"max_id": {
"type": "integer"
},
"page": {
"type": "integer"
},
"base_app": {
"type": "integer"
},
"feature": {
"type": "boolean"
},
"trim_user": {
"type": "boolean"
}
},
"additionalProperties": false
}
本节以JSON schema格式完整地描述CDIF提供的统一API规范中各个字段的详细描述。 在CDIF代码实现中,以下的JSON schema文件被用于校验所有CDIF支持的设备提供的API规范的合法性:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"title": "CDIF device root schema",
"description": "Schema specification for CDIF device model",
"properties": {
"configId": {
"type": "integer",
"maximum": 16777216,
"minimum": 0
},
"specVersion": {
"type": "object",
"properties": {
"major": {
"type": "integer",
"enum": [
1
]
},
"minor": {
"type": "integer",
"enum": [
0
]
}
},
"additionalProperties": false,
"required": [
"major",
"minor"
]
},
"device": {
"description": "Schema for device object",
"type": "object",
"properties": {
"deviceType": {
"type": "string",
"pattern": "^urn\\:[ \\S]{1,64}\\:device\\:[ \\S]{1,64}\\:[0-9]{1,8}$"
},
"friendlyName": {
"type": "string",
"maxLength": 64,
"minLength": 0
},
"manufacturer": {
"type": "string",
"maxLength": 64,
"minLength": 0
},
"manufacturerURL": {
"type": "string",
"format": "uri"
},
"modelDescription": {
"type": "string",
"maxLength": 256,
"minLength": 0
},
"modelName": {
"type": "string",
"maxLength": 128,
"minLength": 0
},
"modelNumber": {
"type": "string",
"maxLength": 128,
"minLength": 0
},
"serialNumber": {
"type": "string",
"maxLength": 128,
"minLength": 0
},
"price": {
"type": "string",
"maxLength": 256,
"minLength": 1
},
"UDN": {
"title": "Schema for device UUID",
"type": "string",
"maxLength": 36,
"minLength": 36,
"pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
},
"UPC": {
"type": "string",
"maxLength": 32,
"minLength": 0
},
"userAuth": {
"type": "boolean"
},
"devicePresentation": {
"type": "boolean"
},
"powerIndex": {
"type": "number",
"minimum": 0
},
"iconList": {
"type": "array",
"maxItems": 5,
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "object",
"properties": {
"mimetype": {
"type": "string",
"maxLength": 18,
"minLength": 7,
"pattern": "^image/[a-zA-Z0-9\\+\\.]{1,12}$"
},
"width": {
"type": "integer",
"maximum": 9999,
"minimum": 1
},
"height": {
"type": "integer",
"maximum": 9999,
"minimum": 1
},
"depth": {
"type": "integer",
"maximum": 99,
"minimum": 1
},
"url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": false,
"required": [
"mimetype",
"width",
"height",
"depth",
"url"
]
}
},
"serviceList": {
"type": "object",
"maxProperties": 32,
"minProperties": 0,
"patternProperties": {
"^urn\\:[\\S]{1,64}\\:serviceID\\:[\\S]{1,64}$": {
"type": "object",
"properties": {
"serviceType": {
"type": "string",
"pattern": "^urn\\:[ \\S]{1,64}\\:service\\:[ \\S]{1,64}\\:[0-9]{1,8}$"
},
"actionList": {
"type": "object",
"maxProperties": 64,
"minProperties": 0,
"patternProperties": {
"^[\\S]{1,128}$": {
"type": "object",
"properties": {
"argumentList": {
"type": "object",
"maxProperties": 32,
"minProperties": 1,
"patternProperties": {
"^[\\S]{1,128}$": {
"type": "object",
"properties": {
"direction": {
"type": "string",
"enum": [
"in",
"out"
]
},
"retval": {
"type": "boolean"
},
"relatedStateVariable": {
"type": "string",
"maxLength": 128,
"minLength": 1
}
},
"required": [
"direction",
"relatedStateVariable"
]
}
},
"additionalProperties": false
},
"realPrice": {
"type": "number"
},
"priceInfo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"price": {
"type": "number"
},
"count": {
"type": "number"
}
},
"required": [
"price",
"count"
]
}
},
"freeCount": {
"type": "integer"
},
"apiCache": {
"type": "number"
},
"apiLog": {
"type": "boolean"
},
"fault": {
"type": "object",
"properties": {
"schema": {
"type": "string"
}
},
"required": [
"schema"
]
}
},
"required": [
"argumentList"
]
}
},
"additionalProperties": false
},
"serviceStateTable": {
"type": "object",
"maxProperties": 256,
"minProperties": 1,
"patternProperties": {
"^[\\S]{1,128}$": {
"type": "object",
"properties": {
"sendEvents": {
"type": "boolean"
},
"dataType": {
"type": "string",
"enum": [
"string",
"boolean",
"integer",
"number",
"object"
]
},
"schema": {
"type": "string"
},
"defaultValue": {
"type": [
"boolean",
"integer",
"number",
"string"
],
"anyOf": [
{
"type": "string",
"maxLength": 1024,
"minLength": 1
},
{
"type": "boolean"
},
{
"type": "integer"
},
{
"type": "number"
}
]
},
"allowedValueRange": {
"type": "object",
"properties": {
"minimum": {
"type": "number"
},
"maximum": {
"type": "number"
},
"step": {
"type": "number"
}
},
"additionalProperties": false,
"required": [
"minimum",
"maximum"
]
},
"allowedValueList": {
"type": "array",
"maxItems": 256,
"minItems": 1,
"uniqueItems": true,
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "number"
}
]
}
}
},
"required": [
"dataType"
]
}
},
"additionalProperties": false
}
},
"required": [
"actionList",
"serviceStateTable"
]
}
},
"additionalProperties": false
},
"deviceList": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/properties/device"
}
}
},
"required": [
"friendlyName",
"manufacturer",
"modelDescription"
]
}
},
"additionalProperties": false,
"required": [
"specVersion",
"device"
]
}
Revision | Author | Description | Date |
0.1 | Miaobo Chen | Initial draft | Nov 7, 2016 |
0.2 | Miaobo Chen | text and spec update | Oct 5, 2017 |