Filter获取请求体body再次读取
工作需要,要将请求和响应做一些处理,写一个filter拦截请求,拦截request中body内容后,字符流关闭,controller取到的请求体内容为空。
从Request中获取输入流,InputStream只能被读取一次。
解决方案
给request添加一个包装类BodyWrapper,继承HttpServletRequestWrapper,
先从request中取输入流,读取流中的数据,然后重写getInputStream()和getReader()方法。
chain.doFilter(requestWrapper, response);
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Enumeration; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import com.xera.fsafesso.HttpHelper; public class BodyWrapper extends HttpServletRequestWrapper {undefined private final byte[] body; public BodyWrapper(HttpServletRequest request) throws IOException {undefined super(request); body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8")); } @Override public BufferedReader getReader() throws IOException {undefined return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException {undefined final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream(){undefined @Override public int read() throws IOException {undefined return bais.read(); } @Override public boolean isFinished() {undefined return false; } @Override public boolean isReady() {undefined return false; } @Override public void setReadListener(ReadListener arg0) {undefined } }; } @Override public String getHeader(String name) {undefined return super.getHeader(name); } @Override public EnumerationgetHeaderNames() {undefined return super.getHeaderNames(); } @Override public Enumeration getHeaders(String name) {undefined return super.getHeaders(name); } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import javax.servlet.ServletRequest; public class HttpHelper {undefined /** * 获取请求Body * @param request * @return */ public static String getBodyString(ServletRequest request) {undefined StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try {undefined inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) {undefined sb.append(line); } } catch (IOException e) {undefined e.printStackTrace(); } finally {undefined if (inputStream != null) {undefined try {undefined inputStream.close(); } catch (IOException e) {undefined e.printStackTrace(); } } if (reader != null) {undefined try {undefined reader.close(); } catch (IOException e) {undefined e.printStackTrace(); } } } return sb.toString(); } }
Filter中写法如下:
HttpServletRequest httpServletRequest = (HttpServletRequest) request; MaprequestMap = this.getTypesafeRequestMap(httpServletRequest); requestWrapper = new BodyWrapper(httpServletRequest); String body = HttpHelper.getBodyString(requestWrapper); log.info("loggingFilter---请求路径 {},请求参数 {},请求体内容 {}",httpServletRequest.getRequestURL(),requestMap,body); chain.doFilter(requestWrapper, response);
在使用注解的方式(即@WebFilter)声明过滤器时,
需要再main函数类上添加@ServletComponentScan(basePackages = "此处写明类地址,格式为包名+类名(如com.*)
Http请求解决body流一旦被读取了就无法二次读取情况
相信大家在工作当中,经常会遇到需要处理http请求及响应body的场景,这里最大的问题应该就是body中流以但被读取就无法二次读取了。
解决request请求流只能读取一次的问题
我们编写一个过滤器,这样就可以重写body了
package com.interceptor; import java.io.IOException; import java.util.Arrays; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyFilter implements Filter { private static String privateKey ; public String getPrivateKey() { return privateKey; } public void setPrivateKey(String key) { privateKey = key; } /** * 排除过滤路径 */ Listignore = Arrays.asList("/xxxx"); /** * 前缀排除 如 /static/goods 排除 */ List ignorePrefix = Arrays.asList( "/css/", "/pop/", "/js/", "/static/", "/images/", "/favicon.ico"); /** * 排除过滤路径 */ List ignoreSuffix = Arrays.asList("/test"); @Override public void init(FilterConfig filterConfig) throws ServletException { //过滤器初始化 } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 防止流读取一次后就没有了, 所以需要将流继续写出去 HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String uri = request.getServletPath(); response.setContentType("application/json;charset=UTF-8"); ServletRequest requestWrapper = null; if(canIgnore(uri)) { requestWrapper = new MyFilterBodyReaderHttpServletRequestWrapper(request); filterChain.doFilter(requestWrapper, response); return; } try { requestWrapper = new MyFilterBodyReaderHttpServletRequestWrapper(request, response, privateKey); } catch (Exception e) { e.printStackTrace(); return; } filterChain.doFilter(requestWrapper, servletResponse); } @Override public void destroy() { //过滤器销毁 } private boolean canIgnore(String uri) { logger.info("过滤器 request uri : {} ",uri); boolean isExcludedPage = false; for (String page : ignore) { if (uri.equals(page)) { logger.info("请求路径不需要拦截,忽略该uri : {} ",uri); isExcludedPage = true; break; } } for (String prefix : ignorePrefix) { if (uri.startsWith(prefix)) { logger.info("请求路径前缀[{}],不拦截该uri : {} ", prefix, uri); isExcludedPage = true; break; } } for (String prefix : ignoreSuffix) { if (uri.endsWith(prefix)) { logger.info("请求路径后缀[{}],不拦截该uri : {} ", prefix, uri); isExcludedPage = true; break; } } return isExcludedPage; } }
package com.interceptor; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Map; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import com.alibaba.fastjson.JSON; /** * @Description: TODO 过滤器处理requestbody获取一次就失效 */ public class MyFilterBodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; /** * TODO 重写requestbody * @param request * @throws IOException */ public MyFilterBodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); String sessionStream = getBodyString(request); body = sessionStream.getBytes(Charset.forName("UTF-8")); } /** * TODO 拦截解密,校验,重写requestbody */ @SuppressWarnings({ "rawtypes", "unchecked" }) public MyFilterBodyReaderHttpServletRequestWrapper(HttpServletRequest request,HttpServletResponse response, String clientKey, Boolean ignoreCheckSign) throws Exception { super(request); String sessionStream = getBodyString(request); Map paramMap = (Map) JSON.parse(sessionStream); /** *自己项目中与合作方的加解密内容 *如:String data= (String) paramMap.get("data"); * String json=xxxxxutil.decrypt(参数); */ body = json.getBytes(Charset.forName("UTF-8")); } /** * TODO 获取请求Body * @param request * @return * @throws */ public String getBodyString(final ServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = cloneInputStream(request.getInputStream()); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("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(); } /** * TODO 无参获取请求Body * @return * @throws */ public String getBodyString() { if (body == null) { return null; } String str = new String(body); return str; } /** * TODO 复制输入流 * @param inputStream * @return * @throws */ public InputStream cloneInputStream(ServletInputStream inputStream) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, len); } byteArrayOutputStream.flush(); } catch (IOException e) { e.printStackTrace(); } InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); return byteArrayInputStream; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return bais.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。