Spring WebFlux如何接收同名multipart/form-data参数?
如何在Spring WebFlux控制器中接收multipart/form-data格式的同名ids参数?
问题场景
前端以multipart/form-data格式提交多个name为ids的参数,请求体示例如下:
Content-Disposition: form-data; name="ids" 30 -----------------------------313589022531437741264012237550 Content-Disposition: form-data; name="ids" 225 -----------------------------313589022531437741264012237550 Content-Disposition: form-data; name="ids" 226 -----------------------------313589022531437741264012237550 Content-Disposition: form-data; name="ids"
尝试用以下Spring WebFlux控制器代码接收:
@PostMapping(value = "/create", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Mono<String> create(@RequestPart("ids") List<Long> ids) { // ... }
使用List<Long>或Long[]类型均无法正常接收,测试请求代码如下:
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); bodyBuilder.part("ids", 22); bodyBuilder.part("ids", 33); webTestClient.post() .uri("/create") .contentType(MediaType.MULTIPART_FORM_DATA) .body(BodyInserters.fromMultipartData(bodyBuilder.build())) .exchange() .expectStatus().isOk();
出现错误:
org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Failed to read HTTP message"; nested exception is org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize value of type `java.util.ArrayList<java.lang.Integer>` from Integer value (token `JsonToken.VALUE_NUMBER_INT`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.ArrayList<java.lang.Integer>` from Integer value (token `JsonToken.VALUE_NUMBER_INT`) at [Source: (org.springframework.core.io.buffer.DefaultDataBuffer$DefaultDataBufferInputStream); line: 1, column: 1] at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.handleReadError(AbstractMessageReaderArgumentResolver.java:224) at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.lambda$readBody$3(AbstractMessageReaderArgumentResolver.java:190) at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
错误原因
@RequestPart默认会将每个表单part视为独立的JSON数据进行解析,但这里每个ids参数都是单个数值,并非JSON数组。当尝试用List<Long>接收时,Jackson会试图把单个数值反序列化为List,从而触发类型不匹配的错误。
解决方案
方案1:改用@RequestParam接收(推荐)
@RequestParam可以自动收集同名的表单参数为集合,不管是application/x-www-form-urlencoded还是multipart/form-data格式都适用:
@PostMapping(value = "/create", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Mono<String> create(@RequestParam("ids") List<Long> ids) { // 处理ids列表 return Mono.just("处理完成,共接收" + ids.size() + "个ID"); }
方案2:用Flux<Part>手动处理多个part
如果必须使用@RequestPart,可以通过Flux<Part>接收所有name为ids的part,再逐个解析内容并收集为List:
import org.springframework.http.codec.multipart.Part; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @PostMapping(value = "/create", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public Mono<String> create(@RequestPart("ids") Flux<Part> idsParts) { return idsParts // 逐个读取part内容并转为Long类型 .flatMap(part -> part.content() .map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); return Long.parseLong(new String(bytes)); })) // 收集为List .collectList() .doOnNext(ids -> { // 处理ids列表逻辑 System.out.println("接收的ID列表:" + ids); }) .map(ids -> "处理完成,共接收" + ids.size() + "个ID"); }
说明
方案1是最简便直接的实现方式,无需额外的手动解析逻辑;方案2适用于必须使用@RequestPart的特殊场景,比如需要同时处理文件上传和表单参数的情况。
内容的提问来源于stack exchange,提问作者Vogap
相关产品推荐
相关产品推荐

