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上下文,异常信息样例:
导入导出主要用于在各个环境间同步接口配置,在开发环境配置好后导到测试环境中测试,测试完后导到生产环境进行发布
目前发布|下线申请有以上两个入口。
申请发布、审核、发布和下线功能的权限可根据须要灵活分配给不一样角色,如:开发人员只能申请发布,上级领导审核,运维或测试人员执行发布、回滚或下线。在开发、测试和预生产环境为了方便开发人员调试也可把申请发布、审核、发布和下线功能都分配给开发人员。