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

Spring WebFlux RestController上传报错:Could not find first boundary

错误原因分析

  1. @RequestBody注解误用:WebFlux处理multipart/form-data请求时,不能用@RequestBody注解。@RequestBody会触发框架将整个请求体作为单一数据流解析,而multipart请求需要按边界分割成多个部分解析,这就导致解析器无法找到multipart的边界标识,抛出"Could not find first boundary"错误。RouterFunction的方式之所以正常,是因为它通过BodyExtractors.toMultipartData()明确指定了multipart解析逻辑,和@RequestBody的处理路径完全不同。

  2. subscribe方法的错误使用:在Controller方法内直接调用subscribe()会脱离Spring WebFlux的响应式上下文管理,导致请求处理流程异常。WebFlux依赖返回的Mono/Flux控制请求生命周期(比如背压、错误处理、资源释放),直接subscribe会让处理逻辑在独立线程执行,无法和请求生命周期绑定。

解决办法

1. 移除@RequestBody注解

WebFlux支持直接将Mono<MultiValueMap<String, Part>>或Flux<Part>作为Controller方法的参数,无需添加@RequestBody,框架会自动使用multipart解析器处理请求。

2. 用响应式操作符替代直接subscribe

将subscribe替换为flatMap、doOnNext等响应式操作符,保持链式调用,让处理逻辑纳入WebFlux的上下文管理。

修正后的Controller代码示例

@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Result<MultiFileUploadResponse>> upload(Mono<MultiValueMap<String, Part>> multiValueMapMono) {
    return multiValueMapMono.flatMap(stringPartMap -> {
        stringPartMap.forEach((partName, value) -> {
            if (value instanceof FilePart) {
                FilePart file = (FilePart) value;
                DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("File name: {}", file.filename());
            }
            if (value instanceof FormFieldPart) {
                FormFieldPart type = (FormFieldPart) value;
                DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("type value: {}", type.value());
            }
        });
        return Mono.just(Result.success(new MultiFileUploadResponse()));
    });
}

@PostMapping(value = "/upload2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Result<MultiFileUploadResponse>> upload2(Flux<Part> input) {
    return input.doOnNext(value -> {
        if (value instanceof FilePart) {
            FilePart file = (FilePart) value;
            DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("File name: {}", file.filename());
        }
        if (value instanceof FormFieldPart) {
            FormFieldPart type = (FormFieldPart) value;
            DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("type value: {}", type.value());
        }
    }).then(Mono.just(Result.success(new MultiFileUploadResponse())));
}

额外说明

如果需要单独接收某个文件或表单字段,还可以用@RequestPart注解指定名称,示例如下:

@PostMapping("/single-upload")
public Mono<Result<Void>> singleUpload(@RequestPart("file") Mono<FilePart> filePart,
                                       @RequestPart("type") Mono<String> type) {
    return filePart.doOnNext(file -> DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("File: {}", file.filename()))
            .then(type.doOnNext(t -> DefaultLogGenerator.BIZ_SERVICE_LOGGER.info("Type: {}", t)))
            .then(Mono.just(Result.success()));
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:45:55