1 简介

Spring MVC中,我们有时需要记录一下请求和返回的内容,方便出现问题时排查。比较Header、Request Body等。这些在Controller也可以记录,但在Filter中会更方便。而我们使用的是OncePerRequestFilter

2 记录请求

2.1 流重复读的问题

可以通过下面的代码来读取请求Body:

byte[] requestBody = StreamUtils.copyToByteArray(request.getInputStream());
log.info("request body = {}", new String(requestBody, StandardCharsets.UTF_8));

但是这里从流读取了一次内容后,后续不可再读了。这就造成了真正处理请求的时候,报错失败,我们需要把Request对象改造成可重复读的类。

2.2 通过Wrapper解决流重复读的问题

为了可以让流重复读,加了以下Wrapper:

public class PkslowRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public PkslowRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = StreamUtils.copyToByteArray(request.getInputStream());
} @Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
} @Override
public boolean isFinished() {
return true;
} @Override
public boolean isReady() {
return true;
} @Override
public void setReadListener(ReadListener readListener) { }
};
} @Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}

这里主要在构造时读了流,然后存在变量body里,每次返回流的时候从body构造回去即可。

在Filter中使用这个Wrapper如下:

PkslowRequestWrapper request = new PkslowRequestWrapper(req);
ServletInputStream servletInputStream = request.getInputStream();
String body = StreamUtils.copyToString(servletInputStream, Charset.defaultCharset());
log.info("Request Body(PkslowRequestWrapper): {}", body);

2.3 内置Filter

其实,针对Request,Spring Boot提供了内置的Filter可以直接记录请求,使用如下:

package com.pkslow.springboot.common.web.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CommonsRequestLoggingFilter; @Configuration
public class PkslowConfig {
@Bean
public CommonsRequestLoggingFilter loggingFilter() {
CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter();
filter.setIncludeHeaders(true);
filter.setIncludeClientInfo(true);
filter.setIncludePayload(true);
filter.setIncludeQueryString(true); filter.setAfterMessagePrefix("CommonsRequestLoggingFilter Request: "); return filter;
}
}

但要开debug级别的日志才会打出来。

logging:
level:
root: debug

日志如下:

DEBUG 20356 --- [nio-8080-exec-1] o.s.w.f.CommonsRequestLoggingFilter      : Before request [POST /hello/pkslow, client=127.0.0.1, headers=[authorization:"Basic xxxxxx", content-length:"37", host:"localhost:8080", connection:"Keep-Alive", user-agent:"Apache-HttpClient/4.5.13 (Java/17.0.5)", accept-encoding:"gzip,deflate", Content-Type:"application/json;charset=UTF-8"]]

3 记录返回

返回也是一样,有流不可重复读的问题,使用Spring自带的ContentCachingResponseWrapper即可。

ContentCachingResponseWrapper response = new ContentCachingResponseWrapper(res);
log.info("Response Code: {}", response.getStatus());
String responseBody = new String(response.getContentAsByteArray(), response.getCharacterEncoding());
log.info("Response Body: {}", responseBody);
response.copyBodyToResponse();

特别注意一定要调用copyBodyToResponse()这个方法,不然无法返回body给请求端了。

4 记录时间

记录整个请求的处理时间请参考: Java如何测量方法执行时间

5 测试

测试一下:

POST http://localhost:8080/hello/pkslow
Content-Type: application/json
Authorization: Basic xxxxxx {
"id": 999,
"value": "content"
}

执行日志结果如下:

6 总结

也可使用ContentCachingRequestWrapper来解决请求流不可重复读的问题,但这个Wrapper是有限制的,具体可以看它源码。也有人提了Issue

代码请看GitHub: https://github.com/LarryDpk/pkslow-samples

最新文章

  1. 对于前端,「微信小程序」其实不美好
  2. JMeter--一、安装JMeter
  3. VSTO开发,转帖
  4. java面向对象编程--第十一章 异常处理
  5. SQL存储过程相关信息查看转
  6. Hibernate学习笔记(四)关系映射之一对一关联映射
  7. C#如何以管理员身份运行程序(转)
  8. 改变HTML中超链接的显示样式
  9. Your build settings specify a provisioning profile with the UUID, no such provisioning profile was found的解决方案
  10. canvas绘制百分比圆环进度条
  11. 如何安装ArchLinux
  12. 记录JavaFx中非常重要的细节,入门了也未必知道
  13. vue2 computed set与get函数
  14. python flask_Sqlalchemy管理数据库
  15. Win7系统分区提示会把选定的基本磁盘转化为动态磁盘
  16. Java 实现 AES 加解密
  17. 深入React技术栈之初入React世界
  18. Bytom国密网说明和指南
  19. 蓝绿部署、红黑部署、AB测试、灰度发布、金丝雀发布、滚动发布的概念与区别(转)
  20. linux如何通过脚本来修改用户的密码?脚本自动化修改用户密码?

热门文章

  1. springMVC实现文件的上传和下载
  2. 在 .NET 7上使用 WASM 和 WASI
  3. 设置CMD命令的初始目录
  4. Jenkins用户管理
  5. C#自定义控件开发(1)
  6. Hutool 的学习
  7. 【Spring系列】- Bean生命周期底层原理
  8. 大趋智能打印机java api
  9. 【Java EE】Day10 JavaScript高级、DOM、BOM、事件
  10. Scrum敏捷开发方法实践