Document multipart data support for WebFlux
Issue: SPR-16040
This commit is contained in:
parent
b97fa4a5ee
commit
4f2e54fc2d
|
@ -372,58 +372,117 @@ This can be automated through the use of
|
|||
[[webflux-web-handler-api]]
|
||||
=== WebHandler API
|
||||
|
||||
`HttpHandler` is the basis for running on different servers. On that base the WebHandler
|
||||
API provides a slightly higher level processing chain of
|
||||
exception handlers
|
||||
({api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler]), filters
|
||||
({api-spring-framework}/web/server/WebFilter.html[WebFilter]), and a target handler
|
||||
({api-spring-framework}/web/server/WebHandler.html[WebHandler]).
|
||||
`HttpHandler` is the lowest level contract for running on different HTTP servers.
|
||||
On top of that foundation, the WebHandler API provides a slightly higher level, but
|
||||
still general purpose, set of components that form a chain of
|
||||
{api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler's],
|
||||
{api-spring-framework}/web/server/WebFilter.html[WebFilter's], and a
|
||||
{api-spring-framework}/web/server/WebHandler.html[WebHandler].
|
||||
|
||||
All components work on `ServerWebExchange` -- a container for the HTTP request and
|
||||
response that also adds request attributes, session attributes, access to form data,
|
||||
multipart data, and more.
|
||||
All WebHandler API components take `ServerWebExchange` as input which goes beyond
|
||||
`ServerHttpRequest` and `ServerHttpResponse` to provide extra building blocks for
|
||||
use in web applications such as request attributes, session attributes, access to parsed
|
||||
form data, multipart data, and more.
|
||||
|
||||
The processing chain can be put together with `WebHttpHandlerBuilder` which builds an
|
||||
`HttpHandler` that in turn can be run with a <<webflux-httphandler,server adapter>>.
|
||||
To use the builder either add components individually or point to an `ApplicationContext`
|
||||
to have the following detected:
|
||||
`WebHttpHandlerBuilder` is used to assemble a request processing chain. You can use
|
||||
methods on the builder to add components manually, or more likely have them detected from
|
||||
a Spring `ApplicationContext`, with the resulting `HttpHandler` ready to run via a
|
||||
<<webflux-httphandler,server adapter>>:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
ApplicationContext context = ...
|
||||
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build()
|
||||
----
|
||||
|
||||
The table below lists the components that `WebHttpHandlerBuilder` detects:
|
||||
|
||||
[cols="2,2,1,3", options="header"]
|
||||
|===
|
||||
|Bean name|Bean type|Count|Description
|
||||
|
||||
|<any>
|
||||
|`WebExceptionHandler`
|
||||
|0..N
|
||||
|Exception handlers to apply after all ``WebFilter``'s and the target `WebHandler`.
|
||||
|
||||
|<any>
|
||||
|`WebFilter`
|
||||
|0..N
|
||||
|Filters to invoke before and after the target `WebHandler`.
|
||||
|
||||
|"webHandler"
|
||||
|WebHandler
|
||||
|`WebHandler`
|
||||
|1
|
||||
|Target handler after filters
|
||||
|
||||
|<any>
|
||||
|WebFilter
|
||||
|0..N
|
||||
|Filters
|
||||
|
||||
|<any>
|
||||
|WebExceptionHandler
|
||||
|0..N
|
||||
|Exception handlers after filter chain
|
||||
|The handler for the request.
|
||||
|
||||
|"webSessionManager"
|
||||
|WebSessionManager
|
||||
|`WebSessionManager`
|
||||
|0..1
|
||||
|Custom session manager; `DefaultWebSessionManager` by default
|
||||
|The manager for ``WebSession``'s exposed through a method on `ServerWebExchange`.
|
||||
|
||||
`DefaultWebSessionManager` by default.
|
||||
|
||||
|"serverCodecConfigurer"
|
||||
|ServerCodecConfigurer
|
||||
|`ServerCodecConfigurer`
|
||||
|0..1
|
||||
|Custom form and multipart data decoders; `ServerCodecConfigurer.create()` by default
|
||||
|For access to ``HttpMessageReader``'s for parsing form data and multipart data that's
|
||||
then exposed through methods on `ServerWebExchange`.
|
||||
|
||||
`ServerCodecConfigurer.create()` by default.
|
||||
|
||||
|"localeContextResolver"
|
||||
|LocaleContextResolver
|
||||
|`LocaleContextResolver`
|
||||
|0..1
|
||||
|Custom resolver for `LocaleContext`; `AcceptHeaderLocaleContextResolver` by default
|
||||
|The resolver for `LocaleContext` exposed through a method on `ServerWebExchange`.
|
||||
|
||||
`AcceptHeaderLocaleContextResolver` by default.
|
||||
|===
|
||||
|
||||
|
||||
[[webflux-form-data]]
|
||||
==== Form Reader
|
||||
|
||||
`ServerWebExchange` exposes the following method for access to form data:
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
Mono<MultiValueMap<String, String>> getFormData();
|
||||
----
|
||||
|
||||
The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse form data
|
||||
("application/x-www-form-urlencoded") into a `MultiValueMap`. By default
|
||||
`FormHttpMessageReader` is configured for use via the `ServerCodecConfigurer` bean
|
||||
(see <<webflux-web-handler-api,Web Handler API>>).
|
||||
|
||||
|
||||
[[webflux-multipart]]
|
||||
==== Multipart Reader
|
||||
[.small]#<<web.adoc#mvc-multipart,Same in Spring MVC>>#
|
||||
|
||||
`ServerWebExchange` exposes the following method for access to multipart data:
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
Mono<MultiValueMap<String, Part>> getMultipartData();
|
||||
----
|
||||
|
||||
The `DefaultServerWebExchange` uses the configured
|
||||
`HttpMessageReader<MultiValueMap<String, Part>>` to parse "multipart/form-data" content
|
||||
into a `MultiValueMap`. At present
|
||||
https://github.com/synchronoss/nio-multipart[Synchronoss NIO Multipart] is the only 3rd
|
||||
party library supported, and the only library we know for non-blocking parsing of
|
||||
multipart requests. It is enabled through the `ServerCodecConfigurer` bean
|
||||
(see <<webflux-web-handler-api,Web Handler API>>).
|
||||
|
||||
To parse multipart data in streaming fashion, use the `Flux<Part>` returned from an
|
||||
`HttpMessageReader<Part>` instead. For example in an annotated controller use of
|
||||
`@RequestPart` implies Map-like access to individual parts by name, and hence requires
|
||||
parsing multipart data in full. By contrast `@RequestBody` can be used to decode the
|
||||
content to `Flux<Part>` without collecting to a `MultiValueMap`.
|
||||
|
||||
|
||||
|
||||
[[webflux-codecs]]
|
||||
=== HTTP Message Codecs
|
||||
|
@ -987,7 +1046,7 @@ Supports reactive types.
|
|||
|
||||
|`@RequestPart`
|
||||
|For access to a part in a "multipart/form-data" request. Supports reactive types.
|
||||
// TODO: See <<webflux-multipart-forms-non-browsers>> and <<webflux-multipart>>.
|
||||
See <<webflux-multipart-forms>> and <<webflux-multipart>>.
|
||||
|
||||
|`java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap`
|
||||
|For access to the model that is used in HTML controllers and exposed to templates as
|
||||
|
@ -1127,10 +1186,8 @@ can be customized through a `WebDataBinder`, see <<mvc-ann-initbinder>>, or by r
|
|||
==== @RequestParam
|
||||
[.small]#<<web.adoc#mvc-ann-requestparam,Same in Spring MVC>>#
|
||||
|
||||
Use the `@RequestParam` annotation to bind Servlet request parameters (i.e. query
|
||||
parameters or form data) to a method argument in a controller.
|
||||
|
||||
The following code snippet shows the usage:
|
||||
Use the `@RequestParam` annotation to bind query parameters to a method argument in a
|
||||
controller. The following code snippet shows the usage:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
|
@ -1153,16 +1210,25 @@ The following code snippet shows the usage:
|
|||
}
|
||||
----
|
||||
|
||||
Parameters using this annotation are required by default, but you can specify that a
|
||||
parameter is optional by setting ``@RequestParam``'s `required` flag to `false` or
|
||||
declare the argument with a `java.util.Optional` wrapper.
|
||||
[TIP]
|
||||
====
|
||||
Unlike the Servlet API "request paramater" concept that conflate query parameters, form
|
||||
data, and multiparts into one, in WebFlux each is accessed individually through the
|
||||
`ServerWebExchange`. While `@RequestParam` binds to query parameters only, you can
|
||||
use data binding to apply query paramerters, form data, and multiparts to a
|
||||
<<webflux-ann-modelattrib-method-args,command object>>.
|
||||
====
|
||||
|
||||
Method parameters using using the `@RequestParam` annotation are required by default, but
|
||||
you can specify that a method parameter is optional by setting ``@RequestParam``'s
|
||||
`required` flag to `false` or by declaring the argument with an `java.util.Optional`
|
||||
wrapper.
|
||||
|
||||
Type conversion is applied automatically if the target method parameter type is not
|
||||
`String`. See <<mvc-ann-typeconversion>>.
|
||||
|
||||
When an `@RequestParam` annotation is declared as `Map<String, String>` or
|
||||
`MultiValueMap<String, String>` argument, the map is populated with all request
|
||||
parameters.
|
||||
`MultiValueMap<String, String>` argument, the map is populated with all query parameters.
|
||||
|
||||
Note that use of `@RequestParam` is optional, e.g. to set its attributes.
|
||||
By default any argument that is a simple value type, as determined by
|
||||
|
@ -1436,6 +1502,87 @@ access pre-existing request attributes created earlier, e.g. by a `WebFilter`:
|
|||
----
|
||||
|
||||
|
||||
[[webflux-multipart-forms]]
|
||||
==== Multipart
|
||||
[.small]#<<web.adoc#mvc-multipart-forms,Same in Spring MVC>>#
|
||||
|
||||
As explained in <<webflux-multipart>>, `ServerWebExchange` provides access to multipart
|
||||
content. The best way to handle a file upload form (e.g. from a browser) in a controller
|
||||
is through data binding to a <<webflux-ann-modelattrib-method-args,command object>>:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
class MyForm {
|
||||
|
||||
private String name;
|
||||
|
||||
private MultipartFile file;
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
|
||||
@Controller
|
||||
public class FileUploadController {
|
||||
|
||||
@PostMapping("/form")
|
||||
public String handleFormUpload(MyForm form, BindingResult errors) {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Multipart requests can also be submitted from non-browser clients in a RESTful service
|
||||
scenario. For example a file along with JSON:
|
||||
|
||||
[literal]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
POST /someUrl
|
||||
Content-Type: multipart/mixed
|
||||
|
||||
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
|
||||
Content-Disposition: form-data; name="meta-data"
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
{
|
||||
"name": "value"
|
||||
}
|
||||
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
|
||||
Content-Disposition: form-data; name="file-data"; filename="file.properties"
|
||||
Content-Type: text/xml
|
||||
Content-Transfer-Encoding: 8bit
|
||||
... File Data ...
|
||||
----
|
||||
|
||||
You can access the "meta-data" part with `@RequestPart` which would deserialize it from
|
||||
JSON (similar to `@RequestBody`) through one of the configured <<webflux-codecs>>:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/")
|
||||
public String handle(**@RequestPart("meta-data") MetaData metadata,
|
||||
@RequestPart("file-data") FilePart file**) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
To access multipart data sequentially, in streaming fashion, use `@RequestBody` with
|
||||
`Flux<Part>` instead. For example:
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/")
|
||||
public String handle(**@RequestBody Flux<Part> parts**) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
[[webflux-ann-modelattrib-methods]]
|
||||
=== Model Methods
|
||||
|
|
|
@ -859,6 +859,7 @@ request with a simple request parameter.
|
|||
|
||||
[[mvc-multipart]]
|
||||
=== Multipart resolver
|
||||
[.small]#<<web-reactive.adoc#webflux-multipart,Same in Spring WebFlux>>#
|
||||
|
||||
`MultipartResolver` from the `org.springframework.web.multipart` package is a strategy
|
||||
for parsing multipart requests including file uploads. There is one implementation
|
||||
|
@ -1797,9 +1798,9 @@ The following code snippet shows the usage:
|
|||
}
|
||||
----
|
||||
|
||||
Parameters using this annotation are required by default, but you can specify that a
|
||||
parameter is optional by setting ``@RequestParam``'s `required` flag to `false` or
|
||||
declare the argument with a `java.util.Optional` wrapper.
|
||||
Method parameters using this annotation are required by default, but you can specify that
|
||||
a method parameter is optional by setting ``@RequestParam``'s `required` flag to `false`
|
||||
or by declaring the argument with an `java.util.Optional` wrapper.
|
||||
|
||||
Type conversion is applied automatically if the target method parameter type is not
|
||||
`String`. See <<mvc-ann-typeconversion>>.
|
||||
|
@ -2192,6 +2193,7 @@ Therefore the use of flash attributes is recommended mainly for redirect scenari
|
|||
|
||||
[[mvc-multipart-forms]]
|
||||
==== Multipart
|
||||
[.small]#<<web-reactive.adoc#webflux-multipart-forms,Same in Spring WebFlux>>#
|
||||
|
||||
After a `MultipartResolver` has been <<mvc-multipart,enabled>>, the content of POST
|
||||
requests with "multipart/form-data" is parsed and accessible as regular request
|
||||
|
@ -2262,7 +2264,7 @@ public class FileUploadController {
|
|||
----
|
||||
|
||||
Multipart requests can also be submitted from non-browser clients in a RESTful service
|
||||
scenario with more types of content. For example a file along with JSON:
|
||||
scenario. For example a file along with JSON:
|
||||
|
||||
[literal]
|
||||
[subs="verbatim,quotes"]
|
||||
|
@ -2293,12 +2295,10 @@ probably want it deserialized from JSON (similar to `@RequestBody`). Use the
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@PostMapping("/someUrl")
|
||||
public String onSubmit(**@RequestPart("meta-data") MetaData metadata,
|
||||
@PostMapping("/")
|
||||
public String handle(**@RequestPart("meta-data") MetaData metadata,
|
||||
@RequestPart("file-data") MultipartFile file**) {
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
Loading…
Reference in New Issue