温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么记录 http 请求/ 响应数据

发布时间:2021-06-21 15:07:12 来源:亿速云 阅读:212 作者:chen 栏目:编程语言

本篇内容介绍了“怎么记录 http 请求/ 响应数据”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

经常会遇到需要处理 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;     @Override     protected 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);         }     }     @Override     public 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;     }     @Override     public void init(FilterConfig filterConfig) throws ServletException {         this.delegate.init(filterConfig);     }     @Override     public 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]+}。

“怎么记录 http 请求/ 响应数据”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI