Spring Gateway自定義請求引數封裝的實現示例
一、需求
在使用spring gateway作為閘道器時,我們需要在經過閘道器的請求中新增一些需要傳遞給後續服務的公共引數,這個時候就可以用到spring gateway提供的自定義請求引數功能了。
二、尋找解決途徑
1、參考官方文件
我們可以猜測,spring gateway作為閘道器功能,肯定會提供很多處理請求引數的功能,於是我們查詢文件得到如下內容:
2、探索GatewayFilterFactory實現規律
- 通過查詢spring官方文件可以看到,spring gateway為我們提供了很多
xxxGatewayFilterFactory
,而這些factory
都有相同點,都是以GatewayFilterFactory
- 在類名中,我們可以根據類名進行大膽的猜測,前面的幾個單詞是描述他的功能的。
- 右側的
yml
配置檔案可以看到,filter的配置也是呈現出一定的規律的。
3、從原始碼獲取實現原理
既然我們是要解決自定義請求引數封裝問題,那麼我們通過上面描述規律,可以很大膽的猜測AddRequestParameteGatewayFilterFactory
就是我們要找的目標。那麼我們檢視一下他的原始碼,看看他是如何實現的。
首先看看AddRequestParameteGatewayFilterFactory
的繼承關係,大概瞭解一下他的組成
從繼承關係來看,還是比較複雜,有點懵逼,那直接進入程式碼看看他的實現。
我們只貼上比較核心的程式碼進行分析,其他程式碼暫時不用關心。
@Override public GatewayFilter apply(NameValueConfig config) { return new GatewayFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) { URI uri = exchange.getRequest().getURI(); StringBuilder query = new StringBuilder(); //獲取請求uri的請求引數(GET請求引數通過拼接key=value形式進行傳參) String originalQuery = uri.getRawQuery(); //判斷最後一個字元是否是&,如果不是則拼接一個&,以備後續的引數進行連線 if (StringUtils.hasText(originalQuery)) { query.append(originalQuery); if (originalQuery.charAt(originalQuery.length() - 1) != '&') { query.append('&'); } } //獲取config中的key、value,然後拼接到uri請求引數後面 String value = ServerWebExchangeUtils.expand(exchange,config.getValue()); // TODO urlencode? query.append(config.getName()); query.append('='); query.append(value); //把請求引數重新拼接回去,並放入request中傳遞到過濾鏈的下一個請求中去 try { URI newUri = UriComponentsBuilder.fromUri(uri) .replaceQuery(query.toString()).build(true).toUri(); ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri) .build(); return chain.filter(exchange.mutate().request(request).build()); } catch (RuntimeException ex) { throw new IllegalStateException( "Invalid URI query: \"" + query.toString() + "\""); } }
此方法是用於封裝請求引數的具體實現,程式碼的具體實現步驟已經通過註釋進行說明。
那config
中的key:value又是如何傳遞進來的呢?
4、filter的配置和引數傳遞
程式碼實現中出現了config
引數的封裝,那這個引數是如何獲得的呢?這時候我們就要去檢視這個filter是如何使用的了。
- 從圖中可以看到,我們的filter就是在這個配置檔案中配置使用的。那為什麼配置檔案中只有
AddRequestParameter
配置,而不是AddRequestParameterGatewayFilterFactory
呢??? - 其實這是spring的一種約定,實現了
GatewayFilterFactory
介面的類在配置使用的時候,需要省略掉後面的GatewayFilterFactory
,僅配置字首即可。 - 同時,配置檔案中的red,blue又是做什麼的???
- 這就是我們需要傳遞的請求引數了,他以key=red,value=blue的方式進行配置。
- 然後spring會幫我們把這個鍵值傳入到上面的apply方法的config中。我們通過config的原始碼就能看到究竟:
可以看到,config類的實現就是一個name
和value
,分別對應了配置檔案中的兩個引數。
三、基於官方實現進行自定義拓展
我們看到,spring利用同種方式,實現了各種filter。但如果這些都不能滿足我們的要求,那該怎麼辦???
1、自定義引數慘景預設
我們現在看到的引數資訊都是寫死在配置檔案中的,無法進行動態引數的傳遞。我們可以設想一個很普通的場景: 我們需要把使用者的登入資訊封裝到請求引數中,供其他服務使用。那這又該如何實現呢???
2、參考官方實現進行自定義
其實如果我們只要模仿官方實現,在uri上拼接我們要傳遞的動態引數就可以了。具體實現如下:
@Override public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) { URI uri = exchange.getRequest().getURI(); StringBuilder query = new StringBuilder(); String originalQuery = uri.getRawQuery(); if (StringUtils.hasText(originalQuery)) { query.append(originalQuery); if (originalQuery.charAt(originalQuery.length() - 1) != '&') { query.append('&'); } } String value = ServerWebExchangeUtils.expand(exchange,config.getValue()); // TODO urlencode? query.append(config.getName()); query.append('='); query.append(value); //獲取redis中使用者的快取資訊,拼接到請求引數後面 String token = exchange.getRequest().getHeaders().getFirst("token"); if (StringUtils.hasText(token)) { AccountEntity accountEntity = accountAdminApiService.loginAccountAdmin(token); //通過發射拿到bean的屬性和值,以備後面進行傳遞引數拼接 Map<String,Object> beanMap = beanValue(accountEntity); if (!CollectionUtils.isEmpty(beanMap)) { for (String key : beanMap.keySet()) { query.append('&').append(key).append('=').append(beanMap.get(key)); } } } //以下與官方程式碼一樣,省略... }
在固定傳參引數拼接後面,直接通過token去獲取快取中的使用者登陸資訊,然後依次拼接對應的屬性和值即可。
四、自定義filter的應用。
網上看了很多人寫的自定義,其實和本文差不多。但是有點沒有提到,定義好了怎麼用啊???
通過使用註解@bean
的方式進行配置,具體可以自己百度。
這裡用一個簡易的配置,套用yml
配置的規律,直接在配置檔案中配置。
default-filters: - AccountRequestParameter=k,v
因為我是全域性使用的,所以使用的是default-filters
進行配置
此外還應該注意,此時自定的filter並不會被spring boot載入,所以在啟動的時候會報錯。
解決辦法:此時還要在對應的類上加上註解@Component
,把bean交給spring管理。
至此,我們自定義請求引數封裝完成!!!
到此這篇關於Spring Gateway自定義請求引數封裝的實現示例的文章就介紹到這了,更多相關Spring Gateway自定義請求引數封裝內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!