⾃定义SpringWebflux过滤器,解决请求body只能获取⼀次的
问题
Webflux 和 servlet⼀样body只能获取⼀次,解决办法也是⼀样的,⾃定义请求包装类
1 背景
为了测试和调试⽅便打印请求和返回的报⽂信息。⼀般可以通过Aop拦截controller⽅法或增加过滤器打印报⽂信息解决。
1.1 Aop⽅法
使⽤Aop的拦截controller⽅法,是将⽅法的model对象参数转成json打印出来,因为是body字符串通过httpMessageConverter转成model对象,在这⾥打印的话就是⼜转回body字符串。有点浪费性能。
1.2 增加⾃定义过滤器的⽅法
⾃定义过滤器的话放在第⼀个过滤器,在转换成对象之前就打印,这样可以减少多余的性能消耗。
2 Webflux创建请求和返回的的装饰类
2.1 Request装饰类
创建包装类PartnerServerHttpRequestDecorator继承ServerHttpRequestDecorator,在含参构造放中打印请求
url,query,headers和报⽂信息。
@Slf4j//lombok插件
public class PartnerServerHttpRequestDecorator extends ServerHttpRequestDecorator {
private Flux<DataBuffer> body;
PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) {
super(delegate);
final String path = URI().getPath();
final String query = URI().getQuery();
final String method = Optional.Method()).orElse(HttpMethod.GET).name();
final String headers = Headers().entrySet()
.stream()
.map(entry -> "            " + Key() + ": [" + String.join(";", Value()) + "]")
.collect(Collectors.joining("\n"));
final MediaType contentType = Headers().getContentType();
if (log.isDebugEnabled()) {
log.debug("\n" +
"HttpMethod : {}\n" +
"Uri        : {}\n" +
"Headers    : \n" +
"{}", method, path + (StringUtils.isEmpty(query) ? "" : "?" + query), headers);
}
Flux<DataBuffer> flux = Body();
if (ains(contentType)) {
body = flux
.publishOn(single()).map(dataBuffer -> LogUtils.loggingRequest(log, dataBuffer));
} else {
body = flux;
}
}
@Override
public Flux<DataBuffer> getBody() {
return body;
}
}
2.2 Response装饰类
创建响应装饰类PartnerServerHttpResponseDecorator继承ServerHttpResponseDecorator
@Slf4j
public class PartnerServerHttpResponseDecorator extends ServerHttpResponseDecorator {
PartnerServerHttpResponseDecorator(ServerHttpResponse delegate) {
super(delegate);
}
@Override
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
return super.writeAndFlushWith(body);
}
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
final MediaType contentType = Headers().getContentType();
if (ains(contentType)) {
if (body instanceof Mono) {
final Mono<DataBuffer> monoBody = (Mono<DataBuffer>) body;
return super.writeWith(monoBody.publishOn(single()).map(dataBuffer -> LogUtils.loggingResponse(log, dataBuffer)));            } else if (body instanceof Flux) {
final Flux<DataBuffer> monoBody = (Flux<DataBuffer>) body;
return super.writeWith(monoBody.publishOn(single()).map(dataBuffer -> LogUtils.loggingResponse(log, dataBuffer)));            }
}
return super.writeWith(body);
}
}
2.3 WebExchange装饰类
创建PayloadServerWebExchangeDecorator类继承ServerWebExchangeDecorator
public class PayloadServerWebExchangeDecorator extends ServerWebExchangeDecorator {
private PartnerServerHttpRequestDecorator requestDecorator;
private PartnerServerHttpResponseDecorator responseDecorator;
public PayloadServerWebExchangeDecorator(ServerWebExchange delegate) {
super(delegate);
requestDecorator = new Request());
responseDecorator = new Response());
}
@Override
public ServerHttpRequest getRequest() {
return requestDecorator;
}
@Override
public ServerHttpResponse getResponse() {
return responseDecorator;
}
}
springboot aop2.4 打印⽇志⼯具类
@SuppressWarnings("WeakerAccess")
public class LogUtils {
public static final List<MediaType> legalLogMediaTypes = wArrayList(MediaType.TEXT_XML,
MediaType.APPLICATION_XML,
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_JSON_UTF8,
MediaType.TEXT_PLAIN,
MediaType.TEXT_XML);
@SuppressWarnings("unchecked")
public static <T extends DataBuffer> T loggingRequest(Logger log, T buffer) {
return logging(log, ">>>>>>>>>>", buffer);
}
public static <T extends DataBuffer> T loggingResponse(Logger log, T buffer) {
return logging(log, "<<<<<<<<<<", buffer);
}
private static <T extends DataBuffer> T logging(Logger log, String inOrOut, T buffer) {
try {
InputStream dataBuffer = buffer.asInputStream();
byte[] bytes = ByteArray(dataBuffer);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));            if (log.isDebugEnabled()) {
log.debug("\n" +
"{}Payload    : {}", inOrOut, new String(bytes));
}
return (T) nettyDataBufferFactory.wrap(bytes);
} catch (IOException e) {
<(e.getMessage(), e);
}
return null;
}
}
3 Spring boot2 中的使⽤⽅法
在config类中增加⼀个bean即可
@Configuration
public class AppConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE) //过滤器顺序
public WebFilter webFilter() {
return (exchange, chain) -> chain.filter(new PayloadServerWebExchangeDecorator(exchange));
}
}
4 创建⼀个接⼝测试
4.1 测试接⼝
@RestController
@RequestMapping("/test")
@Profile({"dev", "test"})// 开发测试环境有效
public class TestController {
@PostMapping
public Mono<Map<String, Object>> test(@RequestBody Map<String, Object> params) {
params.put("success", true);
return Mono.just(params);
}
}
4.2 测试⽤例和结果
4.2.1 测试⽤例
curl -H "content-type: application/json" localhost:8088/test -d "{\"id\":111, \"username\":\"user\", \"password\":\"password\",\"age\":11}"
4.2.2 测试结果
响应信息
⽇志信息截图
5 其他代码

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。