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

使用DTO与MultipartFile数组调用REST接口时的异常排查

Spring WebClient 多部分POST请求失败问题解决

问题描述

尝试通过Spring WebClient发送包含两个部分的POST请求:

  1. RequestDTO对象
  2. MultipartFile类型数组

尝试过以下操作但均失败:

  • 将文件转为List传递
  • 禁用Jackson的FAIL_ON_EMPTY_BEANS配置
    结果抛出CodecException,提示无法序列化sun.nio.ch.ChannelInputStream类;后续尝试将文件转为字符串数组发送,请求发出后后端控制器抛出MissingServletRequestPartException,提示缺少必填的'files'请求部分。

客户端代码

@RequiredArgsConstructor
@Slf4j
@Service
public class RestService {

    private final WebClient webClient;

    public ResponseEntity<ResponseDTO> startProcess(MultiValueMap<String, HttpEntity<?>> data) {
        if (log.isInfoEnabled()) {
            log.info("Client call \"/endpoint\" with request: {}", data.get("request"));
        }

        final var response = this.webClient.post()
                                           .uri(RestEndpoints.ENDPOINT)
                                           .body(BodyInserters.fromMultipartData(data))
                                           .retrieve()
                                           .toEntity(ResponseDTO.class)
                                           .block();
        if (response != null && response.getStatusCode() == HttpStatus.OK) {
            return response;
        }
        throw new RuntimeException("There is an error");
    }

    public ResponseEntity<ResponseDTO> sendToClient(RequestDTO rq, MultipartFile[] files) {
        final var multipartBodyBuilder = new MultipartBodyBuilder();
        multipartBodyBuilder.part("request", rq);
        multipartBodyBuilder.part("files", files);
        final var payload = multipartBodyBuilder.build();
        return this.startProcess(payload);
    }
}

序列化异常信息

org.springframework.core.codec.CodecException: Type definition error: [simple type, class sun.nio.ch.ChannelInputStream]
...
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class sun.nio.ch.ChannelInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.Arrays$ArrayList[0]->org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile["inputStream"])
...

后端控制器代码

@PostMapping(value = "/endpoint", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<ResponseDto> start(@Valid
                     @RequestPart("request")
                     RequestDto request,
                     @RequestPart("files")
                     MultipartFile[] files)

转字符串数组后的异常信息

Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'files' is not present]

错误原因

  1. 序列化失败:直接传递MultipartFile数组时,WebClient会尝试用Jackson序列化整个MultipartFile对象,但其中的inputStream属性(sun.nio.ch.ChannelInputStream类型)无对应序列化器,禁用FAIL_ON_EMPTY_BEANS无法解决该问题,因为该类并非空Bean。
  2. 请求部分不匹配:将文件转为字符串数组后,请求中的'files'部分不再是文件类型,后端期望接收MultipartFile类型的请求部分,因此无法识别。

解决方案

修改sendToClient方法,逐个处理MultipartFile,将其包装为带正确请求头的HttpEntity,避免直接序列化MultipartFile对象:

public ResponseEntity<ResponseDTO> sendToClient(RequestDTO rq, MultipartFile[] files) {
    final var multipartBodyBuilder = new MultipartBodyBuilder();
    // 为RequestDTO指定JSON媒体类型,确保后端正确解析
    multipartBodyBuilder.part("request", rq)
                        .contentType(MediaType.APPLICATION_JSON);
    // 逐个处理文件,包装为HttpEntity并设置正确的请求头
    for (MultipartFile file : files) {
        try {
            HttpHeaders fileHeaders = HttpHeaders.writableHttpHeaders();
            fileHeaders.setContentType(MediaType.parseMediaType(file.getContentType()));
            fileHeaders.setContentDispositionFormData("files", file.getOriginalFilename());
            
            HttpEntity<byte[]> fileEntity = new HttpEntity<>(file.getBytes(), fileHeaders);
            multipartBodyBuilder.part("files", fileEntity);
        } catch (IOException e) {
            log.error("Failed to process file: {}", file.getOriginalFilename(), e);
            throw new RuntimeException("File processing failed", e);
        }
    }
    final var payload = multipartBodyBuilder.build();
    return this.startProcess(payload);
}

关键要点

  • 显式为RequestDTO部分指定MediaType.APPLICATION_JSON,匹配后端的解析要求。
  • 每个文件单独包装为HttpEntity,包含文件字节内容和正确的请求头:
    • Content-Type:使用文件自身的媒体类型
    • Content-Disposition:指定请求部分名称为"files",并附带文件名
  • 避免直接传递MultipartFile对象/数组,防止触发不必要的序列化操作。

内容的提问来源于stack exchange,提问作者D051P0

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 06:01:37