Spring在Filter中记录Web请求Request和返回Response的内容及时长
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
Spring在Filter中记录Web请求Request和返回Response的内容及时长的更多相关文章
- django HTTP请求(Request)和回应(Response)对象
Django使用request和response对象在系统间传递状态.—(阿伦)当一个页面被请示时,Django创建一个包含请求元数据的 HttpRequest 对象. 然后Django调入合适的视图 ...
- Spring中操作日志记录web请求的body报文
在spring中,通常可以使用切面编程方式对web请求记录操作日志.但是这种方式存在一个问题,那就是只能记录url中的请求参数,无法记录POST或者PUT请求的报文体,因为报文体是放在request对 ...
- [转]spring的filter中targetFilterLifecycle作用
在web.xml中进行配置,对所有的URL请求进行过滤,就像"击鼓传花"一样,链式处理. 配置分为两种A和B. A:普通配置 在web.xml中增加如下内容:<filter& ...
- spring boot: filter/interceptor/aop在获取request/method参数上的区别(spring boot 2.3.1)
一,filter/interceptor/aop在获取参数上有什么区别? 1,filter可以修改HttpServletRequest的参数(doFilter方法的功能), interceptor/a ...
- Struts2中获取Web元素request、session、application对象的四种方式
我们在学习web编程的时候,一般都是通过requet.session.application(servletcontext)进行一系列相关的操作,request.session.和applicatio ...
- Spring MVC参数绑定(如何接收请求参数及返回参数)
在SpringMVC interceptor案例实践中遇到了获取jsp表单传递参数失败的问题,怎么的解决的呢?下面详细介绍. 先讲述下https://www.cnblogs.com/ilovebath ...
- javaweb局部刷新-ajax异步请求springMVC显示返回的jsp内容,代替iframe
在jsp上要引入jquery <script src="<%=request.getContextPath()%>/js/jquery_ui/jquery.js" ...
- 使用java的自定义过滤器Filter 处理请求request 并响应response
package com.enation.eop; import java.io.BufferedReader; import java.io.IOException; import java.io.I ...
- 陨石坑之webapi 使用filter中如何结束请求流
先看下正常的结束asp.net 请求流怎么写的 System.Web.HttpContext.Current.Response.Write(“end”); System.Web.HttpContext ...
- 在nginx日志的access log中记录post请求的参数值
背景:有时程序偶出现参数少了或没有提交到下一个链接Url里后出现问题,如何查呢,最好的办法是在nginx上的加post参数,以定位到问题才有可能对某个UIR的代码出现的问题进行排查. og_forma ...
随机推荐
- 表驱动法在STM32中的应用
1.概念 所谓表驱动法(Table-Driven Approach)简而言之就是用查表的方法获取数据.此处的"表"通常为数组,但可视为数据库的一种体现.根据字典中的部首检字表查找读 ...
- SpringCloud(七) - 微信支付
1.开发文档 微信开发文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 安全规范:https://pay.weixin.q ...
- onps栈使用说明(3)——tcp、udp通讯测试
4. tcp客户端 在协议栈源码工程下,存在一个用vs2015建立的TcpServerForStackTesting工程.其运行在windows平台下,模拟实际应用场景下的tcp服务器.当tcp客户端 ...
- Day11.2:标签的使用
标签的使用 当我们在嵌套语句中,例如当我们在for的嵌套循环语句中,想要终止或重新开始当前循环以外的循环的时候,单独仅靠break和continue和还不够,需要在我们想要作用的循环语句处加上一个标签 ...
- 第2-3-2章 环境搭建-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
目录 5. 文件服务开发 5.1 环境搭建 5.1.1 数据库环境搭建 5.1.2 Nacos环境搭建 5.1.3 Nginx环境搭建 5.1.4 maven工程环境搭建 5. 文件服务开发 全套代码 ...
- 2022春每日一题:Day 41
题目:I Hate It 一个基础的线段树模板,单点修改+区间查询 代码: #include <cstdio> #include <cstdlib> #include < ...
- Phalcon notes
1. 半原生数据查询: echo $realUser->getReadConnection()->getSQLStatement();die;
- WSL下的Ubuntu 18.04LTS配置软件源和系统更新
WSL下的Ubuntu 18.04LTS配置软件源和系统更新 设置系统语言为中文 # 安装中文支持 sudo apt-get install -y language-pack-zh-hans # 设置 ...
- 重学c#系列——逆变和协变[二十四]
前言 简单整理一下逆变和协变. 正文 什么是逆变和协变呢? 首先逆变和协变都是术语. 协变表示能够使用比原始指定的派生类型的派生程度更大的类型. 逆变表示能够使用比原始指定的派生类型的派生程度更小的类 ...
- 【接口测试】Postman(一)--接口测试知识准备
1.0 前言 应用程序编程接口(Application Programming Interface, API)是这些年来最流行的技术之一,强大的Web应用程序和领先的移动应用程序都离不开后端强大的 ...