文章目录
1.需求
现在有一个实现了HandlerInterceptor的拦截器,现在要实现以下功能,拦截器只拦截指定两个接口进行处理,然后每个接口都有一个请求体参数,使用@RequestBody
传入,现在需要获取两个请求的请求体数据,请求体采用json传入,格式如下{"queryCode":"AAA","queryParams":{}}
,需要通过请求体参数的queryCode数据来执行处理逻辑。
2.遇到的问题
在Spring框架中,HandlerInterceptor
是一个用于拦截和处理HTTP请求的接口。当你在HandlerInterceptor
中读取了请求体(比如从HttpServletRequest
对象中获取输入流或读取请求体的内容),通常会影响后续其他过滤器或处理器读取请求体。
在 Servlet 请求中,HttpServletRequest 的 getReader() 方法或 getInputStream() 方法只能调用一次。这是因为HTTP请求请求体的输入流只能被读取一次,当一个过滤器或拦截器读取了输入流后,它的内容就被消耗掉了,再次读取时就会得到空数据。因此,如果在拦截器中读取了请求体,后续的处理流程(如控制器、其他过滤器或拦截器)将无法再次读取请求体,这可能导致请求体数据不可用的问题。
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing
3.解决
如果你需要在拦截器中读取请求体,但又希望后续处理能够再次访问请求体数据,可以采用以下方法:
- 缓存请求体数据:在拦截器中将请求体数据读取并缓存,然后再使用
HttpServletRequestWrapper
来包裹原始的HttpServletRequest
,并提供重新读取缓存数据的能力。
4.演示
4.1 代码实现
自定义拦截器
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
@Component
public class CustomInterceptor implements HandlerInterceptor {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
// 包装请求以缓存请求体
CachedBodyHttpServletRequest cachedRequest = new CachedBodyHttpServletRequest(request);
// 获取请求体并解析为JSON对象
String requestBody = cachedRequest.getBody();
MyRequestBody myRequestBody = objectMapper.readValue(requestBody, MyRequestBody.class);
// 缓存请求体数据到请求属性,供postHandle使用
request.setAttribute("cachedBody", requestBody);
// 判断queryCode
if ("A".equals(myRequestBody.getQueryCode())) {
// 在这里执行你需要的逻辑
}
return true; // 继续处理请求
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Obj