重写HttpServletRequestWrapper ,解决getInputStream()只能获取一次数据流的问题
·
背景
项目基于springboot开发,RestFull接口向外暴露的API需要进行参数日志记录,即在进入真正controller方法前,需要先记录处理的参数信息,所以需要对提交的POST流进行JSON读,并将相关参数打印记录。
问题
如果在Filter中使用request.getInputStream()来获取流来得到body中的信息,可以达到预期效果,但是流的获取只能获取一次,之后再获取就获取不到了,导致controller无法拿到参数而报错。
解决
参考相关资料发现实现一个类继承HttpServletRequestWrapper,重写其中的getInputStream方法,让其可以重复获取我们想要的流数据。
代码如下:
package com.wyx.gateway.config;
import org.springframework.util.StringUtils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestContextWrapper extends HttpServletRequestWrapper {
private String requestBody = null;
private HttpServletRequest req = null;
public RequestContextWrapper(HttpServletRequest request, String requestBody) throws IOException {
super(request);
this.requestBody = requestBody;
this.req = request;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new StringReader(requestBody));
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
private InputStream in = new ByteArrayInputStream(
requestBody.getBytes(StringUtils.isEmpty(req.getCharacterEncoding()) ? "UTF-8" : req.getCharacterEncoding()));
// 读取 requestBody 中的数据
@Override
public int read() throws IOException {
return in.read();
}
};
}
}
日志记录Filter参考如下:
package com.wyx.gateway.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.slf4j.MDC;
import org.springframework.util.StringUtils;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
@Slf4j
public class RequestLoggerFilter implements Filter {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 记录链路id,没有的话可以去除
String traceId = StringUtils.replace(UUID.randomUUID().toString(), "-", "");
MDC.put("traceId", traceId);
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String uri = req.getRequestURI();
String param = OBJECT_MAPPER.writeValueAsString(req.getParameterMap());
String requestBody = IOUtils.toString(req.getInputStream(), "UTF-8");
RequestContextWrapper requestWrapper = new RequestContextWrapper(req, requestBody);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(resp);
long startTime = System.currentTimeMillis();
// 跳过健康检查
if (!uri.contains("health")) {
log.info("request_in uri:{}, param: {}, body: {}", uri, param, requestBody);
}
chain.doFilter(requestWrapper, responseWrapper);
String responseBody = IOUtils.toString(responseWrapper.getContentInputStream(), "UTF-8");
responseWrapper.copyBodyToResponse();
long procTime = System.currentTimeMillis() - startTime;
if (!uri.contains("health")) {
log.info("request_out proc_time: {}, response: {}", procTime, responseBody);
}
MDC.clear();
}
@Override
public void destroy() {
}
}
参考:https://cloud.tencent.com/developer/article/1963415
更多推荐



所有评论(0)