点击关注公众号,Java干货及时送达

来源:https://fredal.xin/http-body-recorder

经常会遇到需要处理 http 请求以及响应 body 的场景。

而这里比较大的一个问题是 servle t的 requestBody 或 responseBody 流一旦被读取了就无法二次读取了。

针对这个问题,spring本身提供了解决方案,即:

  • ContentCachingRequestWrapper

  • ContentCachingResponseWrapper。

我们编写一个过滤器:

public abstract class HttpBodyRecorderFilter extends OncePerRequestFilter {private static final int DEFAULT_MAX_PAYLOAD_LENGTH = 1024 * 512;private int maxPayloadLength = DEFAULT_MAX_PAYLOAD_LENGTH;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {boolean isFirstRequest = !isAsyncDispatch(request);HttpServletRequest requestToUse = request;if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)&& (request.getMethod().equals(HttpMethod.PUT.name())|| request.getMethod().equals(HttpMethod.POST.name()))) {requestToUse = new ContentCachingRequestWrapper(request);}HttpServletResponse responseToUse = response;if (!(response instanceof ContentCachingResponseWrapper) && (request.getMethod().equals(HttpMethod.PUT.name())|| request.getMethod().equals(HttpMethod.POST.name()))) {responseToUse = new ContentCachingResponseWrapper(response);}boolean hasException = false;try {filterChain.doFilter(requestToUse, responseToUse);} catch (final Exception e) {hasException = true;throw e;} finally {int code = hasException ? 500 : response.getStatus();if (!isAsyncStarted(requestToUse) && (this.codeMatched(code, AdvancedHunterConfigManager.recordCode()))) {recordBody(createRequest(requestToUse), createResponse(responseToUse));} else {writeResponseBack(responseToUse);}}}protected String createRequest(HttpServletRequest request) {String payload = "";ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);if (wrapper != null) {byte[] buf = wrapper.getContentAsByteArray();payload = genPayload(payload, buf, wrapper.getCharacterEncoding());}return payload;}protected String createResponse(HttpServletResponse resp) {String response = "";ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(resp, ContentCachingResponseWrapper.class);if (wrapper != null) {byte[] buf = wrapper.getContentAsByteArray();try {wrapper.copyBodyToResponse();} catch (IOException e) {e.printStackTrace();}response = genPayload(response, buf, wrapper.getCharacterEncoding());}return response;}protected void writeResponseBack(HttpServletResponse resp) {ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(resp, ContentCachingResponseWrapper.class);if (wrapper != null) {try {wrapper.copyBodyToResponse();} catch (IOException e) {LOG.error("Fail to write response body back", e);}}}private String genPayload(String payload, byte[] buf, String characterEncoding) {if (buf.length > 0 && buf.length < getMaxPayloadLength()) {try {payload = new String(buf, 0, buf.length, characterEncoding);} catch (UnsupportedEncodingException ex) {payload = "[unknown]";}}return payload;}public int getMaxPayloadLength() {return maxPayloadLength;}private boolean codeMatched(int responseStatus, String statusCode) {if (statusCode.matches("^[0-9,]*$")) {String[] filteredCode = statusCode.split(",");return Stream.of(filteredCode).map(Integer::parseInt).collect(Collectors.toList()).contains(responseStatus);} else {return false;}}protected abstract void recordBody(String payload, String response);protected abstract String recordCode();}

这样自定义一个filter继承HttpBodyRecorderFilter,重写recordBody方法就能自定义自己的处理逻辑了。

另外,recordCode方法可用于定义在请求响应码为多少的时候才会去记录body,例如可以定义为只有遇到400或500时才记录body,用于错误侦测。

过滤器的匹配规则比较简单,如果想要像springmvc那样进行匹配,我们可以使用:AntPathMatcher。

class PatternMappingFilterProxy implements Filter {private final Filter delegate;private final List<String> pathUrlPatterns = new ArrayList();private PathMatcher pathMatcher;public PatternMappingFilterProxy(Filter delegate, String... urlPatterns) {Assert.notNull(delegate, "A delegate Filter is required");this.delegate = delegate;int length = urlPatterns.length;pathMatcher = new AntPathMatcher();for (int index = 0; index < length; ++index) {String urlPattern = urlPatterns[index];this.pathUrlPatterns.add(urlPattern);}}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;String path = httpRequest.getRequestURI();if (this.matches(path)) {this.delegate.doFilter(request, response, filterChain);} else {filterChain.doFilter(request, response);}}private boolean matches(String requestPath) {for (String pattern : pathUrlPatterns) {if (pathMatcher.match(pattern, requestPath)) {return true;}}return false;}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {this.delegate.init(filterConfig);}@Overridepublic void destroy() {this.delegate.destroy();}public List<String> getPathUrlPatterns() {return pathUrlPatterns;}public void setPathUrlPatterns(List<String> urlPatterns) {pathUrlPatterns.clear();pathUrlPatterns.addAll(urlPatterns);}}

这样子,PatternMappingFilterProxy装饰了真正的HttpBodyRecorderFilter,支持传入urlPatterns,从而实现像springmvc那样的ant style的匹配。例如对于以下接口:

@PostMapping("/test/{id}")
public Object test(@PathVariable(value =  "id",required =  true)  final Integer index)  {//do something}

可以设置urlPattern为/test/{id:[0-9]+}

热门内容:去大厂面试,说了没高并发经验,面试官还是抓着这个问!重磅 ! Redis+Nginx+JVM+设计模式+Spring全家桶+DubboMySQL太细碎了,我硬生生捋出了一条核心大主线!
不要再自己封装各种Util工具类了,这款神仙级框架你值得拥有!神级开源框架发布!Github排名前三,连Spring Cloud 都被干掉了!
最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

优雅地记录http请求和响应的数据相关推荐

  1. 【Go】优雅的读取http请求或响应的数据-续

    原文链接:https://blog.thinkeridea.com/201902/go/you_ya_de_du_qu_http_qing_qiu_huo_xiang_ying_de_shu_ju_2 ...

  2. jmter生成的报告没有显示请求和响应明细数据

    第一次把环境全部搭建好后运行得出的html报告如下:没有显示Request和Response数据,这样不利于定位问题 估计是jmeter为了减轻客户机负担,就没又默认把这些信息保存,如果想要保存,也可 ...

  3. SpringBoot使用Logbook记录HTTP请求响应日志

    写在前面:2020年面试必备的Java后端进阶面试题总结了一份复习指南在Github上,内容详细,图文并茂,有需要学习的朋友可以Star一下! GitHub地址:https://github.com/ ...

  4. java logbook_SpringBoot使用Logbook记录HTTP请求响应日志

    Spring Boot的httptrace端口能够记录每次访问的请求和响应信息,但是不能记录body,这样在出问题时就不方便排查,而且httptrace不方便在原有的基础上进行扩展,所以只能寻求其他方 ...

  5. HTTP协议(5)HTTP请求和响应

    之前曾介绍过,所有的HTTP通信都被构造成一对HTTP请求和HTTP响应,HTTP协议的请求与响应报文都是由"首部header"和"主体body"两部分组成的. ...

  6. java 响应 请求参数_spring基础----请求与响应的参数(一)

    这里面我们主要介绍一下spring中关于请求和响应参数数据的问题.爱,从来就是一件千回百转的事.不曾被离弃,不曾受伤害,怎懂得爱人?爱,原来是一种经历. spring中的请求与响应 一.spring中 ...

  7. SpringMVC学习----请求与响应

    请求与响应 请求映射路径 提出问题 解决方法 细分请求路径 UserController类 BookController类 在类上方配置的请求映射 请求 请求方式 请求方法 GET请求 传入参数结果 ...

  8. http协议,http状态码,请求,响应

    http 简介 http 是一种超文本传输协议(Hyper Text Transfer Protocol), 可以用来在络中把服务器消息传输到我们的浏览器中.并且Http 协议是一个基于 TCP/IP ...

  9. Android中使用logger打印完整的okhttp网络请求和响应的所有相关信息(请求行、请求头、请求体、响应行、响应行、响应头、响应体)

    如果你的项目中的网络请求库是Retrofit的话,他的底层封装的是OkHttp,通常调试网络接口时都会将网络请求和响应相关数据通过日志的形式打印出来.OkHttp也提供了一个网络拦截器okhttp-l ...

最新文章

  1. jQuery之get方法
  2. Flask 教程 第十六章:全文搜索
  3. Spring mvc+ maven + MyBatis + Oracle + IDEA 项目搭建 - framework 进阶中(一)
  4. 尝试使用Java6API读取java代码
  5. 线性表:5.约瑟夫环,循环链表及其C语言实现
  6. 创业者在创业时经常会问到的一个问题
  7. node.js 安装及配置(hello world)及 node 的包管理器(npm)
  8. 如何在 macOS Monterey 中更改光标的颜色样式?
  9. Web APIs概念详解(附图解)
  10. 追赶法matlab算法,追赶法matlab程序
  11. 三维激光扫描后处理软件_三维激光扫描——钢结构形变检测的利器
  12. 学云计算能从事哪些岗位 未来职业发展是什么样
  13. EN300328测试软件,蓝牙耳机EN300328测试项目。
  14. mysql忘记密码win10_win10 mysql8.0.12 忘记root密码如何重置密码
  15. Android 实现微信,QQ的程序前后台切换:back键切换后台;点击通知栏恢复前台。
  16. Android 详解第三方介质交互之NFC,并且实现读你的交通卡,酒店房卡,学生证!
  17. Ibatis.net + Npgsql +PostgreSql 多线程“Timeout while getting a connection from pool.”
  18. 记今年阿里巴巴招聘的几个失误
  19. 明川和治功现在正说牛家沟那个“母老虎”的事
  20. 计算机毕业设计ssm基于H的新冠防疫宣传网站的设计与实现

热门文章

  1. std::bind介绍
  2. 2021全国高校计算机能力挑战赛(初赛)Java试题四
  3. 阶段1 语言基础+高级_1-3-Java语言高级_02-继承与多态_第5节 final关键字_5_final关键字用于修饰成员变量...
  4. 2.1 mac下多版本jdk的安装和管理
  5. Vue轮播图插件---Vue-Awesome-Swiper
  6. CC2540获取本机MAC地址
  7. Java学习笔记七——数组工具类Arrays
  8. php中类和对象的操作
  9. Java Socket发送与接收HTTP消息简单实现
  10. 大四狗找工作,持续更新