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 and PartEventHttpMessageWriter vs PartHttpMessageWriter) 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.