SpringBoot记录请求体日志
有时经常会遇到将请求体记录到日志的需求。由于请求体是个流,读取完后不能再次被读取,用拦截器等方法不太合适;使用AOP对controller做代理的方式也不可取,因为这样记录的是反序列化成java对象,属性顺序可能会与真实的请求体有所偏差。但SpringBoot提供了RequestBodyAdvice接口,可以方便的记录真实的请求体,又不影响后续请求体的读取。
首先,创建一个实体类。
import lombok.Data;
@Data
public class Person {
private Integer age;
private String name;
}
接着,自定义一个注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface BodyLog {
}
然后实现RequestBodyAdvice接口,此Advice会对标记了BodyLog注解的Controller,并且使用了@RequestBody的方法进行拦截。beforeBodyRead方法中先将请求体读取,然后将读取的内容记录后又写入了body中。
@Slf4j
@ControllerAdvice(annotations = BodyLog.class)
public class BodyLogRequestAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
String bodyString = IoUtil.read(inputMessage.getBody(), StandardCharsets.UTF_8);
log.info("请求体:{}", bodyString);
return new HttpInputMessage() {
@Override
public ByteArrayInputStream getBody() {
return new ByteArrayInputStream(bodyString.getBytes(StandardCharsets.UTF_8));
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return null;
}
}
最后,我们创建一个controller,模拟测试一下。
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/person")
@BodyLog
public class PersonController {
@PostMapping("save")
public String save(@RequestBody Person person) {
return "ok";
}
}
接着我们调用接口
控制台打印出请求体信息。