SpringBoot-打印请求的入参和出参
明确问题点
- 在Controller层接受参数分为两种情况:
情况一:接口使用 @RequestParam 接收参数
情况二:接口使用 @RequestBody 接收参数
针对情况一,代码写起来就非常简单了,我们只需要在拦截器中通过request.getParameterMap() 来获得全部 Parameter 参数就可以了
但是当接口使用 @RequestBody 接收参数时,我们在拦截器中使用同样的方法获取参数,就会出现流已关闭的异常,也就导致参数读取失败了 … 这是因为 Spring 已经对 @RequestBody 提前进行处理,而 HttpServletReqeust 获取输入流时仅允许读取一次,所以会报java.io.IOException: Stream closed。
解决方法就是获取HttpServletReqeust数据将它保存下来
具体代码如下:
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* HttpServletRequest 过滤器
* 解决: request.getInputStream()只能读取一次的问题
* 目标: 流可重复读
*/
@Component
@WebFilter(filterName = "HttpServletRequestFilter",urlPatterns = "/")
public class HttpServletRequestFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) request);
}
//获取请求中的流,将取出来的字符串,再次转换成流,然后把它放入到新 request对象中
// 在chain.doFiler方法中传递新的request对象
if(null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void destroy() {
Filter.super.destroy();
}
/***
* HttpServletRequest 包装器
* 解决: request.getInputStream()只能读取一次的问题
* 目标: 流可重复读
*/
public class RequestWrapper extends HttpServletRequestWrapper {
/**
* 请求体
*/
private String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
body=getBody(request);
}
/**
* 获取请求体
* @param request 请求
* @return 请求体
*/
private String getBody(HttpServletRequest request) {
return HttpContextUtils.getBodyString(request);
}
/**
* 获取请求体
* @return 请求体
*/
public String getBody() {
return body;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
// 创建字节数组输入流
final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
}
}
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class HttpContextUtils {
/**
* 获取query参数
* @param request
* @return
*/
public static Map<String, String> getParameterMapAll(HttpServletRequest request) {
Enumeration<String> parameters = request.getParameterNames();
Map<String, String> params = new HashMap<>();
while (parameters.hasMoreElements()) {
String parameter = parameters.nextElement();
String value = request.getParameter(parameter);
params.put(parameter, value);
}
return params;
}
/**
* 获取请求Body
*
* @param request
* @return
*/
public static String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
过滤器执行之后进入到拦截器。然后拦截器根据request类型不同,解析方式也不同
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.nacos.common.utils.MapUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Set;
@Component
@Slf4j
public class LogPrintInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI=request.getRequestURI();
StringBuffer buffer=new StringBuffer();
if(request instanceof HttpServletRequestFilter.RequestWrapper){
buffer.append(HttpContextUtils.getBodyString(request));
}
if(StringUtils.isEmpty(buffer.toString())){
Map<String, String[]> parameterMap = request.getParameterMap();
try {
if (MapUtils.isNotEmpty(parameterMap)) {
Set<String> keySet = parameterMap.keySet();
for (String key : keySet) {
Object values = parameterMap.get(key);
try {
JSONArray jsonarray = JSON.parseArray(JSON.toJSONString(values));
buffer.append(key + "->" + jsonarray + "; ");
} catch (Exception e) {
buffer.append(key + "->" + values + "; ");
}
}
}
} catch (Exception e) {
log.error("LogPrintInterceptor print error", e);
}
}
log.info("LogPrintInterceptor preHandle print log,requestURI={},params=[{}]",requestURI, buffer);
return true;
}
}
打印返回参数采用继承ResponseBodyAdvice的方式来实现
import com.alibaba.fastjson.JSON;
import com.techwolf.samplebase.common.resp.ApiResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ControllerAdvice
@Slf4j
public class InterceptResponse implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
ServletServerHttpResponse responseTemp = (ServletServerHttpResponse) response;
HttpServletResponse resp = responseTemp.getServletResponse();
ServletServerHttpRequest sshr = (ServletServerHttpRequest) request;
HttpServletRequest req = sshr.getServletRequest();
String requestURI=req.getRequestURI();
if(body instanceof ApiResp){
ApiResp apiResp=(ApiResp) body;
if(apiResp!=null){
log.info("InterceptResponse beforeBodyWrite print log,requestURI={},result={}",requestURI, JSON.toJSONString(body));
}
}
return body;
}
}
在AdminMvcConfig可以配置多个拦截器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class AdminMvcConfig implements WebMvcConfigurer {
@Autowired
private LogPrintInterceptor logPrintInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logPrintInterceptor).addPathPatterns("/api/**");
}
}