提交 4f2e54fc 编写于 作者: R Rossen Stoyanchev

Document multipart data support for WebFlux

Issue: SPR-16040
上级 b97fa4a5
...@@ -372,58 +372,117 @@ This can be automated through the use of ...@@ -372,58 +372,117 @@ This can be automated through the use of
[[webflux-web-handler-api]] [[webflux-web-handler-api]]
=== WebHandler API === WebHandler API
`HttpHandler` is the basis for running on different servers. On that base the WebHandler `HttpHandler` is the lowest level contract for running on different HTTP servers.
API provides a slightly higher level processing chain of On top of that foundation, the WebHandler API provides a slightly higher level, but
exception handlers still general purpose, set of components that form a chain of
({api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler]), filters {api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler's],
({api-spring-framework}/web/server/WebFilter.html[WebFilter]), and a target handler {api-spring-framework}/web/server/WebFilter.html[WebFilter's], and a
({api-spring-framework}/web/server/WebHandler.html[WebHandler]). {api-spring-framework}/web/server/WebHandler.html[WebHandler].
All components work on `ServerWebExchange` -- a container for the HTTP request and All WebHandler API components take `ServerWebExchange` as input which goes beyond
response that also adds request attributes, session attributes, access to form data, `ServerHttpRequest` and `ServerHttpResponse` to provide extra building blocks for
multipart data, and more. 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>>. `WebHttpHandlerBuilder` is used to assemble a request processing chain. You can use
To use the builder either add components individually or point to an `ApplicationContext` methods on the builder to add components manually, or more likely have them detected from
to have the following detected: 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"] [cols="2,2,1,3", options="header"]
|=== |===
|Bean name|Bean type|Count|Description |Bean name|Bean type|Count|Description
|"webHandler"
|WebHandler
|1
|Target handler after filters
|<any> |<any>
|WebFilter |`WebExceptionHandler`
|0..N |0..N
|Filters |Exception handlers to apply after all ``WebFilter``'s and the target `WebHandler`.
|<any> |<any>
|WebExceptionHandler |`WebFilter`
|0..N |0..N
|Exception handlers after filter chain |Filters to invoke before and after the target `WebHandler`.
|"webHandler"
|`WebHandler`
|1
|The handler for the request.
|"webSessionManager" |"webSessionManager"
|WebSessionManager |`WebSessionManager`
|0..1 |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 |`ServerCodecConfigurer`
|0..1 |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 |`LocaleContextResolver`
|0..1 |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]] [[webflux-codecs]]
=== HTTP Message Codecs === HTTP Message Codecs
...@@ -987,7 +1046,7 @@ Supports reactive types. ...@@ -987,7 +1046,7 @@ Supports reactive types.
|`@RequestPart` |`@RequestPart`
|For access to a part in a "multipart/form-data" request. Supports reactive types. |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` |`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 |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 ...@@ -1127,10 +1186,8 @@ can be customized through a `WebDataBinder`, see <<mvc-ann-initbinder>>, or by r
==== @RequestParam ==== @RequestParam
[.small]#<<web.adoc#mvc-ann-requestparam,Same in Spring MVC>># [.small]#<<web.adoc#mvc-ann-requestparam,Same in Spring MVC>>#
Use the `@RequestParam` annotation to bind Servlet request parameters (i.e. query Use the `@RequestParam` annotation to bind query parameters to a method argument in a
parameters or form data) to a method argument in a controller. controller. The following code snippet shows the usage:
The following code snippet shows the usage:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
...@@ -1153,16 +1210,25 @@ The following code snippet shows the usage: ...@@ -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 [TIP]
parameter is optional by setting ``@RequestParam``'s `required` flag to `false` or ====
declare the argument with a `java.util.Optional` wrapper. 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 Type conversion is applied automatically if the target method parameter type is not
`String`. See <<mvc-ann-typeconversion>>. `String`. See <<mvc-ann-typeconversion>>.
When an `@RequestParam` annotation is declared as `Map<String, String>` or When an `@RequestParam` annotation is declared as `Map<String, String>` or
`MultiValueMap<String, String>` argument, the map is populated with all request `MultiValueMap<String, String>` argument, the map is populated with all query parameters.
parameters.
Note that use of `@RequestParam` is optional, e.g. to set its attributes. 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 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`: ...@@ -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]] [[webflux-ann-modelattrib-methods]]
=== Model Methods === Model Methods
......
...@@ -859,6 +859,7 @@ request with a simple request parameter. ...@@ -859,6 +859,7 @@ request with a simple request parameter.
[[mvc-multipart]] [[mvc-multipart]]
=== Multipart resolver === Multipart resolver
[.small]#<<web-reactive.adoc#webflux-multipart,Same in Spring WebFlux>>#
`MultipartResolver` from the `org.springframework.web.multipart` package is a strategy `MultipartResolver` from the `org.springframework.web.multipart` package is a strategy
for parsing multipart requests including file uploads. There is one implementation for parsing multipart requests including file uploads. There is one implementation
...@@ -1797,9 +1798,9 @@ The following code snippet shows the usage: ...@@ -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 Method parameters using this annotation are required by default, but you can specify that
parameter is optional by setting ``@RequestParam``'s `required` flag to `false` or a method parameter is optional by setting ``@RequestParam``'s `required` flag to `false`
declare the argument with a `java.util.Optional` wrapper. or by declaring the argument with an `java.util.Optional` wrapper.
Type conversion is applied automatically if the target method parameter type is not Type conversion is applied automatically if the target method parameter type is not
`String`. See <<mvc-ann-typeconversion>>. `String`. See <<mvc-ann-typeconversion>>.
...@@ -2192,6 +2193,7 @@ Therefore the use of flash attributes is recommended mainly for redirect scenari ...@@ -2192,6 +2193,7 @@ Therefore the use of flash attributes is recommended mainly for redirect scenari
[[mvc-multipart-forms]] [[mvc-multipart-forms]]
==== Multipart ==== Multipart
[.small]#<<web-reactive.adoc#webflux-multipart-forms,Same in Spring WebFlux>>#
After a `MultipartResolver` has been <<mvc-multipart,enabled>>, the content of POST After a `MultipartResolver` has been <<mvc-multipart,enabled>>, the content of POST
requests with "multipart/form-data" is parsed and accessible as regular request requests with "multipart/form-data" is parsed and accessible as regular request
...@@ -2262,7 +2264,7 @@ public class FileUploadController { ...@@ -2262,7 +2264,7 @@ public class FileUploadController {
---- ----
Multipart requests can also be submitted from non-browser clients in a RESTful service 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] [literal]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
...@@ -2293,12 +2295,10 @@ probably want it deserialized from JSON (similar to `@RequestBody`). Use the ...@@ -2293,12 +2295,10 @@ probably want it deserialized from JSON (similar to `@RequestBody`). Use the
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@PostMapping("/someUrl") @PostMapping("/")
public String onSubmit(**@RequestPart("meta-data") MetaData metadata, public String handle(**@RequestPart("meta-data") MetaData metadata,
@RequestPart("file-data") MultipartFile file**) { @RequestPart("file-data") MultipartFile file**) {
// ... // ...
} }
---- ----
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册