Spring Boot 3.X升级后CachedBodyHttpServletRequest处理Multipart请求失效的问题咨询
我之前在Spring Boot 2.X项目中实现了一套API请求响应日志的过滤器,同时为了解决HttpServletRequest的InputStream只能读取一次的问题,自定义了缓存请求体的包装类,这样既能在过滤器里记录完整的请求响应日志,也能在自定义的参数解析器(HandlerMethodArgumentResolver)里正常复用请求体数据。整套配置之前运行得很顺畅,但升级到Spring Boot 3.X后,Multipart请求的处理出现了问题,想请教下怎么解决。
1. 请求响应日志过滤器代码
@Order(value = Ordered.HIGHEST_PRECEDENCE) @Component @Slf4j public class RequestResponseLoggingFilter extends OncePerRequestFilter { @Autowired RequestLoggingService loggingService; @Autowired @Qualifier("handlerExceptionResolver") private HandlerExceptionResolver resolver; @Autowired private RequestMappingHandlerMapping reqMap; @Override public void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException { CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(httpServletRequest); CachedBodyHttpServletResponse cachedBodyHttpServletResponse = new CachedBodyHttpServletResponse(httpServletResponse); try { filterChain.doFilter(cachedBodyHttpServletRequest, cachedBodyHttpServletResponse); } catch (Exception ex) { log.error("Exception during rest call", ex); resolver.resolveException(cachedBodyHttpServletRequest, cachedBodyHttpServletResponse, null, ex); } finally { loggingService.logApiCall(cachedBodyHttpServletRequest, cachedBodyHttpServletResponse); cachedBodyHttpServletResponse.copyBodyToResponse(); } } }
2. 缓存请求体的HttpServletRequest包装类
public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { private byte[] cachedBody; public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { super(request); InputStream requestInputStream = request.getInputStream(); this.cachedBody = StreamUtils.copyToByteArray(requestInputStream); } @Override public ServletInputStream getInputStream() { return new CachedBodyServletInputStream(this.cachedBody); } @Override public BufferedReader getReader() { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody); return new BufferedReader(new InputStreamReader(byteArrayInputStream)); } }
3. 自定义的ServletInputStream实现
@Slf4j public class CachedBodyServletInputStream extends ServletInputStream { private InputStream cachedBodyInputStream; public CachedBodyServletInputStream(byte[] cachedBody) { this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody); } @Override public boolean isFinished() { try { return cachedBodyInputStream.available() == 0; } catch (IOException e) { log.error("Error finishing InputStream ", e); } return false; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { throw new UnsupportedOperationException(); } @Override public int read() throws IOException { return cachedBodyInputStream.read(); } }
4. 控制器方法与自定义参数解析器
控制器的Multipart请求接口:
@PostMapping(path = "/myEndpoint", consumes = {"multipart/form-data","application/octet-stream","application/gzip","application/x-gzip"}, produces = {"application/json"}) public @ResponseBody ResponseEntity<?> upload( @RequestParam Map<String, String> allRequestParams, @PathVariable Map<String, String> allPathVars, @RequestHeader Map<String, String> headers, UploadObject uploadObject) { // 业务逻辑 }
自定义的参数解析器UploadObjectResolver:
@Component public class UploadObjectResolver implements HandlerMethodArgumentResolver { public UploadObjectResolver() { } @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.getParameterType().equals(UploadObject.class); } @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { MultipartRequest multipartrequest = nativeWebRequest.getNativeRequest(MultipartRequest.class); // 处理Multipart请求的逻辑 ..... } }
问题描述
在Spring Boot 2.X中,上面的配置完全正常:过滤器能记录完整的Multipart请求日志,参数解析器也能通过nativeWebRequest.getNativeRequest(MultipartRequest.class)获取到DefaultMultipartHttpServletRequest对象,进而处理Multipart数据。但升级到Spring Boot 3.X后,我发现这里获取到的对象类型变成了StandardMultipartHttpServletRequest,而且现在无法正常识别Multipart请求了,参数解析器里的Multipart处理逻辑完全失效。
想请教下:在Spring Boot 3.X中,有没有办法既缓存Multipart请求的内容(保证过滤器和参数解析器都能使用),又能让自定义参数解析器正常获取到可用的MultipartRequest对象?
内容来源于stack exchange

