工作 HttpServletRequest中InputStream读取问题 Nuyoah 2025-02-17 2025-02-23 最近在设置统一interceptor拦截请求校验请求参数的时候出现了一个问题,就是我需要在拦截器中获取请求头中所带的参数 ,包括GET参数和请求体中的参数。
问题:当在拦截器中拦截到请求体中的参数的时候 对应的controller方法中的**@RequestBody注解失效**,
报错原因:没有传入所需参数
但是我在拦截器中确实获取到了请求头中包含的请求体参数,为什么会在Controller层中获取不到?
原因:当我在读取请求头中的请求体参数的时候,请求体是以流的形式存储,我获取到请求体的时候,请求体中的指针会指到最后一个位置,导致Controller层在通过请求头获取请求体参数的时候发现请求体中的指针后面没有东西 ,于是报错:没有传入请求体参数
解决方法:自己包装一个Request请求类实现ServletRequest类,重写getInputStream方法 ,并使用过滤器,在请求还没有到拦截器之前将请求体换成自己的封装的请求体类即可
自己封装的请求体
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 public class CachingHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte [] body; public CachingHttpServletRequestWrapper (HttpServletRequest request) throws IOException { super (request); this .body = StreamUtils.copyToByteArray(request.getInputStream()); } @Override public ServletInputStream getInputStream () { final ByteArrayInputStream bais = new ByteArrayInputStream (body); return new ServletInputStream () { @Override public boolean isFinished () { return bais.available() == 0 ; } @Override public boolean isReady () { return true ; } @Override public void setReadListener (ReadListener listener) { } @Override public int read () { return bais.read(); } }; } }
使用过滤器来过滤所有请求,并将其中的HttpServletRequest替换成我们包装的CachingHttpServletRequestWrapper
1 2 3 4 5 6 7 8 9 10 @WebFilter(filterName = "CachingHttpServletRequestFilter", urlPatterns = "/*") public class CachingHttpServletRequestFilter implements Filter { @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { CachingHttpServletRequestWrapper cachingHttpServletRequestWrapper = new CachingHttpServletRequestWrapper ((HttpServletRequest) request); chain.doFilter(cachingHttpServletRequestWrapper, response); } }
在SpringBootApplication启动类中添加@ServletComponentScan确保能够读取到@WebFilter注解信息
1 2 3 4 5 6 7 8 9 10 @SpringBootApplication @MapperScan("com.nuyoah.mapper") @EnableTransactionManagement @ServletComponentScan public class NuyoahAdminApplication { public static void main (String[] args) { SpringApplication.run(NuyoahAdminApplication.class); } }
这样在拦截器获取到的时候,读取一次请求头中的内容,请求头中的内容就可以二次读取了
1 2 3 4 5 6 public class SqlInjectionInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return HandlerInterceptor.super .preHandle(request, response, handler); } }