According to this check, the response type for controller method should be Mono<MultiValueMap>
, but when I use this:
@GetMapping("multi-part-map", produces = [MULTIPART_FORM_DATA_VALUE])
fun multipartMap(): Mono<MultiValueMap<String, HttpEntity<*>>> {
val builder = MultipartBodyBuilder()
builder.part("fieldPart", "fieldValue")
builder.part("filePart1", FileSystemResource("...logo.png"))
builder.part("jsonPart", Person("Jason"))
builder.part(
"myPart",
DefaultFormFieldPart(HttpHeaders(), "myPartName", "myPartData")
) // Part from a server request
val parts: MultiValueMap<String, HttpEntity<*>> = builder.build()
return Mono.just(parts)
}
I got this error:
java.lang.ClassCastException: class org.springframework.util.LinkedMultiValueMap cannot be cast to class org.springframework.http.codec.multipart.Part (org.springframework.util.LinkedMultiValueMap and org.springframework.http.codec.multipart.Part are in unnamed module of loader 'app')
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:152) ~[reactor-core-3.5.0.jar:3.5.0]
Because here expects Flux<Part>
response type, so I have to implement something like this:
class DefaultFormFieldPart(
private val headers: HttpHeaders,
private val name: String,
private val value: String
) : Part,
MultiValueMapAdapter<String, String>(emptyMap()) {
override fun content(): Flux<DataBuffer> {
return Flux.defer {
val bytes = this.value.toByteArray(UTF_8)
Flux.just(
DefaultDataBufferFactory.sharedInstance.wrap(
bytes
)
)
}
}
override fun name(): String {
return name
}
override fun headers(): HttpHeaders {
return headers
}
}
@GetMapping("multi-part", produces = [MULTIPART_FORM_DATA_VALUE])
fun multipart(): Flux<DefaultFormFieldPart> {
return Flux.fromIterable(listOf("part1", "part2"))
.map { it ->
DefaultFormFieldPart(HttpHeaders().also {
it.contentType = MediaType.TEXT_PLAIN
}, it, "$it data")
}
}
It makes no sense to check against MultiValueMap
if the encoder only treats it as Flux<Part>
Comment From: CoderYellow
Are there built-in supports for reactive multipart response for server side, like MultipartBodyBuilder
so that we don't have to implement it ourselves? Because all the Part
implementations in spring is not public.
I can only find the client side implementation in the doc
Comment From: rstoyanchev
The MultipartHttpMessageWriter
subclass of MultipartWriterSupport
is meant to support this, but it looks like we're only configuring it on the client side and not on the server side. @poutsma what do you think about the differences between the client and server multipart codecs (MultipartHttpMessageWriter
and PartEventHttpMessageWriter
vs PartHttpMessageWriter
) and about aligning them?
@CoderYellow as for building a multipart response, MultipartBodyBuilder
prepares MultiValueMap
. This issue aside, is there anything preventing you fro using it in a controller method too?
Comment From: CoderYellow
I think we can support Flux<Part>
first, and it's better to provide some api to create the Part
like this
You mean the MultipartBodyBuilder
is suitable for server side too? Then is better to support Mono<MultiValueMap<String, HttpEntity<*>>>
type too.
Comment From: CoderYellow
there are already api to create the Part
, why not expose it
Comment From: poutsma
@poutsma what do you think about the differences between the client and server multipart codecs (
MultipartHttpMessageWriter
andPartEventHttpMessageWriter
vsPartHttpMessageWriter
) and about aligning them?
Sounds good to me.
Comment From: poutsma
@CoderYellow Returning a Mono<MultiValueMap<String, HttpEntity<*>>>
is currently only supported on the client side, but not on the server side. I will make a commit that changes this, so that it also enabled on the server side.
there are already api to create the
Part
, why not expose it
Sending multipart/form-data as a server response is not something we have come across before, so it is quite uncommon. As a result, there was no need for exposing this API (yet).
Comment From: poutsma
After team discussion, it was decided that we should also align the differences between server and client when it comes to multipart HttpMessageReader and decoders (i.e. DefaultPartHttpMessageReader
, MultipartHttpMessageReader
, and PartEventHttpMessageReader
), so that these can be used on both client and server.