国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術(shù)文章
文章詳情頁

Spring Cloud Gateway(讀取、修改 Request Body)的操作

瀏覽:60日期:2023-07-30 10:27:01

Spring Cloud Gateway(以下簡稱 SCG)做為網(wǎng)關(guān)服務(wù),是其他各服務(wù)對外中轉(zhuǎn)站,通過 SCG 進(jìn)行請求轉(zhuǎn)發(fā)。

在請求到達(dá)真正的微服務(wù)之前,我們可以在這里做一些預(yù)處理,比如:來源合法性檢測,權(quán)限校驗(yàn),反爬蟲之類…

因?yàn)闃I(yè)務(wù)需要,我們的服務(wù)的請求參數(shù)都是經(jīng)過加密的。

之前是在各個(gè)微服務(wù)的攔截器里對來解密驗(yàn)證的,現(xiàn)在既然有了網(wǎng)關(guān),自然而然想把這一步驟放到網(wǎng)關(guān)層來統(tǒng)一解決。

Spring Cloud Gateway(讀取、修改 Request Body)的操作

如果是使用普通的 Web 編程中(比如用 Zuul),這本就是一個(gè) pre filter 的事兒,把之前 Interceptor 中代碼搬過來稍微改改就 OK 了。

不過因?yàn)槭褂玫?SCG,它基于 Spring 5 的 WebFlux,即 Reactor 編程,要讀取 Request Body 中的請求參數(shù)就沒那么容易了。

本篇內(nèi)容涉及 WebFlux 的響應(yīng)式編程及 SCG 自定義全局過濾器,如果對這兩者不了解的話,可以先看看相關(guān)的內(nèi)容。

兩個(gè)大坑

我們先建一個(gè) Filter 來看看

public class ValidateFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); MultiValueMap<String, HttpCookie> cookies = request.getCookies(); MultiValueMap<String, String> queryParams = request.getQueryParams(); Flux<DataBuffer> body = request.getBody(); return null; } @Override public int getOrder() { return 0; }}

從上邊的返回值可以看出,如果是取 Header、Cookie、Query Params 都易如反掌,如果你需要校驗(yàn)的數(shù)據(jù)在這三者之中的話,就沒必要往下看了。

說回 Body,這里是一個(gè)Flux<DataBuffer>,即一個(gè)包含 0-N 個(gè)DataBuffer類型元素的異步序列。

首先不考慮 Request Body 只能讀取一次問題(這個(gè)問題可以用緩存解決),我們先來把這個(gè) Flux 轉(zhuǎn)化成我們可以處理的字符串,第一反應(yīng)想到的有兩個(gè)辦法:

block() 異步變同步

subscribe() 訂閱并觸發(fā)序列

BUT,理想很豐滿,現(xiàn)實(shí)卻很骨感——這兩個(gè)辦法都有問題:

WebFlux 中不能使用阻塞的操作

java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-server-epoll-7

subscribe() 只會接收到第一個(gè)發(fā)出的元素,所以會導(dǎo)致獲取不全的問題(太長的 Body 會被截?cái)啵_@個(gè)問題網(wǎng)上有人用 AtomicReference<String> 來包裝獲取到字符串,有人用 StringBuilder/StringBuffer

以上兩個(gè)問題在網(wǎng)上找了半天,也沒找到一個(gè)靠譜的解決辦法,都是人云亦云。特別是第二個(gè)問題的所謂的“解決辦法”,大家無非就在是不遺余力的在展示 DataBuffer 轉(zhuǎn) String 的 N 種寫法,而沒有從根本上解決被截?cái)嗟膯栴}。

正確姿勢

2019.08.26 更新:

評論里有網(wǎng)友提醒到 Spring Cloud Gateway 2.1.2 下 DefaultServerRequest、CachedBodyOutputMessage 類的訪問權(quán)限已經(jīng)改了。這一塊我看了一下,源碼確實(shí)改動了一些,不過 DefaultServerRequest 這個(gè)類已經(jīng)不需要了,而 CachedBodyOutputMessage 類我們可以模(chao)仿(xi)它的實(shí)現(xiàn)。

其實(shí)這里的實(shí)現(xiàn)不管再怎么變,我們只要死盯著 ModifyRequestBodyGatewayFilterFactory 就行了。即使以后這里邊的相關(guān)類的訪問權(quán)限都改成 Default 了,我們也不用一個(gè)個(gè)去抄一遍,只要在org.springframework.cloud.gateway.filter.factory.rewrite 這個(gè) package 下寫我們自己的類就好了。

Spring Cloud Gateway(讀取、修改 Request Body)的操作

———? 分割線 ———-

最終找到解決方案還是通過研讀 SCG 的源碼。

本文使用的版本:

Spring Cloud: Greenwich.RC2

Spring Boot: 2.1.1.RELEASE

在 org.springframework.cloud.gateway.filter.factory.rewrite 包下有個(gè) ModifyRequestBodyGatewayFilterFactory,顧名思義,這就是修改 Request Body 的過濾器工廠類。

但是這個(gè)類我們無法直接使用,因?yàn)橐玫脑掃@個(gè) FilterFactory 只能用 Fluent API 的方式配置,而無法在配置文件中使用,類似于這樣

.route('rewrite_request_upper', r -> r.host('*.rewriterequestupper.org') .filters(f -> f.prefixPath('/httpbin') .addResponseHeader('X-TestHeader', 'rewrite_request_upper') .modifyRequestBody(String.class, String.class, (exchange, s) -> { return Mono.just(s.toUpperCase()+s.toUpperCase()); }) ).uri(uri))

我更喜歡用配置文件來配置路由,所以這種方式并不是我的菜。

這時(shí)候我就需要自己弄一個(gè) GlobalFilter 了。既然官方已經(jīng)提供了“葫蘆”,那么我們就畫個(gè)“瓢”吧。

如果了解的 GatewayFilterFactory 和 GatewayFilter 的關(guān)系的話,不用我說你就知道該怎么辦了。不知道也沒關(guān)系,我們把 ModifyRequestBodyGatewayFilterFactory 中紅框部分 copy 出來,粘貼到我們之前創(chuàng)建的 ValidateFilter#filter 中

Spring Cloud Gateway(讀取、修改 Request Body)的操作

我們稍作修改,即可實(shí)現(xiàn)讀取并修改 Request Body 的功能了(核心部分見上圖黃色箭頭處)

/** * @author yibo */public class ValidateFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerRequest serverRequest = new DefaultServerRequest(exchange); // mediaType MediaType mediaType = exchange.getRequest().getHeaders().getContentType(); // read & modify body Mono<String> modifiedBody = serverRequest.bodyToMono(String.class) .flatMap(body -> { if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) { // origin body map Map<String, Object> bodyMap = decodeBody(body); // TODO decrypt & auth // new body map Map<String, Object> newBodyMap = new HashMap<>(); return Mono.just(encodeBody(newBodyMap)); } return Mono.empty(); }); BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class); HttpHeaders headers = new HttpHeaders(); headers.putAll(exchange.getRequest().getHeaders()); // the new content type will be computed by bodyInserter // and then set in the request decorator headers.remove(HttpHeaders.CONTENT_LENGTH); CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers); return bodyInserter.insert(outputMessage, new BodyInserterContext()) .then(Mono.defer(() -> { ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator( exchange.getRequest()) { @Override public HttpHeaders getHeaders() { long contentLength = headers.getContentLength(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); if (contentLength > 0) {httpHeaders.setContentLength(contentLength); } else {httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, 'chunked'); } return httpHeaders; } @Override public Flux<DataBuffer> getBody() { return outputMessage.getBody(); } }; return chain.filter(exchange.mutate().request(decorator).build()); })); } @Override public int getOrder() { return 0; } private Map<String, Object> decodeBody(String body) { return Arrays.stream(body.split('&')) .map(s -> s.split('=')) .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1])); } private String encodeBody(Map<String, Object> map) { return map.entrySet().stream().map(e -> e.getKey() + '=' + e.getValue()).collect(Collectors.joining('&')); }}

至于拿到 Body 后具體要做什么,也就上邊代碼中的TODO部分,就由你自己來發(fā)揮吧~ 別玩壞就好

建議大家可以多關(guān)注關(guān)注 SCG 的源碼,說不定什么時(shí)候就會多出一些有用的 Filter 或 FilterFactory。

另外,目前 ModifyRequestBodyGatewayFilterFactory 上的 Javadoc 有這么一句話:

This filter is BETA and may be subject to change in a future release.

所以大家要保持關(guān)注呀~

以上這篇Spring Cloud Gateway(讀取、修改 Request Body)的操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 国产精品欧美亚洲 | 亚洲精品不卡午夜精品 | xxxxx性欧美 xxxx肥婆性bbbb欧美 | 国产高清美女一级a毛片久久 | 777444欧美影视| 日韩一级影院 | 亚洲高清视频在线 | 国产成人精品本亚洲 | 伊在人亚洲香蕉精品区 | 久在线播放 | 最新日韩欧美不卡一二三区 | 成人欧美精品久久久久影院 | 一区二区在线免费视频 | 国产日产欧美a级毛片 | 精品一区二区高清在线观看 | 毛片一区| www一级片 | 深夜国产成人福利在线观看女同 | 欧美一级视频在线高清观看 | 久草视频在线免费看 | 欧美精品做人一级爱免费 | 亚洲色色色图 | 国产精品免费视频一区 | 亚洲国产精品看片在线观看 | 免费黄网在线观看 | 亚洲国产系列久久精品99人人 | 亚洲日产2021三区 | 在线日韩中文字幕 | 久操精品视频 | 国产精品视频男人的天堂 | 日本精品一区二区三区在线视频 | 亚洲人成在线观看 | 国产成人亚洲精品一区二区在线看 | 成年男女的免费视频网站 | 国产女王丨vk | 精品日本一区二区三区在线观看 | 欧美色视频日本片高清在线观看 | 国产精品女在线观看 | 91免费观看视频 | 色www永久免费网站国产 | 精品一区国产 |