詳解如何實現(xiàn)OpenAPI開發(fā)動態(tài)處理接口的返回數(shù)據(jù)
0 | 需求說明
業(yè)務(wù)場景:服務(wù)A對接了服務(wù)B,服務(wù)C等服務(wù)的一些接口,然后由服務(wù)A統(tǒng)一暴露接口給到外部用戶使用。
需求是:
- 服務(wù)A可以動態(tài)的接入服務(wù)B/C的接口,對外暴露并無需重啟(《OpenAPI開發(fā) | 如何動態(tài)的添加接口》)
- 對接的服務(wù)B/C的接口部分字段需要過濾掉,不透出給外部用戶(如數(shù)據(jù)庫的自增ID等敏感信息)。

1 | 思路方案
基本思路:在服務(wù)A里對各個服務(wù)接口返回的數(shù)據(jù)進行攔截并二次加工后再返回給前端。
- 攔截:比較簡單,可以在服務(wù)A對其他服務(wù)接口請求的返回之后進行業(yè)務(wù)操作,也可以統(tǒng)一放到切面里用 @After 注解進行操作。從 demo 的快速演示考慮,這里選擇直接在請求的返回體直接進行業(yè)務(wù)操作。
- 二次加工:服務(wù)A對返回body的部分字段過濾掉,不返回給前端。二次加工的方法有很多種,比如:
- a. 用一個 map 去接收 body,然后對這個 body map 進行遍歷,和服務(wù)A里的 map 進行比較, 將服務(wù)A map 里需要的 key-value,從 body map 里遍歷取出,put 到一個新的 map,最后返回這個新的 map 給前端。
- b. 用 string 去接收 body,接收到的body是一個 json 字符串,然后將 json 字符串轉(zhuǎn)成特定的對象(這個對象是返回給前端的),這樣對象里沒有定義的字段在 json 字符串轉(zhuǎn)對象的過程中就會被舍棄。
方案a有幾個缺陷:
- 首先,要求其他服務(wù)接口的返回必須是一個 json 類型(可用 map 接收),如果是一個 json數(shù)組([{},{}])的話, 就無法用map接收,這樣會導(dǎo)致對接入服務(wù)的接口數(shù)據(jù)結(jié)構(gòu)有限制,不ok;
- 其次,map 數(shù)據(jù)類型可能會很復(fù)雜,由于不確定 map 里的 value的數(shù)據(jù)結(jié)構(gòu)是 string,list 還是 map 等,就需要用 instanceof 對所有的數(shù)據(jù)結(jié)構(gòu)進行遍歷判斷再比較賦值,很復(fù)雜,計算效率也不高。
- 沒有可利用的輪子,類似將對象A賦值給對象B的屬性拷貝(BeanUtils.copyProperties()),可以將mapA的 key-value 賦值給mapB
# mapA
{
"a": "a",
"b": "b",
"c": "c"
}
# mapB
{
"a": null,
"b": null,
}
相反,方案b有一個很大的優(yōu)勢:可以利用現(xiàn)成的序列化和反序列化工具(如Gson)來實現(xiàn)我們的需求。先放一個反序列化的工具,后面會用到:
/**
* Json字符串轉(zhuǎn)為指定的對象
* @param ret json字符串
* @param clazz 指定對象的類
* @return T 指定的對象
*/
public class JsonUtil {
public static <T> T jsonStr2Obj(String ret, Class<T> clazz) {
Gson gson = new Gson();
return gson.fromJson(ret, (Type) clazz);
}
}
但是說到這里,解決的只是對接口返回body的修改,沒有體現(xiàn)出標題的“動態(tài)”二字。那么如何可以動態(tài)的對返回的body數(shù)據(jù)進行過濾處理呢?用 groovy 動態(tài)加載類。
2 | 具體實施
- 獲取接口的返回(以string類型):
ResponseEntity<String> exchange = restTemplate.getForEntity($url, String.class); String body = exchange.getBody();
- 通過groovy獲取動態(tài)編譯類
String clazzInString = getFromRedis($key) // 從redis獲取字符串類型的java class Object obj = DynamicClassCompilerUtil.run(clazzInString)
public class DynamicClassCompilerUtil {
public static Object run(String cls) {
Class<?> clazz = new GroovyClassLoader().parseClass(cls);
try {
return clazz.newInstance();
} catch (Exception e) {
log.error("parse groovy class failed: {}", e);
return null;
}
}
}
- 將 body 反序列化
Object ret = JsonUtil.jsonStr2Obj(body, obj.getClass())
該 ret 對象即為過濾后的對象,可以加工后返回給前端。
至此,“對接的服務(wù)B/C的接口部分字段需要過濾掉,不透出給外部用戶(如數(shù)據(jù)庫的自增ID等敏感信息)” 需求實現(xiàn)了。
至于 “服務(wù)A可以動態(tài)的接入服務(wù)B/C的接口,對外暴露并無需重啟” 需求,有時間的話,將會另起一篇來講,更多關(guān)于OpenAPI動態(tài)處理接口返回數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java兩個List<T> 求交集,差集,并集,去重后的并集
本文主要介紹了Java兩個List<T> 求交集,差集,并集,去重后的并集,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
Java使用Lettuce客戶端在Redis在主從復(fù)制模式下命令執(zhí)行的操作
這篇文章主要介紹了Java使用Lettuce客戶端在Redis在主從復(fù)制模式下命令執(zhí)行的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
Javaweb開發(fā)中通過Servlet生成驗證碼圖片
這篇文章主要為大家詳細介紹了Javaweb開發(fā)中通過Servlet生成驗證碼圖片的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05
SpringBoot2零基礎(chǔ)到精通之JUnit 5與指標監(jiān)控
SpringBoot是一種整合Spring技術(shù)棧的方式(或者說是框架),同時也是簡化Spring的一種快速開發(fā)的腳手架,本篇讓我們一起學(xué)習(xí)JUnit 5與指標監(jiān)控2022-03-03

