Spring 使用 filter 过滤器、ContentCachingWrapper 包装类获取请求参数和返回值
Spring Spring Boot About 7,304 wordsHttpServletRequest 获取异常
HttpServletRequest
的流只能被读取一次,在filter
或Interceptor
中调用了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扫描下方二维码关注公众号和小程序↓↓↓
Loading...