前言
在spring boot web项目中 会有一些需求 要求 对 请求之前的信息和返回给请求方信息进行预处理
如 参数、响应信息加密 解密操作 , 记录请求参数和响应信息日志 等操作
原本的httpServletRequest,httpServletResponse 对这些操作不支持 只能通过 servlet 预留的wrapper进行增强 然后进行预处理
具体实现
原理
通过 servlet 预留的wrapper类 对原始的 request、response对象进行装饰
装饰 request示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; 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;
@Slf4j public class MyRequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
public MyRequestWrapper(HttpServletRequest request) { super(request); try { body = IOUtils.toByteArray(request.getInputStream()); } catch (IOException e) { log.error("增强request io异常:{}", request.getRequestURI()); e.printStackTrace(); } }
@Override public ServletInputStream getInputStream() throws IOException { return new SignWrapperInputStream(body); }
@Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); }
public byte[] getBody() { return body; }
public void setBody(byte[] body) { this.body = body; }
private class SignWrapperInputStream extends ServletInputStream {
private ByteArrayInputStream buffer;
public SignWrapperInputStream(byte[] body) { body = (body == null) ? new byte[0] : body; this.buffer = new ByteArrayInputStream(body); }
@Override public int read() throws IOException { return buffer.read(); }
@Override public boolean isFinished() { return buffer.available() == 0; }
@Override public boolean isReady() { return true; }
@Override public void setReadListener(ReadListener listener) { throw new RuntimeException("Not implemented");
} } }
|
装饰 response示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException;
@Slf4j public class MyResponseWrapper extends HttpServletResponseWrapper {
private final WrapperServletOutputStream wrapperServletOutputStream = new WrapperServletOutputStream();
public MyResponseWrapper(HttpServletResponse response) { super(response); }
@Override public ServletOutputStream getOutputStream() throws IOException { return wrapperServletOutputStream; }
@Override public PrintWriter getWriter() throws IOException { return new PrintWriter(wrapperServletOutputStream); }
public byte[] getBodyBytes() { return wrapperServletOutputStream.out.toByteArray(); }
public String getBodyString() { try { return wrapperServletOutputStream.out.toString("UTF-8"); } catch (UnsupportedEncodingException e) { return "[UNSUPPORTED ENCODING]"; } }
public void copyToResponse() { try { getResponse().getOutputStream().write(getBodyBytes()); } catch (IOException e) { e.printStackTrace(); } }
private class WrapperServletOutputStream extends ServletOutputStream { private ByteArrayOutputStream out = new ByteArrayOutputStream();
@Override public boolean isReady() { return true; }
@Override public void setWriteListener(WriteListener writeListener) {
}
@Override public void write(int b) throws IOException { out.write(b); }
@Override public void write(byte[] b) throws IOException { out.write(b); }
@Override public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); } } }
|
在servlet filter中增强 原始的request 和response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component;
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets;
@Component @WebFilter(urlPatterns = "/*") @Slf4j public class MyFilter implements Filter {
@Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { MyRequestWrapper requestWrapper = new MyRequestWrapper((HttpServletRequest) request); MyResponseWrapper responseWrapper = new MyResponseWrapper((HttpServletResponse) response); chain.doFilter(requestWrapper, responseWrapper); log.info("请求地址:{},请求的body{},返回信息:{}", ((HttpServletRequest) request).getRequestURI(), new String(requestWrapper.getBody(), StandardCharsets.UTF_8), responseWrapper.getBodyString()); responseWrapper.copyToResponse(); }
@Override public void destroy() {
} }
|
启动项目
启动项目 访问接口 即可打印出info级别的记录日志
如:
1
| 2019-01-10 17:08:22.570 INFO 22029 --- [nio-8080-exec-7] com.only.base.filter.MyFilter : 请求地址:/api/user,请求的body,返回信息:{"code":-1,"msg":"未知异常"}
|
总结
这个只能在servlet 容器中使用 如果使用的spring boot 2.x 一定要注意 是servlet容器 还是webflux模式的
本身spring 提供了一些 增强的方式 直接查看 HttpServletRequestWrapper、HttpServletResponseWrapper 这两个类的子类即可