用vetr.x写一个HTTP接口适配器, 对接各类形式接口

用vetr.x写一个HTTP接口适配器, 对接各类形式接口

项目地址:https://github.com/hjx601496320/transmitjava

业务说明

在平常开发工做中,咱们常常会遇到要和各类第三方调试接口的状况,若是是简单的几个接口还好,代码写起来很快就写好了。可是若是在某一种业务状况下,好比支付,咱们对接了不少家第三方的支付公司,每一家的支付接口都不同,这时就须要针对多家不一样的接口文档编写不一样的代码。又或者咱们做为接口提供方提供一套标准的接口,可是某些客户会比较强硬,要求你提供的接口须要按照对方的要求来作,这时就又须要苦哈哈的写一个适配他们的代码。这个过程就十分的难受了。linux

我所在的项目就遇到了这种问题,我在的项目是作保险业务的,如今须要对接多家保险公司的接口,每家数据仍是那些常见的数据,可是数据结构都不相同。有些是XML的,有些是JSON的。像性别,证件类型的枚举值也都不大相同。git

因此根据现有的状况,我开发了一个HTTP接口适配工具。用于支持各类类型的HTTP接口转发,转换请求数据,转换响应数据的需求。github

项目说明

业务需求

  1. 可配置,可配置,可配置(重要的说3边)
  2. 能够接受常见的HTTP请求, 好比POST+JSON,POST+XML,GET,POST+表单等
  3. 数据转换过程不须要写java代码。
  4. 能够实现接口加密解密等功能
  5. 数据落库。

开发思路

  1. 由于这个可配置是很重要的需求,可是我又不想写页面,因此定义一个配置文件是必不可少的,这里配置文件我使用json格式。
  2. 对于接口信息的描述,能够写在配置文件中,好比定义一个接受请求的地址,再定义一个转发请求的地址,另外加上他们的数据格式和请求类型的描述。
  3. 数据转换这里,由于涉及到xml和json格式的报文,每一个报文的节点和层级深度都是不同的,另外还要作到可以互相转换。因此个人作法是将他们深度遍历以后,作成Map<String, Object>这种的数据类型,其中Object 可使另一个Map, 也能够是一个List。总之就是一个树状结构。解析完数据以后,将数据放进freemarker模板文件中完成转换。固然这个模板是须要本身编写的。
  4. 再可以完成数据转换以后须要考虑的是参数加密签名的步骤,这能够设计一个接口,作成插件的形式,而后本身实现签名和验签的步骤。就相似jmeter的插件同样,作成一个jar包放在项目里就能够用了。
  5. 数据落库这里很简单,将接受到的原始数据和接口返回的原始数据保存入库,并记录调用开始时间和调用结束时间就行了。
  6. 项目框架上我选择的是vert.x,他是一个事件驱动非阻塞的java框架,根据网上看到的测试结果,效率很是的高,很适合作这种相似中间件的工具。

项目介绍

由于是一个已经开发完成的工具,在公司项目中使用良好。下面说一下如何使用。web

安装

git clone https://github.com/hjx601496320/transmit.git
cd transmit/
mvn package
cd target/
//解压
tar -zxvf transmit.tar.gz
//启动
sh bin/start.sh

这里启动用的是linux的脚本,由于Windows脚本我不会写,因此只有linux的。数据库

也能够在项目中直接启动Main.java中的main方法。json

项目结构

├── bin                         启动脚本
│   ├── restart.sh              重启
│   ├── start.sh                启动
│   └── stop.sh                 中止
├── config                      配置
│   ├── config.json             启动时加载的配置文件
│   └── logback.xml             日志配置,使用logback
├── lib     
│   ├── commons-cli-1.4.jar     依赖,本身添加的插件jar能够放在这里
......
│   ├── transmit-1.0-SNAPSHOT.jar
│   ├── vertx-web-client-3.8.0.jar
│   └── vertx-web-common-3.8.0.jar
├── log                         日志
│   ├── debug
│   │   └── debug.2019-09-17.log
│   ├── error
│   │   └── error.2019-09-17.log
│   └── info
│       └── info.2019-09-17.log
└── sout.log                     启动时输出的日志

配置说明

{

  其余配置
  "config": {
    
    是否缓存模板文件,默认true. 关闭的话每次请求会从新加载模板,方便调试.
    "cache": true,
    
    系统端口号
    "port": 9090,
    
    引用其余的配置文件的文件路径
    "import": [
    ],
    
    其余组件加载, 执行CLass.forName, 能够加载本身定义的一些插件, 完成相似数据入库, 接口签名之类的功能
    "ext": [
      "com.hebaibai.ctrt.Driver"
    ],
    
    数据库配置, 用于保存接口请求日志
    "db": {
      "host": "127.0.0.1",
      "database": "dbname",
      "port": 3306,
      "username": "root",
      "password": "root"
    }
  },
  
  配置示例
  "config-demo": {
  
    接受请求
    "request": {
    
      接受请求的地址 127.0.0.1:9090/download
      "path": "/download",
      
      请求的方式
      "method": "GET",
      
      请求参数类型: FORM(表单提交),  JSON(json),  QUERY(?key=value&key2=value),  TEXT(文本),  XML(xml),
      "request-type": "QUERY",
      
      返回参数类型
      "response-type": "TEXT"
    },
    
    转发的接口配置
    "api": {
    
      接口请求地址
      "url": "http://127.0.0.1:9003/api/download",
      
      插件编号, 在ext中加载来的
      "extCode": "null",
      
      接口请求地址
      "method": "GET",
      
      请求参数类型
      "request-type": "QUERY",
      
      请求超时设置,默认3000 ms, 单位ms
      "timeout": 1,
      
      返回参数类型
      "response-type": "TEXT",
      
      请求参数转换模板
      "request-ftl": "/home/hjx/work/transmit/file/download-req.ftl",
      
      响应参数转换模板
      "response-ftl": "/home/hjx/work/transmit/file/download-res.ftl"
    }
  }
}

数据转换流程

流程图

配置示例

这里说一下,好比须要根据一个第三方接口添加一个转换, 第三方接口信息以下:api

请求地址:http://xxx.xxx.com/text/getOrder缓存

请求方式:POST数据结构

参数类型:JSON

参数示例:

接口请求参数
{
    "header": {
        "code": "123123123",
        "date": "2019-09-19 14:28:57"
    },
    "body": {
        "orderCode": "O1231231231231231"
    }
}
接口返回参数
{
    "code":1
    "msg":"success"
}

而你可以发送的数据是这样的:

请求方式:POST

参数类型:XML

参数示例:

请求数据
<Demo>
  <Info>
    <Code>XXX-1</Code>
    <UUID>d83a011a-958d-4310-a51b-0fb3a4228ef5</UUID>
    <Time>2017-11-15 16:57:36</Time>
  </Info>
  <Order>
      <SerialNo>0</SerialNo>
      <OrderNo>123123123</OrderNo>
      <OrderCode>asdasdasd</OrderCode>
      <Result>1</Result>
  </Order>
</Demo>
须要返回的数据
<Demo>
  <Info>
    <Code>1</Code>
  </Info>
  <Order>
      <Msg>success</Msg>
  </Order>
</Demo>

这时你须要添加以下配置

{
    "config":{
        "port":9527,
        "import":[

        ],
        "ext":[

        ],
        "db":{
            "host":"127.0.0.1",
            "database":"dbname",
            "port":3306,
            "username":"root",
            "password":"root"
        }
    },
    "getOrder":{
        "request":{
            "path":"/getOrder",
            "method":"POST",
            "request-type":"XML",
            "response-type":"XML"
        },
        "api":{
            "url":"http://xxx.xxx.com/text/getOrder",
            "method":"POST",
            "request-type":"JSON",
            "response-type":"JSON",
            "request-ftl":"/home/hjx/work/transmit/file/getOrder-req.json",
            "response-ftl":"/home/hjx/work/transmit/file/getOrder-res.xml"
        }
    }
}
请求转换/home/hjx/work/transmit/file/getOrder-req.xml
{
    "header": {
        "code": "${ROOT.Info.Code}",
        "date": "${ROOT.Info.Time}"
    },
    "body": {
        "orderCode": "${ROOT.Order.OrderCode}"
    }
}
响应转换模板/home/hjx/work/transmit/file/getOrder-res.xml
<Demo>
  <Info>
    <Code>${ROOT.code}</Code>
  </Info>
  <Order>
      <Msg>${ROOT.msg}</Msg>
  </Order>
</Demo>

配置完成后,启动transmit,向http://127.0.0.1:9527/getOrder发送post请求,就能够转换你的请求参数,并完成对http://xxx.xxx.com/text/getOrder接口的调用了。

关于模板中原始数据的访问

原始数据:

<Demo>
  <Info>
    <Code>XXX-1</Code>
    <UUID>d83a011a-958d-4310-a51b-0fb3a4228ef5</UUID>
    <Time>2017-11-15 16:57:36</Time>
  </Info>
  <Order>
      <SerialNo>0</SerialNo>
      <OrderNo>123123123</OrderNo>
      <OrderCode>asdasdasd</OrderCode>
      <Result>1</Result>
  </Order>
</Demo>

对应的节点

${ROOT.Info.Code}=XXX-1
${ROOT.Info.UUID}=d83a011a-958d-4310-a51b-0fb3a4228ef5
${ROOT.Info.Time}=2017-11-15 16:57:36
${ROOT.Order.SerialNo}=0
${ROOT.Order.OrderNo}=123123123
${ROOT.Order.OrderCode}=asdasdasd
${ROOT.Order.Result}=1

插件编写

插件有两种。

一种

是实现接口com.hebaibai.ctrt.transmit.util.ext。这个接口能够处理一些签名之类的操做,其中有四个方法。

/**
     * 用于判断是否使用该插件,extCode为配置文件中的配置
     * @param extCode
     * @return
     */
    boolean support(String extCode);

    /**
     * 获取插件编号
     *
     * @return
     */
    String getCode();

    /**
     * 在api请求前前执行
     *
     * @param value    完整的数据
     * @param valueMap 放进freemarker的数据
     * @return 插件处理后的数据
     */
    String beforRequest(String value, Map<String, Object> valueMap) throws Exception;

    /**
     * 在api响应后执行
     *
     * @param value    完整的数据
     * @param valueMap 放进freemarker的数据
     * @return
     */
    String afterResponse(String value, Map<String, Object> valueMap) throws Exception;

单独新建项目,实现该接口,添加一个类:

package com.hebaibai.ctrt;
import com.hebaibai.ctrt.ext.sign.PICC_XM_testSign;
import com.hebaibai.ctrt.transmit.util.CrtrUtils;

/**
 * 驱动
 */
public class Driver {

    /**
     * 将插件添加进项目
     */
    static {
        System.out.println("加载 签名插件...");
        CrtrUtils.EXT_LIST.add(你编写的插件实例对象);
    }
}

以后将这个项目单独打成jar包,放进项目的lib文件夹中。以后修改配置:

。。。
        "ext":[
            "com.hebaibai.ctrt.Driver"
        ],
。。。

就完成了。

另外一种

添加freemarker自定义指令,操做和上面的插件差很少,须要实现freemarker.template.TemplateDirectiveModel接口,而后

package com.hebaibai.ctrt;
import com.hebaibai.ctrt.ext.sign.PICC_XM_testSign;
import com.hebaibai.ctrt.transmit.util.CrtrUtils;

/**
 * 驱动
 */
public class Driver {

    /**
     * 将插件添加进项目
     */
    static {
        System.out.println("加载 签名插件...");
        CrtrUtils.EXT_LIST.add(你编写的插件实例对象);
        
        //freeMarker 自定义指令
        CrtrUtils.FREEMARKER_DIRECTIVE_MODEL.put("has", new Has());
    }
}

就完成了。

没了

最后项目地址:https://github.com/hjx601496320/transmit

相关文章
相关标签/搜索