SpringCloud Gateway自定义filter获取body中的数据为空的问题
最近在使用SpringCloudGateway进行网关的开发,我使用的版本是:SpringBoot的2.3.4.RELEASE+SpringCloud的Hoxton.SR8,在自定义过滤器时需要获取ServerHttpRequest中body的数据,发现一直无法获取到数据,经过各种百度、谷歌,再加上自己的实践,终于找到解决方案:
1、首先创建一个全局过滤器把body中的数据缓存起来
packagecom.cloudpath.gateway.portal.filter; importlombok.extern.slf4j.Slf4j; importorg.springframework.cloud.gateway.filter.GatewayFilterChain; importorg.springframework.cloud.gateway.filter.GlobalFilter; importorg.springframework.core.Ordered; importorg.springframework.core.io.buffer.DataBuffer; importorg.springframework.core.io.buffer.DataBufferUtils; importorg.springframework.http.server.reactive.ServerHttpRequest; importorg.springframework.http.server.reactive.ServerHttpRequestDecorator; importorg.springframework.stereotype.Component; importorg.springframework.web.server.ServerWebExchange; importreactor.core.publisher.Flux; importreactor.core.publisher.Mono; /** *@authormazhen *@classNameCacheBodyGlobalFilter *@Description把body中的数据缓存起来 *@date2020/10/2818:02 */ @Slf4j @Component publicclassCacheBodyGlobalFilterimplementsOrdered,GlobalFilter{ //publicstaticfinalStringCACHE_REQUEST_BODY_OBJECT_KEY="cachedRequestBodyObject"; @Override publicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){ if(exchange.getRequest().getHeaders().getContentType()==null){ returnchain.filter(exchange); }else{ returnDataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer->{ DataBufferUtils.retain(dataBuffer); Flux cachedFlux=Flux .defer(()->Flux.just(dataBuffer.slice(0,dataBuffer.readableByteCount()))); ServerHttpRequestmutatedRequest=newServerHttpRequestDecorator( exchange.getRequest()){ @Override publicFlux getBody(){ returncachedFlux; } }; //exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY,cachedFlux); returnchain.filter(exchange.mutate().request(mutatedRequest).build()); }); } } @Override publicintgetOrder(){ returnOrdered.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:Onlyoneconnectionreceivesubscriberallowed. atreactor.ipc.netty.channel.FluxReceive.startReceiver(FluxReceive.java:279) atreactor.ipc.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:129) at
所以,必须把CacheBodyGlobalFilter的优先级设到最高。
2、在自定义的过滤器中尝试获取body中的数据
packagecom.cloudpath.iam.gateway.customerfilter; importcom.cloudpath.iam.gateway.utils.FilterRequestResponseUtil; importlombok.extern.slf4j.Slf4j; importorg.springframework.cloud.gateway.filter.GatewayFilter; importorg.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; importorg.springframework.core.io.buffer.DataBuffer; importorg.springframework.http.server.reactive.ServerHttpRequest; importorg.springframework.stereotype.Component; importreactor.core.publisher.Flux; importjava.util.Arrays; importjava.util.List; /** *@authorbymazhen *@ClassnameTestGatewayFilterFactory *@Description自定义过滤器获取body中的数据 *@Date2020/10/2714:38 */ @Component @Slf4j publicclassTestGatewayFilterFactoryextendsAbstractGatewayFilterFactory{ @Override publicList shortcutFieldOrder(){ returnArrays.asList("enabled"); } publicTestGatewayFilterFactory(){ super(Config.class); log.info("LoadedTestGatewayFilterFactory"); } @Override publicGatewayFilterapply(Configconfig){ return(exchange,chain)->{ if(!config.isEnabled()){ returnchain.filter(exchange); } if(null!=exchange){ ServerHttpRequesthttpRequest=exchange.getRequest(); try{ Flux dataBufferFlux=httpRequest.getBody(); //获取body中的数据 Stringbody=FilterRequestResponseUtil.resolveBodyFromRequest(dataBufferFlux); log.info("body:{}",body); }catch(Exceptione){ log.error("异常:",e); returnchain.filter(exchange); } } returnchain.filter(exchange); }; } publicstaticclassConfig{ /** *控制是否开启统计 */ privatebooleanenabled; publicConfig(){ } publicbooleanisEnabled(){ returnenabled; } publicvoidsetEnabled(booleanenabled){ this.enabled=enabled; } } }
3、解析body的工具类
packagecom.cloudpath.iam.gateway.utils; importorg.springframework.core.io.buffer.DataBuffer; importorg.springframework.core.io.buffer.DataBufferUtils; importreactor.core.publisher.Flux; importjava.nio.CharBuffer; importjava.nio.charset.StandardCharsets; importjava.util.concurrent.atomic.AtomicReference; importjava.util.regex.Matcher; importjava.util.regex.Pattern; /** *@authormazhen *@classNameFilterHeadersUtil *@Description过滤器请求/响应工具类 *@date2020/10/299:31 */ publicfinalclassFilterRequestResponseUtil{ /** *springcloudgateway获取post请求的body体 *@parambody *@return */ publicstaticStringresolveBodyFromRequest(Fluxbody){ AtomicReference bodyRef=newAtomicReference<>(); //缓存读取的requestbody信息 body.subscribe(dataBuffer->{ CharBuffercharBuffer=StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); DataBufferUtils.release(dataBuffer); bodyRef.set(charBuffer.toString()); }); //获取requestbody returnbodyRef.get(); } /** *读取body内容 *@parambody *@return */ publicstaticStringresolveBodyFromRequest2(Flux body){ StringBuildersb=newStringBuilder(); body.subscribe(buffer->{ byte[]bytes=newbyte[buffer.readableByteCount()]; buffer.read(bytes); DataBufferUtils.release(buffer); StringbodyString=newString(bytes,StandardCharsets.UTF_8); sb.append(bodyString); }); returnformatStr(sb.toString()); } /** *去掉空格,换行和制表符 *@paramstr *@return */ privatestaticStringformatStr(Stringstr){ if(str!=null&&str.length()>0){ Patternp=Pattern.compile("\\s*|\t|\r|\n"); Matcherm=p.matcher(str); returnm.replaceAll(""); } returnstr; } }
解析body的内容,网上普遍是上面的两种方式,亲测resolveBodyFromRequest方法解析body中的数据,没有1024字节的限制。
ps:我传的参数有1万多字节。。。。。。。
大家可以按需所选。
到此这篇关于SpringCloudGateway自定义filter获取body中的数据为空的文章就介绍到这了,更多相关SpringCloudGateway自定义filter内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!