JSON Schema規範,詳見:javascript
http://json-schema.org/specification.html(opens new window)html
http://json-schema.org/understanding-json-schema/(opens new window)java
支持配置固定值,引用值和腳本json
#固定值api
#引用值數組
#腳本網絡
#星號 *數據結構
星號通配符能夠接收一個返回對象類型的引用值,返回對象裏的字段會合併到目標對象裏運維
樣例:userInfo = {"userName": "Fizz", "userID": 1234}ide
#優先級與覆蓋順序
固定值 < 引用值 < 腳本 < 星號*
當一個字段配置了多種類型的值時按以上順序覆蓋,星號優先級最高
#引用值規範
# 獲取入參請求頭aaa的值 input.request.headers.aaa # 獲取入參請求體bbb字段的值 input.request.body.bbb # 獲取入參URL Query參數fff字段的值 input.request.params.fff # 獲取步驟1裏request1的請求頭ccc的值 step1.request1.request.headers.ccc # 獲取步驟1裏request1的響應體ddd的值 step1.request1.response.body.ddd # 獲取步驟1結果裏eee的值 step1.result.eee
input: 表示調用方的輸入數據,如H5頁面提交上來的參數
stepN.requestN: 表示步驟N裏調用接口N的相關參數
stepN.result: 表示步驟N的轉換結果
#Fallback與預處理條件
Fallback:
當調用接口發生異常(如超時、網絡或系統異常)可配置fallback方案:
預處理: 根據條件判斷是否要調用接口,腳本返回true時才調用接口
#配置步驟結果處理
支持對步驟裏調用的每個接口的返回結果作數據轉換,若是配置數據轉換規則原樣返回並存儲到上下文裏供後續使用
支持對步驟裏調用的一個或多個接口的返回結果作處理,並把處理完的結果存儲到上下文裏供後續使用,不配置則不處理
配置返回給調用方的結果
Fizz
支持經過自定義腳本進行服務編排:
Fizz
支持 javascript
和 groovy
兩種腳本語言,方便開發人員靈活的選擇本身熟悉的語言進行服務編排。
其中,
若是使用javascript
,能夠 經過 common
對象獲取一系列工具函數,方便進行邏輯處理;
而在 groovy
中,全部的工具函數都掛載在 context
下,你能夠很方便的使用它們。
#javascript
編寫JavaScript腳本時,須要按照如下固定格式進行編寫。 function name dyFunc
不可修改。
返回值只能是基本類型,如 string/number/boolean
,object/array
類型的必須經過JSON.stringify
序列化後返回。
Fizz 是經過調用 js引擎執行javascript腳本,而後捕獲執行結果,只能獲取基本的數據類型。
object/array類型捕獲前,會調用原型上的
toString
方法,獲得的結果爲[object type]
的字符串,並不是預期的數據,因此必須經過JSON.stringify()
序列化爲jsonString後再返回。
請勿在js 中使用document等api
function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; // do something... // return string/number/boolean/jsonString return JSON.stringify({}); }
#groovy
編寫groovy腳本時,支持返回groovy支持的任何數據類型。
import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONArray import com.alibaba.fastjson.JSONObject // do something... // return any result return result
在 編輯服務編排接口時,容許在 配置輸入 中,對輸入的數據進行自定義的腳本校驗,校驗 請求頭、請求體、query參數是否經過驗證。
返回的驗證結果,必須是一個 序列化後的 數組,且:
參考示例:
javascript
function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; // 獲取聚合接口用戶輸入的數據 // 獲取請求頭 var token = common.getInputReqHeader(ctx, 'token'); // 獲取請求參數 var account = common.getInputReqParam(ctx, 'account'); var validate = []; // 校驗請求參數 if (!token) { // 將校驗不經過的錯誤信息push到validate中 validate.push('缺乏 token'); } if (!account) { validate.push('缺乏 account'); } // 將 數組 validate 序列化後返回 // 空數組表示校驗經過,非空表示校驗不經過 return JSON.stringify(validate); }
groovy
// 獲取聚合接口用戶輸入的數據 // 獲取請求頭 String token = context.getInputReqHeader('token') // 獲取請求參數 String account = context.getInputReqAttr('params').get('account') List<String> validate = new LinkedList<>() // 校驗請求參數 if (token == null || token.trim().isEmpty()) { // 將校驗不經過的錯誤信息add到validate中 validate.add('缺乏 token') } if (account == null || account.trim().isEmpty()) { validate.add('缺乏 account') } // 空數組表示校驗經過,非空表示校驗不經過 return validate
#輸出 完整response
在 編輯服務編排接口時,容許在 配置輸出 中,自定義輸出結果。
對於返回結果,建議以 { 狀態碼, 請求信息,請求結果 }
的數據結構進行返回,示例以下:
{ "msgCode": 0, // 狀態碼 "message": "success", // 請求信息 "data": { // 請求結果 "count": 1 } }
當腳本內部執行時,檢查到發生異常,須要終止請求,可在 響應的結果中, 添加_stopAndResponse: true
用於中斷,直接將當前結果返回到用戶端。
{ "msgCode": 1, // 狀態碼 "message": "request error", "_stopAndResponse": true // 終止請求並返回響應結果 }
配置輸出腳本示例:
// javascript腳本函數名不能修改 function dyFunc(paramsJsonStr) { var context = JSON.parse(paramsJsonStr)['context']; var data = common.getStepRespBody(context, 'step2', 'request1', 'data'); // do something // 自定義 返回結果,若是返回的Object裏含有_stopAndResponse=true字段時將會終止請求並把腳本結果響應給客戶端(主要用於有異常狀況要終止請求的場景) var result = { // 對於result 內的數據結構無其餘特殊要求,msgCode/message/data字段僅作示例 // _stopAndResponse: true, msgCode: '0', message: '', data: data }; // 返回結果爲Array或Object時要先轉爲json字符串 return JSON.stringify(result); }
#單個字段 輸出腳本處理
在 編輯服務編排接口時,容許在 配置輸出 中,經過腳本處理,自定義單個字段的值。
在字段配置中,選擇 腳本後,便可經過腳本 配置 單個字段的值。
這裏的腳本執行的結果只賦值給單個字段。
// javascript腳本函數名不能修改 function dyFunc(paramsJsonStr) { var context = JSON.parse(paramsJsonStr)['context']; var token = common.getStepRespBody(context, 'step2', 'request1', 'token'); // do something var memberId = parseToken(token); return memberId; }
與 上面的 配置輸入——腳本校驗 和__配置輸出__ 相同。
結果校驗指爲最終返回給用戶端的數據進行校驗。
返回的驗證結果,必須是一個 序列化後的 數組,且:
參考示例:
javascript
function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; // 獲取聚合接口用戶輸入的數據 // 獲取請求頭 var token = common.getInputReqHeader(ctx, 'token'); // 獲取請求參數 var account = common.getInputReqParam(ctx, 'account'); var validate = []; // 校驗請求參數 if (!token) { // 將校驗不經過的錯誤信息push到validate中 validate.push('缺乏 token'); } if (!account) { validate.push('缺乏 account'); } // 將 數組 validate 序列化後返回 // 空數組表示校驗經過,非空表示校驗不經過 return JSON.stringify(validate); }
groovy
// 獲取聚合接口用戶輸入的數據 // 獲取請求頭 String token = context.getInputReqHeader('token') // 獲取請求參數 String account = context.getInputReqAttr('params').get('account') List<String> validate = new LinkedList<>() // 校驗請求參數 if (token == null || token.trim().isEmpty()) { // 將校驗不經過的錯誤信息add到validate中 validate.add('缺乏 token') } if (account == null || account.trim().isEmpty()) { validate.add('缺乏 account') } // 空數組表示校驗經過,非空表示校驗不經過 return validate
context
javascript
腳本中的context
是僅做用域函數做用域中的,做爲 function dyFunc(paramsJsonStr){}
的第一個入參傳入。
function dyFunc(paramsJsonStr) { // 傳入的 paramsJsonStr 僅是一個字符串,須要經過JSON.parse進行序列化後獲取`context` var ctx = JSON.parse(paramsJsonStr)['context']; // do something... }
context
數據結構描述:
interface context { debug: boolean; // 是否DEBUG模式 elapsedTimes: elapsedTime[]; // 各個操做的耗時 input: { // 客戶輸入和接口的返回結果 request: { // 請求 path: string; // 請求路徑 method: string; // 請求方法 POST/GET/PUT/DELETE/... headers: { [head: string]: any; }; // 請求頭 body: { [field: string]: any; }; // 請求體 params: { [param: string]: any; }; // 響應體 }; response: { // 響應 headers: { [head: string]: any; }; // 響應頭 body: { [field: string]: any; }; // 響應體 聚合接口的響應 }; }; [stepName: string]: { // 步驟 [requestName: string]: { // 接口 request: { // // 請求相關參數 url: string; // 請求路徑 method: string; // 請求方法 POST/GET/PUT/DELETE/... headers: { [head: string]: any; }; // 請求頭 body: { [body: string]: any; }; // 請求體 params: { [param: string]: any; }; // 響應體 }; response: { // 響應 根據轉換規則轉換後的接口響應 headers: { [head: string]: any; }; // 響應頭 body: { [field: string]: any; }; // 響應體 }; } }; result: string | number | boolean; // object/array 須要使用 JSON.stirngify 序列化 } interface elapsedTime { [acticeName: string]: number; // 操做名稱:耗時 }
爲了方便在腳本中使用context
,咱們提供了 javascript
和 groovy
兩種腳本的工具函數。
common.getInputReq(ctx)
:
獲取上下文客戶端中請求對象
ctx
: 上下文function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var req = common.getInputReq(ctx); var path = req.path; // 請求路徑 var method = req.method; // 請求方法 var headers = req.headers; // 請求頭 var body = req.body; // 請求體 var params = req.params; // 請求參數 // do something... // return anything string return ''; }
common.getStepReq(ctx, stepName, requestName)
:
獲取上下文步驟中請求接口的請求對象
ctx
: 上下文stepName
: 配置步驟中的 step namerequestName
:配置步驟中的 stepName 對應的 request namefunction dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var req = common.getStepReq(ctx, 'step1', 'request1'); var url = req.url; // 請求路徑 var method = req.method; // 請求方法 var headers = req.headers; // 請求頭 var body = req.body; // 請求體 var params = req.params; // 請求參數 // do something... // return anything string return ''; }
common.getStepResp(ctx, stepName, requestName)
獲取上下文步驟中請求接口的響應對象
ctx
: 上下文stepName
: 配置步驟中的 step namerequestName
:配置步驟中的 stepName 對應的 request namefunction dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var stepResp = common.getStepResp(ctx, 'step1', 'request1'); // do something... // return anything string return ''; }
common.getInputReqHeader(ctx, headerName)
獲取客戶端請求頭
ctx
: 上下文headerName
: 請求頭字段名 【選填】,不傳時返回全部請求頭function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var contentType = common.getInputReqHeader(ctx, 'content-type'); // do something... // return anything string return ''; }
common.getInputReqParam(ctx, paramName)
獲取客戶端URL請求參數(query string)
ctx
: 上下文function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var page = common.getInputReqParam(ctx, 'page'); // do something... // return anything string return ''; }
common.getInputReqBody(ctx, field)
獲取客戶端請求體
ctx
: 上下文field
字段名 【選填】,不傳時返回整個請求體function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var page = common.getInputReqBody(ctx, 'page'); // do something... // return anything string return ''; }
common.getInputRespHeader(ctx, headerName)
獲取返回給客戶端的響應頭
ctx
: 上下文headerName
響應頭字段名 【選填】,不傳時返回全部響應頭function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var page = common.getInputRespHeader(ctx, 'content-type'); // do something... // return anything string return ''; }
common.getInputRespBody(ctx, field)
獲取返回給客戶端的響應體
ctx
: 上下文field
字段名 【選填】,不傳時返回整個響應體function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var page = common.getInputReqBody(ctx, 'page'); // do something... // return anything string return ''; }
common.getStepReqHeader(ctx, stepName, requestName, headerName)
獲取步驟中調用的接口的請求頭
ctx
上下文 【必填】stepName
步驟名【必填】requestName
請求的接口名 【必填】headerName
請求頭字段名 【選填】,不傳時返回全部請求頭function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var contentType = common.getStepReqHeader(ctx, 'step1', 'request1', 'content-type'); // do something... // return anything string return ''; }
common.getStepReqParam(ctx, stepName, requestName, paramName)
獲取步驟中調用的接口的URL參數
ctx
上下文 【必填】stepName
步驟名【必填】requestName
請求的接口名 【必填】paramName
URL參數名 【選填】,不傳時返回全部URL參數function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var page = common.getStepReqParam(ctx, 'step1', 'request1', 'page'); // do something... // return anything string return ''; }
common.getStepReqBody(ctx, stepName, requestName, field)
獲取步驟中調用的接口的請求體
ctx
上下文 【必填】stepName
步驟名【必填】requestName
請求的接口名 【必填】field
字段名 【選填】,不傳時返回整個請求體function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var page = common.getStepReqBody(ctx, 'step1', 'request1', 'page'); // do something... // return anything string return ''; }
common.getStepRespHeader(ctx, stepName, requestName, headerName)
獲取步驟中調用的接口的響應頭
ctx
上下文 【必填】stepName
步驟名【必填】requestName
請求的接口名 【必填】headerName
響應頭字段名 【選填】,不傳時返回全部響應頭function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var contentType = common.getStepRespHeader(ctx, 'step1', 'request1', 'content-type'); // do something... // return anything string return ''; }
common.getStepRespBody(ctx, stepName, requestName, field)
獲取步驟中調用的接口的響應頭
ctx
上下文 【必填】stepName
步驟名【必填】requestName
請求的接口名 【必填】field
字段名 【選填】,不傳時返回整個響應頭function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var page = common.getStepRespBody(ctx, 'step1', 'request1', 'page'); // do something... // return anything string return ''; }
common.getStepResult(ctx, stepName, field)
獲取步驟結果
ctx
上下文 【必填】stepName
步驟名【必填】field
字段名 【選填】,不傳時返回整個步驟結果對象function dyFunc(paramsJsonStr) { var ctx = JSON.parse(paramsJsonStr)['context']; var list = common.getStepResult(ctx, 'step1', 'list'); // do something... // return anything string return ''; }
context.getInputReq()
獲取上下文客戶端中請求對象
Map<String, Object> req = context.getInputReq()
context.getStepReq(stepName, requestName)
:
獲取上下文步驟中請求接口的請求對象
stepName
: 配置步驟中的 step namerequestName
:配置步驟中的 stepName 對應的 request nameMap<String, Object> req = context.getStepReq('step1', 'request1')
context.getStepResp(stepName, requestName)
獲取上下文步驟中請求接口的響應對象
stepName
: 配置步驟中的 step namerequestName
:配置步驟中的 stepName 對應的 request namecontext.getInputReqHeader(headerName)
獲取客戶端請求頭
headerName
: 請求頭字段名 【選填】,不傳時返回全部請求頭context.getInputReqParam(paramName)
獲取客戶端URL請求參數(query string)
context.getInputReqBody(field)
獲取客戶端請求體
field
字段名 【選填】,不傳時返回整個請求體context.getInputRespHeader(headerName)
獲取返回給客戶端的響應頭
headerName
響應頭字段名 【選填】,不傳時返回全部響應頭context.getInputRespBody(field)
獲取返回給客戶端的響應體
field
字段名 【選填】,不傳時返回整個響應體context.getStepReqHeader(ctx, stepName, requestName, headerName)
獲取步驟中調用的接口的請求頭
stepName
步驟名【必填】requestName
請求的接口名 【必填】headerName
請求頭字段名 【選填】,不傳時返回全部請求頭context.getStepReqParam(stepName, requestName, paramName)
獲取步驟中調用的接口的URL參數
stepName
步驟名【必填】requestName
請求的接口名 【必填】paramName
URL參數名 【選填】,不傳時返回全部URL參數context.getStepReqBody(stepName, requestName, field)
獲取步驟中調用的接口的請求體
stepName
步驟名【必填】requestName
請求的接口名 【必填】field
字段名 【選填】,不傳時返回整個請求體context.getStepRespHeader(stepName, requestName, headerName)
獲取步驟中調用的接口的響應頭
stepName
步驟名【必填】requestName
請求的接口名 【必填】headerName
響應頭字段名 【選填】,不傳時返回全部響應頭context.getStepRespBody(stepName, requestName, field)
獲取步驟中調用的接口的響應頭
stepName
步驟名【必填】requestName
請求的接口名 【必填】field
字段名 【選填】,不傳時返回整個響應頭context.getStepResult(stepName, field)
獲取步驟結果
stepName
步驟名【必填】field
字段名 【選填】,不傳時返回整個步驟結果對象當要在腳本里停止請求時能夠經過如下方式來實現
返回一個對象且這個對象包含一個_stopAndResponse等於true的屬性,Fizz會終止後續的操做並把這個對象返回給調用方。
經過腳本能夠實現重定向,腳本返回一個對象且這個對象同時包含_stopAndResponse=true和_redirectUrl屬性,_redirectUrl的值爲重定向的目標URL,Fizz會終止後續的操做並進行重定向。JavaScript腳本樣例以下:
至此服務編排的接口配置完成,但此時還不能經過網關訪問接口,須要到網關管理-路由管理裏配置路由
支持調試模式,在測試接口和正式接口都可使用,修改後從新發布可實時生效,在調試模式下會打印請求日誌及報文,主要用於排查線上問題
當腳本執行異常時context裏會記錄異常信息
// 上下文數據結構設計 // 上下文,用於保存客戶輸入輸出和每一個步驟的輸入與輸出結果 var context = { // 是否DEBUG模式 debug:false, // exception info exceptionMessage: "", exceptionStacks: "", exceptionData: "", // such as script source code that cause exception // ... other fields }
在請求里加上returnContext=true能夠返回context上下文,異常信息樣例:
導入導出主要用於在各個環境間同步接口配置,在開發環境配置好後導到測試環境中測試,測試完後導到生產環境進行發佈
目前發佈|下線申請有以上兩個入口。
申請發佈、審覈、發佈和下線功能的權限可根據須要靈活分配給不一樣角色,如:開發人員只能申請發佈,上級領導審覈,運維或測試人員執行發佈、回滾或下線。在開發、測試和預生產環境爲了方便開發人員調試也可把申請發佈、審覈、發佈和下線功能都分配給開發人員。