经过在拦截器中获取request中的json数据,咱们能够实现对参数进行校验和改写。问题是参数只能在拦截器里获取一次,日后在controller层就没法获取数据,提示body为空。java
在网上查找资料后发现,request的输入流只能读取一次,那么这是为何呢?json
那是由于流对应的是数据,数据放在内存中,有的是部分放在内存中。 read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 因此这就是为何读了一次第二次是空了。 怎么让它不为空呢? 只要inputstream 中的pos 变成0就能够重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是能够重置pos为起始位置,可是不是全部的IO读取流均可以调用该方法! ServletInputStream是不能调用reset方法,这就致使了只能调用一次getInputStream()
HttpServletRequestWrapper是 httpServletRequest 的包装类app
新建一个类继承HttpServletRequestWrapper实现对 httpServletRequest 的装饰,用来获取 body 数据ide
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; private String bodyStr; public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); String bodyString = getBodyString(request); body = bodyString.getBytes(Charset.forName("UTF-8")); bodyStr=bodyString; } public String getBodyStr() { return bodyStr; } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } public String getBodyString(HttpServletRequest request) throws IOException { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader( new InputStreamReader(inputStream, Charset.forName("UTF-8"))); char[] bodyCharBuffer = new char[1024]; int len = 0; while ((len = reader.read(bodyCharBuffer)) != -1) { sb.append(new String(bodyCharBuffer, 0, len)); } } 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(); } }
再新建一个 filter 实现对传入的 httpServletRequest 的转换post
@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"}) public class HttpServletRequestWrapperFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = null; if (request instanceof HttpServletRequest) { HttpServletRequest httpRequest = (HttpServletRequest) request; //遇到post方法才对request进行包装 String methodType = httpRequest.getMethod(); if ("POST".equals(methodType)) { requestWrapper = new BodyReaderHttpServletRequestWrapper( (HttpServletRequest) request); } } if (null == requestWrapper) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } @Override public void destroy() { } }
最后在拦截器就能够获取request中body数据测试
if(request instanceof BodyReaderHttpServletRequestWrapper ){ System.out.println(((BodyReaderHttpServletRequestWrapper) request).getBodyStr()); }
经测试发现并不影响controller层获取body数据ui
为何须要在 filter 里进行对 httpServletRequest 的包装转换,直接在拦截器里进行包装不行嘛?url
过滤器(Filter)和拦截器(Interceptor)之间的最大区别就是,过滤器能够包装Request和Response,而拦截器并不能 用代码描述拦截器和过滤器的流程大概就是这样的: 拦截器:void run () { Request request = new Request(); preHandle(request); service(request); } preHandler(Request request) { request = new RequestWrapper(request); //在这里修改Request的引用,不会影响到service方法的request } 过滤器void run () { Request request = new Request(); doFilter(request); } doFilter(Request request) { request = new RequestWrapper(request); //在这里修改Request的引用,会影响到service方法的request service(request); }