需求:
request的content-type为applciation/json,进入controller之前需要把body中的参数取出来做一次处理,然后和hearder中的另一个参数做对比。
思路:
加一个过滤器,在过滤器中取出参数做处理,然后比较
注意:
body里的数据用流来读取,只能读取一次
HttpServletRequest的输入流只能读取一次的原因
我们先来看看为什么HttpServletRequest的输入流只能读一次,当我们调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承于InputStream。
InputStream的read()方法内部有一个postion,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()会返回-1,表示已经读取完了。如果想要重新读取则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。
InputStream默认不实现reset(),并且markSupported()默认也是返回false,这一点查看其源码便知:
我们再来看看ServletInputStream,可以看到该类没有重写mark(),reset()以及markSupported()方法:
综上,InputStream默认不实现reset的相关方法,而ServletInputStream也没有重写reset的相关方法,这样就无法重复读取流,这就是我们从request对象中获取的输入流就只能读取一次的原因。
重复读取body中数据的方法
这个自定义的requestWrapper继承了HttpServletRequestWrapper ,HttpServletRequestWrapper 是一个ServletRequest的包装类同时也是ServletRequest的实现类。
在这个自定义的requestWrapper里,用一个String做缓存,在构造方法里先把request的body中的数据缓存起来,然后重写了getInputStream,返回这个缓存的body,而不是从流中读取。
这样,在需要多次读取body的地方,只需要在过滤器中把原来的request换成这个自定义的request,然后把这个自定义的带缓存功能的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 |
public class BodyReaderRequestWrapper extends HttpServletRequestWrapper { private final String body;
/** * * @param request */ public BodyReaderRequestWrapper(HttpServletRequest request) throws IOException{ super (request); StringBuilder sb = new StringBuilder(); InputStream ins = request.getInputStream(); BufferedReader isr = null ; try { if (ins != null ){ isr = new BufferedReader( new InputStreamReader(ins)); char [] charBuffer = new char [ 128 ]; int readCount = 0 ; while ((readCount = isr.read(charBuffer)) != - 1 ){ sb.append(charBuffer, 0 ,readCount); } } else { sb.append( "" ); } } catch (IOException e){ throw e; } finally { if (isr != null ) { isr.close(); } }
sb.toString(); body = sb.toString(); }
@Override public BufferedReader getReader() throws IOException { return new BufferedReader( new InputStreamReader( this .getInputStream())); }
@Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes()); ServletInputStream servletIns = 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 byteArrayIns.read(); } }; return servletIns; } } |
springboot的过滤器
2个注解:
@WebFilter (过滤器上) @ServletComponentScan (加在启动类上,支持servlet components扫描(为了webfilter))
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 |
@Order ( 999 ) // 序号越低,优先级越高 // 加上WebFilter即可成为过滤器 @WebFilter (filterName= "myFilter" , urlPatterns= "/api/workorder/service/selfAppeal" ) public class ExternalFilter implements Filter {
private final static Logger logger = LoggerFactory.getLogger(ExternalFilter. class ); @Override public void init(FilterConfig filterConfig) throws ServletException { logger.info( "filter init" ); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ResponseObject object = new ResponseObject(); HttpServletRequest req = (HttpServletRequest)servletRequest; HttpServletResponse res = (HttpServletResponse)servletResponse; // 一个request的包装类,初始化时缓存了body,重写了getInputStream返回缓存的body,实现重复读取body BodyReaderRequestWrapper requestWrapper = new BodyReaderRequestWrapper(req); String requestURI = requestWrapper.getRequestURI(); System.out.println( "--------------------->过滤器:请求地址" + requestURI); String md5 = requestWrapper.getHeader( "md5" ) ;
if (md5 == null || !md5.toLowerCase().equals(MD5.md5(ReqGetBody.getBody(requestWrapper)).toLowerCase())) { object.setStatus( 501 ); object.setStatusText( "数据md5校验失败" ); render(object, res); return ; } // 这里传递下去的就是自定义的request了,所以后续的Controller才能重复读取到body里的参数 filterChain.doFilter(requestWrapper, res); }
@Override public void destroy() { }
/** * @Title: render * @Description: 发送Response * @param object * @param response void * @author MasterYi * @date 2020年1月15日上午10:48:45 */ private void render(ResponseObject object, HttpServletResponse response) { response.setContentType( "application/json;charset=UTF-8" ); try { response.setStatus( 200 ); response.getWriter().write(JSONObject.toJSON(object).toString()); } catch (IOException e) { logger.error( "ExternalFilter写入response异常" ); } } } |
上面的getBody的代码
从body中取值的具体操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class ReqGetBody { static public String getBody(HttpServletRequest request) { try { ServletInputStream in = request.getInputStream(); String body; body = StreamUtils.copyToString(in, Charset.forName( "UTF-8" ));
return body; } catch (IOException e) { e.printStackTrace(); return "" ; } } } |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
原文链接:https://blog.csdn.net/qq_34548229/article/details/104014374
查看更多关于Springboot如何设置过滤器及重复读取request里的body的详细内容...