saas 服務一般提供了一個檔案來描述提供的 OpenAPI,然會每個使用者根據檔案自己實現簽名、呼叫等功能的封裝,這部分會出現各種各樣的問題,因此使用者更希望平臺直接提供一個 SDK。
darabonba 是阿里雲開源的用於 OpenAPI 的 DSL 語言,編寫 darabonba 就可以生成多語言的 SDK 等等。本文不介紹入門(README 比較全)。
首先是 Config 類,一般包括 appKey 和 secretKey,並定義模組變數,定義如下:
// 引入的依賴見最後的推薦依賴
import Util;
import SignatureUtil;
import OpenApiUtil;
type @host = string
type @appKey = string
type @secretKey = string
model Config {
appKey: string(description = "appKey"),
secretKey: string(description = "secretKey")
}
接著是 Client 類的初始化方法裡初始化:
init(config: Config) {
@host = "www.aliyun.com";
@appKey = config.appKey;
@secretKey = config.secretKey;
}
接著定義請求方法:
// 請求的通用引數封裝為一個 model
model Request {
// GET/POST
method: string,
// 請求路徑
pathname: string,
// 拼接引數字串,作為簽名的一部分
param: string,
// 引數是否是 Json 格式
isJson?: boolean
}
// http 請求,返回一個任意型別的結果
api _requestRAny(request: Request, body: any): any {
__request.pathname = request.pathname;
__request.method = request.method;
// 獲取時間戳
var date: string = OpenApiUtil.getTimestamp();
// 計算簽名
__request.headers = _header(@appId, date, _sign(request.param, date));
if (request.isJson) {
__request.headers.accept = 'application/json';
__request.body = Util.toJSONString(body);
} else {
__request.query = Util.stringifyMapValue(Util.assertAsMap(body));
}
} returns {
// 處理 http response
return _handle(__response);
} runtime {
// 超時時間 10 s,你也可以配成模組引數
timeout = 10000
}
// 呼叫 api 的函數必須用 async 關鍵字修飾
async function _requestRObj(request: Request, body: any): object {
// object 等價 map[string]any 等價 $Model,它們之間可以直接轉換
return Util.assertAsMap(_requestRAny(request, body));
}
// 處理 http response,返回任意型別的結果
function _handle(response: $Response): any {
// 讀取響應的資料,通常是一個包含 code、status 和 data 的 Json 串
var result = Util.assertAsMap(Util.readAsJSON(response.body));
if (!Util.equalNumber(response.statusCode, 200) || !Util.assertAsBoolean(result.ok)) {
// 拋異常,通過 throw 關鍵字僅能丟擲一種異常
throw {
message = `httpCode: ${response.statusCode}, serverCode: ${result.code}, reason: ${result.message}`,
code = `${result.code}`
};
}
return result.data;
}
// 構建請求頭
function _header(appKey: string, signedAt: string, sign: string): map[string]string {
var headers: map[string]string = {
host = @endpoint_host,
app_key = appKey,
signed_at = signedAt,
sign = sign
};
return headers;
}
// 簽名
function _sign(param: string, date: string): string {
// 模式字串拼接引數,計算 md5 摘要做簽名
var sign: string = OpenApiUtil.hexEncode(SignatureUtil.MD5Sign(`app_key${@appKey}secret_key${@secretKey}signed_at${date}${param}));
return sign;
}
比如一個接入使用者介面
model UserRequest {
userId: string(description = "userId", name = "userId", required = true),
nickName?: string(description = "nickName", name = "nickName")
}
async function createUser(userRequest: UserRequest): void {
// 校驗引數,UserRequest 的 nickName 使用了 ?:,? 表示不會進行校驗,如果不呼叫這個方法那就沒有區別
Util.validateModel(userRequest);
// 構建通用引數
var request = new Request {
method = @get,
pathname = "/user/createUser",
// 模式字串拼接引數做簽名
param = `nickName${userRequest.nickName}userId${userRequest.userId}`
};
// 發起 http 請求,如果返回值是基本型別比如數位,那麼可以這樣:
_requestRAny(request, accessThirdPartyUserRequest);
}
它們三個是等價的,可以直接轉換,不用轉換,其他的都需要呼叫工具類轉換,這點很重要
直接使用 map[string]any。
_requestRAny(request, {
// 和前面 _header 函數一樣構建 map 一樣
thirdPartyUserId = thirdPartyUserId
})
呼叫 OpenApiUtil.arrayToStringWithSpecifiedStyle() 方法轉為為字串
// 風格自己選,和伺服器端保持一直就可以
var arrayString: string = OpenApiUtil.arrayToStringWithSpecifiedStyle(null, null, null);
_requestRAny 函數返回的是任意型別。
model Response {
items: [ string ](description = "items")
}
// 返回 Response 型別,函數其餘部分不展示了
return {
items = _requestRAny(request, modelRequest)
};
類似 JDK,幾乎必裝的依賴,包含型別轉換、JSON 轉換 等等:Util | Darabonba Repo (aliyun.com)
byte 陣列轉 string,獲取時間戳:OpenApiUtil | Darabonba Repo (aliyun.com)