Spring Boot 2.1.4拦截器中如何获取RequestEntity实例
在Spring Boot Interceptor的preHandle中获取RequestEntity的解决方案
首先得明确为什么你之前的尝试都失败了:
- @Autowired注入RequestEntity行不通:RequestEntity是Spring MVC针对每个请求临时创建的请求域对象,并不是Spring容器管理的单例Bean,所以依赖注入根本找不到对应的实例,自然会空指针。
- Controller方法添加RequestEntity参数也无法在preHandle中获取:Spring MVC会在调用Controller方法之前,通过
HandlerMethodArgumentResolver解析参数并创建RequestEntity,但这个步骤是在Interceptor的preHandle执行之后才发生的,所以preHandle阶段这个对象还没生成。
接下来给你两种可行的解决方案:
方案一:手动从HttpServletRequest构建RequestEntity
RequestEntity本质就是对请求方法、URL、请求头、请求体的封装,我们可以直接从HttpServletRequest中提取这些信息手动构建,而且为了避免请求体只能读取一次的问题,需要用ContentCachingRequestWrapper包装请求:
@Override public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception { // 包装请求,支持重复读取请求体 ContentCachingRequestWrapper request = new ContentCachingRequestWrapper(requestServlet); // 1. 获取请求方法 HttpMethod method = HttpMethod.valueOf(request.getMethod()); // 2. 获取请求URI URI uri = URI.create(request.getRequestURL().toString()); // 3. 构建请求头 HttpHeaders headers = new HttpHeaders(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); headers.add(headerName, request.getHeader(headerName)); } // 4. 获取请求体(这里以String为例,根据你的需求调整类型) byte[] bodyBytes = request.getContentAsByteArray(); String body = new String(bodyBytes, request.getCharacterEncoding()); // 5. 构建RequestEntity实例 RequestEntity<String> requestEntity = new RequestEntity<>(body, headers, method, uri); // 现在就可以正常使用requestEntity了 System.out.println(requestEntity.getHeaders()); return true; }
这个方案的优势是不依赖Controller的方法参数,不管你的Controller有没有声明RequestEntity参数,都能在preHandle中获取到。
方案二:自定义参数解析器缓存RequestEntity(仅适用于Controller声明了RequestEntity参数的场景)
如果你的Controller方法必然会声明RequestEntity参数,可以自定义一个HandlerMethodArgumentResolver,在解析出RequestEntity后把它存入请求属性中,后续在Interceptor的postHandle中获取(注意:preHandle阶段还是拿不到,因为参数解析在preHandle之后):
- 自定义参数解析器:
public class CachingRequestEntityArgumentResolver extends RequestEntityMethodArgumentResolver { public CachingRequestEntityArgumentResolver(List<HttpMessageConverter<?>> converters) { super(converters); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { RequestEntity<?> requestEntity = (RequestEntity<?>) super.resolveArgument(parameter, mavContainer, webRequest, binderFactory); // 存入请求属性 webRequest.setAttribute("REQUEST_ENTITY", requestEntity, NativeWebRequest.SCOPE_REQUEST); return requestEntity; } }
- 注册这个解析器:
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private List<HttpMessageConverter<?>> messageConverters; @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new CachingRequestEntityArgumentResolver(messageConverters)); } }
- 在Interceptor的postHandle中获取:
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { RequestEntity<?> requestEntity = (RequestEntity<?>) request.getAttribute("REQUEST_ENTITY"); if (requestEntity != null) { System.out.println(requestEntity.getHeaders()); } }
不过这个方案只能在postHandle中获取,如果你必须在preHandle中拿到,方案一是更合适的选择。
内容的提问来源于stack exchange,提问作者Xenonite
相关产品推荐
相关产品推荐

