數(shù)據(jù)總線
1. 數(shù)據(jù)集成介紹
數(shù)據(jù)集成標準化的目標是規(guī)范應用之間數(shù)據(jù)的傳遞方式和表達方式。
傳遞方式:即應用之間的數(shù)據(jù)如何流通。平臺提供了對數(shù)據(jù)進行增刪改查的4個API,以及HTTP2方式的消息訂閱機制。
表達方式:即應用之間如何對數(shù)據(jù)內(nèi)容有一致的理解。為了實現(xiàn)這個目標,需要做到如下兩點,一是數(shù)據(jù)結(jié)構(gòu)需要由小二后臺統(tǒng)一管控;二是應用集成對接之前(比如應用上架的時候)需要聲明本應用對哪些數(shù)據(jù)模型產(chǎn)生什么樣的數(shù)據(jù)操作(如查詢、新增,或者訂閱)。
基于以上邏輯,為了實現(xiàn)應用的數(shù)據(jù)集成能力,分別需要執(zhí)行相應的應用聲明、應用開發(fā)、應用集成。
2. 應用聲明
應用的聲明是指應用在上架到市場時,由應用開發(fā)者自行聲明的,包含兩部分聲明內(nèi)容:應用涉及到的數(shù)據(jù)模型;以及對這些模型所做的相應的操作。這里指出應用對數(shù)據(jù)模型的操作,目的有兩個,一是用戶可以感知應用對數(shù)據(jù)的操作范圍,二是平臺根據(jù)聲明的操作決定對應用的操作權(quán)限。
3. 應用集成
所謂的“數(shù)據(jù)集成”,是指應用之間數(shù)據(jù)共享的方式,比如兩個應用,當涉及到相同的數(shù)據(jù)模型時,是共享一份數(shù)據(jù),還是獨立管理各自的數(shù)據(jù)。通過應用聲明,應用在上架到應用市場之后,用戶就能感知應用與數(shù)據(jù)之間的關(guān)系。目前通用的集成方案是以項目為隔離維度的。也就是說,在一個項目內(nèi)的所有應用,他們關(guān)聯(lián)的相同的數(shù)據(jù)模型,會被默認放在同一個隔離區(qū)域ScopeID內(nèi)。
4. 應用開發(fā) - 數(shù)據(jù)操作
應用之間共享數(shù)據(jù)時,需要向提平臺操作數(shù)據(jù),或者從平臺訂閱數(shù)據(jù)。數(shù)據(jù)操作方面,這里主要涉及4個接口:新增數(shù)據(jù)、查詢數(shù)據(jù)、刪除數(shù)據(jù)、修改數(shù)據(jù)。詳見后面的API參考。同時,為了便于應用開發(fā),平臺提供了SDK,簡化開發(fā)。
4.1 SDK介紹
4.1.1 Java SDK
1. 依賴
依賴的三方庫:
<dependency>
<groupId>com.aliyun.api.gateway</groupId>
<artifactId>sdk-core-java</artifactId>
<version>1.0.4</version>
</dependency>
依賴的工具類:
git clone https://github.com/aliyun/iotx-api-gateway-client.git
2. 示例代碼
import com.alibaba.cloudapi.sdk.model.ApiResponse;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.iotx.api.client.IoTApiClientBuilderParams;
import com.aliyun.iotx.api.client.IoTApiRequest;
import com.aliyun.iotx.api.client.SyncApiClient;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
public class Demo {
// 依賴的工具類從 https://github.com/aliyun/iotx-api-gateway-client 獲取
public static void main(String[] args) throws UnsupportedEncodingException{
String appKey = "YOUR_APP_KEY";
String appSecret = "YOUR_APP_SECRET";
String modelId = "YOUR_MODEL_ID";
String dataId = "YOUR_DATA_ID";
dataQuery(appKey, appSecret, modelId, dataId);
}
public static void dataQuery(String appKey, String appSecret,String modelId,String dataId) throws UnsupportedEncodingException {
IoTApiClientBuilderParams ioTApiClientBuilderParams =
new IoTApiClientBuilderParams();
// 填寫應用的appkey信息
ioTApiClientBuilderParams.setAppKey(appKey);
ioTApiClientBuilderParams.setAppSecret(appSecret);
SyncApiClient syncClient = new SyncApiClient(ioTApiClientBuilderParams);
IoTApiRequest request = new IoTApiRequest();
// 設(shè)置api的版本
request.setApiVer("0.0.5");
// 接口參數(shù)
request.putParam("modelId",modelId);
ArrayList returnFields = new ArrayList();
returnFields.add("*");
request.putParam("returnFields", returnFields);
JSONArray conditions = new JSONArray();
JSONObject condition = new JSONObject();
condition.put("fieldName", "id");
condition.put("operate", "eq");
condition.put("value", dataId);
conditions.add(condition);
request.putParam("conditions", conditions);
ApiResponse response = syncClient.postBody("api.link.aliyun.com",
"/data/model/data/query", request, true);
int code = response.getCode();
String res = new String(response.getBody());
System.out.println("response code = " + response.getCode() + " response = " + new String(response.getBody(), "UTF-8"));
}
}
4.1.2 Python SDK
1. 依賴
依賴的三方庫:無
2. 示例代碼
Step 1: 下載示例工程代碼。示例工程基于Python 2.7,Python3請自行適配。
git clone https://github.com/aliyun/api-gateway-demo-sign-python.git
Step 2: 修改signature_composer.py文件第49行
修改前: string_to_sign.append(_build_resource(uri=uri, body=body))
修改后: string_to_sign.append(uri)
Step 3: 修改ClientDemo.py如下,執(zhí)行調(diào)用
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import json
sys_path = sys.path[0]
if not os.path.isdir(sys_path):
sys_path = os.path.dirname(sys_path)
os.chdir(sys_path)
from com.aliyun.api.gateway.sdk import client
from com.aliyun.api.gateway.sdk.http import request
from com.aliyun.api.gateway.sdk.common import constant
host = "https://api.link.aliyun.com"
url = "/data/model/data/query"
cli = client.DefaultClient(app_key="YOUR_APP_KEY", app_secret="YOUR_APP_SECRET")
req_post = request.Request(host=host, protocol=constant.HTTPS, url=url, method="POST", time_out=30000)
req_post.set_content_type(constant.CONTENT_TYPE_STREAM)
body = {
"request":{
"apiVer":"0.0.2"
},
"params":{
"modelId":"EMPLOYEE",
"returnFields":["employee_no", "name"],
"conditions":[{"fieldName":"id","value":"1","operate":"mt"}],
"pageSize": 100,
"pageNum": 1
},
"version":"1.0"
}
bb = json.dumps(body).encode("utf-8")
req_post.set_body(bb)
print '==================================================='
print str(cli.execute(req_post)).decode('string_escape')
print '==================================================='
4.1.3 JS SDK
1. 依賴
$ # save into package.json dependencies with -S
$ npm install aliyun-api-gateway -S
$ # you can use cnpm for fast install
$ cnpm install aliyun-api-gateway -S
2. 示例代碼
'use strict';
const co = require('co');
const Client = require('aliyun-api-gateway').Client;
const client = new Client('YOUR_APP_KEY','YOUR_APP_SECRET');
co(function* () {
var url = 'http://api.link.aliyun.com/test1234';
var result = yield client.post(url, {
data: {
'testtest': 'query1Value'
},
headers: {
accept: 'application/json'
}
});
console.log(JSON.stringify(result));
});
4.2 數(shù)據(jù)操作API
4.2.1 API 描述
API名稱 | API描述 | API Path | API 版本 |
新增數(shù)據(jù) | 基于已經(jīng)創(chuàng)建且被授權(quán)寫入的模型,進行數(shù)據(jù)的新增。 | /data/model/data/insert | 0.0.3 |
刪除數(shù)據(jù) | 基于已經(jīng)創(chuàng)建且被授權(quán)刪除的模型,進行數(shù)據(jù)的更新。 | /data/model/data/delete | 0.0.2 |
修改數(shù)據(jù) | 基于已經(jīng)創(chuàng)建且被授權(quán)更新的模型,進行數(shù)據(jù)的更新。 | /data/model/data/update | 0.0.2 |
查詢數(shù)據(jù) | 基于已經(jīng)創(chuàng)建且被授權(quán)查詢的模型,進行數(shù)據(jù)的查詢。 | /data/model/data/query | 0.0.3 |
獲取文件上傳地址 | 對于模型中指定為“圖片”標簽的字段,在該字段需要填寫文件名,并且文件名需要通過該接口獲取,該接口同時還會返回一個URL供用戶上傳文件。該地址有效期是10秒。 | /data/model/data/upload | 0.0.1 |
獲取文件下載地址 | 對于模型中指定為“圖片”標簽的字段,在該字段的內(nèi)容是一個系統(tǒng)分配的文件名,用戶可以通過本接口,傳入這個文件名,獲取真實的文件下載地址。該地址有效期是10秒。 | /data/model/data/download | 0.0.1 |
4.2.2 API 約定
日期類型的參數(shù)傳入格式:當前時間到格林威治時間1970年01月01日00時00分00秒的毫秒數(shù)。
數(shù)量查詢的一些約定:
單次查詢最多返回200條數(shù)據(jù),未指定分頁參數(shù)情況下,查詢返回滿足條件的前200條,可根據(jù)返回參數(shù)中的hasNext判斷是否有更多數(shù)據(jù)。
若需要按照指定條件返回數(shù)據(jù)總數(shù),則指定返回參數(shù)為COUNT,API返回參數(shù)中會帶有COUNT以及對應的值。
更新刪除的約定:單次操作,最多支持200條數(shù)據(jù)。
運算符定義:
運算符 | 含義 | 備注 |
eq | equals | 相等 |
neq | not equals | 不相等 |
lt | less than | 小于 |
lteq | less than or equals | 小于等于 |
mt | more than | 大于 |
mteq | more than or equals | 大于等于 |
bt | between | 在..之間 |
in | in | 在..之內(nèi) |
nin | not in | 不在..之內(nèi) |
nul | is null | 為空 |
nnul | is not null | 不為空 |
數(shù)據(jù)包括系統(tǒng)屬性,API中不允許賦值和更新系統(tǒng)屬性,系統(tǒng)屬性如下:
屬性 | 描述 |
id | 數(shù)據(jù)主鍵 |
creator | 數(shù)據(jù)創(chuàng)建者 |
modifier | 數(shù)據(jù)修改者 |
gmt_create | 數(shù)據(jù)創(chuàng)建時間 |
gmt_modified | 數(shù)據(jù)修改時間 |
4.2.3 數(shù)據(jù)新增API
1. 請求參數(shù)
參數(shù) | 類型 | 描述 | 是否必傳 |
modelId | String | 數(shù)據(jù)模型id | 是 |
properties | JSON | 數(shù)據(jù)字段鍵值對,增加的字段的鍵只能是模型包含的字段,否則會報錯,其中 Boolean屬性的property傳入"true"和"false"或者0和1。如:{"name":"xxx", "age":18} | 是 |
scopeId | String | 經(jīng)常是項目的id,該參數(shù)非必填,一般上架應用被授權(quán)之后,會被默認綁定到一個scope中,因此當前操作會被默認操作到被綁定到的這一個scope中。但是,對于集成應用,也有可能被綁定到多個scope中,此時該操作需要填入scopeId。 | 否 |
appId | String | 對于SaaS應用,需要填該值 | 否 |
2. 返回參數(shù)
參數(shù) | 類型 | 描述 |
data | Long | 數(shù)據(jù)主鍵id |
3. 請求示例
{
"request": {
"apiVer": "0.0.3" // api版本號
},
"id": 1508212818676,// request里的全局唯一id透傳
"params": {
"modelId":"XXX123", //數(shù)據(jù)模型id
"scopeId":"fdbsdj1dfjdubgxxx", //數(shù)據(jù)模型id
"properties":{
"BRAND":"BMW",
"MODE":"5",
"CREATE_DATE":1526969423,
"LONG_SIZE":3.3,
"LONG_SIZE":2.2
}
},
"version": "1.0" // 請求協(xié)議版本
}
4. 返回示例
{
"code": 200,
"message": "success",
"localizedMsg": null,
"data": 12345
}
5. 返回碼
狀態(tài)碼 | 描述 | 其他說明 |
200 | 成功 | |
460 | 參數(shù)驗證異常 | 會帶有驗證異常的詳細說明 |
500 | 服務異常 | server error |
52002 | 無訪問權(quán)限 | |
52005 | 找不到目標存儲 | |
52009 | 參數(shù)和模型定義不匹配 | |
52011 | 數(shù)據(jù)類型校驗錯誤 |
4.2.4 數(shù)據(jù)刪除API
1. 請求參數(shù)
參數(shù) | 類型 | 是否必傳 | 描述 |
modelId | String | 是 | 數(shù)據(jù)模型id |
conditions | JSON | 否 | 數(shù)據(jù)條件,由字段名、運算符、比較值組成一個condition。如:[{"fieldName": "id", "operate": "eq", "value": 7}] |
appId | String | 否 | 對于SaaS應用,需要填該值 |
scopeId | String | 否 | 經(jīng)常是項目的id,該參數(shù)非必填,一般上架應用被授權(quán)之后,會被默認綁定到一個scope中,因此當前操作會被默認操作到被綁定到的這一個scope中。但是,對于集成應用,也有可能被綁定到多個scope中,此時該操作需要填入scopeId。 |
其中condition針對不同類型的屬性支持的運算符:
屬性 | 支持的運算符 |
Integer | eq,neq,lt,lteq,mt,mteq,bt,in,nin,nnul |
String | eq,neq,nnul,in,nin |
Double | eq,neq,lt,lteq,mt,mteq,bt,in,nin,nnul |
Boolean | eq,nnul |
Date | eq,neq,lt,lteq,mt,mteq,bt,nnul |
2. 返回參數(shù)
參數(shù) | 類型 | 描述 |
data | Integer | 刪除數(shù)據(jù)的條數(shù) |
3. 請求示例
{
"request": {
"apiVer": "0.1.0" // api版本號
},
"id": 1508212818676,// request里的全局唯一id透傳
"params": {
"modelId":"XXX123",
"conditions":[
{"fieldName":"BRAND","value":"BMW","operate":"eq"},
{"fieldName":"MODE","value":"X5","operate":"eq"}
]
},
"version": "1.0" // 請求協(xié)議版本
}
4. 返回示例
{
"code": 200,
"message": "success",
"localizedMsg": null,
"data":100
}
5. 返回碼
狀態(tài)碼 | 描述 | 說明 |
200 | 成功 | |
460 | 參數(shù)驗證異常 | 會帶有驗證異常的詳細說明 |
500 | 服務異常 | server error |
52002 | 無訪問權(quán)限 |
4.2.5 數(shù)據(jù)修改API
1. 請求參數(shù)
參數(shù) | 類型 | 是否必傳 | 描述 |
modelId | String | 是 | 數(shù)據(jù)模型id |
updateDetails | JSON | 是 | 更新的具體字段和值,字段需要是模型中包含的字段,否則會報錯,其中 Boolean屬性的property傳入"true"和"false"或者0和1。如:{"name": "xxxx", "age":20} |
conditions | JSON | 否 | 條件,由字段名、運算符、比較值組成一個condition,格式如下:[{"fieldName": "id", "operate": "eq", "value": 7}]。fieldName表示字段的名稱;operate表示操作符,操作符見上表;value表示值。 |
appId | String | 否 | 對于SaaS應用,需要填該值 |
scopeId | String | 否 | 經(jīng)常是項目的id,該參數(shù)非必填,一般上架應用被授權(quán)之后,會被默認綁定到一個scope中,因此當前操作會被默認操作到被綁定到的這一個scope中。但是,對于集成應用,也有可能被綁定到多個scope中,此時該操作需要填入scopeId。 |
其中condition針對不同類型的屬性支持的運算符:
屬性 | 支持的運算符 | |
Integer | eq,neq,lt,lteq,mt,mteq,bt,in,nin,nnul | |
String | eq,neq,nnul,in,nin | |
Double | eq,neq,lt,lteq,mt,mteq,bt,in,nin,nnul | |
Boolean | eq,nnul | |
Date | eq,neq,lt,lteq,mt,mteq,bt,nnul |
2. 返回參數(shù)
參數(shù) | 類型 | 描述 |
data | Integer | 更新數(shù)據(jù)的條數(shù) |
3. 請求示例
{
"request": {
"apiVer": "0.0.3" // api版本號
},
"id": 1508212818676,// request里的全局唯一id透傳
"params": {
"modelId":"XXX123",
"conditions":[
{"fieldName":"BRAND","value":"BMW","operate":"eq"},
{"fieldName":"MODE","value":"X5","operate":"eq"}
],
"updateDetails":{
"LONG_SIZE":4,
"WIDTH_SIZE":3
}
},
"version": "1.0" // 請求協(xié)議版本
}
4. 返回示例
{
"code": 200,
"message": "success",
"localizedMsg": null,
"data":100
}
5. 返回碼
狀態(tài)碼 | 描述 | |
200 | 成功 | |
460 | 參數(shù)驗證異常 | 會帶有驗證異常的詳細說明 |
500 | 服務異常 | server error |
52002 | 無訪問權(quán)限 | |
52006 | 資源更新錯誤 | |
52011 | 數(shù)據(jù)類型校驗錯誤 |
4.2.6 數(shù)據(jù)查詢API
1. 請求參數(shù)
參數(shù) | 類型 | 是否必傳 | 說明 |
modelId | String | 是 | 數(shù)據(jù)模型ID |
returnFields | JSON | 是 | 指定返回的字段。1.若期望返回所有字段,則傳入?yún)?shù)為["*"];2.若期望返回數(shù)據(jù)總數(shù),則傳入?yún)?shù)為["COUNT"]。如:["*"]或者["name","age"]["COUNT"] |
conditions | JSON | 否 | 條件,由字段名、運算符、比較值組成一個condition。如:[{"fieldName": "id", "operate": "eq", "value": 7}] |
orderBy | JSON | 否 | 排序條件,由增序或降序以及排序字段組成。如:{"asc":"true", "orderByFields":["name","age"]} |
pageNum | Integer | 否 | 分頁頁數(shù) |
pageSize | Integer | 否 | 分頁每頁數(shù)量 |
appId | String | 否 | 對于SaaS應用,需要填該值 |
scopeId | String | 否 | 經(jīng)常是項目的id,該參數(shù)非必填,一般上架應用被授權(quán)之后,會被默認綁定到一個scope中,因此當前操作會被默認操作到被綁定到的這一個scope中。但是,對于集成應用,也有可能被綁定到多個scope中,此時該操作需要填入scopeId。 |
其中condition針對不同類型的屬性支持的運算符:
屬性 | 支持的運算符 |
Integer | eq,neq,lt,lteq,mt,mteq,bt,in,nin,nnul |
String | eq,neq,nnul,in,nin |
Double | eq,neq,lt,lteq,mt,mteq,bt,in,nin,nnul |
Boolean | eq,nnul |
Date | eq,neq,lt,lteq,mt,mteq,bt,nnul |
2. 返回參數(shù)
參數(shù) | 類型 | 描述 |
data | String | 查詢返回數(shù)據(jù)內(nèi)容,其中,Boolean類型的屬性返回值為0或1 |
3. 請求示例-1
{
"request": {
"apiVer": "0.0.2" // api版本號
},
"id": 1508212818676,// request里的全局唯一id透傳
"params": {
"modelId":"XXX123",
"returnFields":["MODE","ENGINE"],
"conditions":[
{"fieldName":"BRAND","value":"BMW","operate":"eq"},
{"fieldName":"WIDTH_SIZE","value":1,"value2":2,"operate":"bt"},
{"fieldName":"LONG_SIZE","value":"3","operate":"mt"}
],
"orderBy":{
"asc":true,
"orderByFields":["CREATE_DATA","MODE"]
},
"pageNum":1,
"pageSize":10
}
},
"version": "1.0" // 請求協(xié)議版本
}
4. 返回示例-1
{
"code": 200,
"localizedMsg": null,
"data": "{\"count\":1,
\"hasNext\":false,
\"items\":[{\"gmt_create\":1551872701000,\"MODE\":\"33\",\"ENGINE\":\"44\",\"id\":2,\"gmt_modified\":1551872714000}],
\"pageNum\":1,
\"pageSize\":10}",
"message": "success"
}
5. 請求示例-2
{
"request": {
"apiVer": "0.1.0" // api版本號
},
"id": 1508212818676,// request里的全局唯一id透傳
"params": {
"modelId":"XXX123",
"returnFields":["COUNT",
"conditions":[
{"fieldName":"BRAND","value":"BMW","operate":"eq"},
{"fieldName":"WIDTH_SIZE","value":1,"value2":2,"operate":"bt"},
{"fieldName":"LONG_SIZE","value":"3","operate":"mt"}
],
"pageNum":1,
"pageSize":10
}
},
"version": "1.0" // 請求協(xié)議版本
}
6. 返回示例-2
{
"code": 200,
"localizedMsg": null,
"data": "{
\"hasNext\":false,
\"items\":[{
\"COUNT\":100
}]
}"
"message": "success"
}
7. 返回碼
狀態(tài)碼 | 描述 | |
200 | 成功 | |
460 | 參數(shù)驗證異常 | 會帶有驗證異常的詳細說明 |
500 | 服務異常 | server error |
52002 | 無訪問權(quán)限 | |
52005 | 目標存儲未找到 |
4.2.7 獲取文件上傳地址API
1. 請求參數(shù)
參數(shù) | 類型 | 是否必傳 | 描述 |
modelId | String | 是 | 數(shù)據(jù)模型id |
version | String | 是 | 數(shù)據(jù)模型的版本號 |
fileSize | Integer | 是 | 文件大小,以字節(jié)為單位,目前系統(tǒng)不支持5M以上文件 |
attrName | String | 是 | 屬性名稱,模型中包含的屬性名稱,不包含會報錯進行提示 |
fileType | String | 是 | 文件類型,目前系統(tǒng)只支持bmp、png、gif、jpg |
appId | String | 否 | 對于SaaS應用,需要填該值 |
scopeId | String | 否 | 經(jīng)常是項目的id,該參數(shù)非必填,一般上架應用被授權(quán)之后,會被默認綁定到一個scope中,因此當前操作會被默認操作到被綁定到的這一個scope中。但是,對于集成應用,也有可能被綁定到多個scope中,此時該操作需要填入scopeId。 |
2. 返回參數(shù)
參數(shù) | 類型 | 描述 |
url | String | 數(shù)據(jù)上傳的url,url有效時間10s |
fileName | String | 文件名稱,系統(tǒng)隨機分配的文件名稱,用戶在得到這個文件名之后,應該將其放在相應模型數(shù)據(jù)的文件類型的字段上,例如:xxxxx.jpg |
3. 請求示例
{
"request": {
"apiVer": "0.0.1" // api版本號
},
"id": 1508212818671, // request里的全局唯一id透傳
"params": {
"modelId":"XXX123", //數(shù)據(jù)模型id
"fileSize":"3.2", //文件大小
"attrName":"name", //屬性名稱
"fileType":"jpg", //文件類型
"version":"1.0", //模型版本
},
"version": "1.0" // 請求協(xié)議版本
}
4. 返回示例
{
"code": 200, // 返回是否成功,只要不是200說明返回不成功
"message": "success", // 如果失敗,會返回失敗的信息描述
"localizedMsg": null,
"data": {"url":"http://xxx.xxx.xx","fileName":"xxx.jpg"} // 返回的數(shù)據(jù)
}
5. 返回碼
狀態(tài)碼 | 描述 | |
200 | 成功 | |
460 | 參數(shù)驗證異常 | 會帶有驗證異常的詳細說明 |
500 | 服務異常 | server error |
52002 | 無訪問權(quán)限 | |
52005 | 找不到目標存儲 | |
52009 | 參數(shù)和模型定義不匹配 | |
52011 | 數(shù)據(jù)類型校驗錯誤 | |
52064 | 屬性字段沒有相應的圖片標簽 | |
52063 | 文件大小不能大于5M |
4.2.8 獲取文件下載地址API
1. 請求參數(shù)
參數(shù) | 類型 | 是否必傳 | 描述 |
modelId | String | 是 | 數(shù)據(jù)模型id |
version | String | 是 | 數(shù)據(jù)模型的版本號 |
attrName | String | 是 | 屬性名稱,模型中包含的屬性名稱,不包含會報錯進行提示 |
fileName | String | 是 | 文件名稱,必須為獲取文件上傳地址API中返回的fileName參數(shù),例如:xxxxx.jpg |
appId | String | 否 | 對于SaaS應用,需要填該值 |
scopeId | String | 否 | 經(jīng)常是項目的id,該參數(shù)非必填,一般上架應用被授權(quán)之后,會被默認綁定到一個scope中,因此當前操作會被默認操作到被綁定到的這一個scope中。但是,對于集成應用,也有可能被綁定到多個scope中,此時該操作需要填入scopeId。 |
2. 返回參數(shù)
參數(shù) | 類型 | 描述 |
url | String | 數(shù)據(jù)上傳的url,url有效時間10s |
3. 請求示例
{
"request": {
"apiVer": "0.0.1" // api版本號
},
"id": 1508212818671, // request里的全局唯一id透傳
"params": {
"modelId":"XXX123", //數(shù)據(jù)模型id
"scopeId":"fdbsdj1dfjdubgxxx", //業(yè)務隔離id
"attrName":"name", //屬性名稱
"fileName":"xdsfxv.jpg", //文件類型
"version":"1.0", //模型版本
},
"version": "1.0" // 請求協(xié)議版本
}
4. 返回示例
{
"code": 200, // 返回是否成功,只要不是200說明返回不成功
"message": "success", // 如果失敗,會返回失敗的信息描述
"localizedMsg": null,
"data": {"url":"http://xxx.xxx.xx"} // 返回的數(shù)據(jù)下載查看的url
}
5. 返回碼
狀態(tài)碼 | 描述 | |
200 | 成功 | |
460 | 參數(shù)驗證異常 | 會帶有驗證異常的詳細說明 |
500 | 服務異常 | server error |
52002 | 無訪問權(quán)限 | |
52005 | 找不到目標存儲 | |
52009 | 參數(shù)和模型定義不匹配 | |
52011 | 數(shù)據(jù)類型校驗錯誤 | |
52064 | 屬性字段沒有相應的圖片標簽 |
5. 應用開發(fā) - 數(shù)據(jù)訂閱
應用可以通過AMQP方式,訂閱數(shù)據(jù)的變更消息(新增、刪除、修改)。應用自身不需要發(fā)布數(shù)據(jù)的變更消息到通道中,這些消息的產(chǎn)生是由應用通過上面的數(shù)據(jù)操作API,對數(shù)據(jù)進行操作之后,由平臺產(chǎn)生。平臺產(chǎn)生時間之后,會將消息通過訂閱關(guān)系發(fā)送到訂閱方。在這個消息通信中,每一個應用實例,由AppKey標識身份。對于單租戶型的應用,每一個AppKey代表了一次應用分發(fā)的實例;但是,對于SaaS應用,他的一個AppKey代表了該應用對應的所有租戶的身份,因此,在SaaS應用按照AppKey得到消息之后,需要自行根據(jù)訂閱到的數(shù)據(jù)內(nèi)容中的AppID字段,將數(shù)據(jù)對應到不用的用戶中。
5.1 SDK介紹
5.1.1 Java SDK
1. 依賴引用
在工程中添加 maven 依賴接入 SDK。
<!-- amqp 1.0 qpid client -->
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>qpid-jms-client</artifactId>
<version>0.40.0</version>
</dependency>
<!-- util for base64-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
2. 身份認證
使用服務端訂閱功能,需要基于AppKey進行身份認證并建立連接。該AppKey根據(jù)應用類型不同,有兩種來源:
對于獨立單租戶托管并分發(fā)的應用,該AppKey是由托管平臺在應用實例化分發(fā)部署應用時,自動產(chǎn)生,并注入在主機的環(huán)境變量中,應用事先并不知道該值,需要動態(tài)從環(huán)境變量中獲取。
對于共享型應用,該AppKey是該SaaS應用在注冊多租戶接口時,從平臺創(chuàng)建,并硬編碼在應用中。
public static void main(String[] args) throws Exception {
String clientId = UUID.randomUUID().toString(); //填寫唯一的id,比如uuid
String random = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 6);
//按照阿里云IoT規(guī)范,組裝UserName
String userName = clientId + "|authMode=appkey"
+ ",signMethod=SHA256"
+ ",random=" + random
+ ",appKey=" + "{appkey:純數(shù)字}"
//+ ",iotInstanceId=" + iotInstanceId, //如果是企業(yè)實例ID,那么需要補充這個參數(shù)。如果是公共實例ID,那么隱藏這個參數(shù)
+ ",groupId=" + "{appkey:純數(shù)字}"
+ "|";
//按照阿里云IoT規(guī)范,計算簽名,組裝password
String signContent = "random=" + random;
String password = doSign(signContent, "{app-secret}", "HmacSHA256");
//如果是公共實例,那么使用如下鏈接地址。如果是企業(yè)實例,需要在實例管理中查看amqp鏈接地址。
String connectionUrl =
"failover:(amqps://{aliyun-uid}.iot-amqp.cn-shanghai.aliyuncs.com:5671?amqp.idleTimeout=80000)"
+ "?failover.maxReconnectAttempts=10&failover.reconnectDelay=30";
Hashtable<String, String> hashtable = new Hashtable<String, String>();
hashtable.put("connectionfactory.SBCF", connectionUrl);
hashtable.put("queue.QUEUE", "default");
hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
Context context = new InitialContext(hashtable);
ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
Destination queue = (Destination)context.lookup("QUEUE");
Connection connection = cf.createConnection(userName, password);
MyJmsConnectionListener myJmsConnectionListener = new MyJmsConnectionListener();
((JmsConnection)connection).addConnectionListener(myJmsConnectionListener);
// Create Session
// Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手動調(diào)用message.acknowledge()
// Session.AUTO_ACKNOWLEDGE: SDK自動ack(推薦)
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection.start();
// Create Receiver Link
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(messageListener);
}
/**
* 按照指定簽名算法計算password簽名
*/
private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
Mac mac = Mac.getInstance(signMethod);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(toSignString.getBytes());
return Hex.encodeHexString(rawHmac);
}
}
注:connectionUrlTemplate 的寫法如下所示:
上海:
公共實例:amqps://${uid}.iot-amqp.cn-shanghai.aliyuncs.com:5671
購買的實例:在LP控制臺“實例管理”--“查看終端節(jié)點”里找到AMQP接入點
3. 設(shè)置消息接受接口
使用AMQP方式,建立連接之前,需要提供消息接收接口,用于處理回調(diào)的消息,需要通過consumer.setMessageListener(messageListener)來設(shè)置,其中messageListener是一個自定義實現(xiàn)MessageListener接口的類,如下所示:
private static MessageListener messageListener = new MessageListener() {
@Override
public void onMessage(final Message message) {
try {
//1.收到消息之后一定要ACK。
// 推薦做法:創(chuàng)建Session選擇Session.AUTO_ACKNOWLEDGE,這里會自動ACK。
// 其他做法:創(chuàng)建Session選擇Session.CLIENT_ACKNOWLEDGE,這里一定要調(diào)message.acknowledge()來ACK。
// message.acknowledge();
//2.建議異步處理收到的消息,確保onMessage函數(shù)里沒有耗時邏輯。
// 如果業(yè)務處理耗時過程過長阻塞住線程,可能會影響SDK收到消息后的正常回調(diào)。
executorService.submit(new Runnable() {
@Override
public void run() {
processMessage(message);
}
});
} catch (Exception e) {
logger.error("submit task occurs exception ", e);
}
}
};
private static class MyJmsConnectionListener implements JmsConnectionListener() {
/**
* 連接成功建立
* @param remoteURI
*/
@Override
public void onConnectionEstablished(URI remoteURI) {
logger.info("onConnectionEstablished, remoteUri:{}", remoteURI);
}
/**
* 最終連接失敗,嘗試過最大重試次數(shù)之后
* @param error
*/
@Override
public void onConnectionFailure(Throwable error) {
logger.error("onConnectionFailure, {}", error.getMessage());
}
/**
* 連接中斷
* @param remoteURI
*/
@Override
public void onConnectionInterrupted(URI remoteURI) {
logger.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
}
/**
* 連接中斷后又自動重連上
* @param remoteURI
*/
@Override
public void onConnectionRestored(URI remoteURI) {
logger.info("onConnectionRestored, remoteUri:{}", remoteURI);
}
@Override
public void onInboundMessage(JmsInboundMessageDispatch envelope) {
logger.info("onInboundMessage, {}", envelope);
}
@Override
public void onSessionClosed(Session session, Throwable cause) {
logger.error("onSessionClosed, " + session, cause);
}
@Override
public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {
logger.error("onConsumerClosed, " + consumer, cause);
}
@Override
public void onProducerClosed(MessageProducer producer, Throwable cause) {
logger.error("onProducerClosed, " + producer, cause);
}
}
其中的content內(nèi)容是一個JSON對象字符串,字段信息如下:
// 模型ID
String modelId;
// 變更數(shù)據(jù)的ID
List<Long> dataIds;
// 操作類型:insert/update/delete
String operateType;
// 訂閱的appId, 以appKey授權(quán)時為空
String appId;
String scopeId;
// 模型的邏輯隔離id
String logicalModelIsoId;
// 模型實例id
String modelInstanceId;
// 數(shù)據(jù)實例id
String dataInstanceId;