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 ...
随机推荐
- 看了同事这10个IDEA神级插件,我也悄悄安装了
昨天,有读者私信发我一篇文章,说里面提到的 Intellij IDEA 插件真心不错,基本上可以一站式开发了,希望能分享给更多的小伙伴,我在本地装了体验了一下,觉得确实值得推荐,希望小伙伴们有时间也可 ...
- KatalonRecorder系列(一):基本使用+XPath元素定位
一.简介 Katalon Recorder是基于selenium的浏览器插件,支持火狐和chrome.可以录制web上的操作并回放,还能导入导出脚本. 二.安装 可在谷歌商店或者火狐附件组件中搜索并选 ...
- springboot中使用mybatis_plus逆向工程
创建springboot项目,选择图片中所示依赖 mybatis-plus生成的依赖 <!-- mybatis_plus --> <dependency> <groupI ...
- 查看服务器出口ip
[root@iZap201hv2fcgry1alvbznZ ~]# curl cip.cc IP : xxx.xxx.xx.xx 地址 : 中国 浙江 绍兴 运营商 : 移动 数据二 : 浙江省绍兴市 ...
- 网络编程:软件开发架构、架构总结、网络编程前戏、OSI七层协议简介、OSI七层协议之物理连接层、数据链路层、网络相关专业名词、OSI七层协议之网络层
目录 软件开发架构 架构总结 网络编程前戏 OSI七层协议简介 OSI七层协议之物理连接层 OSI七层协议之数据链路层 网络相关专业名词 OSI七层协议之网络层 OSI七层协议之传输层 软件开发架构 ...
- APACHE快速安装流程梳理
操作参考教程:https://www.cnblogs.com/haw2106/p/9839655.html 快速安装开始: [环境配置1] yum -y install gcc gcc-c++ wge ...
- Promise基础知识
Promise 1.Promise的前置小知识 进程(厂房) 程序的运行环境 线程(工人) 线程是实际进行运算的东西 同步 通常情况代码都是自上向下一行一行执行的 前边的代码不执行后边的代码也不会执行 ...
- PW6276是一颗高效同步升压转换芯片,内部集成低阻抗功率 MOS
概述PW6276是一颗高效同步升压转换芯片,内部集成低阻抗功率 MOS. 具有短路保护功能内部集成软启动电路,无需外部补偿电容,外部反馈网络.PW6276采用 SOP8-EP 封装配合较少的外围原件使 ...
- Ubuntu 22.04 搭建K8s集群
目录 1. 虚拟机基础配置 配置静态ip 设置主机名 设置hosts 安装ssh 2. Ubuntu系统设置 禁用swap 修改内核参数 3. 安装containerd 4. 安装Kubernetes ...
- 【Java】【数据库】索引为何使查询变得更快?--B+树
排序数据的二分查找 二分查找的时间复杂度是\(O(log_2n)\),明显快于暴力搜索. 索引 建立索引的数据,就是通过事先排好顺序,在查找时可以应用二分查找来提高查询效率. 所以索引应该尽可能建立在 ...