Migrate to Asciidoctor Tabs
This commit is contained in:
@@ -25,8 +25,11 @@ By default, `@ControllerAdvice` methods apply to every request (that is, all con
|
||||
but you can narrow that down to a subset of controllers by using attributes on the
|
||||
annotation, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// Target all Controllers annotated with @RestController
|
||||
@ControllerAdvice(annotations = RestController.class)
|
||||
@@ -41,8 +44,9 @@ annotation, as the following example shows:
|
||||
public class ExampleAdvice3 {}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
// Target all Controllers annotated with @RestController
|
||||
@ControllerAdvice(annotations = [RestController::class])
|
||||
@@ -56,6 +60,7 @@ annotation, as the following example shows:
|
||||
@ControllerAdvice(assignableTypes = [ControllerInterface::class, AbstractController::class])
|
||||
public class ExampleAdvice3 {}
|
||||
----
|
||||
======
|
||||
|
||||
The selectors in the preceding example are evaluated at runtime and may negatively impact
|
||||
performance if used extensively. See the
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
`@ExceptionHandler` methods to handle exceptions from controller methods. The following
|
||||
example includes such a handler method:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Controller
|
||||
public class SimpleController {
|
||||
@@ -21,6 +24,7 @@ example includes such a handler method:
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Declaring an `@ExceptionHandler`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
||||
@@ -22,8 +22,11 @@ with a `WebDataBinder` argument, for registrations, and a `void` return value.
|
||||
The following example uses the `@InitBinder` annotation:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Controller
|
||||
public class FormController {
|
||||
@@ -38,6 +41,7 @@ The following example uses the `@InitBinder` annotation:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using the `@InitBinder` annotation.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -64,8 +68,11 @@ Alternatively, when using a `Formatter`-based setup through a shared
|
||||
controller-specific `Formatter` instances, as the following example shows:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Controller
|
||||
public class FormController {
|
||||
@@ -78,6 +85,7 @@ controller-specific `Formatter` instances, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Adding a custom formatter (a `DateFormatter`, in this case).
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
||||
@@ -15,14 +15,18 @@ JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
|
||||
|
||||
The following code sample demonstrates how to get the cookie value:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/demo")
|
||||
public void handle(@CookieValue("JSESSIONID") String cookie) { // <1>
|
||||
//...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Get the cookie value.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
||||
@@ -7,21 +7,27 @@
|
||||
container object that exposes request headers and the body. The following example uses an
|
||||
`HttpEntity`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
public void handle(HttpEntity<Account> entity) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
fun handle(entity: HttpEntity<Account>) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
@@ -13,8 +13,11 @@ which allows rendering only a subset of all fields in an `Object`. To use it wit
|
||||
`@ResponseBody` or `ResponseEntity` controller methods, you can use Jackson's
|
||||
`@JsonView` annotation to activate a serialization view class, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@RestController
|
||||
public class UserController {
|
||||
@@ -54,8 +57,9 @@ which allows rendering only a subset of all fields in an `Object`. To use it wit
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@RestController
|
||||
class UserController {
|
||||
@@ -75,6 +79,7 @@ which allows rendering only a subset of all fields in an `Object`. To use it wit
|
||||
interface WithPasswordView : WithoutPasswordView
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
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.
|
||||
|
||||
@@ -19,8 +19,11 @@ to mask variable content. That said, if you want to access matrix variables from
|
||||
controller method, you need to add a URI variable to the path segment where matrix
|
||||
variables are expected. The following example shows how to do so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// GET /pets/42;q=11;r=22
|
||||
|
||||
@@ -31,8 +34,10 @@ variables are expected. The following example shows how to do so:
|
||||
// q == 11
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
// GET /pets/42;q=11;r=22
|
||||
|
||||
@@ -43,14 +48,18 @@ variables are expected. The following example shows how to do so:
|
||||
// q == 11
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
Given that all path segments can contain matrix variables, you may sometimes need to
|
||||
disambiguate which path variable the matrix variable is expected to be in,
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// GET /owners/42;q=11/pets/21;q=22
|
||||
|
||||
@@ -63,8 +72,10 @@ as the following example shows:
|
||||
// q2 == 22
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/owners/{ownerId}/pets/{petId}")
|
||||
fun findPet(
|
||||
@@ -75,12 +86,16 @@ as the following example shows:
|
||||
// q2 == 22
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
You can define a matrix variable may be defined as optional and specify a default value
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// GET /pets/42
|
||||
|
||||
@@ -90,8 +105,10 @@ as the following example shows:
|
||||
// q == 1
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
// GET /pets/42
|
||||
|
||||
@@ -101,11 +118,15 @@ as the following example shows:
|
||||
// q == 1
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
To get all matrix variables, use a `MultiValueMap`, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
|
||||
|
||||
@@ -118,8 +139,10 @@ To get all matrix variables, use a `MultiValueMap`, as the following example sho
|
||||
// petMatrixVars: ["q" : 22, "s" : 23]
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
|
||||
|
||||
@@ -132,5 +155,6 @@ To get all matrix variables, use a `MultiValueMap`, as the following example sho
|
||||
// petMatrixVars: ["q" : 22, "s" : 23]
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
@@ -9,12 +9,16 @@ the values of query parameters and form fields whose names match to field names.
|
||||
referred to as data binding, and it saves you from having to deal with parsing and
|
||||
converting individual query parameters and form fields. The following example binds an instance of `Pet`:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@ModelAttribute Pet pet) { } // <1>
|
||||
----
|
||||
======
|
||||
<1> Bind an instance of `Pet`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -45,8 +49,11 @@ Data binding can result in errors. By default, a `WebExchangeBindException` is r
|
||||
to check for such errors in the controller method, you can add a `BindingResult` argument
|
||||
immediately next to the `@ModelAttribute`, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { <1>
|
||||
@@ -56,6 +63,7 @@ immediately next to the `@ModelAttribute`, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Adding a `BindingResult`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -76,8 +84,11 @@ You can automatically apply validation after data binding by adding the
|
||||
xref:core/validation/beanvalidation.adoc[Bean Validation] and
|
||||
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). The following example uses the `@Valid` annotation:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { // <1>
|
||||
@@ -87,6 +98,7 @@ xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). The following ex
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using `@Valid` on a model attribute argument.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -110,8 +122,11 @@ argument, you must declare the `@ModelAttribute` argument before it without a re
|
||||
type wrapper, as shown earlier. Alternatively, you can handle any errors through the
|
||||
reactive type, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
|
||||
@@ -124,8 +139,10 @@ reactive type, as the following example shows:
|
||||
});
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
fun processSubmit(@Valid @ModelAttribute("pet") petMono: Mono<Pet>): Mono<String> {
|
||||
@@ -138,6 +155,7 @@ reactive type, as the following example shows:
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
Note that use of `@ModelAttribute` is optional -- for example, to set its attributes.
|
||||
By default, any argument that is not a simple value type (as determined by
|
||||
|
||||
@@ -9,8 +9,11 @@ is through data binding to a xref:web/webflux/controller/ann-methods/modelattrib
|
||||
as the following example shows:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
class MyForm {
|
||||
|
||||
@@ -32,8 +35,10 @@ as the following example shows:
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
class MyForm(
|
||||
val name: String,
|
||||
@@ -49,6 +54,7 @@ as the following example shows:
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
--
|
||||
|
||||
You can also submit multipart requests from non-browser clients in a RESTful service
|
||||
@@ -77,8 +83,11 @@ Content-Transfer-Encoding: 8bit
|
||||
You can access individual parts with `@RequestPart`, as the following example shows:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/")
|
||||
public String handle(@RequestPart("meta-data") Part metadata, // <1>
|
||||
@@ -86,6 +95,7 @@ You can access individual parts with `@RequestPart`, as the following example sh
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using `@RequestPart` to get the metadata.
|
||||
<2> Using `@RequestPart` to get the file.
|
||||
|
||||
@@ -107,14 +117,18 @@ To deserialize the raw part content (for example, to JSON -- similar to `@Reques
|
||||
you can declare a concrete target `Object`, instead of `Part`, as the following example shows:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/")
|
||||
public String handle(@RequestPart("meta-data") MetaData metadata) { // <1>
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using `@RequestPart` to get the metadata.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -136,8 +150,11 @@ in the controller method by declaring the argument with an async wrapper and the
|
||||
error related operators:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/")
|
||||
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
|
||||
@@ -145,28 +162,34 @@ error related operators:
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@PostMapping("/")
|
||||
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData): String {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
--
|
||||
|
||||
To access all multipart data as a `MultiValueMap`, you can use `@RequestBody`,
|
||||
as the following example shows:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/")
|
||||
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) { // <1>
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using `@RequestBody`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -196,8 +219,11 @@ 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",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/")
|
||||
public void handle(@RequestBody Flux<PartEvent> allPartsEvents) { <1>
|
||||
@@ -224,6 +250,7 @@ For example:
|
||||
}));
|
||||
}
|
||||
----
|
||||
======
|
||||
<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.
|
||||
|
||||
@@ -7,14 +7,18 @@ Similarly to `@SessionAttribute`, you can use the `@RequestAttribute` annotation
|
||||
access pre-existing request attributes created earlier (for example, by a `WebFilter`),
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/")
|
||||
public String handle(@RequestAttribute Client client) { <1>
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using `@RequestAttribute`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
||||
@@ -7,8 +7,11 @@ You can use the `@RequestBody` annotation to have the request body read and dese
|
||||
`Object` through an xref:web/webflux/reactive-spring.adoc#webflux-codecs[HttpMessageReader].
|
||||
The following example uses a `@RequestBody` argument:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
public void handle(@RequestBody Account account) {
|
||||
@@ -16,34 +19,42 @@ The following example uses a `@RequestBody` argument:
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
fun 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.
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
public void handle(@RequestBody Mono<Account> account) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
fun handle(@RequestBody accounts: Flow<Account>) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
You can use the xref:web/webflux/config.adoc#webflux-config-message-codecs[HTTP message codecs] option of the xref:web/webflux/dispatcher-handler.adoc#webflux-framework-config[WebFlux Config] to
|
||||
configure or customize message readers.
|
||||
@@ -55,21 +66,27 @@ The exception contains a `BindingResult` with error details and can be handled i
|
||||
controller method by declaring the argument with an async wrapper and then using error
|
||||
related operators:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
public void handle(@Valid @RequestBody Mono<Account> account) {
|
||||
// use one of the onError* operators...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@PostMapping("/accounts")
|
||||
fun handle(@Valid @RequestBody account: Mono<Account>) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
@@ -21,8 +21,11 @@ Keep-Alive 300
|
||||
|
||||
The following example gets the value of the `Accept-Encoding` and `Keep-Alive` headers:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/demo")
|
||||
public void handle(
|
||||
@@ -31,6 +34,7 @@ The following example gets the value of the `Accept-Encoding` and `Keep-Alive` h
|
||||
//...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Get the value of the `Accept-Encoding` header.
|
||||
<2> Get the value of the `Keep-Alive` header.
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
You can use the `@RequestParam` annotation to bind query parameters to a method argument in a
|
||||
controller. The following code snippet shows the usage:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Controller
|
||||
@RequestMapping("/pets")
|
||||
@@ -25,6 +28,7 @@ controller. The following code snippet shows the usage:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using `@RequestParam`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
||||
@@ -7,8 +7,11 @@ You can use the `@ResponseBody` annotation on a method to have the return serial
|
||||
to the response body through an xref:web/webflux/reactive-spring.adoc#webflux-codecs[HttpMessageWriter]. The following
|
||||
example shows how to do so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/accounts/{id}")
|
||||
@ResponseBody
|
||||
@@ -16,8 +19,10 @@ example shows how to do so:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/accounts/{id}")
|
||||
@ResponseBody
|
||||
@@ -25,6 +30,7 @@ example shows how to do so:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
`@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
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
|
||||
`ResponseEntity` is like xref:web/webflux/controller/ann-methods/responsebody.adoc[`@ResponseBody`] but with status and headers. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/something")
|
||||
public ResponseEntity<String> handle() {
|
||||
@@ -15,8 +18,10 @@
|
||||
return ResponseEntity.ok().eTag(etag).body(body);
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/something")
|
||||
fun handle(): ResponseEntity<String> {
|
||||
@@ -25,6 +30,7 @@
|
||||
return ResponseEntity.ok().eTag(etag).build(body)
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
WebFlux supports using a single value xref:web-reactive.adoc#webflux-reactive-libraries[reactive type] to
|
||||
produce the `ResponseEntity` asynchronously, and/or single and multi-value reactive types
|
||||
|
||||
@@ -7,14 +7,18 @@ If you need access to pre-existing session attributes that are managed globally
|
||||
(that is, outside the controller -- for example, by a filter) and may or may not be present,
|
||||
you can use the `@SessionAttribute` annotation on a method parameter, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/")
|
||||
public String handle(@SessionAttribute User user) { // <1>
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using `@SessionAttribute`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
||||
@@ -11,8 +11,11 @@ requests to access.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Controller
|
||||
@SessionAttributes("pet") <1>
|
||||
@@ -20,6 +23,7 @@ Consider the following example:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using the `@SessionAttributes` annotation.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -38,8 +42,11 @@ it is automatically promoted to and saved in the `WebSession`. It remains there
|
||||
another controller method uses a `SessionStatus` method argument to clear the storage,
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Controller
|
||||
@SessionAttributes("pet") // <1>
|
||||
@@ -58,6 +65,7 @@ as the following example shows:
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Using the `@SessionAttributes` annotation.
|
||||
<2> Using a `SessionStatus` variable.
|
||||
|
||||
|
||||
@@ -24,8 +24,11 @@ related to the request body).
|
||||
|
||||
The following example uses a `@ModelAttribute` method:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@ModelAttribute
|
||||
public void populateModel(@RequestParam String number, Model model) {
|
||||
@@ -33,8 +36,10 @@ The following example uses a `@ModelAttribute` method:
|
||||
// add more ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@ModelAttribute
|
||||
fun populateModel(@RequestParam number: String, model: Model) {
|
||||
@@ -42,25 +47,32 @@ The following example uses a `@ModelAttribute` method:
|
||||
// add more ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The following example adds one attribute only:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@ModelAttribute
|
||||
public Account addAccount(@RequestParam String number) {
|
||||
return accountRepository.findAccount(number);
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@ModelAttribute
|
||||
fun addAccount(@RequestParam number: String): Account {
|
||||
return accountRepository.findAccount(number);
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: When a name is not explicitly specified, a default name is chosen based on the type,
|
||||
as explained in the javadoc for {api-spring-framework}/core/Conventions.html[`Conventions`].
|
||||
@@ -73,8 +85,11 @@ attributes can be transparently resolved (and the model updated) to their actual
|
||||
at the time of `@RequestMapping` invocation, provided a `@ModelAttribute` argument is
|
||||
declared without a wrapper, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@ModelAttribute
|
||||
public void addAccount(@RequestParam String number) {
|
||||
@@ -87,8 +102,10 @@ declared without a wrapper, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
import org.springframework.ui.set
|
||||
|
||||
@@ -103,6 +120,7 @@ declared without a wrapper, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
In addition, any model attributes that have a reactive type wrapper are resolved to their
|
||||
@@ -115,8 +133,11 @@ controllers, unless the return value is a `String` that would otherwise be inter
|
||||
as a view name. `@ModelAttribute` can also help to customize the model attribute name,
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/accounts/{id}")
|
||||
@ModelAttribute("myAccount")
|
||||
@@ -125,8 +146,10 @@ as the following example shows:
|
||||
return account;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/accounts/{id}")
|
||||
@ModelAttribute("myAccount")
|
||||
@@ -135,6 +158,7 @@ as the following example shows:
|
||||
return account
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,8 +23,11 @@ using `@RequestMapping`, which, by default, matches to all HTTP methods. At the
|
||||
|
||||
The following example uses type and method level mappings:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@RestController
|
||||
@RequestMapping("/persons")
|
||||
@@ -42,8 +45,10 @@ The following example uses type and method level mappings:
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@RestController
|
||||
@RequestMapping("/persons")
|
||||
@@ -61,6 +66,7 @@ The following example uses type and method level mappings:
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
|
||||
[[webflux-ann-requestmapping-uri-templates]]
|
||||
@@ -106,29 +112,38 @@ You can map requests by using glob patterns and wildcards:
|
||||
Captured URI variables can be accessed with `@PathVariable`, as the following example shows:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/owners/{ownerId}/pets/{petId}")
|
||||
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/owners/{ownerId}/pets/{petId}")
|
||||
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
--
|
||||
|
||||
You can declare URI variables at the class and method levels, as the following example shows:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Controller
|
||||
@RequestMapping("/owners/{ownerId}") // <1>
|
||||
@@ -140,6 +155,7 @@ You can declare URI variables at the class and method levels, as the following e
|
||||
}
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Class-level URI mapping.
|
||||
<2> Method-level URI mapping.
|
||||
|
||||
@@ -179,22 +195,28 @@ syntax: `{varName:regex}`. For example, given a URL of `/spring-web-3.0.5.jar`,
|
||||
extracts the name, version, and file extension:
|
||||
|
||||
--
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
|
||||
public void handle(@PathVariable String version, @PathVariable String ext) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
|
||||
fun handle(@PathVariable version: String, @PathVariable ext: String) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
--
|
||||
|
||||
URI path patterns can also have embedded `${...}` placeholders that are resolved on startup
|
||||
@@ -234,22 +256,28 @@ sorted last instead. If two patterns are both catch-all, the longer is chosen.
|
||||
You can narrow the request mapping based on the `Content-Type` of the request,
|
||||
as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@PostMapping(path = "/pets", consumes = "application/json")
|
||||
public void addPet(@RequestBody Pet pet) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@PostMapping("/pets", consumes = ["application/json"])
|
||||
fun addPet(@RequestBody pet: Pet) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The consumes attribute also supports negation expressions -- for example, `!text/plain` means any
|
||||
content type other than `text/plain`.
|
||||
@@ -269,8 +297,11 @@ TIP: `MediaType` provides constants for commonly used media types -- for example
|
||||
You can narrow the request mapping based on the `Accept` request header and the list of
|
||||
content types that a controller method produces, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping(path = "/pets/{petId}", produces = "application/json")
|
||||
@ResponseBody
|
||||
@@ -278,8 +309,10 @@ content types that a controller method produces, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/pets/{petId}", produces = ["application/json"])
|
||||
@ResponseBody
|
||||
@@ -287,6 +320,7 @@ content types that a controller method produces, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
The media type can specify a character set. Negated expressions are supported -- for example,
|
||||
`!text/plain` means any content type other than `text/plain`.
|
||||
@@ -307,14 +341,18 @@ You can narrow request mappings based on query parameter conditions. You can tes
|
||||
presence of a query parameter (`myParam`), for its absence (`!myParam`), or for a
|
||||
specific value (`myParam=myValue`). The following examples tests for a parameter with a value:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") // <1>
|
||||
public void findPet(@PathVariable String petId) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Check that `myParam` equals `myValue`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -329,14 +367,18 @@ specific value (`myParam=myValue`). The following examples tests for a parameter
|
||||
|
||||
You can also use the same with request header conditions, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") // <1>
|
||||
public void findPet(@PathVariable String petId) {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Check that `myHeader` equals `myValue`.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
@@ -401,8 +443,11 @@ You can programmatically register Handler methods, which can be used for dynamic
|
||||
registrations or for advanced cases, such as different instances of the same handler
|
||||
under different URLs. The following example shows how to do so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Configuration
|
||||
public class MyConfig {
|
||||
@@ -421,6 +466,7 @@ under different URLs. The following example shows how to do so:
|
||||
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Inject target handlers and the handler mapping for controllers.
|
||||
<2> Prepare the request mapping metadata.
|
||||
<3> Get the handler method.
|
||||
|
||||
@@ -12,8 +12,11 @@ a web component.
|
||||
To enable auto-detection of such `@Controller` beans, you can add component scanning to
|
||||
your Java configuration, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@Configuration
|
||||
@ComponentScan("org.example.web") // <1>
|
||||
@@ -22,6 +25,7 @@ your Java configuration, as the following example shows:
|
||||
// ...
|
||||
}
|
||||
----
|
||||
======
|
||||
<1> Scan the `org.example.web` package.
|
||||
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
|
||||
Reference in New Issue
Block a user