Spring 使用 filter 过滤器、ContentCachingWrapper 包装类获取请求参数和返回值

Spring Spring Boot About 7,304 words

HttpServletRequest 获取异常

HttpServletRequest的流只能被读取一次,在filterInterceptor中调用了getInputStream()后,会报错:

DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed]

或者

DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public com.example.filter.dto.Response com.example.filter.controller.HelloController.helloPost(com.example.filter.dto.ReqBody)]

ContentCachingWrapper

Spring提供的Servlet包装类。

ContentCachingRequestWrapper

ContentCachingRequestWrapper中缓存的内容需要在请求处理之后才能读取到。

ContentCachingRequestWrapper还可以限制请求字节数。

ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(req, 30) { // 限制30个字节
    @Override
    protected void handleContentOverflow(int contentCacheLimit) {
        throw new RuntimeException("over limit#" + contentCacheLimit);
    }
};

ContentCachingResponseWrapper

使用ContentCachingResponseWrapper包装过的HttpServletResponse,在使用完之后需要调用下copyBodyToResponse()方法。

说明

同样适用于@ControllerAdvice有全局捕获异常的场景。

示例代码

@Slf4j
@WebFilter(urlPatterns = {"/filter/*"})
@Order(Integer.MIN_VALUE)
public class MyOncePreRequestFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String requestMethod = request.getMethod();
        boolean shouldWrapMethod = Objects.equals(requestMethod, HttpMethod.PUT.name()) || Objects.equals(requestMethod, HttpMethod.POST.name());

        boolean isFirstRequest = !isAsyncDispatch(request);

        boolean shouldWrapRequest = isFirstRequest && !(request instanceof ContentCachingRequestWrapper) && shouldWrapMethod;
        HttpServletRequest requestToUse = shouldWrapRequest ? new ContentCachingRequestWrapper(request) : request;

        boolean shouldWrapResponse = !(response instanceof ContentCachingResponseWrapper) && shouldWrapMethod;
        HttpServletResponse responseToUse = shouldWrapResponse ? new ContentCachingResponseWrapper(response) : response;

        long startTime = System.currentTimeMillis();
        Throwable t = null;
        try {
            filterChain.doFilter(requestToUse, responseToUse);
        } catch (Exception e) {
            t = e;
            throw e;
        } finally {
            doSaveAccessLog(requestToUse, responseToUse, System.currentTimeMillis() - startTime, t);
        }

    }

    private void doSaveAccessLog(HttpServletRequest request, HttpServletResponse response, long useTime, Throwable t) {
        if (isAsyncStarted(request)) {
            copyResponse(response);
            return;
        }
        try {
            String requestUri = request.getRequestURI();
            String requestHeaders = getRequestHeaders(request);
            String requestParams = getRequestParams(request);
            String requestString = getRequestString(request);
            String responseString = getResponseString(response);
            int responseStatus = response.getStatus();

            List<String> logs = new ArrayList<>();
            logs.add("time=" + useTime + "ms");
            logs.add("uri=" + requestUri);
            logs.add("headers=" + requestHeaders);
            logs.add("status=" + responseStatus);
            logs.add("requestContentType=" + request.getContentType());
            logs.add("responseContentType=" + response.getContentType());
            logs.add("params=" + requestParams);
            logs.add("request=" + requestString);
            logs.add("response=" + responseString);
            if (t != null) {
                logs.add("exception=" + t.getCause().getMessage());
            }

            // TODO persist log
            log.info(String.join(",", logs));
        } catch (Throwable e) {
            log.error("got an exception when saving access log", e);
        } finally {
            copyResponse(response);
        }
    }

    private void copyResponse(HttpServletResponse response) {
        ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            try {
                wrapper.copyBodyToResponse();
            } catch (IOException ignored) {
            }
        }
    }

    private String getRequestHeaders(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        List<String> headers = new ArrayList<>();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            headers.add(key + ':' + request.getHeader(key));
        }
        return '[' + String.join(",", headers) + ']';
    }

    private String getRequestParams(HttpServletRequest request) {
        Map<String, String[]> requestParams = new HashMap<>(request.getParameterMap());
        List<String> pairs = new ArrayList<>();
        if (!CollectionUtils.isEmpty(requestParams)) {
            for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
                String name = entry.getKey();
                String[] value = entry.getValue();
                if (value == null) {
                    pairs.add(name + "=");
                } else {
                    for (String v : value) {
                        pairs.add(name + "=" + v.trim());
                    }
                }
            }
        }
        String requestParamsStr = CollectionUtils.isEmpty(pairs) ? "" : String.join("&", pairs);
        if (Objects.equals(request.getContentType(), MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
            try {
                requestParamsStr = URLDecoder.decode(requestParamsStr, StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException ignore) {
            }
        }
        return requestParamsStr;
    }

    private String getRequestString(HttpServletRequest request) {
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            try {
                byte[] buf = wrapper.getContentAsByteArray();
                return new String(buf, wrapper.getCharacterEncoding()).replaceAll("[\n\r]", "");
            } catch (UnsupportedEncodingException e) {
                return "[UNKNOWN]";
            }
        }
        return "";
    }

    private String getResponseString(HttpServletResponse response) {
        ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            try {
                byte[] buf = wrapper.getContentAsByteArray();
                return new String(buf, wrapper.getCharacterEncoding()).replaceAll("[\n\r]", "");
            } catch (UnsupportedEncodingException e) {
                return "[UNKNOWN]";
            }
        }
        return "";
    }

}

参考

https://www.jb51.net/article/221738.htm

https://github.com/howardliu-cn/effective-spring/tree/main/spring-filter

Views: 3,310 · Posted: 2022-10-12

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓


Today On History
Browsing Refresh