<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          優(yōu)雅地記錄http請求和響應的數(shù)據(jù)

          共 6846字,需瀏覽 14分鐘

           ·

          2020-12-25 09:17

          走過路過不要錯過

          點擊藍字關注我們


          經(jīng)常會遇到需要處理http請求以及響應body的場景。而這里比較大的一個問題是servlet的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,用于錯誤偵測。

          過濾器的匹配規(guī)則比較簡單,如果想要像springmvc那樣進行匹配,我們可以使用AntPathMatcher。

          class PatternMappingFilterProxy implements Filter {
          private final Filter delegate;
          private final List 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 getPathUrlPatterns() {
          return pathUrlPatterns;
          }
          public void setPathUrlPatterns(List urlPatterns) {
          pathUrlPatterns.clear();
          pathUrlPatterns.addAll(urlPatterns);
          }
          }

          這樣子,PatternMappingFilterProxy裝飾了真正的HttpBodyRecorderFilter,支持傳入urlPatterns,從而實現(xiàn)像springmvc那樣的ant style的匹配。例如對于以下接口:


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

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



          往期精彩推薦



          騰訊、阿里、滴滴后臺面試題匯總總結(jié) — (含答案)

          面試:史上最全多線程面試題 !

          最新阿里內(nèi)推Java后端面試題

          JVM難學?那是因為你沒認真看完這篇文章


          END


          關注作者微信公眾號 —《JAVA爛豬皮》


          了解更多java后端架構(gòu)知識以及最新面試寶典


          你點的每個好看,我都認真當成了


          看完本文記得給作者點贊+在看哦~~~大家的支持,是作者源源不斷出文的動力


          作者:fredalxin
          地址:https://fredal.xin/http-body-recorder

          瀏覽 87
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  女人20岁毛片60分钟 | 91AV久久久| 日韩黄色电影在线看 | 青娱乐亚洲视频 | 三级片91久久精品欧美亚洲三级片 |