一、需求
如题。
二、思路与分析
- 拦截器无法实现如题的功能,因为请求体数据和响应数据获取一次后就清空了,若多次获取会报异常,且无法对请求进行包装。
- 过滤器可以在doFilter中放入请求和响应,所以可以在filter对请求进行包装,以便进行二次获取。但是无法直接注入,可以利用ApplicationContextAware解决或直接使用OncePerRequestFilter。
- 切面也可实现功能。
本文不采用切面,而是通过过滤器实现
三、具体代码
请求包装类:
package com.lihenggen.demo.test.filter;
import cn.hutool.core.io.IoUtil;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* @author lihg
*/
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper{
private byte[] body;
private BufferedReader reader;
private ServletInputStream inputStream;
public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException{
super(request);
loadBody(request);
}
private void loadBody(HttpServletRequest request) throws IOException{
body = IoUtil.readBytes(request.getInputStream());
inputStream = new RequestCachingInputStream(body);
}
public byte[] getBody() {
return body;
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (inputStream != null) {
return inputStream;
}
return super.getInputStream();
}
@Override
public BufferedReader getReader() throws IOException {
if (reader == null) {
reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
}
return reader;
}
private static class RequestCachingInputStream extends ServletInputStream {
private final ByteArrayInputStream inputStream;
public RequestCachingInputStream(byte[] bytes) {
inputStream = new ByteArrayInputStream(bytes);
}
@Override
public int read() {
return inputStream.read();
}
@Override
public boolean isFinished() {
return inputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readlistener) {
}
}
}
过滤器代码:
package com.lihenggen.demo.test.filter;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONUtil;
import com.lihenggen.demo.test.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
* @author lihg
*/
@Component
@Slf4j
//@Order(0)
//@WebFilter(filterName = "thirdPartRecordFilter", urlPatterns = "/restful/*")
public class ThirdPartRecordFilter extends OncePerRequestFilter {
private static final String THIRD_PART_PATTERN = "/restful/**";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
// 只对第三方接口行拦截
if (!new AntPathMatcher().match(THIRD_PART_PATTERN, request.getServletPath())) {
chain.doFilter(request, response);
return;
}
Long startTime = System.currentTimeMillis();
// 转换成包装类,避免请求体和响应数据读取后数据为空的问题
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);
System.out.println("======================================================================================================");
log.info("请求ip:{}", IpUtil.getIpAddr((HttpServletRequest) request));
log.info("认证信息:{}", requestWrapper.getHeader("Authorization"));
log.info("请求路径:{}", requestWrapper.getRequestURI());
Map<String, String[]> parameterMap = requestWrapper.getParameterMap();
parameterMap.keySet().stream().forEach(key -> {
log.info("请求参数:{}" + key + " : " + ArrayUtil.join(parameterMap.get(key), ",", "[", "]"));
});
log.info(new String(requestWrapper.getBody(), request.getCharacterEncoding()));
try {
chain.doFilter(requestWrapper, responseWrapper);
// httpStatus正常
log.info("httpStatus:{}", responseWrapper.getStatus());
byte[] content = responseWrapper.getContentAsByteArray();
Long costTime = System.currentTimeMillis() - startTime;
log.info("请求耗时:{}", costTime);
// 记录响应具体内容
if (ArrayUtil.isNotEmpty(content)) {
String resultData = new String(content, responseWrapper.getCharacterEncoding());
if (JSONUtil.isJson(resultData)) {
log.info("状态码:{}", StrUtil.toString(JSONUtil.getByPath(JSONUtil.parseObj(resultData), "code")));
}
log.info("请求结果: {}", resultData);
// todo 持久化
ServletOutputStream out = response.getOutputStream();
out.write(content);
out.flush();
}
} catch (Exception e) {
// httpStatus异常或代码异常,注意,经过测试这里哪怕代码异常,前端接收的状态码为500,但responseWrapper.getStatus()仍为200
log.info("httpStatus:{}", responseWrapper.getStatus());
log.info("异常堆栈:【{}】", ExceptionUtil.stacktraceToString(e));
// todo 持久化
throw e;
}
}
}
获取请求IP地址工具类:
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpUtil {
/**
* 获取当前网络ip
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
//根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
}