转发自私人博客https://www.jgayb.cn/?p=253
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 body;
PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) {
super(delegate);
final String path = delegate.getURI().getPath();
final String query = delegate.getURI().getQuery();
final String method = Optional.ofNullable(delegate.getMethod()).orElse(HttpMethod.GET).name();
final String headers = delegate.getHeaders().entrySet()
.stream()
.map(entry -> " " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
.collect(Collectors.joining("\n"));
final MediaType contentType = delegate.getHeaders().getContentType();
if (log.isDebugEnabled()) {
log.debug("\n" +
"HttpMethod : {}\n" +
"Uri : {}\n" +
"Headers : \n" +
"{}", method, path + (StringUtils.isEmpty(query) ? "" : "?" + query), headers);
}
Flux flux = super.getBody();
if (LogUtils.legalLogMediaTypes.contains(contentType)) {
body = flux
.publishOn(single()).map(dataBuffer -> LogUtils.loggingRequest(log, dataBuffer));
} else {
body = flux;
}
}
@Override
public Flux getBody() {
return body;
}
}
2.2 Response装饰类
创建响应装饰类PartnerServerHttpResponseDecorator继承ServerHttpResponseDecorator
@Slf4j
public class PartnerServerHttpResponseDecorator extends ServerHttpResponseDecorator {
PartnerServerHttpResponseDecorator(ServerHttpResponse delegate) {
super(delegate);
}
@Override
public Mono writeAndFlushWith(Publisher extends Publisher extends DataBuffer>> body) {
return super.writeAndFlushWith(body);
}
@Override
public Mono writeWith(Publisher extends DataBuffer> body) {
final MediaType contentType = super.getHeaders().getContentType();
if (LogUtils.legalLogMediaTypes.contains(contentType)) {
if (body instanceof Mono) {
final Mono monoBody = (Mono) body;
return super.writeWith(monoBody.publishOn(single()).map(dataBuffer -> LogUtils.loggingResponse(log, dataBuffer)));
} else if (body instanceof Flux) {
final Flux monoBody = (Flux) 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 PartnerServerHttpRequestDecorator(delegate.getRequest());
responseDecorator = new PartnerServerHttpResponseDecorator(delegate.getResponse());
}
@Override
public ServerHttpRequest getRequest() {
return requestDecorator;
}
@Override
public ServerHttpResponse getResponse() {
return responseDecorator;
}
}
2.4 打印日志工具类
@SuppressWarnings("WeakerAccess")
public class LogUtils {
public static final List legalLogMediaTypes = Lists.newArrayList(MediaType.TEXT_XML,
MediaType.APPLICATION_XML,
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_JSON_UTF8,
MediaType.TEXT_PLAIN,
MediaType.TEXT_XML);
@SuppressWarnings("unchecked")
public static T loggingRequest(Logger log, T buffer) {
return logging(log, ">>>>>>>>>>", buffer);
}
public static T loggingResponse(Logger log, T buffer) {
return logging(log, "<<<<<<<<<<", buffer);
}
private static T logging(Logger log, String inOrOut, T buffer) {
try {
InputStream dataBuffer = buffer.asInputStream();
byte[] bytes = IOUtils.toByteArray(dataBuffer);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
if (log.isDebugEnabled()) {
log.debug("\n" +
"{}Payload : {}", inOrOut, new String(bytes));
}
DataBufferUtils.release(buffer);
return (T) nettyDataBufferFactory.wrap(bytes);
} catch (IOException e) {
log.error(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
4.2 测试用例和结果
4.2.1 测试用例
curl -H "content-type: application/json" http://localhost:8088/test -d "{\"id\":111, \"username\":\"user\", \"password\":\"password\",\"age\":11}"
4.2.2 测试结果
响应信息
日志信息截图
5 其他代码
package com.quaerolife.machineremote.domain.filter;
import com.google.common.collect.Lists;
import io.netty.buffer.UnpooledByteBufAllocator;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.MediaType;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* Created by jone.wang on 2018/10/26.
* Description:
*/
@SuppressWarnings("WeakerAccess")
public class LogUtils {
public static final List legalLogMediaTypes = Lists.newArrayList(MediaType.TEXT_XML,
MediaType.APPLICATION_XML,
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_JSON_UTF8,
MediaType.TEXT_PLAIN,
MediaType.TEXT_XML);
@SuppressWarnings("unchecked")
public static T loggingRequest(Logger log, T buffer) {
return logging(log, ">>>>>>>>>>", buffer);
}
public static T loggingResponse(Logger log, T buffer) {
return logging(log, "<<<<<<<<<<", buffer);
}
private static T logging(Logger log, String inOrOut, T buffer) {
try {
InputStream dataBuffer = buffer.asInputStream();
byte[] bytes = IOUtils.toByteArray(dataBuffer);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
if (log.isDebugEnabled()) {
log.debug("\n" +
"{}Payload : {}", inOrOut, new String(bytes));
}
DataBufferUtils.release(buffer);
return (T) nettyDataBufferFactory.wrap(bytes);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return null;
}
}