home: false
title: 服务编排配置
创建服务
创建聚合接口
配置输入
- 配置输入的定义包括3部分:请求头、请求体和Query参数
- 基于JSON Schema规范
- 自带校验规则
- 支持自定义脚本实现复杂的逻辑校验
JSON Schema规范,详见:
http://json-schema.org/specification.html
http://json-schema.org/understanding-json-schema/
配置校验结果
- 校验不通过时,Fizz会把校验失败的原因(如:订单ID不能为空)放到上下文的validateMsg字段里
- 可以自定义返回给调用方的报文格式,如 msgCode, message
- 支持自定义响应头
- 支持自定义脚本处理校验结果
配置步骤
配置步骤的基础信息
配置步骤的接口入出参
步骤说明
- 一个聚合接口可包含多个步骤
- 一个步骤可包含多个请求(即调用多个接口)
- 步骤间是串联顺序执行
- 一个步骤内的多个请求并行执行
数据转换
支持配置固定值,引用值和脚本
固定值
引用值
脚本
星号 *
星号通配符可以接收一个返回对象类型的引用值,返回对象里的字段会合并到目标对象里
样例:userInfo = {"userName": "Fizz", "userID": 1234}
优先级与覆盖顺序
固定值 < 引用值 < 脚本 < 星号*
当一个字段配置了多种类型的值时按以上顺序覆盖,星号优先级最高
引用值规范
# 获取入参请求头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
- 支持单值引用,如:string,int等
- 支持对象类型的引用
input: 表示调用方的输入数据,如H5页面提交上来的参数
stepN.requestN: 表示步骤N里调用接口N的相关参数
stepN.result: 表示步骤N的转换结果
Fallback与预处理条件
Fallback:
当调用接口发生异常(如超时、网络或系统异常)可配置fallback方案:
- Stop: 终止请求并立即返回
- Continue: 继续后续的操作,且要设置默认的fallback json
预处理: 根据条件判断是否要调用接口,脚本返回true时才调用接口
配置步骤结果处理
支持对步骤里调用的每一个接口的返回结果做数据转换,如果配置数据转换规则原样返回并存储到上下文里供后续使用
支持对步骤里调用的一个或多个接口的返回结果做处理,并把处理完的结果存储到上下文里供后续使用,不配置则不处理
配置输出
配置返回给调用方的结果
- 支持配置响应头
- 支持配置响应体
- 支持自定脚本处理复杂的业务逻辑
脚本
目前支持以下脚本语言:
Javascript (推荐) - ECMAScript 5标准
JS脚本只支持单函数,且函数名不可变,在创建脚本时系统会自动生成初始模板,模板里包含相关使用说明
Groovy
common.js 提供了操作context上下文的便捷操作函数
/**
* context 上下文便捷操作函数
*
*/
var common = {
/* *********** private function begin *********** */
// 获取上下文中客户端请求对象
getInputReq: function (ctx) {
if (!ctx || !ctx['input'] || !ctx['input']['request']) {
return {};
}
return ctx['input']['request']
},
// 获取上下文步骤中请求接口的请求对象
getStepReq: function (ctx, stepName, requestName) {
if (!ctx || !stepName || !requestName) {
return {};
}
if (!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] ||
!ctx[stepName]['requests'][requestName]['request']) {
return {};
}
return ctx[stepName]['requests'][requestName]['request'];
},
// 获取上下文步骤中请求接口的响应对象
getStepResp: function (ctx, stepName, requestName) {
if (!ctx || !stepName || !requestName) {
return {};
}
if (!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] ||
!ctx[stepName]['requests'][requestName]['response']) {
return {};
}
return ctx[stepName]['requests'][requestName]['response'];
},
/* *********** private function end *********** */
/* *********** input begin ************ */
/**
* 获取客户端请求头
* @param {*} ctx 上下文 【必填】
* @param {*} headerName 请求头字段名 【选填】,不传时返回所有请求头
*/
getInputReqHeader: function (ctx, headerName) {
var req = this.getInputReq(ctx);
var headers = req['headers'] || {};
return headerName ? headers[headerName] : headers;
},
/**
* 获取客户端URL请求参数(query string)
* @param {*} ctx 上下文 【必填】
* @param {*} paramName URL参数名 【选填】,不传时返回所有请求参数
*/
getInputReqParam: function (ctx, paramName) {
var req = this.getInputReq(ctx);
var params = req['params'] || {};
return paramName ? params[paramName] : params;
},
/**
* 获取客户端请求体
* @param {*} ctx 上下文 【必填】
* @param {*} field 字段名 【选填】,不传时返回整个请求体
*/
getInputReqBody: function (ctx, field) {
var req = this.getInputReq(ctx);
var body = req['body'] || {};
return field ? body[field] : body;
},
/**
* 获取返回给客户端的响应头
* @param {*} ctx 上下文 【必填】
* @param {*} headerName 响应头字段名 【选填】,不传时返回所有响应头
*/
getInputRespHeader: function (ctx, headerName) {
var req = this.getInputReq(ctx);
var headers = req['headers'] || {};
return headerName ? headers[headerName] : headers;
},
/**
* 获取返回给客户端的响应体
* @param {*} ctx 上下文 【必填】
* @param {*} field 字段名 【选填】,不传时返回整个响应体
*/
getInputRespBody: function (ctx, field) {
var req = this.getInputReq(ctx);
var body = req['body'] || {};
return field ? body[field] : body;
},
/* *********** input begin ************ */
/* *********** step request begin ************ */
/**
* 获取步骤中调用的接口的请求头
* @param {*} ctx 上下文 【必填】
* @param {*} stepName 步骤名【必填】
* @param {*} requestName 请求的接口名 【必填】
* @param {*} headerName 请求头字段名 【选填】,不传时返回所有请求头
*/
getStepReqHeader: function (ctx, stepName, requestName, headerName) {
var req = this.getStepReq(ctx, stepName, requestName);
var headers = req['headers'] || {};
return headerName ? headers[headerName] : headers;
},
/**
* 获取步骤中调用的接口的URL参数
* @param {*} ctx 上下文 【必填】
* @param {*} stepName 步骤名【必填】
* @param {*} requestName 请求的接口名 【必填】
* @param {*} paramName URL参数名 【选填】,不传时返回所有URL参数
*/
getStepReqParam: function (ctx, stepName, requestName, paramName) {
var req = this.getStepReq(ctx, stepName, requestName);
var params = req['params'] || {};
return paramName ? params[paramName] : params;
},
/**
* 获取步骤中调用的接口的请求体
* @param {*} ctx 上下文 【必填】
* @param {*} stepName 步骤名【必填】
* @param {*} requestName 请求的接口名 【必填】
* @param {*} field 字段名 【选填】,不传时返回整个请求体
*/
getStepReqBody: function (ctx, stepName, requestName, field) {
var req = this.getStepReq(ctx, stepName, requestName);
var body = req['body'] || {};
return field ? body[field] : body;
},
/**
* 获取步骤中调用的接口的响应头
* @param {*} ctx 上下文 【必填】
* @param {*} stepName 步骤名【必填】
* @param {*} requestName 请求的接口名 【必填】
* @param {*} headerName 响应头字段名 【选填】,不传时返回所有响应头
*/
getStepRespHeader: function (ctx, stepName, requestName, headerName) {
var resp = this.getStepResp(ctx, stepName, requestName);
var headers = resp['headers'] || {};
return headerName ? headers[headerName] : headers;
},
/**
* 获取步骤中调用的接口的响应头
* @param {*} ctx 上下文 【必填】
* @param {*} stepName 步骤名【必填】
* @param {*} requestName 请求的接口名 【必填】
* @param {*} field 字段名 【选填】,不传时返回整个响应头
*/
getStepRespBody: function (ctx, stepName, requestName, field) {
var resp = this.getStepResp(ctx, stepName, requestName);
var body = resp['body'] || {};
return field ? body[field] : body;
},
/**
* 获取步骤结果
* @param {*} ctx 上下文 【必填】
* @param {*} stepName 步骤名【必填】
* @param {*} field 字段名 【选填】,不传时返回整个步骤结果对象
*/
getStepResult: function (ctx, stepName, field) {
if (!ctx || !stepName || !ctx[stepName]) {
return {};
}
var result = ctx[stepName]['result'] || {};
return field ? result[field] : result;
}
/* *********** step request end ************ */
};
context.js 数据结构
// 上下文,用于保存客户输入输出和每个步骤的输入与输出结果
var context = {
// 是否DEBUG模式
debug:false,
// 各个操作的耗时
elapsedTimes: [{
[actionName]: 123, // 操作名称:耗时
}],
// 客户输入和接口的返回结果
input: {
request:{
path: "",
method: "GET/POST",
headers: {},
body: {},
params: {}
},
response: { // 聚合接口的响应
headers: {},
body: {}
}
},
// 步骤
step1: {
requests: {
// 接口1
request1: {
// 请求相关参数
request:{
url: "",
method: "GET/POST",
headers: {},
body: {}
},
// 根据转换规则转换后的接口响应
response: {
headers: {},
body: {}
}
},
// 接口2
request2: {
request:{
url: "",
method: "GET/POST",
headers: {},
body: {}
},
response: {
headers: {},
body: {}
}
}
//...
},
// 步骤结果
result: {}
}
};
异常处理
当要在脚本里中止请求时可以通过以下方式来实现
返回一个对象且这个对象包含一个_stopAndResponse等于true的属性,Fizz会终止后续的操作并把这个对象返回给调用方。
在线测试
- 支持在线实时测试
- 支持测试接口和正式接口隔离
- 支持返回上下文,可以查看整个执行过程中各个步骤及请求的输入与输出
- 支持保存历史测试记录
支持调试模式,在测试接口和正式接口均可使用,修改后重新发布可实时生效,在调试模式下会打印请求日志及报文,主要用于排查线上问题
导入导出
导入导出主要用于在各个环境间同步接口配置,在开发环境配置好后导到测试环境中测试,测试完后导到生产环境进行发布
发布|下线和审核
目前发布|下线申请有以上两个入口。
- 批量发布:对发布单里的接口进行批量发布
- 批量回滚:对发布单里的接口进行批量回滚
- 发布:实时发布到网关
- 回滚:支持回滚到历史任何一个版本,可在发布历史里指定一个版本进行回滚
- 下线:从网关删除接口,在后台可以通过发布功能再次上线
发布流程说明
申请发布、审核、发布和下线功能的权限可根据需要灵活分配给不同角色,如:开发人员只能申请发布,上级领导审核,运维或测试人员执行发布、回滚或下线。在开发、测试和预生产环境为了方便开发人员调试也可把申请发布、审核、发布和下线功能都分配给开发人员。