spring boot拦截器获取requestBody的巨坑
SpringBoot拦截器获取RequestBody的巨坑
在SpringBoot开发中,拦截器(Interceptor)是常用的功能组件,但获取RequestBody时却隐藏着不少坑点,让不少开发者踩坑。
问题现象
当你在拦截器的`preHandle`方法中尝试通过`HttpServletRequest`获取请求体时,可能会发现:
```java
Stringbody=request.getReader().lines().collect(Collectors.joining());
```
这段代码在某些情况下会抛出`IllegalStateException`异常,提示"Streamalreadyclosed"。
根本原因
1.请求流只能读取一次:Servlet规范规定,HttpServletRequest的输入流只能被读取一次,读取后流就会关闭。如果在拦截器中读取了,后续Controller就无法再获取。
2.Spring的@RequestBody处理机制:SpringMVC在处理@RequestBody参数时,会先读取请求体进行反序列化。如果拦截器先读取了请求体,会导致后续处理失败。
解决方案
1.使用ContentCachingRequestWrapper包装请求:
```java
publicclassRequestWrapperFilterimplementsFilter{
@Override
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain){
ContentCachingRequestWrapperwrapper=newContentCachingRequestWrapper((HttpServletRequest)request);
chain.doFilter(wrapper,response);
}
}
```
2.避免在拦截器中直接读取请求体:将需要请求体数据的逻辑移到Controller或Service层处理
3.使用AOP替代拦截器:对于需要请求体数据的场景,考虑使用SpringAOP的环绕通知
最佳实践
-对于只需要请求头/URL参数的验证,优先使用拦截器
-必须处理请求体时,确保使用RequestWrapper缓存请求体
-考虑将复杂请求体处理逻辑后置到业务层
记住:拦截器的设计初衷是处理请求的元数据而非请求体内容,理解这一设计理念能避免很多不必要的麻烦。
在SpringBoot开发中,拦截器(Interceptor)是常用的功能组件,但获取RequestBody时却隐藏着不少坑点,让不少开发者踩坑。
问题现象
当你在拦截器的`preHandle`方法中尝试通过`HttpServletRequest`获取请求体时,可能会发现:
```java
Stringbody=request.getReader().lines().collect(Collectors.joining());
```
这段代码在某些情况下会抛出`IllegalStateException`异常,提示"Streamalreadyclosed"。
根本原因
1.请求流只能读取一次:Servlet规范规定,HttpServletRequest的输入流只能被读取一次,读取后流就会关闭。如果在拦截器中读取了,后续Controller就无法再获取。
2.Spring的@RequestBody处理机制:SpringMVC在处理@RequestBody参数时,会先读取请求体进行反序列化。如果拦截器先读取了请求体,会导致后续处理失败。
解决方案
1.使用ContentCachingRequestWrapper包装请求:
```java
publicclassRequestWrapperFilterimplementsFilter{
@Override
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain){
ContentCachingRequestWrapperwrapper=newContentCachingRequestWrapper((HttpServletRequest)request);
chain.doFilter(wrapper,response);
}
}
```
2.避免在拦截器中直接读取请求体:将需要请求体数据的逻辑移到Controller或Service层处理
3.使用AOP替代拦截器:对于需要请求体数据的场景,考虑使用SpringAOP的环绕通知
最佳实践
-对于只需要请求头/URL参数的验证,优先使用拦截器
-必须处理请求体时,确保使用RequestWrapper缓存请求体
-考虑将复杂请求体处理逻辑后置到业务层
记住:拦截器的设计初衷是处理请求的元数据而非请求体内容,理解这一设计理念能避免很多不必要的麻烦。