用vetr.x寫一個HTTP介面介面卡, 對接各種形式介面
用vetr.x寫一個HTTP介面介面卡, 對接各種形式介面
專案地址:https://github.com/hjx601496320/transmit
業務說明
在日常開發工作中,我們經常會遇到要和各種第三方除錯介面的情況,如果是簡單的幾個介面還好,程式碼寫起來很快就寫好了。但是如果在某一種業務情況下,比如支付,我們對接了很多家第三方的支付公司,每一家的支付介面都不一樣,這時就需要針對多家不同的介面文件編寫不同的程式碼。又或者我們作為介面提供方提供一套標準的介面,但是某些客戶會比較強硬,要求你提供的介面需要按照對方的要求來做,這時就又需要苦哈哈的寫一個適配他們的程式碼。這個過程就十分的難受了。
我所在的專案就遇到了這種問題,我在的專案是做保險業務的,現在需要對接多家保險公司的介面,每家資料還是那些常見的資料,但是資料結構都不相同。有些是XML的,有些是JSON的。像性別,證件型別的列舉值也都不大相同。
所以根據現有的情況,我開發了一個HTTP介面適配工具。用於支援各種型別的HTTP介面轉發,轉換請求資料,轉換響應資料的需求。
專案說明
業務需求
- 可配置,可配置,可配置(重要的說3邊)
- 可以接受常見的HTTP請求, 比如POST+JSON,POST+XML,GET,POST+表單等
- 資料轉換過程不需要寫java程式碼。
- 可以實現介面加密解密等功能
- 資料落庫。
開發思路
- 因為這個可配置是很重要的需求,但是我又不想寫頁面,所以定義一個配置檔案是必不可少的,這裡配置檔案我使用json格式。
- 對於介面資訊的描述,可以寫在配置檔案中,比如定義一個接受請求的地址,再定義一個轉發請求的地址,另外加上他們的資料格式和請求型別的描述。
- 資料轉換這裡,因為涉及到xml和json格式的報文,每個報文的節點和層級深度都是不一樣的,另外還要做到能夠互相轉換。所以我的做法是將他們深度遍歷之後,做成Map<String, Object>這種的資料型別,其中Object 可以使另外一個Map, 也可以是一個List。總之就是一個樹狀結構。解析完資料之後,將資料放進freemarker模板檔案中完成轉換。當然這個模板是需要自己編寫的。
- 再能夠完成資料轉換之後需要考慮的是引數加密和簽名的步驟,這可以設計一個介面,做成外掛的形式,然後自己實現簽名和驗籤的步驟。就類似jmeter的外掛一樣,做成一個jar包放在專案裡就可以用了。
- 資料落庫這裡很簡單,將接受到的原始資料和介面返回的原始資料儲存入庫,並記錄呼叫開始時間和呼叫結束時間就好了。
- 專案框架上我選擇的是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/trans