用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

相關文章
相關標籤/搜索