# 创建服务

# 创建聚合接口

# 配置输入

  • 配置输入的定义包括3部分:请求头、请求体和Query参数
  • 支持JSON和XML类型请求体
  • 自带校验规则
  • 支持自定义脚本实现复杂的逻辑校验

数据校验遵循JSON Schema规范,详见:

http://json-schema.org/specification.html (opens new window)

http://json-schema.org/understanding-json-schema/ (opens new window)

# 配置校验结果

  • 校验不通过时,FizzGate会把校验失败的原因(如:订单ID不能为空)放到上下文的validateMsg字段里
  • 可以自定义返回给调用方的报文格式,如 msgCode, message
  • 支持自定义响应头
  • 支持自定义脚本处理校验结果

# 配置步骤(v2)

# 步骤说明

  • 一个聚合接口可包含多个步骤
  • 一个步骤可包含多个请求(即调用多个接口), 支持RESTful/Dubbo/gRPC接口
  • 步骤间是串联顺序执行
  • 一个步骤内的多个请求并行执行

# 配置步骤的基础信息

支持RESTful/Dubbo/gRPC接口

gRPC服务需启用反射功能, 样例:

server = ServerBuilder.forPort(port)
    .addService(new UserService())
    .addService(new ShoppingCartService())
    // gRPC reflection enabled
    .addService(ProtoReflectionService.newInstance())
    .build();

# 配置步骤的接口入出参

字段支持多级,用点分隔,如 user.name 或 user.age

RESTful接口的请求体和响应体支持JSON和XML内容类型,系统会把XML转换为JSON,在配置时统一按JSON格式配置,XML与JSON转换规则如下:

  • 每个元素(节点)都会转换成一个JSON对象, 子元素转换为JSON对象的字段

  • 元素的属性名转换为JSON对象的一个字段,字段名会加上连字符前缀

  • 当元素有属性时元素的内容转换为JSON对象的#text字段

    <?xml version="1.0" encoding="utf-8"?>
    <library>
        <owner>John Doe</owner>
        <book id="007">James Bond</book>
        <book id="000">Book for the dummies</book>
    </library>
    

    转换为JSON:

    {
      "library": {
        "owner": "John Doe",
        "book": [
          {
            "-id": "007",
            "#text": "James Bond"
          },
          {
            "-id": "000",
            "#text": "Book for the dummies"
          }
        ]
      }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <library>
        <owner>John Doe</owner>
        <date>2021-03-31</date>
    </library>
    

    转换为JSON:

    {
      "library": {
        "owner": "John Doe",
        "date": "2021-03-31"
      }
    }
    
    <?xml version="1.0" encoding="utf-8"?>
    <library>
        <book>James Bond</book>
    </library>
    

    默认转换为JSON:

    {
      "library": { "book": "James Bond" }
    }
    

    可通过指明数组节点路径/library/book转换为JSON:

    {
      "library": { "book": ["James Bond"] }
    }
    

# 配置步骤(v3)

# 编辑器说明

v3版本新增API聚合编辑器,支持以流程图的方式进行编排,方便直观。功能和限制如下:

  • 支持请求节点和容器节点
  • 支持条件组件和循环组件
  • 支持并发和串行执行
  • 支持分支和聚合
  • 一个聚合接口可包含若干个节点(请求节点或容器节点)
  • 一个容器节点可包含0个或若干个请求节点
  • 一个容器节点最多只能包含一个开始节点,最多只能包含一个结束节点
  • 容器内的节点不可连接容器外的节点

# API聚合编辑器

# 操作指引

  • 拖拽左边的节点到右边区域进行添加
  • 拖拽左边的条件组件和循环组件到右边的请求节点或容器节点进行组件的添加
  • 双击右边获取的节点可进行节点属性编辑
  • 单击节点下方的组件区域可进行组件属性编辑(如上图圈选2区域)

# 样例

# 数据转换

支持配置固定值、引用值、函数和脚本

# 固定值

# 引用值

# 函数

函数相关文档请参考:内置函数列表>>

# 脚本

# 星号 *

星号通配符可以接收一个返回对象类型的引用值,返回对象里的字段会合并到目标对象里

样例: 假如:step1.request2.response.body = {"userName": "FizzGate", "userID": 999} 则整个接口的输出为 {"userName": "FizzGate", "userID": 999, "a": 123}

# 波浪号 ~

波浪号通配符用于透传数据,如果配置了波浪号通配符将忽略其它字段的配置。波浪号通配符可以接收任意数据类型,接收到的值会直接赋值给对应body、请求头或Query参数。

样例: 假如:step1.request2.response.body = {"userName": "FizzGate", "userID": 999} 则整个接口的输出为 {"userName": "FizzGate", "userID": 999},其它字段(a,dubbo_result,grpc_result)会被忽略。

# 优先级与覆盖顺序

固定值 < 引用值/函数 < 脚本 < 星号* < 波浪号~

当一个字段配置了多种类型的值时按以上顺序覆盖,通配符优先级最高

# 引用值规范

获取入参请求头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

获取步骤1里request1的响应码的值
step1.request1.response.httpStatus

获取公共资源account.name的值
g.account.name

默认值:input.request.body.bbb|123
管道符|后面是默认值,当引用值为空时返回默认值

  • 支持单值引用,如:string,int等
  • 支持对象类型的引用

input: 表示调用方的输入数据,如H5页面提交上来的参数

stepN.requestN: 表示步骤N里调用接口N的相关参数

stepN.result: 表示步骤N的转换结果

g: 表示公共资源(管理后台:网关管理->公共资源)

如:有公共资源account={"name":"abc","password":"123456"}, g.account.name表示引用account里的name的值:abc,g.account表示引用acount的值:{"name":"abc","password":"123456"}

# Jsonpath规范

说明:引用值只支持以下JsonPath操作

  • 字符串使用单引号,例:['name']
  • 过滤操作用空隔号隔开,例:[?(@.type == 1)]
支持操作 说明
@ 当前节点(做为过滤表达式的谓词使用)
* 通用配配符,可以表示一个名字或数字。
.. 深层扫描。 可以理解为递归搜索。
.<name> 表示一个子节点
['<name>' (, '<name>')] 表示一个或多个子节点
[<number> (, <number>)] 表示一个或多个数组下标(负号为倒数)
[start:end] 数组片段,区间为[start,end),不包含end(负号为倒数)
[?(<expression>)] 过滤表达式。 表达式结果必须是一个布尔值。
支持过滤操作符(操作符两边要加空隔) 说明
== left等于right(注意1不等于'1')
!= 不等于
< 小于
<= 小于等于
> 大于
>= 大于等于
=~ 匹配正则表达式[?(@.name =~ /foo.*?/i)]
in 左边存在于右边 [?(@.size in ['S', 'M'])]
nin 左边不存在于右边
支持尾部函数 说明
min() 计算数字数组的最小值
max() 计算数字数组的最大值
avg() 计算数字数组的平均值
sum() 计算数字数组的汇总值
length() 计算数字数组的元素数量

举例:

假如步骤1里的请求1的返回body如下

{
    "name": "ABCD",
    "data": [
        {
            "id": 1,
            "title": "title 1",
            "hits": 32344
        },
        {
            "id": 2,
            "title": "title 2",
            "hits": 566
        },
        {
            "id": 3,
            "title": "title 3",
            "hits": 7889
        },
        {
            "id": 4,
            "title": "title 4",
            "hits": 100
        },
        {
            "id": 5,
            "title": "title 5",
            "hits": 1688
        }
    ]
}

通过上图的引用值表达式可以得到如下结果:

{
    "hits": [
        32344,
        566,
        7889,
        100,
        1688
    ],
    "name": "ABCD",
    "totalHits": 42587,
    "allTitleList": [
        "title 1",
        "title 2",
        "title 3",
        "title 4",
        "title 5"
    ],
    "id2Title": "title 2"
}

更多例子可参考JsonPath官方文档:https://github.com/json-path/JsonPath

# Fallback与预处理条件

Fallback:

当调用接口发生异常(如超时、网络或系统异常)可配置fallback方案:

  • Stop: 终止请求并立即返回
  • Continue: 继续后续的操作,且要设置默认的fallback json

预处理: 根据条件判断是否要调用接口,脚本返回true时才调用接口

# 配置步骤结果处理

  • 支持对步骤里调用的每一个接口的返回结果做数据转换,如果不配置数据转换规则原样返回并存储到上下文里供后续使用

  • 支持对步骤里调用的一个或多个接口的返回结果做处理,并把处理完的结果存储到上下文里供后续使用,不配置则不处理

# 配置输出

配置返回给调用方的结果

  • 支持配置响应头
  • 支持配置响应体
  • 支持自定脚本处理复杂的业务逻辑

# 脚本

# 简介

FizzGate 支持通过自定义脚本进行服务编排:

  • 配置输入 中 通过 脚本校验 输入内容;
  • 配置输出 中 通过 脚本 定义 输出内容,也可以细化到 某个输出字段的 脚本处理;
  • 配置步骤 中 通过 脚本 定义 配置入参、配置响应 的返回内容;
  • 结果校验 中 通过 脚本 完全自定义校验逻辑,校验输出内容。

FizzGate支持 javascriptgroovy 两种脚本语言,方便开发人员灵活的选择自己熟悉的语言进行服务编排。

其中,

如果使用javascript ,可以 通过 common 对象获取一系列工具函数,方便进行逻辑处理;

而在 groovy 中,所有的工具函数都挂载在 context下,你可以很方便的使用它们。

# 脚本编写格式

# javascript

编写JavaScript脚本时,需要按照以下固定格式进行编写。 function name dyFunc 不可修改。

返回值只能是基本类型,如 string/number/booleanobject/array类型的必须通过JSON.stringify 序列化后返回。

FizzGate 是通过调用 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参数是否通过验证。

返回的验证结果,必须是一个 序列化后的 数组,且:

  • 如果校验通过,则返回一个空数组;
  • 如果校验不通过,将校验不通过的错误信息,push到数组中后返回。

参考示例:

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;
}

# 配置步骤

与 上面的 配置输入——脚本校验 和__配置输出__ 相同。

# 结果校验

结果校验指为最终返回给用户端的数据进行校验。

返回的验证结果,必须是一个 序列化后的 数组,且:

  • 如果校验通过,则返回一个空数组;
  • 如果校验不通过,将校验不通过的错误信息,push到数组中后返回。

参考示例:

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

# javascript 脚本中的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 ,我们提供了 javascriptgroovy 两种脚本的工具函数。

# 工具函数——javascript

  • 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 name
    • requestName :配置步骤中的 stepName 对应的 request name
    function 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 name
    • requestName :配置步骤中的 stepName 对应的 request name
    function 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: 上下文
    • paramName URL参数名 【选填】,不传时返回所有请求参数
    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 '';
    }
    

# 工具函数——groovy

  • context.getInputReq()

    获取上下文客户端中请求对象

    Map<String, Object> req = context.getInputReq()
    
  • context.getStepReq(stepName, requestName):

    获取上下文步骤中请求接口的请求对象

    • stepName: 配置步骤中的 step name
    • requestName :配置步骤中的 stepName 对应的 request name
    Map<String, Object> req = context.getStepReq('step1', 'request1')
    
  • context.getStepResp(stepName, requestName)

    获取上下文步骤中请求接口的响应对象

    • stepName: 配置步骤中的 step name
    • requestName :配置步骤中的 stepName 对应的 request name
  • context.getInputReqHeader(headerName)

    获取客户端请求头

    • headerName: 请求头字段名 【选填】,不传时返回所有请求头
  • context.getInputReqParam(paramName)

    获取客户端URL请求参数(query string)

    • paramName URL参数名 【选填】,不传时返回所有请求参数
  • 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的属性,FizzGate会终止后续的操作并把这个对象返回给调用方。

# 重定向

通过脚本可以实现重定向,脚本返回一个对象且这个对象同时包含_stopAndResponse=true和_redirectUrl属性,_redirectUrl的值为重定向的目标URL,FizzGate会终止后续的操作并进行重定向。JavaScript脚本样例如下:

# 配置路由

至此服务编排的接口配置完成,此时还不能通过网关访问接口,需要到网关管理-路由管理里配置路由。

  • v2.0.0以下版本的测试功能需要配置路由才可以测试且路由的前端和后端服务名和路径需要保持一致;
  • v2.0.0或以上版本的测试功能默认不需求配置路由即可直接测试, 可通过修改application.yml的aggregate_test_auth配置项来开启测试路由配置。正式接口仍需配置路由才可以访问。

# 在线测试

  • 支持在线实时测试
  • 支持测试接口和正式接口隔离
  • 支持返回上下文,可以查看整个执行过程中各个步骤及请求的输入与输出
  • 支持保存历史测试记录

支持调试模式,在测试接口和正式接口均可使用,修改后重新发布可实时生效,在调试模式下会打印请求日志及报文,主要用于排查线上问题

# 脚本执行异常

当脚本执行异常时context里会记录异常信息

  • exceptionMessage 异常信息
  • exceptionStacks 异常堆栈信息
  • exceptionData 引起异常的脚本数据
// 上下文数据结构设计
// 上下文,用于保存客户输入输出和每个步骤的输入与输出结果
var context = {
	// 是否DEBUG模式
	debug:false,
	
	// exception info
	exceptionMessage: "",
	exceptionStacks: "",
  exceptionData: "", // such as script source code that cause exception

  // ... other fields
} 

在请求里加上returnContext=true可以返回context上下文,异常信息样例:

# 导入导出

导入导出主要用于在各个环境间同步接口配置,在开发环境配置好后导到测试环境中测试,测试完后导到生产环境进行发布

# 发布|下线和审核

目前发布|下线申请有以上两个入口。

  • 批量发布:对发布单里的接口进行批量发布
  • 批量回滚:对发布单里的接口进行批量回滚
  • 发布:实时发布到网关
  • 回滚:支持回滚到历史任何一个版本,可在发布历史里指定一个版本进行回滚
  • 下线:从网关删除接口,在后台可以通过发布功能再次上线

# 发布流程说明

申请发布、审核、发布和下线功能的权限可根据需要灵活分配给不同角色,如:开发人员只能申请发布,上级领导审核,运维或测试人员执行发布、回滚或下线。在开发、测试和预生产环境为了方便开发人员调试也可把申请发布、审核、发布和下线功能都分配给开发人员。

# 配置请求头

服务编排在调用后端的接口时默认会带上以下请求头: host,X-Real-IP,X-Forwarded-Proto,X-Forwarded-For

可在fizz-gateway-node的application.yml配置文件里修改:

gateway:
  aggr:
    # set headers when calling the backend API
    proxy_set_headers: host,X-Real-IP,X-Forwarded-Proto,X-Forwarded-For