如何解决请求参数为JSON时,采用IO流读取,只能请求一次的问题?
- 一、错误演示
- 1. 创建项目,添加所需依赖
- 2. 配置redis环境
- 3. 写一个简单的测试请求
- 4. 写一个拦截器,拦截请求
- 5. WebConfig 注册拦截器
- 6. 测试请求
- 二、问题解决
- 1. 创建 RepeatableReadRequestWrapper 类,包装请求
- 2. 创建 RepeatableRequestFilter 过滤器,过滤器在拦截器之前执行,如果请求参数为JSON,则包装请求
- 3. WebConfig 注册过滤器
- 4. 再次测试
一、错误演示
- 当客户端发送一个参数为JSON的请求时,我现在想使用拦截器先将它拦截并缓存起来,但是发现拦截器拦截之后,请求就无法再一次获取到数据,如下:
1. 创建项目,添加所需依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 配置redis环境
server.port=8888
spring.redis.port=6379
spring.redis.host=localhost
3. 写一个简单的测试请求
@RestController
public class HelloController {
@PostMapping("/hello")
public String hello(@RequestBody String json) {
return json;
}
}
4. 写一个拦截器,拦截请求
@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String s = request.getReader().readLine();
System.out.println("request.getReader().readLine()= " + s);
return true;
}
}
5. WebConfig 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
RepeatSubmitInterceptor repeatSubmitInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
}
}
6. 测试请求
二、问题解决
采用一个装饰者模式,将请求重新包装一下
1. 创建 RepeatableReadRequestWrapper 类,包装请求
public class RepeatableReadRequestWrapper extends HttpServletRequestWrapper {
private final byte[] bytes;
public RepeatableReadRequestWrapper(HttpServletRequest request, HttpServletResponse response) throws IOException {
super(request);
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
bytes = request.getReader().readLine().getBytes();
}
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
return 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 bi.read();
}
@Override
public int available() throws IOException {
return bytes.length;
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
2. 创建 RepeatableRequestFilter 过滤器,过滤器在拦截器之前执行,如果请求参数为JSON,则包装请求
public class RepeatableRequestFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (StringUtils.startsWithIgnoreCase(request.getContentType(),"application/json")){
RepeatableReadRequestWrapper requestWrapper = new RepeatableReadRequestWrapper(request, response);
filterChain.doFilter(requestWrapper,response);
return;
}
filterChain.doFilter(request,response);
}
}
3. WebConfig 注册过滤器
@Bean
FilterRegistrationBean<RepeatableRequestFilter> repeatableRequestFilterFilterRegistrationBean(){
FilterRegistrationBean<RepeatableRequestFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new RepeatableRequestFilter());
bean.addUrlPatterns("/*");
return bean;
}
4. 再次测试
这样,就解决了请求参数为JSON时,采用IO流读取,只能请求一次的问题。
点击跳转源码仓库地址