您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關SpringCloud Gateway自定義filter獲取body中的數據為空如何解決,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
1、首先創建一個全局過濾器把body中的數據緩存起來
package com.cloudpath.gateway.portal.filter; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * @author mazhen * @className CacheBodyGlobalFilter * @Description 把body中的數據緩存起來 * @date 2020/10/28 18:02 */ @Slf4j @Component public class CacheBodyGlobalFilter implements Ordered, GlobalFilter { // public static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { if (exchange.getRequest().getHeaders().getContentType() == null) { return chain.filter(exchange); } else { return DataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> { DataBufferUtils.retain(dataBuffer); Flux<DataBuffer> cachedFlux = Flux .defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount()))); ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator( exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; //exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, cachedFlux); return chain.filter(exchange.mutate().request(mutatedRequest).build()); }); } } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
CacheBodyGlobalFilter這個全局過濾器的目的就是把原有的request請求中的body內容讀出來,并且使用ServerHttpRequestDecorator這個請求裝飾器對request進行包裝,重寫getBody方法,并把包裝后的請求放到過濾器鏈中傳遞下去。這樣后面的過濾器中再使用exchange.getRequest().getBody()來獲取body時,實際上就是調用的重載后的getBody方法,獲取的最先已經緩存了的body數據。這樣就能夠實現body的多次讀取了。
值得一提的是,這個過濾器的order設置的是Ordered.HIGHEST_PRECEDENCE,即最高優先級的過濾器。優先級設置這么高的原因是某些系統內置的過濾器可能也會去讀body,這樣就會導致我們自定義過濾器中獲取body的時候報body只能讀取一次這樣的錯誤如下:
java.lang.IllegalStateException: Only one connection receive subscriber allowed. at reactor.ipc.netty.channel.FluxReceive.startReceiver(FluxReceive.java:279) at reactor.ipc.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:129) at
所以,必須把CacheBodyGlobalFilter的優先級設到最高。
2、在自定義的過濾器中嘗試獲取body中的數據
package com.cloudpath.iam.gateway.customerfilter; import com.cloudpath.iam.gateway.utils.FilterRequestResponseUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import java.util.Arrays; import java.util.List; /** * @author by mazhen * @Classname TestGatewayFilterFactory * @Description 自定義過濾器獲取body中的數據 * @Date 2020/10/27 14:38 */ @Component @Slf4j public class TestGatewayFilterFactory extends AbstractGatewayFilterFactory<TestGatewayFilterFactory.Config> { @Override public List<String> shortcutFieldOrder() { return Arrays.asList("enabled"); } public TestGatewayFilterFactory() { super(Config.class); log.info("Loaded TestGatewayFilterFactory"); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { if (!config.isEnabled()) { return chain.filter(exchange); } if (null != exchange) { ServerHttpRequest httpRequest = exchange.getRequest(); try { Flux<DataBuffer> dataBufferFlux = httpRequest.getBody(); //獲取body中的數據 String body = FilterRequestResponseUtil.resolveBodyFromRequest(dataBufferFlux); log.info("body:{}",body); } catch (Exception e) { log.error("異常:",e); return chain.filter(exchange); } } return chain.filter(exchange); }; } public static class Config { /** * 控制是否開啟統計 */ private boolean enabled; public Config() { } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } } }
3、解析body的工具類
package com.cloudpath.iam.gateway.utils; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import reactor.core.publisher.Flux; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author mazhen * @className FilterHeadersUtil * @Description 過濾器請求/響應工具類 * @date 2020/10/29 9:31 */ public final class FilterRequestResponseUtil { /** * spring cloud gateway 獲取post請求的body體 * @param body * @return */ public static String resolveBodyFromRequest( Flux<DataBuffer> body){ AtomicReference<String> bodyRef = new AtomicReference<>(); // 緩存讀取的request body信息 body.subscribe(dataBuffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); DataBufferUtils.release(dataBuffer); bodyRef.set(charBuffer.toString()); }); //獲取request body return bodyRef.get(); } /** * 讀取body內容 * @param body * @return */ public static String resolveBodyFromRequest2( Flux<DataBuffer> body){ StringBuilder sb = new StringBuilder(); body.subscribe(buffer -> { byte[] bytes = new byte[buffer.readableByteCount()]; buffer.read(bytes); DataBufferUtils.release(buffer); String bodyString = new String(bytes, StandardCharsets.UTF_8); sb.append(bodyString); }); return formatStr(sb.toString()); } /** * 去掉空格,換行和制表符 * @param str * @return */ private static String formatStr(String str){ if (str != null && str.length() > 0) { Pattern p = Pattern.compile("\\s*|\t|\r|\n"); Matcher m = p.matcher(str); return m.replaceAll(""); } return str; } }
解析body的內容,網上普遍是上面的兩種方式,親測resolveBodyFromRequest方法解析body中的數據,沒有1024字節的限制。
以上就是SpringCloud Gateway自定義filter獲取body中的數據為空如何解決,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。