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


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

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

    业务说明

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

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

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

    项目说明

    业务需求

    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框架,根据网上看到的测试结果,效率非常的高,很适合做这种类似中间件的工具。

    项目介绍

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

    安装

    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方法。

    项目结构

    ├── 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"
        }
      }
    }
    

    数据转换流程

    流程图

    配置示例

    这里说一下,比如需要根据一个第三方接口添加一个转换, 第三方接口信息如下:

    请求地址: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

  • 相关阅读:
    用Python查找数组中出现奇数次的那个数字
    python之路--MySQL多表查询
    python之路--MySQl单表查询
    python之路--MySQL 库,表的详细操作
    python之路--MySQL数据库初识
    python之路--线程的其他方法
    python之路--关于线程的一些方法
    python之路--管道, 事件, 信号量, 进程池
    python之路--进程内容补充
    python之路--操作系统介绍,进程的创建
  • 原文地址:https://www.cnblogs.com/hebaibai/p/11549953.html
Copyright © 2020-2023  润新知