响应式Spring中如何同时接收Flux<Part>与String请求参数
问题原因
你的代码拿不到uploadType值的核心原因有两个:
- 方法上的
uploadType参数没有加任何绑定注解,Spring WebFlux默认会从URL查询参数(即?uploadType=xxx这种拼接在路径上的参数)里找对应值,但你是把这个参数放在multipart/form-data的请求表单体里的,自然匹配不到,返回null。 - 你对
@RequestBody绑定的Flux<Part>流直接做了instanceof FilePart过滤,非文件类型的普通表单字段(对应FormFieldPart实现类)全部被你过滤丢弃了,就算手动从流里找也拿不到这个参数。
正确传参&取值方式
Spring WebFlux对multipart请求里的普通表单字段(String、Enum等基础类型)支持两种主流取值方式,按需选择即可:
方式1:用@RequestPart注解直接绑定(最简便)
给普通参数加上@RequestPart注解,指定和表单字段一致的名称,Spring会自动从multipart请求体里提取对应值,Enum类型直接替换参数类型即可,框架会自动做类型转换。
修正后的代码示例:
@RestController @RequestMapping(path = "/upload") public class ReactiveUploadResource { Logger LOGGER = LoggerFactory.getLogger(ReactiveUploadResource.class); @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public Flux<String> uploadHandler( @RequestBody Flux<Part> parts, // 加@RequestPart注解绑定表单里的uploadType字段,Enum类型直接替换为对应枚举类即可 @RequestPart("uploadType") String uploadType ) { return parts .filter(part -> part instanceof FilePart) .cast(FilePart.class) .flatMap(item -> saveFile(item, uploadType)); } }
小提示:如果参数是可选非必填,给
@RequestPart加required = false属性,或者把参数类型改为Optional<String>/Mono<String>即可。如果你是把参数拼在URL路径后面传的,把注解换成@RequestParam就行。
方式2:手动从Part流中提取参数
如果你不想加额外注解,也可以从Flux<Part>流里自己筛出普通表单字段取值,注意Flux是一次性流,多次消费必须先做缓存,否则流读完就空了。
代码示例:
@RestController @RequestMapping(path = "/upload") public class ReactiveUploadResource { Logger LOGGER = LoggerFactory.getLogger(ReactiveUploadResource.class); @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public Flux<String> uploadHandler(@RequestBody Flux<Part> parts) { // 先缓存Part流,避免多次消费导致流耗尽 Flux<Part> cachedParts = parts.cache(); // 提取表单中uploadType字段的值 Mono<String> uploadTypeMono = cachedParts .filter(part -> "uploadType".equals(part.name()) && part instanceof FormFieldPart) .cast(FormFieldPart.class) .map(FormFieldPart::value) .single(); // 非必填参数可以换成singleOptional() // 提取文件结合参数处理 return cachedParts .filter(part -> part instanceof FilePart) .cast(FilePart.class) .zipWith(uploadTypeMono.repeat()) .flatMap(tuple -> saveFile(tuple.getT1(), tuple.getT2())); } }
内容的提问来源于stack exchange,提问作者Masi Boo
相关产品推荐
相关产品推荐

