mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Fix Kotlin example in Multipart Content
Closes gh-36094 Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
This commit is contained in:
committed by
Sébastien Deleuze
parent
192da6229c
commit
9abe4b46ca
+5
-85
@@ -103,8 +103,8 @@ Kotlin::
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/")
|
||||
fun handle(@RequestPart("meta-data") Part metadata, // <1>
|
||||
@RequestPart("file-data") FilePart file): String { // <2>
|
||||
fun handle(@RequestPart("meta-data") metadata: Part, // <1>
|
||||
@RequestPart("file-data") file: FilePart): String { // <2>
|
||||
// ...
|
||||
}
|
||||
----
|
||||
@@ -169,7 +169,7 @@ Kotlin::
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/")
|
||||
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData): String {
|
||||
fun handle(@Valid @RequestPart("meta-data") metadata: Mono<MetaData>): String {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
@@ -202,7 +202,7 @@ Kotlin::
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/")
|
||||
fun handle(@RequestBody parts: MultiValueMap<String, Part>): String { // <1>
|
||||
fun handle(@RequestBody parts: Mono<MultiValueMap<String, Part>>): String { // <1>
|
||||
// ...
|
||||
}
|
||||
----
|
||||
@@ -227,87 +227,7 @@ when uploading. If the file is large enough to be split across multiple buffers,
|
||||
|
||||
For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/")
|
||||
public void handle(@RequestBody Flux<PartEvent> allPartsEvents) { <1>
|
||||
allPartsEvents.windowUntil(PartEvent::isLast) <2>
|
||||
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> { <3>
|
||||
if (signal.hasValue()) {
|
||||
PartEvent event = signal.get();
|
||||
if (event instanceof FormPartEvent formEvent) { <4>
|
||||
String value = formEvent.value();
|
||||
// handle form field
|
||||
}
|
||||
else if (event instanceof FilePartEvent fileEvent) { <5>
|
||||
String filename = fileEvent.filename();
|
||||
Flux<DataBuffer> contents = partEvents.map(PartEvent::content); <6>
|
||||
// handle file upload
|
||||
}
|
||||
else {
|
||||
return Mono.error(new RuntimeException("Unexpected event: " + event));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return partEvents; // either complete or error signal
|
||||
}
|
||||
}));
|
||||
}
|
||||
----
|
||||
<1> Using `@RequestBody`.
|
||||
<2> The final `PartEvent` for a particular part will have `isLast()` set to `true`, and can be
|
||||
followed by additional events belonging to subsequent parts.
|
||||
This makes the `isLast` property suitable as a predicate for the `Flux::windowUntil` operator, to
|
||||
split events from all parts into windows that each belong to a single part.
|
||||
<3> The `Flux::switchOnFirst` operator allows you to see whether you are handling a form field or
|
||||
file upload.
|
||||
<4> Handling the form field.
|
||||
<5> Handling the file upload.
|
||||
<6> The body contents must be completely consumed, relayed, or released to avoid memory leaks.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/")
|
||||
fun handle(@RequestBody allPartsEvents: Flux<PartEvent>) = { // <1>
|
||||
allPartsEvents.windowUntil(PartEvent::isLast) <2>
|
||||
.concatMap {
|
||||
it.switchOnFirst { signal, partEvents -> <3>
|
||||
if (signal.hasValue()) {
|
||||
val event = signal.get()
|
||||
if (event is FormPartEvent) { <4>
|
||||
val value: String = event.value();
|
||||
// handle form field
|
||||
} else if (event is FilePartEvent) { <5>
|
||||
val filename: String = event.filename();
|
||||
val contents: Flux<DataBuffer> = partEvents.map(PartEvent::content); <6>
|
||||
// handle file upload
|
||||
} else {
|
||||
return Mono.error(RuntimeException("Unexpected event: " + event));
|
||||
}
|
||||
} else {
|
||||
return partEvents; // either complete or error signal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Using `@RequestBody`.
|
||||
<2> The final `PartEvent` for a particular part will have `isLast()` set to `true`, and can be
|
||||
followed by additional events belonging to subsequent parts.
|
||||
This makes the `isLast` property suitable as a predicate for the `Flux::windowUntil` operator, to
|
||||
split events from all parts into windows that each belong to a single part.
|
||||
<3> The `Flux::switchOnFirst` operator allows you to see whether you are handling a form field or
|
||||
file upload.
|
||||
<4> Handling the form field.
|
||||
<5> Handling the file upload.
|
||||
<6> The body contents must be completely consumed, relayed, or released to avoid memory leaks.
|
||||
======
|
||||
include-code::./PartEventController[tag=snippet,indent=0]
|
||||
|
||||
Received part events can also be relayed to another service by using the `WebClient`.
|
||||
See xref:web/webflux-webclient/client-body.adoc#webflux-client-body-multipart[Multipart Data].
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webflux.controller.annmethods.partevent;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.codec.multipart.FilePartEvent;
|
||||
import org.springframework.http.codec.multipart.FormPartEvent;
|
||||
import org.springframework.http.codec.multipart.PartEvent;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
public class PartEventController {
|
||||
|
||||
// tag::snippet[]
|
||||
@PostMapping("/")
|
||||
public void handle(@RequestBody Flux<PartEvent> allPartsEvents) { // Using @RequestBody.
|
||||
|
||||
// The final PartEvent for a particular part will have isLast() set to true, and can be
|
||||
// followed by additional events belonging to subsequent parts.
|
||||
// This makes the isLast property suitable as a predicate for the Flux::windowUntil operator, to
|
||||
// split events from all parts into windows that each belong to a single part.
|
||||
allPartsEvents.windowUntil(PartEvent::isLast)
|
||||
|
||||
// The Flux::switchOnFirst operator allows you to see whether you are handling
|
||||
// a form field or file upload.
|
||||
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> {
|
||||
if (signal.hasValue()) {
|
||||
PartEvent event = signal.get();
|
||||
if (event instanceof FormPartEvent formEvent) {
|
||||
String value = formEvent.value();
|
||||
// Handling the form field.
|
||||
}
|
||||
else if (event instanceof FilePartEvent fileEvent) {
|
||||
String filename = fileEvent.filename();
|
||||
|
||||
// The body contents must be completely consumed, relayed, or released to avoid memory leaks.
|
||||
Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
|
||||
// Handling the file upload.
|
||||
}
|
||||
else {
|
||||
return Mono.error(new RuntimeException("Unexpected event: " + event));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return partEvents; // either complete or error signal
|
||||
}
|
||||
return Mono.empty();
|
||||
}));
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2026-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.docs.web.webflux.controller.annmethods.partevent
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer
|
||||
import org.springframework.http.codec.multipart.FilePartEvent
|
||||
import org.springframework.http.codec.multipart.FormPartEvent
|
||||
import org.springframework.http.codec.multipart.PartEvent
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
@RestController
|
||||
class PartEventController {
|
||||
|
||||
// tag::snippet[]
|
||||
@PostMapping("/")
|
||||
fun handle(@RequestBody allPartsEvents: Flux<PartEvent>) { // Using @RequestBody.
|
||||
|
||||
// The final PartEvent for a particular part will have isLast() set to true, and can be
|
||||
// followed by additional events belonging to subsequent parts.
|
||||
// This makes the isLast property suitable as a predicate for the Flux::windowUntil operator, to
|
||||
// split events from all parts into windows that each belong to a single part.
|
||||
allPartsEvents.windowUntil(PartEvent::isLast)
|
||||
.concatMap {
|
||||
|
||||
// The Flux::switchOnFirst operator allows you to see whether you are handling
|
||||
// a form field or file upload.
|
||||
it.switchOnFirst { signal, partEvents ->
|
||||
if (signal.hasValue()) {
|
||||
val event = signal.get()
|
||||
if (event is FormPartEvent) {
|
||||
val value: String = event.value()
|
||||
// Handling the form field.
|
||||
} else if (event is FilePartEvent) {
|
||||
val filename: String = event.filename()
|
||||
|
||||
// The body contents must be completely consumed, relayed, or released to avoid memory leaks.
|
||||
val contents: Flux<DataBuffer> = partEvents.map(PartEvent::content)
|
||||
// Handling the file upload.
|
||||
} else {
|
||||
return@switchOnFirst Mono.error(RuntimeException("Unexpected event: $event"))
|
||||
}
|
||||
} else {
|
||||
return@switchOnFirst partEvents // either complete or error signal
|
||||
}
|
||||
Mono.empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
// end::snippet[]
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user