SpringBoot如何获取http请求参数
1.1、获取http请求参数是⼀种刚需
我想有的⼩伙伴肯定有过获取http请求的需要,⽐如想
1. 前置获取参数,统计请求数据
2. 做服务的接⼝签名校验
3. 敏感接⼝监控⽇志
4. 敏感接⼝防重复提交
等等各式各样的场景,这时你就需要获取 HTTP 请求的参数或者请求body,⼀般思路有两种,⼀种就是⾃定义个AOP去拦截⽬标⽅法,第⼆种就是使⽤。整体⽐较来说,使⽤更灵活些,因为每个接⼝的请求参数定义不同,使⽤AOP很难细粒度的获取到变量参数,本⽂主线是采⽤来获取HTTP请求。
1.2、定义获取请求
基于 spring-boot-starter-parent 2.1.9.RELEASE
看起来这个很简单,这⾥就直接上code,定义个
/**
* @author axin
* @summary HTTP请求
*/
@Slf4j
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求参数
String queryString = QueryString();
log.info("请求参数:{}", queryString);
//获取请求body
byte[] bodyBytes = InputStream());
String body = new String(bodyBytes, CharacterEncoding());
log.info("请求体:{}", body);
return true;
}
}
然后把这个配置⼀下中:
/**
* WebMVC配置,你可以集中在这⾥配置、过滤器、静态资源缓存等
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");
}
}
定义个接⼝测试⼀下
/**
* @author axin
* @summary 提交测试接⼝
*/
@Slf4j
@RestController
public class MyHTTPController {
@GetMapping("/v1/get")
public void get(@RequestParam("one") String one,
@RequestParam("two") BigDecimal number) {
log.info("参数:{},{}", one, number);
}
@PostMapping("/v1/post")
public void check(@RequestBody User user) {
log.info("{}", JSONString(user));
}
}
GET请求获取请求参数⽰例:
POST请求获取请求Body⽰例:
我们发现在获取HTTP请求的body时出现了 400 (Required request body is missing: public void com.ller.MyHTTPController.check(com.axin.world.domain.User));同时也发现竟然⾛了两遍,这⼜是咋回事呢?
1.3、为什么会重复调两遍呢?
其实是因为 tomcat截取到异常后就转发到/error页⾯,就在这个转发的过程中导致了springmvc重新开始DispatcherServlet的整个流程,所以执⾏了两次,我们可以看下第⼆次调⽤时的url路径:
1.4、ServletInputStream(CoyoteInputStream) 输⼊流⽆法重复调⽤
⽽之前出现的 Required request body is missing 错误其实是ServletInputStream被读取后⽆法第⼆次再读取了,所以我们要把读取过的内容存下来,然后需要的时候对外提供可被重复读取的ByteArrayInputStream。
对于MVC的过滤器来说,我们就需要重写 ServletInputStream 的 getInputStream()⽅法。
1.5、⾃定义 HttpServletRequestWrapper
为了重写 ServletInputStream 的 getInputStream()⽅法,我们需要⾃定义⼀个 HttpServletRequestWrapper :
/**
* @author Axin
* @summary ⾃定义 HttpServletRequestWrapper 来包装输⼊流
*/
public class AxinHttpServletRequestWrapper extends HttpServletRequestWrapper {
/
**
* 缓存下来的HTTP body
*/
private byte[] body;
public AxinHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = InputStream());
}
/**
* 重新包装输⼊流
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
InputStream bodyStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
ad();
}
/**
* 下⾯的⽅法⼀般情况下不会被使⽤,如果你引⼊了⼀些需要使⽤ServletInputStream的外部组件,可以重点关注⼀下。 * @return
*/
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
InputStream bodyStream = new ByteArrayInputStream(body);
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
然后定义⼀个 DispatcherServlet⼦类来分派上⾯⾃定义的 AxinHttpServletRequestWrapper :
/
**
* @author Axin
* @summary ⾃定义 DispatcherServlet 来分派 AxinHttpServletRequestWrapper
*/
public class AxinDispatcherServlet extends DispatcherServlet {
/**
* 包装成我们⾃定义的request
* @param request
* @param response
springboot aop* @throws Exception
*/
@Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
super.doDispatch(new AxinHttpServletRequestWrapper(request), response);
}
}
然后配置⼀下:
/**
* WebMVC配置,你可以集中在这⾥配置、过滤器、静态资源缓存等
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");
}
@Bean
@Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new AxinDispatcherServlet();
}
}
再调⽤⼀下 POST请求:
请求成功!
1.5、总结⼀下展望⼀下
如果你想对HTTP请求做些骚操作,那么前置获取HTTP请求参数是前提,为此⽂本给出了使⽤MVC获取参数的样例。
在获取HTTP Body 的时候,出现了 Required request body is missing 的错误,同时还出现执⾏了两遍的问题,这是因为 ServletInputStream被读取了两遍导致的,tomcat截取到异常后就转发到/error 页⾯被拦截到了,也就执⾏了两遍。
为此我们通过⾃定义 HttpServletRequestWrapper 来包装⼀个可被重读读取的输⼊流,来达到期望的拦截效果。
在获取到HTTP的请求参数后,我们可以前置做很多操作,⽐如常⽤的服务端接⼝签名验证,敏感接⼝防重复请求等等。
个⼈⽔平有限,如果⽂章有逻辑错误或表述问题还请指出,欢迎⼀起交流。
到此这篇关于SpringBoot如何获取http请求参数的⽂章就介绍到这了,更多相关SpringBoot获取http请求参数内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论