You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 3.X升级后CachedBodyHttpServletRequest处理Multipart请求失效的问题咨询

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 08:40:27