1 簡介
本文使用的spring cloud版本:2020.0.1
關於Spring Cloud Gateway報文獲取,網上寫法較多參考ModifyRequestBodyGatewayFilterFactory,經過非嚴謹測試其性能下降劇烈。
本文同樣參考Spring Cloud Gateway源碼,只不過參考的是ReadBodyRoutePredicateFactory,經過非嚴謹測試該方式性能相較上述方案有了巨大提升。
2 寫法核心邏輯(直接仿寫ReadBodyRoutePredicateFactory,定義一個Filter)
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//WebFilterChain GatewayFilterChain ServerHttpRequest currentHttpRequest = exchange.getRequest(); if(logReqRespUtils.needGenerateSn(currentHttpRequest.getMethod())){ return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange,(serverHttpRequest) -> ServerRequest .create(exchange.mutate().request(serverHttpRequest).build(), messageReaders) .bodyToMono(String.class) .defaultIfEmpty("") .doOnNext(objectValue -> {
//objectValue即為請求Body內容 }) .flatMap(bodyString->{ if (serverHttpRequest == exchange.getRequest()) { return chain.filter(exchange); } //ServerWebExchangeUtils.cacheRequestBodyAndRequest中已經緩存了可重復讀的request 並且request也已經轉換成了可重復讀Body的request ServerHttpRequest cacheRequest = (ServerHttpRequest)exchange.getAttributes().get(ServerWebExchangeUtils.CACHED_SERVER_HTTP_REQUEST_DECORATOR_ATTR); return chain.filter(exchange.mutate().request(cacheRequest).build()); }) ); } return chain.filter(exchange); }
3 源碼簡析
ReadBodyRoutePredicateFactory可用來對請求體內容進行判斷。
如此其中一定會獲取請求體。
該文件短短一百多行代碼,很快就可以定位到其核心部分:
在完成request和requestBody的緩存之后,通過ServerHttpRequest創建了一個ServerRequest ,拿到請求體的字符串,之后進行判斷。
查看 ServerWebExchangeUtils相關代碼,顯然可見:
獲取了body(Flux<DataBuffer>),之后裝飾了一個新的Request,在exchange中將dataBuffer(請求體)和裝飾后的Request進行緩存。之后交由我們自定義的邏輯進行處理。
上圖的 decorate方法中有一處 用到了Netty的retainedSlice方法:
大意就是創建了一個ByteBuf的副本,不過是同源的,改內容會彼此影響,操作索引不會彼此影響。因此可以重復讀,不需要我們手動釋放,最后交由Spring去釋放掉。