采用 ModifyRequestBodyGatewayFilterFactory读取对请求参数
自定义 getRewriteFunction 方法, 获取 body 信息
private RewriteFunction<Object, Object> getRewriteFunction() {
return (serverWebExchange, body) -> {
// 这里的body就是请求体参数
try {
requestBody.set(JSON.toJSONString(body));
} catch (Exception e) {
log.error("body 转换异常", e);
}
return Mono.just(body);
};
}
定义ModifyRequestBodyGatewayFilterFactory配置
private ModifyRequestBodyGatewayFilterFactory.Config getConfig() {
ModifyRequestBodyGatewayFilterFactory.Config cf = new ModifyRequestBodyGatewayFilterFactory.Config();
cf.setRewriteFunction(Object.class, Object.class, getRewriteFunction());
return cf;
}
初始化
@PostConstruct
public void init() {
this.delegate = new ModifyRequestBodyGatewayFilterFactory().apply(this.getConfig());
}
读取响应消息内容
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
ServerHttpResponse originalResponse = exchange.getResponse();
//如果是post请求,将请求体取出来,再写入
HttpMethod method = serverHttpRequest.getMethod();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decorator = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
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, StandardCharsets.UTF_8);
log.info("响应内容: {}", responseData);
byte[] uppedContent = new String(responseData.getBytes(), StandardCharsets.UTF_8).getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
};
// GET 请求直接读取
if (HttpMethod.GET.equals(method)) {
return chain.filter(exchange.mutate().response(decorator).build());
}
return delegate.filter(exchange.mutate().response(decorator).build(), chain);
}
版本环境
Spring Boot 2.1.5.RELEASE
Spring Cloud Gateway 2.1.3.RELEASE
HttpServletRequest 的输入流只能读取一次的原因
我们先来看看为什么HttpServletRequest的输入流只能读一次,当我们调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承于InputStream。
InputStream的read()方法内部有一个postion,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()会返回-1,表示已经读取完了。如果想要重新读取则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。
InputStream默认不实现reset(),并且markSupported()默认也是返回 false
SpringCloudGateway 读取 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)
CacheBodyGlobalFilter
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;
/**
* 解决 POST 请求 body 只能读取一次问题
* 将原有的 body 数据读取出来
* 使用 ServerHttpRequestDecorator 包装 Request
* 重写 getBody() 方法,将包装后的请求放入过滤链传递下去
* 此后过滤器通过 ServerWebExchange#getRequest()#getBody() 时,实际调用重写后的 getBody()
* 获取到的 body 数据为缓存的数据
* <p>
* 过滤器优先级必须为 HIGHEST_PRECEDENCE 最高级,避免某些过滤器提前读取 body
*
* @author author
* @date 2021-12-07
* {@see <a href="https://www.codenong.com/j5e82a0f451882573a13/"/>}
*/
@Component
public class CacheBodyGlobalFilter implements Ordered, GlobalFilter {
@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;
}
};
return chain.filter(exchange.mutate().request(mutatedRequest).build());
});
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
ReadBodyFilter.java
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
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.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author author
* @version 1.0.0
* @Description 读取 Request 请求参数
* @date 2021-10-12 14:34:00
*/
@Slf4j
@Component
public class ReadBodyFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// order 排序需小于-1 才可读取到 response body 内容
return -2;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return read(exchange, chain);
}
@SuppressWarnings({"unchecked", "NullableProblems"})
private Mono<Void> read(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
ServerHttpResponse originalResponse = exchange.getResponse();
//如果是post请求,将请求体取出来,再写入
HttpMethod method = serverHttpRequest.getMethod();
//请求参数,post从请求里获取请求体
String requestBodyStr = HttpMethod.POST.equals(method) ? resolveBodyFromRequest(serverHttpRequest) : null;
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {//解决返回体分段传输
StringBuffer stringBuffer = new StringBuffer();
dataBuffers.forEach(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);
try {
stringBuffer.append(new String(content, StandardCharsets.UTF_8));
} catch (Exception e) {
log.error("--list.add--error", e);
}
});
String result = stringBuffer.toString();
// result就是response的值,想修改、查看就随意而为了
Map<String, String> urlParams = serverHttpRequest.getQueryParams().toSingleValueMap();
log.info("请求地址:【{}】请求参数:GET【{}】|POST:【\n{}\n】,响应数据:【\n{}\n】", serverHttpRequest.getURI(), urlParams, requestBodyStr, result);
byte[] uppedContent = new String(result.getBytes(), StandardCharsets.UTF_8).getBytes();
originalResponse.getHeaders().setContentLength(uppedContent.length);
return bufferFactory.wrap(uppedContent);
}));
}
// if body is not a flux. never got there.
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
/**
* 从Flux<DataBuffer>中获取字符串的方法
*
* @return 请求体
*/
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();
}
}