标签:cached build app react 忘记 前端 方法 循环 一起
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) { //获取请求体 Flux<DataBuffer> body = serverHttpRequest.getBody(); AtomicReference<String> bodyRef = new AtomicReference<>(); body.subscribe(buffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); DataBufferUtils.release(buffer); bodyRef.set(charBuffer.toString()); }); //获取request body return bodyRef.get(); }
选择是使用代码的形式配置路由,在路由里面配置ReadBodyPredicate预言类。
RouteLocatorBuilder.Builder serviceProvider = builder. routes().route("info-service", r -> r.readBody(String.class, requestBody -> { log.info("requestBody is {}", requestBody); return true; }).and().path("/info/test"). filters(f -> { f.filter(requestFilter); return f; }) .uri("info-service")); RouteLocator routeLocator = serviceProvider.build();
然后通过全局Filter中的 exchange的方式获取属性的形式获取
String body = exchange.getAttribute("cachedRequestBodyObject");
获取post请求的字符串形式以后呢,转换也比较麻烦,如果不是form表单形式的post请求还比较好转换,如果是form表单的形式,再加上"multipart/form-data"的形式,直接获取的就是http请求过来的数据,没有封装。
通过继承ReadBodyPredicateFactory的形式,可以达到route编码的形式
参考代码地址:https://github.com/spring-cloud/spring-cloud-gateway/issues/1307
继承ReadBodyPredicateFactory
@Component public class GatewayReadBodyPredicate extends ReadBodyPredicateFactory { public static final String REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; @Override public AsyncPredicate<ServerWebExchange> applyAsync(Config config) { config.setPredicate(t -> true); return super.applyAsync(config); } }
配置:
routes: - id: info-service uri: lb://info-service predicates: - Path=/info/test/** - name: GatewayReadBodyPredicate args: inClass: java.lang.String
springcloud-gateway,github上给出的配置中,可以将配置信息中的参数的inClass设置为org.springframework.util.MultiValueMap,但是在实际使用过程中,如果是文件和form表单一起提交的话,抛出异常。
github截图
异常信息如下:
org.springframework.web.server.UnsupportedMediaTypeStatusException: 415 UNSUPPORTED_MEDIA_TYPE
"Content type ‘multipart/form-data;boundary=-------------------------
-873485462073103209590464‘ not supported for bodyType=org.springframework.util.MultiValueMap<?, ?>"
1
2
3
个人没有找到合适的类型来接收请求内容类型是**“multipart/form-data”**的java类型参数,最终放弃使用这种形式。
最近在做网关改造,想要通过Gateway过滤器获取ResponseBody的值,查看了网上的帖子和官网内容:
帖子:https://cloud.tencent.com/developer/article/1384111
但在实际操作中却掉坑里了,居然不起作用,就是不进重写但writeWith方法
示例:
1 @Component 2 @Order(-2) 3 public class EncryptResponseBodyFilter implements GlobalFilter { 4 5 @Override 6 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 7 ServerHttpResponse originalResponse = exchange.getResponse(); 8 DataBufferFactory bufferFactory = originalResponse.bufferFactory(); 9 ServerHttpResponseDecorator response = new ServerHttpResponseDecorator(originalResponse) { 10 11 @Override 12 public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { 13 if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) { 14 15 Flux<? extends DataBuffer> fluxBody = Flux.from(body); 16 return super.writeWith(fluxBody.map(dataBuffer -> { 17 // probably should reuse buffers 18 byte[] content = new byte[dataBuffer.readableByteCount()]; 19 dataBuffer.read(content); 20 //释放掉内存 21 DataBufferUtils.release(dataBuffer); 22 String s = new String(content, Charset.forName("UTF-8")); 23 //TODO,s就是response的值,想修改、查看就随意而为了 24 byte[] uppedContent = s.getBytes(); 25 return bufferFactory.wrap(uppedContent); 26 })); 27 } 28 return super.writeWith(body); 29 } 30 31 @Override 32 public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) { 33 return writeWith(Flux.from(body) 34 .flatMapSequential(p -> p)); 35 } 36 };37 return chain.filter(exchange.mutate().response(response).build()); 38 }
后来与帖子和官网代码内容对比,只有Order的方式不同,就换了下实现Ordered重写getOrder方法方式试试,结果出乎意料,居然好用了。order<-2才行。
后来发现响应数据存在分段响应的问题:既然返回内容过大存在分段传出,比如json传输了一半,另一半丢失,所以我们需要考虑报文拼装方式,只有报文全部返回才统一返回给前端。大报文我们需要对fluxBody流循环拼接处理,所以改变方式如下,把fluxBody.map变为fluxBody.buffer().map,从而可以foreach循环Body体了。《参考:https://blog.csdn.net/fayeyiwang/article/details/9137// JSON响应数据拼接容器》
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); ServerHttpResponseDecorator response = new ServerHttpResponseDecorator(originalResponse) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) { // 获取ContentType,判断是否返回JSON格式数据 String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR); if(StringUtils.isNotBlank(originalResponseContentType) && originalResponseContentType.contains("application/json")) { // 如果需要加密才进行处理 if (canEncrypt(exchange.getRequest())) { Flux<? extends DataBuffer> fluxBody = Flux.from(body); return super.writeWith(fluxBody.buffer().map(dataBuffers -> { DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); DataBuffer join = dataBufferFactory.join(dataBuffers); byte[] content = new byte[join.readableByteCount()]; join.read(content); DataBufferUtils.release(join); String responseData = new String(content, Charsets.UTF_8); // responseData = beforeBodyWriteInternal(responseData, exchange.getRequest()); responseData = "\"" + beforeBodyWriteInternal(responseData, exchange.getRequest()) + "\""; //自定义处理返回结果的方法 byte[] uppedContent = new String(responseData.getBytes(), Charset.forName("UTF-8")).getBytes(); originalResponse.getHeaders().setContentLength(uppedContent.length); // 设置加密头标识 originalResponse.getHeaders().set("encrypt", "true"); return bufferFactory.wrap(uppedContent); })); } } } return super.writeWith(body); } @Override public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) { return writeWith(Flux.from(body) .flatMapSequential(p -> p)); } }; return chain.filter(exchange.mutate().response(response).build()); }
1、响应体报文过大: 起初直接读取buffer的响应信息,包小的情况没有问题,但是包大了会抛出json无法转换异常,因为没能读取完整的响应内容,参考ModifyRequestBodyGatewayFilter,等待buffer全部读完再转为数组,然后执行处理。本质原因是底层的Reactor-Netty的数据块读取大小限制导致获取到的DataBuffer实例里面的数据是不完整的。
2、修改响应信息后,响应的ContentLength会发生变化,忘记修改response中的Content-Length长度,导致前端请求无法获取修改后的响应结果。
flux = flux.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
3、order值必须小于-1,因为覆盖返回响应体,自定义的GlobalFilter必须比NettyWriteResponseFilter处理完后执行。order越小越早进行处理,越晚处理响应结果。
参考:https://blog.csdn.net/lance_lan/article/details/103885177
https://blog.csdn.net/weiwoyonzhe/article/details/90814680
https://www.cnblogs.com/commissar-Xia/p/11651196.html
标签:cached build app react 忘记 前端 方法 循环 一起
原文地址:https://www.cnblogs.com/duanxz/p/14832886.html