SpringBoot拦截器如何获取http请求参数
1.1、获取http请求参数是一种刚需
我想有的小伙伴肯定有过获取http请求的需要,比如想
- 前置获取参数,统计请求数据
- 做服务的接口签名校验
- 敏感接口监控日志
- 敏感接口防重复提交
等等各式各样的场景,这时你就需要获取HTTP请求的参数或者请求body,一般思路有两种,一种就是自定义个AOP去拦截目标方法,第二种就是使用拦截器。整体比较来说,使用拦截器更灵活些,因为每个接口的请求参数定义不同,使用AOP很难细粒度的获取到变量参数,本文主线是采用拦截器来获取HTTP请求。
1.2、定义拦截器获取请求
基于spring-boot-starter-parent2.1.9.RELEASE
看起来这个很简单,这里就直接上code,定义个拦截器
/** *@authoraxin *@summaryHTTP请求拦截器 */ @Slf4j publicclassRequestInterceptorimplementsHandlerInterceptor{ @Override publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{ //获取请求参数 StringqueryString=request.getQueryString(); log.info("请求参数:{}",queryString); //获取请求body byte[]bodyBytes=StreamUtils.copyToByteArray(request.getInputStream()); Stringbody=newString(bodyBytes,request.getCharacterEncoding()); log.info("请求体:{}",body); returntrue; } }
然后把这个拦截器配置一下中:
/** *WebMVC配置,你可以集中在这里配置拦截器、过滤器、静态资源缓存等 */ @Configuration publicclassWebMvcConfigimplementsWebMvcConfigurer{ @Override publicvoidaddInterceptors(InterceptorRegistryregistry){ registry.addInterceptor(newRequestInterceptor()).addPathPatterns("/**"); } }
定义个接口测试一下
/** *@authoraxin *@summary提交测试接口 */ @Slf4j @RestController publicclassMyHTTPController{ @GetMapping("/v1/get") publicvoidget(@RequestParam("one")Stringone, @RequestParam("two")BigDecimalnumber){ log.info("参数:{},{}",one,number); } @PostMapping("/v1/post") publicvoidcheck(@RequestBodyUseruser){ log.info("{}",JSON.toJSONString(user)); } }
GET请求获取请求参数示例:
POST请求获取请求Body示例:
我们发现拦截器在获取HTTP请求的body时出现了400(Requiredrequestbodyismissing:publicvoidcom.axin.world.controller.MyHTTPController.check(com.axin.world.domain.User));同时也发现拦截器竟然走了两遍,这又是咋回事呢?
1.3、为什么拦截器会重复调两遍呢?
其实是因为tomcat截取到异常后就转发到/error页面,就在这个转发的过程中导致了springmvc重新开始DispatcherServlet的整个流程,所以拦截器执行了两次,我们可以看下第二次调用时的url路径:
1.4、ServletInputStream(CoyoteInputStream)输入流无法重复调用
而之前出现的Requiredrequestbodyismissing错误其实是ServletInputStream被读取后无法第二次再读取了,所以我们要把读取过的内容存下来,然后需要的时候对外提供可被重复读取的ByteArrayInputStream。
对于MVC的过滤器来说,我们就需要重写ServletInputStream的getInputStream()方法。
1.5、自定义HttpServletRequestWrapper
为了重写ServletInputStream的getInputStream()方法,我们需要自定义一个HttpServletRequestWrapper:
/** *@authorAxin *@summary自定义HttpServletRequestWrapper来包装输入流 */ publicclassAxinHttpServletRequestWrapperextendsHttpServletRequestWrapper{ /** *缓存下来的HTTPbody */ privatebyte[]body; publicAxinHttpServletRequestWrapper(HttpServletRequestrequest)throwsIOException{ super(request); body=StreamUtils.copyToByteArray(request.getInputStream()); } /** *重新包装输入流 *@return *@throwsIOException */ @Override publicServletInputStreamgetInputStream()throwsIOException{ InputStreambodyStream=newByteArrayInputStream(body); returnnewServletInputStream(){ @Override publicintread()throwsIOException{ returnbodyStream.read(); } /** *下面的方法一般情况下不会被使用,如果你引入了一些需要使用ServletInputStream的外部组件,可以重点关注一下。 *@return */ @Override publicbooleanisFinished(){ returnfalse; } @Override publicbooleanisReady(){ returntrue; } @Override publicvoidsetReadListener(ReadListenerreadListener){ } }; } @Override publicBufferedReadergetReader()throwsIOException{ InputStreambodyStream=newByteArrayInputStream(body); returnnewBufferedReader(newInputStreamReader(getInputStream())); } }
然后定义一个DispatcherServlet子类来分派上面自定义的AxinHttpServletRequestWrapper:
/** *@authorAxin *@summary自定义DispatcherServlet来分派AxinHttpServletRequestWrapper */ publicclassAxinDispatcherServletextendsDispatcherServlet{ /** *包装成我们自定义的request *@paramrequest *@paramresponse *@throwsException */ @Override protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{ super.doDispatch(newAxinHttpServletRequestWrapper(request),response); } }
然后配置一下:
/** *WebMVC配置,你可以集中在这里配置拦截器、过滤器、静态资源缓存等 */ @Configuration publicclassWebMvcConfigimplementsWebMvcConfigurer{ @Override publicvoidaddInterceptors(InterceptorRegistryregistry){ registry.addInterceptor(newRequestInterceptor()).addPathPatterns("/**"); } @Bean @Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) publicDispatcherServletdispatcherServlet(){ returnnewAxinDispatcherServlet(); } }
再调用一下POST请求:
请求成功!
1.5、总结一下展望一下
如果你想对HTTP请求做些骚操作,那么前置获取HTTP请求参数是前提,为此文本给出了使用MVC拦截器获取参数的样例。
在获取HTTPBody的时候,出现了Requiredrequestbodyismissing的错误,同时拦截器还出现执行了两遍的问题,这是因为ServletInputStream被读取了两遍导致的,tomcat截取到异常后就转发到/error页面被拦截器拦截到了,拦截器也就执行了两遍。
为此我们通过自定义HttpServletRequestWrapper来包装一个可被重读读取的输入流,来达到期望的拦截效果。
在获取到HTTP的请求参数后,我们可以前置做很多操作,比如常用的服务端接口签名验证,敏感接口防重复请求等等。
个人水平有限,如果文章有逻辑错误或表述问题还请指出,欢迎一起交流。
到此这篇关于SpringBoot拦截器如何获取http请求参数的文章就介绍到这了,更多相关SpringBoot拦截器获取http请求参数内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。