From 61fc32155464f597eec0f1eefd9cdcb5c45f81c3 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 16 Jan 2018 22:10:04 -0500 Subject: [PATCH] WebFlux docs: Request|ResponseBody + Http|ResponseEntity Issue: SPR-16040 --- src/docs/asciidoc/web/webflux.adoc | 212 +++++++++++++++++++++++++++-- src/docs/asciidoc/web/webmvc.adoc | 196 +++++++++++--------------- 2 files changed, 283 insertions(+), 125 deletions(-) diff --git a/src/docs/asciidoc/web/webflux.adoc b/src/docs/asciidoc/web/webflux.adoc index c6d50b5723f..d2368ba1881 100644 --- a/src/docs/asciidoc/web/webflux.adoc +++ b/src/docs/asciidoc/web/webflux.adoc @@ -1037,12 +1037,11 @@ type. See <>. |`@RequestBody` |For access to the HTTP request body. Body content is converted to the declared method argument type using ``HttpMessageReader``'s. Supports reactive types. -// TODO: See <>. +<>. |`HttpEntity` |For access to request headers and body. The body is converted with ``HttpMessageReader``'s. -Supports reactive types. -// TODO: See <>. +Supports reactive types. See <>. |`@RequestPart` |For access to a part in a "multipart/form-data" request. Supports reactive types. @@ -1061,11 +1060,10 @@ Note that use of `@ModelAttribute` is optional, e.g. to set its attributes. See "Any other argument" further below in this table. |`Errors`, `BindingResult` -|For access to errors from the data binding and validation applied to a command object; -this argument must be declared immediately after a command object (i.e. -`@ModelAttribute` argument). If this is not declared in the controller method signature, -errors result in a `BindException`. See <> for more -details. +|For access to errors from validation and data binding for a command object +(i.e. `@ModelAttribute` argument), or errors from the validation of an `@RequestBody` or +`@RequestPart` arguments; an `Errors`, or `BindingResult` argument must be declared +immediately after the validated method argument. |`SessionStatus` + class-level `@SessionAttributes` |For marking form processing complete which triggers cleanup of session attributes @@ -1108,12 +1106,12 @@ values. |`@ResponseBody` |The return value is encoded through ``HttpMessageWriter``s and written to the response. -// TODO: See <>. +See <>. |`HttpEntity`, `ResponseEntity` |The return value specifies the full response including HTTP headers and body be encoded through ``HttpMessageWriter``s and written to the response. -// TODO: See <>. +See <>. |`HttpHeaders` |For returning a response with headers and no body. @@ -1583,6 +1581,200 @@ To access multipart data sequentially, in streaming fashion, use `@RequestBody` } ---- +`@RequestPart` can be used in combination with `javax.validation.Valid`, or Spring's +`@Validated` annotation, which causes Standard Bean Validation to be applied. +By default validation errors cause a `WebExchangeBindException` which is turned +into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally +within the controller through an `Errors` or `BindingResult` argument: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- +@PostMapping("/") +public String handle(**@Valid** @RequestPart("meta-data") MetaData metadata, + **BindingResult result**) { + // ... +} +---- + + +[[webflux-ann-requestbody]] +==== @RequestBody +[.small]#<># + +Use the `@RequestBody` annotation to have the request body read and deserialized into an +Object through an <>. +Below is an example with an `@RequestBody` argument: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @PostMapping("/accounts") + public void handle(@RequestBody Account account) { + // ... + } +---- + +Unlike Spring MVC, in WebFlux the `@RequestBody` method argument supports reactive types +and fully non-blocking reading and (client-to-server) streaming: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @PostMapping("/accounts") + public void handle(@RequestBody Mono account) { + // ... + } +---- + +You can use the <> option of the <> to +configure or customize message readers. + +`@RequestBody` can be used in combination with `javax.validation.Valid`, or Spring's +`@Validated` annotation, which causes Standard Bean Validation to be applied. +By default validation errors cause a `WebExchangeBindException` which is turned +into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally +within the controller through an `Errors` or `BindingResult` argument: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @PostMapping("/accounts") + public void handle(@Valid @RequestBody Account account, BindingResult result) { + // ... + } +---- + + +[[webflux-ann-httpentity]] +==== HttpEntity +[.small]#<># + +`HttpEntity` is more or less identical to using <> but based on a +container object that exposes request headers and body. Below is an example: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @PostMapping("/accounts") + public void handle(HttpEntity entity) { + // ... + } +---- + + +[[webflux-ann-responsebody]] +==== @ResponseBody +[.small]#<># + +Use the `@ResponseBody` annotation on a method to have the return serialized to the +response body through an <>. For example: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @GetMapping("/accounts/{id}") + @ResponseBody + public Account handle() { + // ... + } +---- + +`@ResponseBody` is also supported at the class level in which case it is inherited by +all controller methods. This is the effect of `@RestController` which is nothing more +than a meta-annotation marked with `@Controller` and `@ResponseBody`. + +`@ResponseBody` supports reactive types which means you can return Reactor or RxJava +types and have the asynchronous values they produce rendered to the response. +For additional details on JSON rendering see <>. + +`@ResponseBody` methods can be combined with JSON serialization views. +See <> for details. + +You can use the <> option of the <> to +configure or customize message writing. + + +[[webflux-ann-responseentity]] +==== ResponseEntity +[.small]#<># + +`ResponseEntity` is more or less identical to using <> but based +on a container object that specifies request headers and body. Below is an example: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @PostMapping("/something") + public ResponseEntity handle() { + // ... + URI location = ... + return new ResponseEntity.created(location).build(); + } +---- + + +[[webflux-ann-jackson]] +==== Jackson JSON + +[[webflux-ann-jsonview]] +===== Jackson serialization views +[.small]#<># + +Spring WebFlux provides built-in support for +http://wiki.fasterxml.com/JacksonJsonViews[Jackson's Serialization Views] +which allows rendering only a subset of all fields in an Object. To use it with +`@ResponseBody` or `ResponseEntity` controller methods, use Jackson's +`@JsonView` annotation to activate a serialization view class: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @RestController + public class UserController { + + @GetMapping("/user") + @JsonView(User.WithoutPasswordView.class) + public User getUser() { + return new User("eric", "7!jd#h23"); + } + } + + public class User { + + public interface WithoutPasswordView {}; + public interface WithPasswordView extends WithoutPasswordView {}; + + private String username; + private String password; + + public User() { + } + + public User(String username, String password) { + this.username = username; + this.password = password; + } + + @JsonView(WithoutPasswordView.class) + public String getUsername() { + return this.username; + } + + @JsonView(WithPasswordView.class) + public String getPassword() { + return this.password; + } + } +---- + +[NOTE] +==== +`@JsonView` allows an array of view classes but you can only specify only one per +controller method. Use a composite interface if you need to activate multiple views. +==== + + [[webflux-ann-modelattrib-methods]] === Model Methods diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index 7e691e0ae3e..4d427fe262a 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -1623,11 +1623,10 @@ Note that use of `@ModelAttribute` is optional, e.g. to set its attributes. See "Any other argument" further below in this table. |`Errors`, `BindingResult` -|For access to errors from the data binding and validation applied to a command object; -this argument must be declared immediately after a command object (i.e. -`@ModelAttribute` argument). If this is not declared in the controller method signature, -errors result in a `BindException`. See <> for more -details. +|For access to errors from validation and data binding for a command object +(i.e. `@ModelAttribute` argument), or errors from the validation of an `@RequestBody` or +`@RequestPart` arguments; an `Errors`, or `BindingResult` argument must be declared +immediately after the validated method argument. |`SessionStatus` + class-level `@SessionAttributes` |For marking form processing complete which triggers cleanup of session attributes @@ -1672,7 +1671,8 @@ response. See <>. |`HttpEntity`, `ResponseEntity` |The return value specifies the full response including HTTP headers and body be converted -through ``HttpMessageConverter``s and written to the response. See <>. +through ``HttpMessageConverter``s and written to the response. +See <>. |`HttpHeaders` |For returning a response with headers and no body. @@ -2295,146 +2295,121 @@ probably want it deserialized from JSON (similar to `@RequestBody`). Use the [source,java,indent=0] [subs="verbatim,quotes"] ---- - @PostMapping("/") - public String handle(**@RequestPart("meta-data") MetaData metadata, - @RequestPart("file-data") MultipartFile file**) { - // ... - } +@PostMapping("/") +public String handle(**@RequestPart("meta-data") MetaData metadata, + @RequestPart("file-data") MultipartFile file**) { + // ... +} +---- + +`@RequestPart` can be used in combination with `javax.validation.Valid`, or Spring's +`@Validated` annotation, which causes Standard Bean Validation to be applied. +By default validation errors cause a `MethodArgumentNotValidException` which is turned +into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally +within the controller through an `Errors` or `BindingResult` argument: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- +@PostMapping("/") +public String handle(**@Valid** @RequestPart("meta-data") MetaData metadata, + **BindingResult result**) { + // ... +} ---- [[mvc-ann-requestbody]] ==== @RequestBody +[.small]#<># -The `@RequestBody` method parameter annotation indicates that a method parameter should -be bound to the value of the HTTP request body. For example: +Use the `@RequestBody` annotation to have the request body read and deserialized into an +Object through an <>. +Below is an example with an `@RequestBody` argument: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @PutMapping("/something") - public void handle(@RequestBody String body, Writer writer) throws IOException { - writer.write(body); + @PostMapping("/accounts") + public void handle(@RequestBody Account account) { + // ... } ---- -You convert the request body to the method argument by using an -<>. -`HttpMessageConverter` is responsible for converting from the HTTP request message to an -object and converting from an object to the HTTP response body. The -`RequestMappingHandlerAdapter` supports the `@RequestBody` annotation with the following -default `HttpMessageConverters`: - -* `ByteArrayHttpMessageConverter` converts byte arrays -* `StringHttpMessageConverter` converts strings -* `FormHttpMessageConverter` converts form data to/from a `MultiValueMap` -* `SourceHttpMessageConverter` converts to/from a `javax.xml.transform.Source`` - -For more information on these converters, see <>. Also note that if using the MVC namespace or the MVC Java config, -a wider range of message converters are registered by default, including default JSON -and XML payload converters (if e.g. Jackson, Gson and/or JAXB2 are present at runtime). -See <> for more information on MVC setup options. +You can use the <> option of the <> to +configure or customize message conversion. -For a custom example, if you intend to read and write XML using the `spring-oxm` module, -you need to configure the `MarshallingHttpMessageConverter` with a specific `Marshaller` -implementation from the `org.springframework.oxm` package. The example below shows how to -do that directly in your configuration but if your application is configured through the -MVC namespace or the MVC Java config see <> instead. +`@RequestBody` can be used in combination with `javax.validation.Valid`, or Spring's +`@Validated` annotation, which causes Standard Bean Validation to be applied. +By default validation errors cause a `MethodArgumentNotValidException` which is turned +into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally +within the controller through an `Errors` or `BindingResult` argument: -[source,xml,indent=0] +[source,java,indent=0] [subs="verbatim,quotes"] ---- - - - - - - - - - - - - - - - - + @PostMapping("/accounts") + public void handle(@Valid @RequestBody Account account, BindingResult result) { + // ... + } ---- -An `@RequestBody` method parameter can be annotated with `@Valid`, in which case it will -be validated using the configured `Validator` instance. When using the MVC namespace or -the MVC Java config, a JSR-303 validator is configured automatically assuming a JSR-303 -implementation is available on the classpath. - -Just like with `@ModelAttribute` parameters, an `Errors` argument can be used to examine -the errors. If such an argument is not declared, a `MethodArgumentNotValidException` -will be raised. The exception is handled in the `DefaultHandlerExceptionResolver`, which -sends a `400` error back to the client. - -[NOTE] -==== -Also see <> for -information on configuring message converters and a validator through the MVC namespace -or the MVC Java config. -==== - [[mvc-ann-httpentity]] ==== HttpEntity +[.small]#<># -`HttpEntity` is similar to `@RequestBody` but also with access to request headers: +`HttpEntity` is more or less identical to using <> but based on a +container object that exposes request headers and body. Below is an example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @RequestMapping("/something") - public ResponseEntity handle(HttpEntity requestEntity) throws UnsupportedEncodingException { - String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader"); - byte[] requestBody = requestEntity.getBody(); + @PostMapping("/accounts") + public void handle(HttpEntity entity) { // ... } ---- -The above example gets the value of the `MyRequestHeader` request header, and reads the -body as a byte array. As with `@RequestBody`, Spring uses `HttpMessageConverter` to -convert from and to the request and response streams. For more information on these -converters, see the previous section and <>. - [[mvc-ann-responsebody]] ==== @ResponseBody +[.small]#<># -The `@ResponseBody` annotation is similar to `@RequestBody`. This annotation can be placed -on a method and indicates that the return type should be written straight to the HTTP -response body (and not placed in a Model, or interpreted as a view name). For example: +Use the `@ResponseBody` annotation on a method to have the return serialized to the +response body through an +<>. For example: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @GetMapping("/something") + @GetMapping("/accounts/{id}") @ResponseBody - public String helloWorld() { - return "Hello World"; + public Account handle() { + // ... } ---- -The above example will result in the text `Hello World` being written to the HTTP -response stream. +`@ResponseBody` is also supported at the class level in which case it is inherited by +all controller methods. This is the effect of `@RestController` which is nothing more +than a meta-annotation marked with `@Controller` and `@ResponseBody`. + +`@ResponseBody` may be used with reactive types. +See <> and <> for more details. + +You can use the <> option of the <> to +configure or customize message conversion. -As with `@RequestBody`, Spring converts the returned object to a response body by using -an <>. +`@ResponseBody` methods can be combined with JSON serialization views. +See <> for details. [[mvc-ann-responseentity]] ==== ResponseEntity +[.small]#<># -The is similar to `@ResponseBody` but besides providing the response body, `ResponseEntity` -also allows setting response headers: +`ResponseEntity` is more or less identical to using <> but based +on a container object that specifies request headers and body. Below is an example: [source,java,indent=0] [subs="verbatim,quotes"] @@ -2442,31 +2417,24 @@ also allows setting response headers: @PostMapping("/something") public ResponseEntity handle() { // ... - URI location = ... ; + URI location = ... return new ResponseEntity.created(location).build(); } ---- -As with `@ResponseBody`, Spring uses -<> to -convert from and to the request and response streams. For more information on these -converters, see the previous section and <>. - [[mvc-ann-jackson]] ==== Jackson JSON [[mvc-ann-jsonview]] ===== Jackson serialization views +[.small]#<># -It can sometimes be useful to filter contextually the object that will be serialized to the -HTTP response body. In order to provide such capability, Spring MVC has built-in support for -rendering with http://wiki.fasterxml.com/JacksonJsonViews[Jackson's Serialization Views]. - -To use it with an `@ResponseBody` controller method or controller methods that return -`ResponseEntity`, simply add the `@JsonView` annotation with a class argument specifying -the view class or interface to be used: +Spring MVC provides built-in support for +http://wiki.fasterxml.com/JacksonJsonViews[Jackson's Serialization Views] +which allows rendering only a subset of all fields in an Object. To use it with +`@ResponseBody` or `ResponseEntity` controller methods, use Jackson's +`@JsonView` annotation to activate a serialization view class: [source,java,indent=0] [subs="verbatim,quotes"] @@ -2511,10 +2479,8 @@ the view class or interface to be used: [NOTE] ==== -Note that despite `@JsonView` allowing for more than one class to -be specified, the use on a controller method is only supported with -exactly one class argument. Consider the use of a composite interface -if you need to enable multiple views. +`@JsonView` allows an array of view classes but you can only specify only one per +controller method. Use a composite interface if you need to activate multiple views. ==== For controllers relying on view resolution, simply add the serialization view class