Migrate to Asciidoctor Tabs

This commit is contained in:
Rob Winch
2023-04-20 16:21:36 -05:00
committed by rstoyanchev
parent 71154fd16b
commit 39146f9066
243 changed files with 7124 additions and 1779 deletions

View File

@@ -84,8 +84,11 @@ annotation enables cross-origin requests on annotated controller methods, as the
following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RestController
@RequestMapping("/account")
@@ -103,8 +106,10 @@ following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RestController
@RequestMapping("/account")
@@ -122,6 +127,7 @@ following example shows:
}
}
----
======
--
By default, `@CrossOrigin` allows:
@@ -142,8 +148,11 @@ the `allowOriginPatterns` property may be used to match to a dynamic set of orig
The following example specifies a certain domain and sets `maxAge` to an hour:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@@ -161,8 +170,10 @@ The following example specifies a certain domain and sets `maxAge` to an hour:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@CrossOrigin("https://domain2.com", maxAge = 3600)
@RestController
@@ -180,14 +191,18 @@ The following example specifies a certain domain and sets `maxAge` to an hour:
}
}
----
======
--
You can use `@CrossOrigin` at both the class and the method level,
as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@CrossOrigin(maxAge = 3600) // <1>
@RestController
@@ -206,6 +221,7 @@ as the following example shows:
}
}
----
======
<1> Using `@CrossOrigin` at the class level.
<2> Using `@CrossOrigin` at the method level.
@@ -261,8 +277,11 @@ the `allowOriginPatterns` property may be used to match to a dynamic set of orig
To enable CORS in the WebFlux Java configuration, you can use the `CorsRegistry` callback,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -282,8 +301,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -302,6 +323,7 @@ as the following example shows:
}
}
----
======
@@ -321,8 +343,11 @@ CORS.
To configure the filter, you can declare a `CorsWebFilter` bean and pass a
`CorsConfigurationSource` to its constructor, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Bean
CorsWebFilter corsFilter() {
@@ -343,8 +368,10 @@ To configure the filter, you can declare a `CorsWebFilter` bean and pass a
return new CorsWebFilter(source);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Bean
fun corsFilter(): CorsWebFilter {
@@ -365,3 +392,4 @@ To configure the filter, you can declare a `CorsWebFilter` bean and pass a
return CorsWebFilter(source)
}
----
======

View File

@@ -30,8 +30,11 @@ difference that router functions provide not just data, but also behavior.
`RouterFunctions.route()` provides a router builder that facilitates the creation of routers,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
@@ -64,6 +67,7 @@ as the following example shows:
}
}
----
======
<1> Create router using `route()`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -133,80 +137,113 @@ while access to the body is provided through the `body` methods.
The following example extracts the request body to a `Mono<String>`:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Mono<String> string = request.bodyToMono(String.class);
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val string = request.awaitBody<String>()
----
======
The following example extracts the body to a `Flux<Person>` (or a `Flow<Person>` in Kotlin),
where `Person` objects are decoded from some serialized form, such as JSON or XML:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Flux<Person> people = request.bodyToFlux(Person.class);
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val people = request.bodyToFlow<Person>()
----
======
The preceding examples are shortcuts that use the more general `ServerRequest.body(BodyExtractor)`,
which accepts the `BodyExtractor` functional strategy interface. The utility class
`BodyExtractors` provides access to a number of instances. For example, the preceding examples can
also be written as follows:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Mono<String> string = request.body(BodyExtractors.toMono(String.class));
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val string = request.body(BodyExtractors.toMono(String::class.java)).awaitSingle()
val people = request.body(BodyExtractors.toFlux(Person::class.java)).asFlow()
----
======
The following example shows how to access form data:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Mono<MultiValueMap<String, String>> map = request.formData();
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val map = request.awaitFormData()
----
======
The following example shows how to access multipart data as a map:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Mono<MultiValueMap<String, Part>> map = request.multipartData();
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val map = request.awaitMultipartData()
----
======
The following example shows how to access multipart data, one at a time, in streaming fashion:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Flux<PartEvent> allPartEvents = request.bodyToFlux(PartEvent.class);
allPartsEvents.windowUntil(PartEvent::isLast)
@@ -232,8 +269,9 @@ allPartsEvents.windowUntil(PartEvent::isLast)
}));
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val parts = request.bodyToFlux<PartEvent>()
allPartsEvents.windowUntil(PartEvent::isLast)
@@ -258,6 +296,7 @@ allPartsEvents.windowUntil(PartEvent::isLast)
}
}
----
======
Note that the body contents of the `PartEvent` objects must be completely consumed, relayed, or released to avoid memory leaks.
@@ -269,47 +308,65 @@ a `build` method to create it. You can use the builder to set the response statu
headers, or to provide a body. The following example creates a 200 (OK) response with JSON
content:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Mono<Person> person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val person: Person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(person)
----
======
The following example shows how to build a 201 (CREATED) response with a `Location` header and no body:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
URI location = ...
ServerResponse.created(location).build();
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val location: URI = ...
ServerResponse.created(location).build()
----
======
Depending on the codec used, it is possible to pass hint parameters to customize how the
body is serialized or deserialized. For example, to specify a https://www.baeldung.com/jackson-json-view-annotation[Jackson JSON view]:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...);
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView::class.java).body(...)
----
======
[[webflux-fn-handler-classes]]
@@ -318,17 +375,23 @@ ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView::cla
We can write a handler function as a lambda, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().bodyValue("Hello World");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val helloWorld = HandlerFunction<ServerResponse> { ServerResponse.ok().bodyValue("Hello World") }
----
======
--
That is convenient, but in an application we need multiple functions, and multiple inline
@@ -338,8 +401,11 @@ has a similar role as `@Controller` in an annotation-based application.
For example, the following class exposes a reactive `Person` repository:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@@ -370,6 +436,7 @@ public class PersonHandler {
}
}
----
======
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
@@ -422,8 +489,11 @@ A functional endpoint can use Spring's xref:web/webmvc/mvc-config/validation.ado
apply validation to the request body. For example, given a custom Spring
xref:web/webmvc/mvc-config/validation.adoc[Validator] implementation for a `Person`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class PersonHandler {
@@ -445,6 +515,7 @@ xref:web/webmvc/mvc-config/validation.adoc[Validator] implementation for a `Pers
}
}
----
======
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
@@ -515,15 +586,20 @@ and so on.
The following example uses a request predicate to create a constraint based on the `Accept`
header:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().bodyValue("Hello World")).build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val route = coRouter {
GET("/hello-world", accept(TEXT_PLAIN)) {
@@ -531,6 +607,7 @@ header:
}
}
----
======
You can compose multiple request predicates together by using:
@@ -568,8 +645,11 @@ There are also other ways to compose multiple router functions together:
The following example shows the composition of four routes:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
@@ -586,6 +666,7 @@ RouterFunction<ServerResponse> route = route()
.add(otherRoute) // <4>
.build();
----
======
<1> pass:q[`GET /person/{id}`] with an `Accept` header that matches JSON is routed to
`PersonHandler.getPerson`
<2> `GET /person` with an `Accept` header that matches JSON is routed to
@@ -630,8 +711,11 @@ this duplication by using a type-level `@RequestMapping` annotation that maps to
router function builder. For instance, the last few lines of the example above can be
improved in the following way by using nested routes:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = route()
.path("/person", builder -> builder // <1>
@@ -640,6 +724,7 @@ RouterFunction<ServerResponse> route = route()
.POST(handler::createPerson))
.build();
----
======
<1> Note that second parameter of `path` is a consumer that takes the router builder.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -660,8 +745,11 @@ the `nest` method on the builder.
The above still contains some duplication in the form of the shared `Accept`-header predicate.
We can further improve by using the `nest` method together with `accept`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
@@ -671,8 +759,10 @@ We can further improve by using the `nest` method together with `accept`:
.POST(handler::createPerson))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val route = coRouter {
"/person".nest {
@@ -684,6 +774,7 @@ We can further improve by using the `nest` method together with `accept`:
}
}
----
======
[[webflux-fn-running]]
@@ -721,8 +812,11 @@ starter.
The following example shows a WebFlux Java configuration (see
xref:web/webflux/dispatcher-handler.adoc[DispatcherHandler] for how to run it):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -756,8 +850,10 @@ xref:web/webflux/dispatcher-handler.adoc[DispatcherHandler] for how to run it):
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -788,6 +884,7 @@ xref:web/webflux/dispatcher-handler.adoc[DispatcherHandler] for how to run it):
}
}
----
======
@@ -803,8 +900,11 @@ The filter will apply to all routes that are built by the builder.
This means that filters defined in nested routes do not apply to "top-level" routes.
For instance, consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
@@ -818,6 +918,7 @@ For instance, consider the following example:
.after((request, response) -> logResponse(response)) // <2>
.build();
----
======
<1> The `before` filter that adds a custom request header is only applied to the two GET routes.
<2> The `after` filter that logs the response is applied to all routes, including the nested ones.
@@ -853,8 +954,11 @@ Now we can add a simple security filter to our route, assuming that we have a `S
can determine whether a particular path is allowed.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
SecurityManager securityManager = ...
@@ -874,8 +978,10 @@ The following example shows how to do so:
})
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val securityManager: SecurityManager = ...
@@ -895,6 +1001,7 @@ The following example shows how to do so:
}
}
----
======
The preceding example demonstrates that invoking the `next.handle(ServerRequest)` is optional.
We only let the handler function be run when access is allowed.

View File

@@ -47,8 +47,11 @@ integration for using Spring WebFlux with FreeMarker templates.
The following example shows how to configure FreeMarker as a view technology:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -69,8 +72,10 @@ The following example shows how to configure FreeMarker as a view technology:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -88,6 +93,7 @@ The following example shows how to configure FreeMarker as a view technology:
}
}
----
======
Your templates need to be stored in the directory specified by the `FreeMarkerConfigurer`,
shown in the preceding example. Given the preceding configuration, if your controller
@@ -106,8 +112,11 @@ properties on the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property
a `java.util.Properties` object, and the `freemarkerVariables` property requires a
`java.util.Map`. The following example shows how to use a `FreeMarkerConfigurer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -127,8 +136,10 @@ a `java.util.Properties` object, and the `freemarkerVariables` property requires
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -143,6 +154,7 @@ a `java.util.Properties` object, and the `freemarkerVariables` property requires
}
}
----
======
See the FreeMarker documentation for details of settings and variables as they apply to
the `Configuration` object.
@@ -245,8 +257,11 @@ You can declare a `ScriptTemplateConfigurer` bean to specify the script engine t
the script files to load, what function to call to render templates, and so on.
The following example uses Mustache templates and the Nashorn JavaScript engine:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -268,8 +283,10 @@ The following example uses Mustache templates and the Nashorn JavaScript engine:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -288,6 +305,7 @@ The following example uses Mustache templates and the Nashorn JavaScript engine:
}
}
----
======
The `render` function is called with the following parameters:
@@ -307,8 +325,11 @@ https://en.wikipedia.org/wiki/Polyfill[polyfill] in order to emulate some
browser facilities not available in the server-side script engine.
The following example shows how to set a custom render function:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -330,8 +351,10 @@ The following example shows how to set a custom render function:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -350,6 +373,7 @@ The following example shows how to set a custom render function:
}
}
----
======
NOTE: Setting the `sharedEngine` property to `false` is required when using non-thread-safe
script engines with templating libraries not designed for concurrency, such as Handlebars or

View File

@@ -5,8 +5,11 @@ You can add attributes to a request. This is convenient if you want to pass info
through the filter chain and influence the behavior of filters for a given request.
For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient client = WebClient.builder()
.filter((request, next) -> {
@@ -22,8 +25,10 @@ For example:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = WebClient.builder()
.filter { request, _ ->
@@ -37,6 +42,7 @@ For example:
.retrieve()
.awaitBody<Unit>()
----
======
Note that you can configure a `defaultRequest` callback globally at the
`WebClient.Builder` level which lets you insert attributes into all requests,

View File

@@ -4,8 +4,11 @@
The request body can be encoded from any asynchronous type handled by `ReactiveAdapterRegistry`,
like `Mono` or Kotlin Coroutines `Deferred` as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Person> personMono = ... ;
@@ -16,8 +19,10 @@ like `Mono` or Kotlin Coroutines `Deferred` as the following example shows:
.retrieve()
.bodyToMono(Void.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val personDeferred: Deferred<Person> = ...
@@ -28,11 +33,15 @@ like `Mono` or Kotlin Coroutines `Deferred` as the following example shows:
.retrieve()
.awaitBody<Unit>()
----
======
You can also have a stream of objects be encoded, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Flux<Person> personFlux = ... ;
@@ -43,8 +52,10 @@ You can also have a stream of objects be encoded, as the following example shows
.retrieve()
.bodyToMono(Void.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val people: Flow<Person> = ...
@@ -55,12 +66,16 @@ You can also have a stream of objects be encoded, as the following example shows
.retrieve()
.awaitBody<Unit>()
----
======
Alternatively, if you have the actual value, you can use the `bodyValue` shortcut method,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Person person = ... ;
@@ -71,8 +86,10 @@ as the following example shows:
.retrieve()
.bodyToMono(Void.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val person: Person = ...
@@ -83,6 +100,7 @@ as the following example shows:
.retrieve()
.awaitBody<Unit>()
----
======
@@ -93,8 +111,11 @@ To send form data, you can provide a `MultiValueMap<String, String>` as the body
content is automatically set to `application/x-www-form-urlencoded` by the
`FormHttpMessageWriter`. The following example shows how to use `MultiValueMap<String, String>`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
MultiValueMap<String, String> formData = ... ;
@@ -104,8 +125,10 @@ content is automatically set to `application/x-www-form-urlencoded` by the
.retrieve()
.bodyToMono(Void.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val formData: MultiValueMap<String, String> = ...
@@ -115,11 +138,15 @@ content is automatically set to `application/x-www-form-urlencoded` by the
.retrieve()
.awaitBody<Unit>()
----
======
You can also supply form data in-line by using `BodyInserters`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.web.reactive.function.BodyInserters.*;
@@ -129,8 +156,10 @@ You can also supply form data in-line by using `BodyInserters`, as the following
.retrieve()
.bodyToMono(Void.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.reactive.function.BodyInserters.*
@@ -140,6 +169,7 @@ You can also supply form data in-line by using `BodyInserters`, as the following
.retrieve()
.awaitBody<Unit>()
----
======
@@ -151,8 +181,11 @@ either `Object` instances that represent part content or `HttpEntity` instances
headers for a part. `MultipartBodyBuilder` provides a convenient API to prepare a
multipart request. The following example shows how to create a `MultiValueMap<String, ?>`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("fieldPart", "fieldValue");
@@ -162,8 +195,10 @@ multipart request. The following example shows how to create a `MultiValueMap<St
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val builder = MultipartBodyBuilder().apply {
part("fieldPart", "fieldValue")
@@ -174,6 +209,7 @@ multipart request. The following example shows how to create a `MultiValueMap<St
val parts = builder.build()
----
======
In most cases, you do not have to specify the `Content-Type` for each part. The content
type is determined automatically based on the `HttpMessageWriter` chosen to serialize it
@@ -184,8 +220,11 @@ builder `part` methods.
Once a `MultiValueMap` is prepared, the easiest way to pass it to the `WebClient` is
through the `body` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
MultipartBodyBuilder builder = ...;
@@ -195,8 +234,10 @@ through the `body` method, as the following example shows:
.retrieve()
.bodyToMono(Void.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val builder: MultipartBodyBuilder = ...
@@ -206,6 +247,7 @@ through the `body` method, as the following example shows:
.retrieve()
.awaitBody<Unit>()
----
======
If the `MultiValueMap` contains at least one non-`String` value, which could also
represent regular form data (that is, `application/x-www-form-urlencoded`), you need not
@@ -215,8 +257,11 @@ set the `Content-Type` to `multipart/form-data`. This is always the case when us
As an alternative to `MultipartBodyBuilder`, you can also provide multipart content,
inline-style, through the built-in `BodyInserters`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.web.reactive.function.BodyInserters.*;
@@ -226,8 +271,10 @@ inline-style, through the built-in `BodyInserters`, as the following example sho
.retrieve()
.bodyToMono(Void.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.reactive.function.BodyInserters.*
@@ -237,6 +284,7 @@ inline-style, through the built-in `BodyInserters`, as the following example sho
.retrieve()
.awaitBody<Unit>()
----
======
[[partevent]]
=== `PartEvent`
@@ -252,8 +300,11 @@ the `WebClient`.
For instance, this sample will POST a multipart form containing a form field and a file.
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Resource resource = ...
Mono<String> result = webClient
@@ -266,8 +317,10 @@ Mono<String> result = webClient
.retrieve()
.bodyToMono(String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
var resource: Resource = ...
var result: Mono<String> = webClient
@@ -282,6 +335,7 @@ var result: Mono<String> = webClient
.retrieve()
.bodyToMono()
----
======
On the server side, `PartEvent` objects that are received via `@RequestBody` or
`ServerRequest::bodyToFlux(PartEvent.class)` can be relayed to another service

View File

@@ -21,26 +21,35 @@ You can also use `WebClient.builder()` with further options:
For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient client = WebClient.builder()
.codecs(configurer -> ... )
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val webClient = WebClient.builder()
.codecs { configurer -> ... }
.build()
----
======
Once built, a `WebClient` is immutable. However, you can clone it and build a
modified copy as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient client1 = WebClient.builder()
.filter(filterA).filter(filterB).build();
@@ -52,8 +61,10 @@ modified copy as follows:
// client2 has filterA, filterB, filterC, filterD
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client1 = WebClient.builder()
.filter(filterA).filter(filterB).build()
@@ -65,6 +76,7 @@ modified copy as follows:
// client2 has filterA, filterB, filterC, filterD
----
======
[[webflux-client-builder-maxinmemorysize]]
== MaxInMemorySize
@@ -79,20 +91,26 @@ org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on m
To change the limit for default codecs, use the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient webClient = WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val webClient = WebClient.builder()
.codecs { configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024) }
.build()
----
======
@@ -101,8 +119,11 @@ To change the limit for default codecs, use the following:
To customize Reactor Netty settings, provide a pre-configured `HttpClient`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpClient httpClient = HttpClient.create().secure(sslSpec -> ...);
@@ -110,8 +131,10 @@ To customize Reactor Netty settings, provide a pre-configured `HttpClient`:
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val httpClient = HttpClient.create().secure { ... }
@@ -119,6 +142,7 @@ To customize Reactor Netty settings, provide a pre-configured `HttpClient`:
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()
----
======
[[webflux-client-builder-reactor-resources]]
@@ -137,20 +161,26 @@ Netty global resources are shut down when the Spring `ApplicationContext` is clo
as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Bean
public ReactorResourceFactory reactorResourceFactory() {
return new ReactorResourceFactory();
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Bean
fun reactorResourceFactory() = ReactorResourceFactory()
----
======
--
You can also choose not to participate in the global Reactor Netty resources. However,
@@ -158,8 +188,11 @@ in this mode, the burden is on you to ensure that all Reactor Netty client and s
instances use shared resources, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Bean
public ReactorResourceFactory resourceFactory() {
@@ -181,6 +214,7 @@ instances use shared resources, as the following example shows:
return WebClient.builder().clientConnector(connector).build(); // <3>
}
----
======
<1> Create resources independent of global ones.
<2> Use the `ReactorClientHttpConnector` constructor with resource factory.
<3> Plug the connector into the `WebClient.Builder`.
@@ -216,8 +250,11 @@ instances use shared resources, as the following example shows:
To configure a connection timeout:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import io.netty.channel.ChannelOption;
@@ -228,8 +265,10 @@ To configure a connection timeout:
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import io.netty.channel.ChannelOption
@@ -240,11 +279,15 @@ To configure a connection timeout:
.clientConnector(ReactorClientHttpConnector(httpClient))
.build();
----
======
To configure a read or write timeout:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
@@ -257,8 +300,10 @@ To configure a read or write timeout:
// Create WebClient...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler
@@ -271,30 +316,40 @@ To configure a read or write timeout:
// Create WebClient...
----
======
To configure a response timeout for all requests:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// Create WebClient...
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// Create WebClient...
----
======
To configure a response timeout for a specific request:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient.create().get()
.uri("https://example.org/path")
@@ -305,8 +360,10 @@ To configure a response timeout for a specific request:
.retrieve()
.bodyToMono(String.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
WebClient.create().get()
.uri("https://example.org/path")
@@ -317,6 +374,7 @@ To configure a response timeout for a specific request:
.retrieve()
.bodyToMono(String::class.java)
----
======
@@ -325,8 +383,11 @@ To configure a response timeout for a specific request:
The following example shows how to customize the JDK `HttpClient`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpClient httpClient = HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
@@ -339,8 +400,9 @@ The following example shows how to customize the JDK `HttpClient`:
WebClient webClient = WebClient.builder().clientConnector(connector).build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val httpClient = HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
@@ -351,6 +413,7 @@ The following example shows how to customize the JDK `HttpClient`:
val webClient = WebClient.builder().clientConnector(connector).build()
----
======
@@ -360,8 +423,11 @@ The following example shows how to customize the JDK `HttpClient`:
The following example shows how to customize Jetty `HttpClient` settings:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpClient httpClient = new HttpClient();
httpClient.setCookieStore(...);
@@ -370,8 +436,10 @@ The following example shows how to customize Jetty `HttpClient` settings:
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val httpClient = HttpClient()
httpClient.cookieStore = ...
@@ -380,6 +448,7 @@ The following example shows how to customize Jetty `HttpClient` settings:
.clientConnector(JettyClientHttpConnector(httpClient))
.build();
----
======
--
By default, `HttpClient` creates its own resources (`Executor`, `ByteBufferPool`, `Scheduler`),
@@ -391,8 +460,11 @@ declaring a Spring-managed bean of type `JettyResourceFactory`, as the following
shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Bean
public JettyResourceFactory resourceFactory() {
@@ -411,6 +483,7 @@ shows:
return WebClient.builder().clientConnector(connector).build(); <2>
}
----
======
<1> Use the `JettyClientHttpConnector` constructor with resource factory.
<2> Plug the connector into the `WebClient.Builder`.
@@ -442,8 +515,11 @@ shows:
The following example shows how to customize Apache HttpComponents `HttpClient` settings:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
clientBuilder.setDefaultRequestConfig(...);
@@ -453,8 +529,10 @@ The following example shows how to customize Apache HttpComponents `HttpClient`
WebClient webClient = WebClient.builder().clientConnector(connector).build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = HttpAsyncClients.custom().apply {
setDefaultRequestConfig(...)
@@ -462,5 +540,6 @@ The following example shows how to customize Apache HttpComponents `HttpClient`
val connector = HttpComponentsClientHttpConnector(client)
val webClient = WebClient.builder().clientConnector(connector).build()
----
======

View File

@@ -9,8 +9,11 @@ e.g. via `concatMap`, then you'll need to use the Reactor `Context`.
The Reactor `Context` needs to be populated at the end of a reactive chain in order to
apply to all operations. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient client = WebClient.builder()
.filter((request, next) ->
@@ -28,6 +31,7 @@ apply to all operations. For example:
})
.contextWrite(context -> context.put("foo", ...));
----
======

View File

@@ -5,8 +5,11 @@ The `exchangeToMono()` and `exchangeToFlux()` methods (or `awaitExchange { }` an
are useful for more advanced cases that require more control, such as to decode the response differently
depending on the response status:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Person> entityMono = client.get()
.uri("/persons/1")
@@ -21,8 +24,10 @@ depending on the response status:
}
});
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val entity = client.get()
.uri("/persons/1")
@@ -36,6 +41,7 @@ val entity = client.get()
}
}
----
======
When using the above, after the returned `Mono` or `Flux` completes, the response body
is checked and if not consumed it is released to prevent memory and connection leaks.

View File

@@ -4,8 +4,11 @@
You can register a client filter (`ExchangeFilterFunction`) through the `WebClient.Builder`
in order to intercept and modify requests, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient client = WebClient.builder()
.filter((request, next) -> {
@@ -18,8 +21,10 @@ in order to intercept and modify requests, as the following example shows:
})
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = WebClient.builder()
.filter { request, next ->
@@ -32,12 +37,16 @@ in order to intercept and modify requests, as the following example shows:
}
.build()
----
======
This can be used for cross-cutting concerns, such as authentication. The following example uses
a filter for basic authentication through a static factory method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
@@ -45,8 +54,10 @@ a filter for basic authentication through a static factory method:
.filter(basicAuthentication("user", "password"))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication
@@ -54,12 +65,16 @@ a filter for basic authentication through a static factory method:
.filter(basicAuthentication("user", "password"))
.build()
----
======
Filters can be added or removed by mutating an existing `WebClient` instance, resulting
in a new `WebClient` instance that does not affect the original one. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.web.reactive.function.client.ExchangeFilterFunctions.basicAuthentication;
@@ -69,13 +84,16 @@ in a new `WebClient` instance that does not affect the original one. For example
})
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = webClient.mutate()
.filters { it.add(0, basicAuthentication("user", "password")) }
.build()
----
======
`WebClient` is a thin facade around the chain of filters followed by an
`ExchangeFunction`. It provides a workflow to make requests, to encode to and from higher
@@ -85,8 +103,11 @@ its content or to otherwise propagate it downstream to the `WebClient` which wil
the same. Below is a filter that handles the `UNAUTHORIZED` status code but ensures that
any response content, whether expected or not, is released:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public ExchangeFilterFunction renewTokenFilter() {
return (request, next) -> next.exchange(request).flatMap(response -> {
@@ -103,8 +124,10 @@ any response content, whether expected or not, is released:
});
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun renewTokenFilter(): ExchangeFilterFunction? {
return ExchangeFilterFunction { request: ClientRequest?, next: ExchangeFunction ->
@@ -123,6 +146,7 @@ any response content, whether expected or not, is released:
}
}
----
======

View File

@@ -3,8 +3,11 @@
The `retrieve()` method can be used to declare how to extract the response. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient client = WebClient.create("https://example.org");
@@ -13,8 +16,10 @@ The `retrieve()` method can be used to declare how to extract the response. For
.retrieve()
.toEntity(Person.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = WebClient.create("https://example.org")
@@ -23,11 +28,15 @@ The `retrieve()` method can be used to declare how to extract the response. For
.retrieve()
.toEntity<Person>().awaitSingle()
----
======
Or to get only the body:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient client = WebClient.create("https://example.org");
@@ -36,8 +45,10 @@ Or to get only the body:
.retrieve()
.bodyToMono(Person.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = WebClient.create("https://example.org")
@@ -46,32 +57,42 @@ Or to get only the body:
.retrieve()
.awaitBody<Person>()
----
======
To get a stream of decoded objects:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Flux<Quote> result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(Quote.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlow<Quote>()
----
======
By default, 4xx or 5xx responses result in an `WebClientResponseException`, including
sub-classes for specific HTTP status codes. To customize the handling of error
responses, use `onStatus` handlers as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
@@ -80,8 +101,10 @@ responses, use `onStatus` handlers as follows:
.onStatus(HttpStatus::is5xxServerError, response -> ...)
.bodyToMono(Person.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
@@ -90,6 +113,7 @@ responses, use `onStatus` handlers as follows:
.onStatus(HttpStatus::is5xxServerError) { ... }
.awaitBody<Person>()
----
======

View File

@@ -3,8 +3,11 @@
`WebClient` can be used in synchronous style by blocking at the end for the result:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Person person = client.get().uri("/person/{id}", i).retrieve()
.bodyToMono(Person.class)
@@ -15,8 +18,10 @@
.collectList()
.block();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val person = runBlocking {
client.get().uri("/person/{id}", i).retrieve()
@@ -29,12 +34,16 @@
.toList()
}
----
======
However if multiple calls need to be made, it's more efficient to avoid blocking on each
response individually, and instead wait for the combined result:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<Person> personMono = client.get().uri("/person/{id}", personId)
.retrieve().bodyToMono(Person.class);
@@ -50,8 +59,10 @@ response individually, and instead wait for the combined result:
})
.block();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val data = runBlocking {
val personDeferred = async {
@@ -67,6 +78,7 @@ response individually, and instead wait for the combined result:
mapOf("person" to personDeferred.await(), "hobbies" to hobbiesDeferred.await())
}
----
======
The above is merely one example. There are lots of other patterns and operators for putting
together a reactive pipeline that makes many remote calls, potentially some nested,

View File

@@ -23,8 +23,11 @@ server-side applications that handle WebSocket messages.
To create a WebSocket server, you can first create a `WebSocketHandler`.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketSession;
@@ -37,8 +40,10 @@ The following example shows how to do so:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.reactive.socket.WebSocketHandler
import org.springframework.web.reactive.socket.WebSocketSession
@@ -50,11 +55,15 @@ The following example shows how to do so:
}
}
----
======
Then you can map it to a URL:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
class WebConfig {
@@ -69,8 +78,10 @@ Then you can map it to a URL:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class WebConfig {
@@ -84,13 +95,17 @@ Then you can map it to a URL:
}
}
----
======
If using the xref:web/webflux/dispatcher-handler.adoc#webflux-framework-config[WebFlux Config] there is nothing
further to do, or otherwise if not using the WebFlux config you'll need to declare a
`WebSocketHandlerAdapter` as shown below:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
class WebConfig {
@@ -103,8 +118,10 @@ further to do, or otherwise if not using the WebFlux config you'll need to decla
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class WebConfig {
@@ -115,6 +132,7 @@ further to do, or otherwise if not using the WebFlux config you'll need to decla
fun handlerAdapter() = WebSocketHandlerAdapter()
}
----
======
@@ -155,8 +173,11 @@ receives a cancellation signal.
The most basic implementation of a handler is one that handles the inbound stream. The
following example shows such an implementation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class ExampleHandler implements WebSocketHandler {
@@ -173,6 +194,7 @@ following example shows such an implementation:
}
}
----
======
<1> Access the stream of inbound messages.
<2> Do something with each message.
<3> Perform nested asynchronous operations that use the message content.
@@ -208,8 +230,11 @@ xref:core/databuffer-codec.adoc[Data Buffers and Codecs].
The following implementation combines the inbound and outbound streams:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class ExampleHandler implements WebSocketHandler {
@@ -229,6 +254,7 @@ The following implementation combines the inbound and outbound streams:
}
}
----
======
<1> Handle the inbound message stream.
<2> Create the outbound message, producing a combined flow.
<3> Return a `Mono<Void>` that does not complete while we continue to receive.
@@ -261,8 +287,11 @@ The following implementation combines the inbound and outbound streams:
Inbound and outbound streams can be independent and be joined only for completion,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class ExampleHandler implements WebSocketHandler {
@@ -285,6 +314,7 @@ as the following example shows:
}
}
----
======
<1> Handle inbound message stream.
<2> Send outgoing messages.
<3> Join the streams and return a `Mono<Void>` that completes when either stream ends.
@@ -359,8 +389,11 @@ such properties as shown in the corresponding section of the
xref:web/webflux/config.adoc#webflux-config-websocket-service[WebFlux Config], or otherwise if
not using the WebFlux config, use the below:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
class WebConfig {
@@ -378,8 +411,10 @@ not using the WebFlux config, use the below:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class WebConfig {
@@ -397,6 +432,7 @@ not using the WebFlux config, use the below:
}
}
----
======
Check the upgrade strategy for your server to see what options are available. Currently,
only Tomcat and Jetty expose such options.
@@ -429,8 +465,11 @@ API to suspend receiving messages for back pressure.
To start a WebSocket session, you can create an instance of the client and use its `execute`
methods:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebSocketClient client = new ReactorNettyWebSocketClient();
@@ -440,8 +479,10 @@ methods:
.doOnNext(System.out::println)
.then());
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val client = ReactorNettyWebSocketClient()
@@ -452,6 +493,7 @@ methods:
.then()
}
----
======
Some clients, such as Jetty, implement `Lifecycle` and need to be stopped and started
before you can use them. All clients have constructor options related to configuration

View File

@@ -30,8 +30,11 @@ While https://tools.ietf.org/html/rfc7234#section-5.2.2[RFC 7234] describes all
directives for the `Cache-Control` response header, the `CacheControl` type takes a
use case-oriented approach that focuses on the common scenarios, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// Cache for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);
@@ -45,8 +48,9 @@ use case-oriented approach that focuses on the common scenarios, as the followin
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Cache for an hour - "Cache-Control: max-age=3600"
val ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS)
@@ -60,6 +64,7 @@ use case-oriented approach that focuses on the common scenarios, as the followin
val ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic()
----
======
@@ -73,8 +78,11 @@ against conditional request headers. A controller can add an `ETag` and `Cache-C
settings to a `ResponseEntity`, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {
@@ -90,8 +98,9 @@ settings to a `ResponseEntity`, as the following example shows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/book/{id}")
fun showBook(@PathVariable id: Long): ResponseEntity<Book> {
@@ -106,6 +115,7 @@ settings to a `ResponseEntity`, as the following example shows:
.body(book)
}
----
======
--
The preceding example sends a 304 (NOT_MODIFIED) response with an empty body if the comparison
@@ -116,8 +126,11 @@ You can also make the check against conditional request headers in the controlle
as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RequestMapping
public String myHandleMethod(ServerWebExchange exchange, Model model) {
@@ -132,6 +145,7 @@ as the following example shows:
return "myViewName";
}
----
======
<1> Application-specific calculation.
<2> Response has been set to 304 (NOT_MODIFIED). No further processing.
<3> Continue with request processing.

View File

@@ -22,8 +22,11 @@ xref:web/webflux/config.adoc#webflux-config-advanced-java[Advanced Configuration
You can use the `@EnableWebFlux` annotation in your Java config, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -31,13 +34,15 @@ You can use the `@EnableWebFlux` annotation in your Java config, as the followin
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
class WebConfig
----
======
The preceding example registers a number of Spring WebFlux
xref:web/webflux/dispatcher-handler.adoc#webflux-special-bean-types[infrastructure beans] and adapts to dependencies
@@ -52,8 +57,11 @@ available on the classpath -- for JSON, XML, and others.
In your Java configuration, you can implement the `WebFluxConfigurer` interface,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -63,8 +71,9 @@ as the following example shows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -73,6 +82,7 @@ class WebConfig : WebFluxConfigurer {
// Implement configuration methods...
}
----
======
@@ -85,8 +95,11 @@ for customization via `@NumberFormat` and `@DateTimeFormat` on fields.
To register custom formatters and converters in Java config, use the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -99,8 +112,10 @@ To register custom formatters and converters in Java config, use the following:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -111,14 +126,18 @@ To register custom formatters and converters in Java config, use the following:
}
}
----
======
By default Spring WebFlux considers the request Locale when parsing and formatting date
values. This works for forms where dates are represented as Strings with "input" form
fields. For "date" and "time" form fields, however, browsers use a fixed format defined
in the HTML spec. For such cases date and time formatting can be customized as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -132,8 +151,10 @@ in the HTML spec. For such cases date and time formatting can be customized as f
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -146,6 +167,7 @@ in the HTML spec. For such cases date and time formatting can be customized as f
}
}
----
======
NOTE: See xref:core/validation/format.adoc#format-FormatterRegistrar-SPI[`FormatterRegistrar` SPI]
and the `FormattingConversionServiceFactoryBean` for more information on when to
@@ -165,8 +187,11 @@ is registered as a global xref:core/validation/validator.adoc[validator] for use
In your Java configuration, you can customize the global `Validator` instance,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -179,8 +204,10 @@ as the following example shows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -192,12 +219,16 @@ as the following example shows:
}
----
======
Note that you can also register `Validator` implementations locally,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class MyController {
@@ -209,8 +240,10 @@ as the following example shows:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class MyController {
@@ -221,6 +254,7 @@ as the following example shows:
}
}
----
======
TIP: If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and
@@ -238,8 +272,11 @@ but you can also enable a query parameter-based strategy.
The following example shows how to customize the requested content type resolution:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -251,8 +288,10 @@ The following example shows how to customize the requested content type resoluti
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -263,6 +302,7 @@ The following example shows how to customize the requested content type resoluti
}
}
----
======
@@ -272,8 +312,11 @@ The following example shows how to customize the requested content type resoluti
The following example shows how to customize how the request and response body are read and written:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -285,8 +328,10 @@ The following example shows how to customize how the request and response body a
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -297,6 +342,7 @@ The following example shows how to customize how the request and response body a
}
}
----
======
`ServerCodecConfigurer` provides a set of default readers and writers. You can use it to add
more readers and writers, customize the default ones, or replace the default ones completely.
@@ -323,8 +369,11 @@ It also automatically registers the following well-known modules if they are det
The following example shows how to configure view resolution:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -336,8 +385,10 @@ The following example shows how to configure view resolution:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -348,13 +399,17 @@ The following example shows how to configure view resolution:
}
}
----
======
The `ViewResolverRegistry` has shortcuts for view technologies with which the Spring Framework
integrates. The following example uses FreeMarker (which also requires configuring the
underlying FreeMarker view technology):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -376,8 +431,10 @@ underlying FreeMarker view technology):
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -395,11 +452,15 @@ underlying FreeMarker view technology):
}
}
----
======
You can also plug in any `ViewResolver` implementation, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -413,8 +474,10 @@ You can also plug in any `ViewResolver` implementation, as the following example
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -426,14 +489,18 @@ You can also plug in any `ViewResolver` implementation, as the following example
}
}
----
======
To support xref:web/webflux/dispatcher-handler.adoc#webflux-multiple-representations[Content Negotiation] and rendering other formats
through view resolution (besides HTML), you can configure one or more default views based
on the `HttpMessageWriterView` implementation, which accepts any of the available
xref:web/webflux/reactive-spring.adoc#webflux-codecs[Codecs] from `spring-web`. The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -451,8 +518,10 @@ xref:web/webflux/reactive-spring.adoc#webflux-codecs[Codecs] from `spring-web`.
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -469,6 +538,7 @@ xref:web/webflux/reactive-spring.adoc#webflux-codecs[Codecs] from `spring-web`.
// ...
}
----
======
See xref:web/webflux-view.adoc[View Technologies] for more on the view technologies that are integrated with Spring WebFlux.
@@ -488,8 +558,11 @@ and a reduction in HTTP requests made by the browser. The `Last-Modified` header
evaluated and, if present, a `304` status code is returned. The following listing shows
the example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -504,8 +577,10 @@ the example:
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -518,6 +593,7 @@ the example:
}
}
----
======
See also xref:web/webflux/caching.adoc#webflux-caching-static-resources[HTTP caching support for static resources].
@@ -533,8 +609,11 @@ JavaScript resources used with a module loader).
The following example shows how to use `VersionResourceResolver` in your Java configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -550,8 +629,10 @@ The following example shows how to use `VersionResourceResolver` in your Java co
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -566,6 +647,7 @@ The following example shows how to use `VersionResourceResolver` in your Java co
}
----
======
You can use `ResourceUrlProvider` to rewrite URLs and apply the full chain of resolvers and
transformers (for example, to insert versions). The WebFlux configuration provides a `ResourceUrlProvider`
@@ -606,8 +688,11 @@ You can customize options related to path matching. For details on the individua
{api-spring-framework}/web/reactive/config/PathMatchConfigurer.html[`PathMatchConfigurer`] javadoc.
The following example shows how to use `PathMatchConfigurer`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -621,8 +706,10 @@ The following example shows how to use `PathMatchConfigurer`:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -636,6 +723,7 @@ The following example shows how to use `PathMatchConfigurer`:
}
}
----
======
[TIP]
====
@@ -664,8 +752,11 @@ In some cases it may be necessary to create the `WebSocketHandlerAdapter` bean w
provided `WebSocketService` service which allows configuring WebSocket server properties.
For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -679,8 +770,10 @@ For example:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -695,6 +788,7 @@ For example:
}
}
----
======
@@ -713,8 +807,11 @@ For advanced mode, you can remove `@EnableWebFlux` and extend directly from
`DelegatingWebFluxConfiguration` instead of implementing `WebFluxConfigurer`,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class WebConfig extends DelegatingWebFluxConfiguration {
@@ -722,8 +819,10 @@ as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class WebConfig : DelegatingWebFluxConfiguration {
@@ -731,6 +830,7 @@ as the following example shows:
// ...
}
----
======
You can keep existing methods in `WebConfig`, but you can now also override bean declarations
from the base class and still have any number of other `WebMvcConfigurer` implementations on

View File

@@ -10,8 +10,11 @@ do not have to extend base classes nor implement specific interfaces.
The following listing shows a basic example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RestController
public class HelloController {
@@ -22,8 +25,10 @@ The following listing shows a basic example:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RestController
class HelloController {
@@ -32,6 +37,7 @@ The following listing shows a basic example:
fun handle() = "Hello WebFlux"
}
----
======
In the preceding example, the method returns a `String` to be written to the response body.

View File

@@ -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

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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>) {
// ...
}
----
======

View File

@@ -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.

View File

@@ -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]
}
----
======

View File

@@ -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

View File

@@ -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.

View File

@@ -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"]

View File

@@ -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>) {
// ...
}
----
======

View File

@@ -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.

View File

@@ -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"]

View File

@@ -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

View File

@@ -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

View File

@@ -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"]

View File

@@ -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.

View File

@@ -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
}
----
======

View File

@@ -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.

View File

@@ -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"]

View File

@@ -25,18 +25,24 @@ Spring configuration in a WebFlux application typically contains:
The configuration is given to `WebHttpHandlerBuilder` to build the processing chain,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
ApplicationContext context = ...
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val context: ApplicationContext = ...
val handler = WebHttpHandlerBuilder.applicationContext(context).build()
----
======
The resulting `HttpHandler` is ready for use with a xref:web/webflux/reactive-spring.adoc#webflux-httphandler[server adapter].

View File

@@ -84,42 +84,57 @@ https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-the-Spr
The code snippets below show using the `HttpHandler` adapters with each server API:
*Reactor Netty*
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bindNow();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val handler: HttpHandler = ...
val adapter = ReactorHttpHandlerAdapter(handler)
HttpServer.create().host(host).port(port).handle(adapter).bindNow()
----
======
*Undertow*
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val handler: HttpHandler = ...
val adapter = UndertowHttpHandlerAdapter(handler)
val server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build()
server.start()
----
======
*Tomcat*
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);
@@ -133,8 +148,10 @@ The code snippets below show using the `HttpHandler` adapters with each server A
server.setPort(port);
server.start();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val handler: HttpHandler = ...
val servlet = TomcatHttpHandlerAdapter(handler)
@@ -148,11 +165,15 @@ The code snippets below show using the `HttpHandler` adapters with each server A
server.setPort(port)
server.start()
----
======
*Jetty*
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);
@@ -168,8 +189,10 @@ The code snippets below show using the `HttpHandler` adapters with each server A
server.addConnector(connector);
server.start();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val handler: HttpHandler = ...
val servlet = JettyHttpHandlerAdapter(handler)
@@ -185,6 +208,7 @@ The code snippets below show using the `HttpHandler` adapters with each server A
server.addConnector(connector)
server.start()
----
======
*Servlet Container*
@@ -277,16 +301,22 @@ Spring ApplicationContext, or that can be registered directly with it:
`ServerWebExchange` exposes the following method for accessing form data:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<MultiValueMap<String, String>> getFormData();
----
Kotlin::
+
[source,Kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
suspend fun getFormData(): MultiValueMap<String, String>
----
======
The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse form data
(`application/x-www-form-urlencoded`) into a `MultiValueMap`. By default,
@@ -300,16 +330,22 @@ The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse
`ServerWebExchange` exposes the following method for accessing multipart data:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Mono<MultiValueMap<String, Part>> getMultipartData();
----
Kotlin::
+
[source,Kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
suspend fun getMultipartData(): MultiValueMap<String, Part>
----
======
The `DefaultServerWebExchange` uses the configured
`HttpMessageReader<MultiValueMap<String, Part>>` to parse `multipart/form-data`,
@@ -621,8 +657,11 @@ headers are masked by default and you must explicitly enable their logging in fu
The following example shows how to do so for server-side requests:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebFlux
@@ -634,8 +673,10 @@ The following example shows how to do so for server-side requests:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebFlux
@@ -646,11 +687,15 @@ The following example shows how to do so for server-side requests:
}
}
----
======
The following example shows how to do so for client-side requests:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
Consumer<ClientCodecConfigurer> consumer = configurer ->
configurer.defaultCodecs().enableLoggingRequestDetails(true);
@@ -659,8 +704,10 @@ The following example shows how to do so for client-side requests:
.exchangeStrategies(strategies -> strategies.codecs(consumer))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val consumer: (ClientCodecConfigurer) -> Unit = { configurer -> configurer.defaultCodecs().enableLoggingRequestDetails(true) }
@@ -668,6 +715,7 @@ The following example shows how to do so for client-side requests:
.exchangeStrategies({ strategies -> strategies.codecs(consumer) })
.build()
----
======
[[webflux-logging-appenders]]
@@ -693,8 +741,11 @@ or xref:web/webflux/reactive-spring.adoc#webflux-logging-sensitive-data[logging
The following example shows how to do so for client-side requests:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebClient webClient = WebClient.builder()
.codecs(configurer -> {
@@ -703,8 +754,10 @@ The following example shows how to do so for client-side requests:
})
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val webClient = WebClient.builder()
.codecs({ configurer ->
@@ -713,4 +766,5 @@ The following example shows how to do so for client-side requests:
})
.build()
----
======

View File

@@ -83,8 +83,11 @@ The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation enables cross-origin requests on annotated controller methods,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RestController
@RequestMapping("/account")
@@ -102,8 +105,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RestController
@RequestMapping("/account")
@@ -121,6 +126,7 @@ as the following example shows:
}
}
----
======
By default, `@CrossOrigin` allows:
@@ -139,8 +145,11 @@ the `allowOriginPatterns` property may be used to match to a dynamic set of orig
`@CrossOrigin` is supported at the class level, too, and is inherited by all methods,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@@ -158,8 +167,10 @@ public class AccountController {
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@CrossOrigin(origins = ["https://domain2.com"], maxAge = 3600)
@RestController
@@ -176,12 +187,16 @@ public class AccountController {
// ...
}
----
======
You can use `@CrossOrigin` at both the class level and the method level,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@CrossOrigin(maxAge = 3600)
@RestController
@@ -200,8 +215,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@CrossOrigin(maxAge = 3600)
@RestController
@@ -220,6 +237,7 @@ as the following example shows:
}
}
----
======
@@ -257,8 +275,11 @@ the `allowOriginPatterns` property may be used to match to a dynamic set of orig
To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -278,8 +299,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -298,6 +321,7 @@ as the following example shows:
}
}
----
======
@@ -341,8 +365,11 @@ CORS.
To configure the filter, pass a `CorsConfigurationSource` to its constructor, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
CorsConfiguration config = new CorsConfiguration();
@@ -359,8 +386,10 @@ following example shows:
CorsFilter filter = new CorsFilter(source);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
val config = CorsConfiguration()
@@ -377,3 +406,4 @@ following example shows:
val filter = CorsFilter(source)
----
======

View File

@@ -30,8 +30,11 @@ difference that router functions provide not just data, but also behavior.
`RouterFunctions.route()` provides a router builder that facilitates the creation of routers,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.servlet.function.RequestPredicates.*;
@@ -64,6 +67,7 @@ as the following example shows:
}
}
----
======
<1> Create router using `route()`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -125,44 +129,62 @@ while access to the body is provided through the `body` methods.
The following example extracts the request body to a `String`:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
String string = request.body(String.class);
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val string = request.body<String>()
----
======
The following example extracts the body to a `List<Person>`,
where `Person` objects are decoded from a serialized form, such as JSON or XML:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
List<Person> people = request.body(new ParameterizedTypeReference<List<Person>>() {});
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val people = request.body<Person>()
----
======
The following example shows how to access parameters:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
MultiValueMap<String, String> params = request.params();
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val map = request.params()
----
======
[[webmvc-fn-response]]
@@ -173,69 +195,94 @@ a `build` method to create it. You can use the builder to set the response statu
headers, or to provide a body. The following example creates a 200 (OK) response with JSON
content:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Person person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val person: Person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person)
----
======
The following example shows how to build a 201 (CREATED) response with a `Location` header and no body:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
URI location = ...
ServerResponse.created(location).build();
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val location: URI = ...
ServerResponse.created(location).build()
----
======
You can also use an asynchronous result as the body, in the form of a `CompletableFuture`,
`Publisher`, or any other type supported by the `ReactiveAdapterRegistry`. For instance:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Mono<Person> person = webClient.get().retrieve().bodyToMono(Person.class);
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
----
Kotlin::
+
[source,kotlin,role="secondary"]
.Kotlin
----
val person = webClient.get().retrieve().awaitBody<Person>()
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person)
----
======
If not just the body, but also the status or headers are based on an asynchronous type,
you can use the static `async` method on `ServerResponse`, which
accepts `CompletableFuture<ServerResponse>`, `Publisher<ServerResponse>`, or
any other asynchronous type supported by the `ReactiveAdapterRegistry`. For instance:
[tabs]
======
Java::
+
[source,java,role="primary"]
.Java
----
Mono<ServerResponse> asyncResponse = webClient.get().retrieve().bodyToMono(Person.class)
.map(p -> ServerResponse.ok().header("Name", p.name()).body(p));
ServerResponse.async(asyncResponse);
----
======
https://www.w3.org/TR/eventsource/[Server-Sent Events] can be provided via the
static `sse` method on `ServerResponse`. The builder provided by that method
allows you to send Strings, or other objects as JSON. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public RouterFunction<ServerResponse> sse() {
return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> {
@@ -258,8 +305,10 @@ allows you to send Strings, or other objects as JSON. For example:
// and done at some point
sseBuilder.complete();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
fun sse(): RouterFunction<ServerResponse> = router {
GET("/sse") { request -> ServerResponse.sse { sseBuilder ->
@@ -282,6 +331,7 @@ allows you to send Strings, or other objects as JSON. For example:
// and done at some point
sseBuilder.complete()
----
======
@@ -291,18 +341,24 @@ allows you to send Strings, or other objects as JSON. For example:
We can write a handler function as a lambda, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().body("Hello World");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val helloWorld: (ServerRequest) -> ServerResponse =
{ ServerResponse.ok().body("Hello World") }
----
======
--
That is convenient, but in an application we need multiple functions, and multiple inline
@@ -312,8 +368,11 @@ has a similar role as `@Controller` in an annotation-based application.
For example, the following class exposes a reactive `Person` repository:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@@ -350,6 +409,7 @@ public class PersonHandler {
}
----
======
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
@@ -397,8 +457,11 @@ A functional endpoint can use Spring's xref:web/webmvc/mvc-config/validation.ado
apply validation to the request body. For example, given a custom Spring
xref:web/webmvc/mvc-config/validation.adoc[Validator] implementation for a `Person`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class PersonHandler {
@@ -422,6 +485,7 @@ xref:web/webmvc/mvc-config/validation.adoc[Validator] implementation for a `Pers
}
}
----
======
<1> Create `Validator` instance.
<2> Apply validation.
<3> Raise exception for a 400 response.
@@ -492,15 +556,20 @@ and so on.
The following example uses a request predicate to create a constraint based on the `Accept`
header:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().body("Hello World")).build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.servlet.function.router
@@ -510,6 +579,7 @@ header:
}
}
----
======
You can compose multiple request predicates together by using:
@@ -547,8 +617,11 @@ There are also other ways to compose multiple router functions together:
The following example shows the composition of four routes:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.servlet.function.RequestPredicates.*;
@@ -565,6 +638,7 @@ The following example shows the composition of four routes:
.add(otherRoute) // <4>
.build();
----
======
<1> pass:q[`GET /person/{id}`] with an `Accept` header that matches JSON is routed to
`PersonHandler.getPerson`
<2> `GET /person` with an `Accept` header that matches JSON is routed to
@@ -611,8 +685,11 @@ When using annotations, you would remove this duplication by using a type-level
In WebMvc.fn, path predicates can be shared through the `path` method on the router function builder.
For instance, the last few lines of the example above can be improved in the following way by using nested routes:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = route()
.path("/person", builder -> builder // <1>
@@ -621,6 +698,7 @@ RouterFunction<ServerResponse> route = route()
.POST(handler::createPerson))
.build();
----
======
<1> Note that second parameter of `path` is a consumer that takes the router builder.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -643,8 +721,11 @@ the `nest` method on the builder.
The above still contains some duplication in the form of the shared `Accept`-header predicate.
We can further improve by using the `nest` method together with `accept`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
@@ -654,8 +735,10 @@ We can further improve by using the `nest` method together with `accept`:
.POST(handler::createPerson))
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.servlet.function.router
@@ -669,6 +752,7 @@ We can further improve by using the `nest` method together with `accept`:
}
}
----
======
[[webmvc-fn-running]]
@@ -693,8 +777,11 @@ starter.
The following example shows a WebFlux Java configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableMvc
@@ -728,8 +815,10 @@ The following example shows a WebFlux Java configuration:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableMvc
@@ -760,6 +849,7 @@ The following example shows a WebFlux Java configuration:
}
}
----
======
@@ -775,8 +865,11 @@ The filter will apply to all routes that are built by the builder.
This means that filters defined in nested routes do not apply to "top-level" routes.
For instance, consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
@@ -790,6 +883,7 @@ For instance, consider the following example:
.after((request, response) -> logResponse(response)) // <2>
.build();
----
======
<1> The `before` filter that adds a custom request header is only applied to the two GET routes.
<2> The `after` filter that logs the response is applied to all routes, including the nested ones.
@@ -827,8 +921,11 @@ Now we can add a simple security filter to our route, assuming that we have a `S
can determine whether a particular path is allowed.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
SecurityManager securityManager = ...
@@ -848,8 +945,10 @@ The following example shows how to do so:
})
.build();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.servlet.function.router
@@ -871,6 +970,7 @@ The following example shows how to do so:
}
}
----
======
The preceding example demonstrates that invoking the `next.handle(ServerRequest)` is optional.
We only let the handler function be run when access is allowed.

View File

@@ -32,8 +32,11 @@ A simple PDF view for a word list could extend
`org.springframework.web.servlet.view.document.AbstractPdfView` and implement the
`buildPdfDocument()` method, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class PdfWordList extends AbstractPdfView {
@@ -47,8 +50,10 @@ A simple PDF view for a word list could extend
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class PdfWordList : AbstractPdfView() {
@@ -62,6 +67,7 @@ A simple PDF view for a word list could extend
}
}
----
======
A controller can return such a view either from an external view definition
(referencing it by name) or as a `View` instance from the handler method.

View File

@@ -10,8 +10,11 @@ package `org.springframework.web.servlet.view.feed`.
optionally override the `buildFeedMetadata()` method (the default implementation is
empty). The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SampleContentAtomView extends AbstractAtomFeedView {
@@ -28,8 +31,10 @@ empty). The following example shows how to do so:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SampleContentAtomView : AbstractAtomFeedView() {
@@ -44,11 +49,15 @@ empty). The following example shows how to do so:
}
}
----
======
Similar requirements apply for implementing `AbstractRssFeedView`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class SampleContentRssView extends AbstractRssFeedView {
@@ -65,8 +74,10 @@ Similar requirements apply for implementing `AbstractRssFeedView`, as the follow
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class SampleContentRssView : AbstractRssFeedView() {
@@ -81,6 +92,7 @@ Similar requirements apply for implementing `AbstractRssFeedView`, as the follow
}
}
----
======

View File

@@ -15,8 +15,11 @@ integration for using Spring MVC with FreeMarker templates.
The following example shows how to configure FreeMarker as a view technology:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -37,8 +40,10 @@ The following example shows how to configure FreeMarker as a view technology:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -56,6 +61,7 @@ The following example shows how to configure FreeMarker as a view technology:
}
}
----
======
The following example shows how to configure the same in XML:
@@ -364,8 +370,11 @@ and a default value in the form backing object, the HTML resembles the following
If your application expects to handle cities by internal codes (for example), you can create the map of
codes with suitable keys, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
Map<String, String> cityMap = new LinkedHashMap<>();
@@ -378,8 +387,10 @@ codes with suitable keys, as the following example shows:
return model;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
protected fun referenceData(request: HttpServletRequest): Map<String, *> {
val cityMap = linkedMapOf(
@@ -390,6 +401,7 @@ codes with suitable keys, as the following example shows:
return hashMapOf("cityMap" to cityMap)
}
----
======
The code now produces output where the radio values are the relevant codes, but the
user still sees the more user-friendly city names, as follows:

View File

@@ -15,8 +15,11 @@ NOTE: The Groovy Markup Template engine requires Groovy 2.3.1+.
The following example shows how to configure the Groovy Markup Template Engine:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -37,8 +40,10 @@ The following example shows how to configure the Groovy Markup Template Engine:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -56,6 +61,7 @@ The following example shows how to configure the Groovy Markup Template Engine:
}
}
----
======
The following example shows how to configure the same in XML:

View File

@@ -185,8 +185,11 @@ This tag renders an HTML `input` tag with the `type` set to `checkbox`.
Assume that our `User` has preferences such as newsletter subscription and a list of
hobbies. The following example shows the `Preferences` class:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class Preferences {
@@ -219,8 +222,10 @@ hobbies. The following example shows the `Preferences` class:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class Preferences(
var receiveNewsletter: Boolean,
@@ -228,6 +233,7 @@ hobbies. The following example shows the `Preferences` class:
var favouriteWord: String
)
----
======
The corresponding `form.jsp` could then resemble the following:
@@ -582,8 +588,11 @@ Assume that we want to display all error messages for the `firstName` and `lastN
fields once we submit the form. We have a validator for instances of the `User` class
called `UserValidator`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class UserValidator implements Validator {
@@ -597,8 +606,10 @@ called `UserValidator`, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class UserValidator : Validator {
@@ -612,6 +623,7 @@ called `UserValidator`, as the following example shows:
}
}
----
======
The `form.jsp` could be as follows:
@@ -785,8 +797,11 @@ web.xml, as the following example shows:
The following example shows the corresponding `@Controller` method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
@@ -794,8 +809,10 @@ The following example shows the corresponding `@Controller` method:
return "redirect:/owners/" + ownerId;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RequestMapping(method = [RequestMethod.DELETE])
fun deletePet(@PathVariable ownerId: Int, @PathVariable petId: Int): String {
@@ -803,6 +820,7 @@ The following example shows the corresponding `@Controller` method:
return "redirect:/owners/$ownerId"
}
----
======
[[mvc-view-jsp-formtaglib-html5]]
=== HTML5 Tags

View File

@@ -53,8 +53,11 @@ You can declare a `ScriptTemplateConfigurer` bean to specify the script engine t
the script files to load, what function to call to render templates, and so on.
The following example uses Mustache templates and the Nashorn JavaScript engine:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -76,8 +79,10 @@ The following example uses Mustache templates and the Nashorn JavaScript engine:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -96,6 +101,7 @@ The following example uses Mustache templates and the Nashorn JavaScript engine:
}
}
----
======
The following example shows the same arrangement in XML:
@@ -114,8 +120,11 @@ The following example shows the same arrangement in XML:
The controller would look no different for the Java and XML configurations, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class SampleController {
@@ -128,8 +137,10 @@ The controller would look no different for the Java and XML configurations, as t
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class SampleController {
@@ -142,6 +153,7 @@ The controller would look no different for the Java and XML configurations, as t
}
}
----
======
The following example shows the Mustache template:
@@ -176,8 +188,11 @@ browser facilities that are not available in the server-side script engine.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -199,8 +214,10 @@ The following example shows how to do so:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -219,6 +236,7 @@ The following example shows how to do so:
}
}
----
======
NOTE: Setting the `sharedEngine` property to `false` is required when using non-thread-safe
script engines with templating libraries not designed for concurrency, such as Handlebars or

View File

@@ -22,8 +22,11 @@ Configuration is standard for a simple Spring web application: The MVC configura
has to define an `XsltViewResolver` bean and regular MVC annotation configuration.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@EnableWebMvc
@ComponentScan
@@ -39,8 +42,10 @@ The following example shows how to do so:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@EnableWebMvc
@ComponentScan
@@ -54,6 +59,7 @@ The following example shows how to do so:
}
}
----
======
[[mvc-view-xslt-controllercode]]
@@ -64,8 +70,11 @@ We also need a Controller that encapsulates our word-generation logic.
The controller logic is encapsulated in a `@Controller` class, with the
handler method being defined as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class XsltController {
@@ -88,8 +97,10 @@ handler method being defined as follows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.ui.set
@@ -114,6 +125,7 @@ handler method being defined as follows:
}
}
----
======
So far, we have only created a DOM document and added it to the Model map. Note that you
can also load an XML file as a `Resource` and use it instead of a custom DOM document.

View File

@@ -21,8 +21,11 @@ Once the asynchronous request processing feature is xref:web/webmvc/mvc-ann-asyn
in the Servlet container, controller methods can wrap any supported controller method
return value with `DeferredResult`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping("/quotes")
@ResponseBody
@@ -35,8 +38,10 @@ return value with `DeferredResult`, as the following example shows:
// From some other thread...
deferredResult.setResult(result);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/quotes")
@ResponseBody
@@ -49,6 +54,7 @@ return value with `DeferredResult`, as the following example shows:
// From some other thread...
deferredResult.setResult(result)
----
======
The controller can produce the return value asynchronously, from a different thread -- for
example, in response to an external event (JMS message), a scheduled task, or other event.
@@ -61,16 +67,21 @@ example, in response to an external event (JMS message), a scheduled task, or ot
A controller can wrap any supported return value with `java.util.concurrent.Callable`,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@PostMapping
public Callable<String> processUpload(final MultipartFile file) {
return () -> "someView";
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping
fun processUpload(file: MultipartFile) = Callable<String> {
@@ -78,6 +89,7 @@ as the following example shows:
"someView"
}
----
======
The return value can then be obtained by running the given task through the
xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-configuration-spring-mvc[configured] `TaskExecutor`.
@@ -211,8 +223,11 @@ each object is serialized with an
xref:integration/rest-clients.adoc#rest-message-conversion[`HttpMessageConverter`] and written to the
response, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping("/events")
public ResponseBodyEmitter handle() {
@@ -230,8 +245,10 @@ response, as the following example shows:
// and done at some point
emitter.complete();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/events")
fun handle() = ResponseBodyEmitter().apply {
@@ -247,6 +264,7 @@ response, as the following example shows:
// and done at some point
emitter.complete()
----
======
You can also use `ResponseBodyEmitter` as the body in a `ResponseEntity`, letting you
customize the status and headers of the response.
@@ -267,8 +285,11 @@ https://www.w3.org/TR/eventsource/[Server-Sent Events], where events sent from t
are formatted according to the W3C SSE specification. To produce an SSE
stream from a controller, return `SseEmitter`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handle() {
@@ -286,8 +307,10 @@ stream from a controller, return `SseEmitter`, as the following example shows:
// and done at some point
emitter.complete();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/events", produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
fun handle() = SseEmitter().apply {
@@ -303,6 +326,7 @@ stream from a controller, return `SseEmitter`, as the following example shows:
// and done at some point
emitter.complete()
----
======
While SSE is the main option for streaming into browsers, note that Internet Explorer
does not support Server-Sent Events. Consider using Spring's
@@ -320,8 +344,11 @@ Sometimes, it is useful to bypass message conversion and stream directly to the
`OutputStream` (for example, for a file download). You can use the `StreamingResponseBody`
return value type to do so, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping("/download")
public StreamingResponseBody handle() {
@@ -333,14 +360,17 @@ return value type to do so, as the following example shows:
};
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/download")
fun handle() = StreamingResponseBody {
// write...
}
----
======
You can use `StreamingResponseBody` as the body in a `ResponseEntity` to
customize the status and headers of the response.

View File

@@ -32,8 +32,11 @@ While https://tools.ietf.org/html/rfc7234#section-5.2.2[RFC 7234] describes all
directives for the `Cache-Control` response header, the `CacheControl` type takes a
use case-oriented approach that focuses on the common scenarios:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// Cache for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);
@@ -46,8 +49,10 @@ use case-oriented approach that focuses on the common scenarios:
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Cache for an hour - "Cache-Control: max-age=3600"
val ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS)
@@ -60,6 +65,7 @@ use case-oriented approach that focuses on the common scenarios:
// "Cache-Control: max-age=864000, public, no-transform"
val ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic()
----
======
`WebContentGenerator` also accepts a simpler `cachePeriod` property (defined in seconds) that
works as follows:
@@ -81,8 +87,11 @@ against conditional request headers. A controller can add an `ETag` header and `
settings to a `ResponseEntity`, as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {
@@ -97,8 +106,10 @@ settings to a `ResponseEntity`, as the following example shows:
.body(book);
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/book/{id}")
fun showBook(@PathVariable id: Long): ResponseEntity<Book> {
@@ -113,6 +124,7 @@ settings to a `ResponseEntity`, as the following example shows:
.body(book)
}
----
======
--
The preceding example sends a 304 (NOT_MODIFIED) response with an empty body if the comparison
@@ -123,8 +135,11 @@ You can also make the check against conditional request headers in the controlle
as the following example shows:
--
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RequestMapping
public String myHandleMethod(WebRequest request, Model model) {
@@ -139,6 +154,7 @@ as the following example shows:
return "myViewName";
}
----
======
<1> Application-specific calculation.
<2> The response has been set to 304 (NOT_MODIFIED) -- no further processing.
<3> Continue with the request processing.

View File

@@ -12,8 +12,11 @@ For advanced mode, you can remove `@EnableWebMvc` and extend directly from
`DelegatingWebMvcConfiguration` instead of implementing `WebMvcConfigurer`,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class WebConfig extends DelegatingWebMvcConfiguration {
@@ -21,8 +24,10 @@ as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
class WebConfig : DelegatingWebMvcConfiguration() {
@@ -30,6 +35,7 @@ as the following example shows:
// ...
}
----
======
You can keep existing methods in `WebConfig`, but you can now also override bean declarations
from the base class, and you can still have any number of other `WebMvcConfigurer` implementations on

View File

@@ -5,8 +5,11 @@ The MVC namespace does not have an advanced mode. If you need to customize a pro
a bean that you cannot change otherwise, you can use the `BeanPostProcessor` lifecycle
hook of the Spring `ApplicationContext`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Component
public class MyPostProcessor implements BeanPostProcessor {
@@ -16,8 +19,10 @@ hook of the Spring `ApplicationContext`, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Component
class MyPostProcessor : BeanPostProcessor {
@@ -27,6 +32,7 @@ hook of the Spring `ApplicationContext`, as the following example shows:
}
}
----
======
Note that you need to declare `MyPostProcessor` as a bean, either explicitly in XML or

View File

@@ -16,8 +16,11 @@ more details.
In Java configuration, you can customize requested content type resolution, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -30,8 +33,10 @@ following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -43,6 +48,7 @@ following example shows:
}
}
----
======
The following example shows how to achieve the same configuration in XML:

View File

@@ -8,8 +8,11 @@ for customization via `@NumberFormat` and `@DateTimeFormat` on fields.
To register custom formatters and converters in Java config, use the following:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -21,8 +24,10 @@ To register custom formatters and converters in Java config, use the following:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -33,6 +38,7 @@ To register custom formatters and converters in Java config, use the following:
}
}
----
======
To do the same in XML config, use the following:
@@ -78,8 +84,11 @@ values. This works for forms where dates are represented as Strings with "input"
fields. For "date" and "time" form fields, however, browsers use a fixed format defined
in the HTML spec. For such cases date and time formatting can be customized as follows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -93,8 +102,10 @@ in the HTML spec. For such cases date and time formatting can be customized as f
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -107,6 +118,7 @@ in the HTML spec. For such cases date and time formatting can be customized as f
}
}
----
======
NOTE: See xref:core/validation/format.adoc#format-FormatterRegistrar-SPI[the `FormatterRegistrar` SPI]
and the `FormattingConversionServiceFactoryBean` for more information on when to use

View File

@@ -6,8 +6,11 @@
In Java configuration, you can implement the `WebMvcConfigurer` interface, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -16,8 +19,10 @@ following example shows:
// Implement configuration methods...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -26,6 +31,7 @@ following example shows:
// Implement configuration methods...
}
----
======
In XML, you can check attributes and sub-elements of `<mvc:annotation-driven/>`. You can

View File

@@ -15,8 +15,11 @@ lower than that of the `DefaultServletHttpRequestHandler`, which is `Integer.MAX
The following example shows how to enable the feature by using the default setup:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -28,8 +31,10 @@ The following example shows how to enable the feature by using the default setup
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -40,6 +45,7 @@ The following example shows how to enable the feature by using the default setup
}
}
----
======
The following example shows how to achieve the same configuration in XML:
@@ -57,8 +63,11 @@ If the default Servlet has been custom-configured with a different name, or if a
different Servlet container is being used where the default Servlet name is unknown,
then you must explicitly provide the default Servlet's name, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -70,8 +79,10 @@ then you must explicitly provide the default Servlet's name, as the following ex
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -82,6 +93,7 @@ then you must explicitly provide the default Servlet's name, as the following ex
}
}
----
======
The following example shows how to achieve the same configuration in XML:

View File

@@ -6,21 +6,27 @@
In Java configuration, you can use the `@EnableWebMvc` annotation to enable MVC
configuration, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
public class WebConfig {
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
class WebConfig
----
======
In XML configuration, you can use the `<mvc:annotation-driven>` element to enable MVC
configuration, as the following example shows:

View File

@@ -4,8 +4,11 @@
In Java configuration, you can register interceptors to apply to incoming requests, as
the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -18,8 +21,10 @@ the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -31,6 +36,7 @@ the following example shows:
}
}
----
======
The following example shows how to achieve the same configuration in XML:

View File

@@ -12,8 +12,11 @@ You can customize `HttpMessageConverter` in Java configuration by overriding
The following example adds XML and Jackson JSON converters with a customized
`ObjectMapper` instead of the default ones:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -30,8 +33,10 @@ The following example adds XML and Jackson JSON converters with a customized
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -45,6 +50,7 @@ The following example adds XML and Jackson JSON converters with a customized
converters.add(MappingJackson2HttpMessageConverter(builder.build()))
converters.add(MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()))
----
======
In the preceding example,
{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`]

View File

@@ -9,8 +9,11 @@ For details on the individual options, see the
The following example shows how to customize path matching in Java configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -26,8 +29,10 @@ The following example shows how to customize path matching in Java configuration
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -42,6 +47,7 @@ The following example shows how to customize path matching in Java configuration
}
}
----
======
The following example shows how to customize path matching in XML configuration:

View File

@@ -15,8 +15,11 @@ so that HTTP conditional requests are supported with `"Last-Modified"` headers.
The following listing shows how to do so with Java configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -30,8 +33,10 @@ The following listing shows how to do so with Java configuration:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -44,6 +49,7 @@ The following listing shows how to do so with Java configuration:
}
}
----
======
The following example shows how to achieve the same configuration in XML:
@@ -69,8 +75,11 @@ JavaScript resources used with a module loader.
The following example shows how to use `VersionResourceResolver` in Java configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -85,8 +94,10 @@ The following example shows how to use `VersionResourceResolver` in Java configu
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -100,6 +111,7 @@ The following example shows how to use `VersionResourceResolver` in Java configu
}
}
----
======
The following example shows how to achieve the same configuration in XML:

View File

@@ -11,8 +11,11 @@ registered as a global xref:core/validation/validator.adoc[Validator] for use wi
In Java configuration, you can customize the global `Validator` instance, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -24,8 +27,10 @@ following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -36,6 +41,7 @@ following example shows:
}
}
----
======
The following example shows how to achieve the same configuration in XML:
@@ -59,8 +65,11 @@ The following example shows how to achieve the same configuration in XML:
Note that you can also register `Validator` implementations locally, as the following
example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class MyController {
@@ -71,8 +80,10 @@ example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class MyController {
@@ -83,6 +94,7 @@ example shows:
}
}
----
======
TIP: If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and
mark it with `@Primary` in order to avoid conflict with the one declared in the MVC configuration.

View File

@@ -7,8 +7,11 @@ logic to run before the view generates the response.
The following example of Java configuration forwards a request for `/` to a view called `home`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -20,8 +23,10 @@ The following example of Java configuration forwards a request for `/` to a view
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -32,6 +37,7 @@ The following example of Java configuration forwards a request for `/` to a view
}
}
----
======
The following example achieves the same thing as the preceding example, but with XML, by
using the `<mvc:view-controller>` element:

View File

@@ -8,8 +8,11 @@ The MVC configuration simplifies the registration of view resolvers.
The following Java configuration example configures content negotiation view
resolution by using JSP and Jackson as a default `View` for JSON rendering:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -22,8 +25,10 @@ resolution by using JSP and Jackson as a default `View` for JSON rendering:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -35,6 +40,7 @@ resolution by using JSP and Jackson as a default `View` for JSON rendering:
}
}
----
======
The following example shows how to achieve the same configuration in XML:
@@ -75,8 +81,11 @@ The MVC namespace provides dedicated elements. The following example works with
In Java configuration, you can add the respective `Configurer` bean,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
@EnableWebMvc
@@ -96,8 +105,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@EnableWebMvc
@@ -114,6 +125,7 @@ as the following example shows:
}
}
----
======

View File

@@ -9,8 +9,11 @@ exception handling, and more. Annotated controllers have flexible method signatu
do not have to extend base classes nor implement specific interfaces.
The following example shows a controller defined by annotations:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class HelloController {
@@ -22,8 +25,10 @@ The following example shows a controller defined by annotations:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.ui.set
@@ -37,6 +42,7 @@ The following example shows a controller defined by annotations:
}
}
----
======
In the preceding example, the method accepts a `Model` and returns a view name as a `String`,
but many other options exist and are explained later in this chapter.

View File

@@ -23,8 +23,11 @@ By contrast, global `@ModelAttribute` and `@InitBinder` methods are applied _bef
The `@ControllerAdvice` annotation has attributes that let you narrow the set of controllers
and handlers that they apply to. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
@@ -38,8 +41,10 @@ and handlers that they apply to. For example:
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = [RestController::class])
@@ -53,6 +58,7 @@ and handlers that they apply to. For example:
@ControllerAdvice(assignableTypes = [ControllerInterface::class, AbstractController::class])
class ExampleAdvice3
----
======
The selectors in the preceding example are evaluated at runtime and may negatively impact
performance if used extensively. See the

View File

@@ -6,8 +6,11 @@
`@Controller` and xref:web/webmvc/mvc-controller/ann-advice.adoc[@ControllerAdvice] classes can have
`@ExceptionHandler` methods to handle exceptions from controller methods, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class SimpleController {
@@ -20,8 +23,10 @@
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class SimpleController {
@@ -34,6 +39,7 @@
}
}
----
======
The exception may match against a top-level exception being propagated (e.g. a direct
`IOException` being thrown) or against a nested cause within a wrapper exception (e.g.
@@ -48,42 +54,54 @@ is used to sort exceptions based on their depth from the thrown exception type.
Alternatively, the annotation declaration may narrow the exception types to match,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
----
======
You can even use a list of specific exception types with a very generic argument signature,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: Exception): ResponseEntity<String> {
// ...
}
----
======
[NOTE]
====

View File

@@ -21,8 +21,11 @@ do, except for `@ModelAttribute` (command object) arguments. Typically, they are
with a `WebDataBinder` argument (for registrations) and a `void` return value.
The following listing shows an example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class FormController {
@@ -37,6 +40,7 @@ The following listing shows an example:
// ...
}
----
======
<1> Defining an `@InitBinder` method.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -61,8 +65,11 @@ Alternatively, when you use a `Formatter`-based setup through a shared
`FormattingConversionService`, you can re-use the same approach and register
controller-specific `Formatter` implementations, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class FormController {
@@ -75,6 +82,7 @@ controller-specific `Formatter` implementations, as the following example shows:
// ...
}
----
======
<1> Defining an `@InitBinder` method on a custom formatter.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]

View File

@@ -15,14 +15,18 @@ JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
The following example shows 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 value of the `JSESSIONID` cookie.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]

View File

@@ -6,22 +6,28 @@
`HttpEntity` is more or less identical to using xref:web/webmvc/mvc-controller/ann-methods/requestbody.adoc[`@RequestBody`] but is based on a
container object that exposes request headers and body. The following listing shows an example:
[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>) {
// ...
}
----
======

View File

@@ -13,8 +13,11 @@ which allow rendering only a subset of all fields in an `Object`. To use it with
`@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 {
@@ -53,8 +56,10 @@ which allow rendering only a subset of all fields in an `Object`. To use it with
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RestController
class UserController {
@@ -72,6 +77,7 @@ which allow rendering only a subset of all fields in an `Object`. To use it with
interface WithPasswordView : WithoutPasswordView
}
----
======
NOTE: `@JsonView` allows an array of view classes, but you can specify only one per
controller method. If you need to activate multiple views, you can use a composite interface.
@@ -79,8 +85,11 @@ controller method. If you need to activate multiple views, you can use a composi
If you want to do the above programmatically, instead of declaring an `@JsonView` annotation,
wrap the return value with `MappingJacksonValue` and use it to supply the serialization view:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RestController
public class UserController {
@@ -94,8 +103,10 @@ wrap the return value with `MappingJacksonValue` and use it to supply the serial
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RestController
class UserController {
@@ -108,12 +119,16 @@ wrap the return value with `MappingJacksonValue` and use it to supply the serial
}
}
----
======
For controllers that rely on view resolution, you can add the serialization view class
to the model, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class UserController extends AbstractController {
@@ -126,8 +141,10 @@ to the model, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class UserController : AbstractController() {
@@ -140,6 +157,7 @@ to the model, as the following example shows:
}
}
----
======

View File

@@ -18,8 +18,11 @@ method must use a URI variable to mask that variable content and ensure the requ
be matched successfully independent of matrix variable order and presence.
The following example uses a matrix variable:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// GET /pets/42;q=11;r=22
@@ -30,8 +33,10 @@ The following example uses a matrix variable:
// q == 11
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// GET /pets/42;q=11;r=22
@@ -42,13 +47,17 @@ The following example uses a matrix variable:
// q == 11
}
----
======
Given that all path segments may contain matrix variables, you may sometimes need to
disambiguate which path variable the matrix variable is expected to be in.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// GET /owners/42;q=11/pets/21;q=22
@@ -61,8 +70,10 @@ The following example shows how to do so:
// q2 == 22
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// GET /owners/42;q=11/pets/21;q=22
@@ -75,12 +86,16 @@ The following example shows how to do so:
// q2 == 22
}
----
======
A matrix variable may be defined as optional and a default value specified, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
// GET /pets/42
@@ -90,8 +105,10 @@ following example shows:
// q == 1
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
// GET /pets/42
@@ -101,11 +118,15 @@ following example shows:
// q == 1
}
----
======
To get all matrix variables, you can 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, you can use a `MultiValueMap`, as the following exa
// 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,6 +155,7 @@ To get all matrix variables, you can use a `MultiValueMap`, as the following exa
// petMatrixVars: ["q" : 22, "s" : 23]
}
----
======
Note that you need to enable the use of matrix variables. In the MVC Java configuration,
you need to set a `UrlPathHelper` with `removeSemicolonContent=false` through

View File

@@ -9,14 +9,18 @@ values from HTTP Servlet request parameters whose names match to field names. Th
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 shows how to do so:
[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>
// method logic...
}
----
======
<1> Bind an instance of `Pet`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -51,14 +55,18 @@ In the following example, the model attribute name is `account` which matches th
path variable `account`, and there is a registered `Converter<String, Account>` which
could load the `Account` from a data store:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@PutMapping("/accounts/{account}")
public String save(@ModelAttribute("account") Account account) { // <1>
// ...
}
----
======
<1> Bind an instance of `Account` using an explicit attribute name.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -82,8 +90,11 @@ Data binding can result in errors. By default, a `BindException` is raised. Howe
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>
@@ -93,6 +104,7 @@ to the `@ModelAttribute`, as the following example shows:
// ...
}
----
======
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -112,8 +124,11 @@ In some cases, you may want access to a model attribute without data binding. Fo
cases, you can inject the `Model` into the controller and access it directly or,
alternatively, set `@ModelAttribute(binding=false)`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@ModelAttribute
public AccountForm setUpForm() {
@@ -131,6 +146,7 @@ alternatively, set `@ModelAttribute(binding=false)`, as the following example sh
// ...
}
----
======
<1> Setting `@ModelAttribute(binding=false)`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -159,8 +175,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 shows how to do so:
[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>
@@ -170,6 +189,7 @@ xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). The following ex
// ...
}
----
======
<1> Validate the `Pet` instance.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]

View File

@@ -8,8 +8,11 @@ requests with `multipart/form-data` is parsed and accessible as regular request
parameters. The following example accesses one regular form field and one uploaded
file:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
public class FileUploadController {
@@ -27,8 +30,10 @@ file:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
class FileUploadController {
@@ -46,6 +51,7 @@ file:
}
}
----
======
Declaring the argument type as a `List<MultipartFile>` allows for resolving multiple
files for the same parameter name.
@@ -62,8 +68,11 @@ xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[command
and file from the preceding example could be fields on a form object,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
class MyForm {
@@ -88,8 +97,10 @@ as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyForm(val name: String, val file: MultipartFile, ...)
@@ -107,6 +118,7 @@ as the following example shows:
}
}
----
======
Multipart requests can also be submitted from non-browser clients in a RESTful service
@@ -137,8 +149,11 @@ probably want it deserialized from JSON (similar to `@RequestBody`). Use the
`@RequestPart` annotation to access a multipart after converting it with an
xref:integration/rest-clients.adoc#rest-message-conversion[HttpMessageConverter]:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata,
@@ -146,8 +161,10 @@ xref:integration/rest-clients.adoc#rest-message-conversion[HttpMessageConverter]
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData,
@@ -155,6 +172,7 @@ xref:integration/rest-clients.adoc#rest-message-conversion[HttpMessageConverter]
// ...
}
----
======
You can use `@RequestPart` in combination with `jakarta.validation.Valid` or use Spring's
`@Validated` annotation, both of which cause Standard Bean Validation to be applied.
@@ -163,8 +181,11 @@ into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation erro
within the controller through an `Errors` or `BindingResult` argument,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") MetaData metadata,
@@ -172,8 +193,10 @@ as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData,
@@ -181,6 +204,7 @@ as the following example shows:
// ...
}
----
======

View File

@@ -26,8 +26,11 @@ Note that URI template variables from the present request are automatically made
available when expanding a redirect URL, and you don't need to explicitly add them
through `Model` or `RedirectAttributes`. The following example shows how to define a redirect:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@PostMapping("/files/{path}")
public String upload(...) {
@@ -35,8 +38,10 @@ through `Model` or `RedirectAttributes`. The following example shows how to defi
return "redirect:files/{path}";
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/files/{path}")
fun upload(...): String {
@@ -44,6 +49,7 @@ through `Model` or `RedirectAttributes`. The following example shows how to defi
return "redirect:files/{path}"
}
----
======
Another way of passing data to the redirect target is by using flash attributes. Unlike
other redirect attributes, flash attributes are saved in the HTTP session (and, hence, do

View File

@@ -7,14 +7,18 @@ Similar to `@SessionAttribute`, you can use the `@RequestAttribute` annotations
access pre-existing request attributes created earlier (for example, by a Servlet `Filter`
or `HandlerInterceptor`):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping("/")
public String handle(@RequestAttribute Client client) { // <1>
// ...
}
----
======
<1> Using the `@RequestAttribute` annotation.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]

View File

@@ -7,22 +7,28 @@ You can use the `@RequestBody` annotation to have the request body read and dese
`Object` through an xref:integration/rest-clients.adoc#rest-message-conversion[`HttpMessageConverter`].
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) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/accounts")
fun handle(@RequestBody account: Account) {
// ...
}
----
======
You can use the xref:web/webmvc/mvc-config/message-converters.adoc[Message Converters] option of the xref:web/webmvc/mvc-config.adoc[MVC Config] to
@@ -35,21 +41,27 @@ into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation erro
within the controller through an `Errors` or `BindingResult` argument,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@PostMapping("/accounts")
public void handle(@Valid @RequestBody Account account, BindingResult result) {
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Account, result: BindingResult) {
// ...
}
----
======

View File

@@ -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.

View File

@@ -8,8 +8,11 @@ query parameters or form data) to a method argument in a controller.
The following example shows how to do so:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
@RequestMapping("/pets")
@@ -28,6 +31,7 @@ The following example shows how to do so:
}
----
======
<1> Using `@RequestParam` to bind `petId`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]

View File

@@ -8,8 +8,11 @@ to the response body through an
xref:integration/rest-clients.adoc#rest-message-conversion[HttpMessageConverter].
The following listing shows an example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@GetMapping("/accounts/{id}")
@ResponseBody
@@ -17,8 +20,10 @@ The following listing shows an example:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/accounts/{id}")
@ResponseBody
@@ -26,6 +31,7 @@ The following listing shows an example:
// ...
}
----
======
`@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

View File

@@ -5,8 +5,11 @@
`ResponseEntity` is like xref:web/webmvc/mvc-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)
}
----
======
Spring MVC supports using a single value xref:web/webmvc/mvc-ann-async.adoc#mvc-ann-async-reactive-types[reactive type]
to produce the `ResponseEntity` asynchronously, and/or single and multi-value reactive

View File

@@ -8,14 +8,18 @@ If you need access to pre-existing session attributes that are managed globally
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
----
@RequestMapping("/")
public String handle(@SessionAttribute User user) { <1>
// ...
}
----
======
<1> Using a `@SessionAttribute` annotation.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]

View File

@@ -11,8 +11,11 @@ requests to access.
The following example uses the `@SessionAttributes` annotation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
@SessionAttributes("pet") // <1>
@@ -20,6 +23,7 @@ The following example uses the `@SessionAttributes` annotation:
// ...
}
----
======
<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 HTTP Servlet session. It remain
until 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>
@@ -57,6 +64,7 @@ storage, as the following example shows:
}
}
----
======
<1> Storing the `Pet` value in the Servlet session.
<2> Clearing the `Pet` value from the Servlet session.

View File

@@ -24,8 +24,11 @@ related to the request body.
The following example shows 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 shows 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 shows a `@ModelAttribute` method:
// add more ...
}
----
======
The following example adds only one attribute:
[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 `Object`
@@ -74,8 +86,11 @@ attribute. This is typically not required, as it is the default behavior in HTML
unless the return value is a `String` that would otherwise be interpreted as a view name.
`@ModelAttribute` can also 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")
@@ -84,8 +99,10 @@ unless the return value is a `String` that would otherwise be interpreted as a v
return account;
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
@@ -94,6 +111,7 @@ unless the return value is a `String` that would otherwise be interpreted as a v
return account
}
----
======

View File

@@ -23,8 +23,11 @@ A `@RequestMapping` is still needed at the class level to express shared mapping
The following example has 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 has 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 has type and method level mappings:
}
}
----
======
@@ -102,28 +108,37 @@ Some example patterns:
Captured URI variables can be accessed with `@PathVariable`. For example:
[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}")
@@ -135,8 +150,10 @@ You can declare URI variables at the class and method levels, as the following e
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
@RequestMapping("/owners/{ownerId}")
@@ -148,6 +165,7 @@ You can declare URI variables at the class and method levels, as the following e
}
}
----
======
URI variables are automatically converted to the appropriate type, or `TypeMismatchException`
is raised. Simple types (`int`, `long`, `Date`, and so on) are supported by default and you can
@@ -162,22 +180,28 @@ The syntax `{varName:regex}` declares a URI variable with a regular expression t
syntax of `{varName:regex}`. For example, given URL `"/spring-web-3.0.5.jar"`, the following method
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 name, @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 name: String, @PathVariable version: String, @PathVariable ext: String) {
// ...
}
----
======
URI path patterns can also have embedded `${...}` placeholders that are resolved on startup
by using `PropertySourcesPlaceholderConfigurer` against local, system, environment, and
@@ -274,14 +298,18 @@ recommendations related to RFD.
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") // <1>
public void addPet(@RequestBody Pet pet) {
// ...
}
----
======
<1> Using a `consumes` attribute to narrow the mapping by the content type.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -312,8 +340,11 @@ TIP: `MediaType` provides constants for commonly used media types, such as
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") // <1>
@ResponseBody
@@ -321,6 +352,7 @@ content types that a controller method produces, as the following example shows:
// ...
}
----
======
<1> Using a `produces` attribute to narrow the mapping by the content type.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -353,14 +385,18 @@ You can narrow request mappings based on request parameter conditions. You can t
presence of a request parameter (`myParam`), for the absence of one (`!myParam`), or for a
specific value (`myParam=myValue`). The following example shows how to test for a specific 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> Testing whether `myParam` equals `myValue`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -375,14 +411,18 @@ specific value (`myParam=myValue`). The following example shows how to test for
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> Testing whether `myHeader` equals `myValue`.
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
@@ -455,8 +495,11 @@ You can programmatically register handler methods, which you can use for dynamic
registrations or for advanced cases, such as different instances of the same handler
under different URLs. The following example registers a handler method:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Configuration
public class MyConfig {
@@ -474,6 +517,7 @@ under different URLs. The following example registers a handler method:
}
}
----
======
<1> Inject the target handler and the handler mapping for controllers.
<2> Prepare the request mapping meta data.
<3> Get the handler method.

View File

@@ -12,8 +12,11 @@ annotated class, indicating its role as 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")
@@ -22,8 +25,10 @@ your Java configuration, as the following example shows:
// ...
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Configuration
@ComponentScan("org.example.web")
@@ -32,6 +37,7 @@ your Java configuration, as the following example shows:
// ...
}
----
======
The following example shows the XML configuration equivalent of the preceding example:

View File

@@ -18,8 +18,11 @@ The following example of the Java configuration registers and initializes
the `DispatcherServlet`, which is auto-detected by the Servlet container
(see xref:web/webmvc/mvc-servlet/container-config.adoc[Servlet Config]):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@@ -38,8 +41,10 @@ the `DispatcherServlet`, which is auto-detected by the Servlet container
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyWebApplicationInitializer : WebApplicationInitializer {
@@ -57,6 +62,7 @@ the `DispatcherServlet`, which is auto-detected by the Servlet container
}
}
----
======
NOTE: In addition to using the ServletContext API directly, you can also extend
`AbstractAnnotationConfigDispatcherServletInitializer` and override specific methods

View File

@@ -5,8 +5,11 @@ In a Servlet environment, you have the option of configuring the Servlet contain
programmatically as an alternative or in combination with a `web.xml` file.
The following example registers a `DispatcherServlet`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
import org.springframework.web.WebApplicationInitializer;
@@ -23,8 +26,10 @@ The following example registers a `DispatcherServlet`:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
import org.springframework.web.WebApplicationInitializer
@@ -40,6 +45,7 @@ The following example registers a `DispatcherServlet`:
}
}
----
======
`WebApplicationInitializer` is an interface provided by Spring MVC that ensures your
@@ -52,8 +58,11 @@ location of the `DispatcherServlet` configuration.
This is recommended for applications that use Java-based Spring configuration, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@@ -73,8 +82,10 @@ following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
@@ -91,12 +102,16 @@ following example shows:
}
}
----
======
If you use XML-based Spring configuration, you should extend directly from
`AbstractDispatcherServletInitializer`, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
@@ -118,8 +133,10 @@ If you use XML-based Spring configuration, you should extend directly from
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
@@ -138,13 +155,17 @@ If you use XML-based Spring configuration, you should extend directly from
}
}
----
======
`AbstractDispatcherServletInitializer` also provides a convenient way to add `Filter`
instances and have them be automatically mapped to the `DispatcherServlet`, as the
following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
@@ -157,8 +178,10 @@ following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
@@ -169,6 +192,7 @@ following example shows:
}
}
----
======
Each filter is added with a default name based on its concrete type and automatically
mapped to the `DispatcherServlet`.

View File

@@ -24,8 +24,11 @@ image::mvc-context-hierarchy.png[]
The following example configures a `WebApplicationContext` hierarchy:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@@ -45,8 +48,10 @@ The following example configures a `WebApplicationContext` hierarchy:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
@@ -63,6 +68,7 @@ The following example configures a `WebApplicationContext` hierarchy:
}
}
----
======
TIP: If an application context hierarchy is not required, applications can return all
configuration through `getRootConfigClasses()` and `null` from `getServletConfigClasses()`.

View File

@@ -74,8 +74,11 @@ Servlet container makes an ERROR dispatch within the container to the configured
to a `@Controller`, which could be implemented to return an error view name with a model
or to render a JSON response, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RestController
public class ErrorController {
@@ -89,8 +92,10 @@ or to render a JSON response, as the following example shows:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RestController
class ErrorController {
@@ -104,6 +109,7 @@ or to render a JSON response, as the following example shows:
}
}
----
======
TIP: The Servlet API does not provide a way to create error page mappings in Java. You can,
however, use both a `WebApplicationInitializer` and a minimal `web.xml`.

View File

@@ -25,8 +25,11 @@ through the `enableLoggingRequestDetails` property on `DispatcherServlet`.
The following example shows how to do so by using Java configuration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class MyInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
@@ -53,8 +56,10 @@ public class MyInitializer
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class MyInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
@@ -75,6 +80,7 @@ public class MyInitializer
}
}
----
======

View File

@@ -28,8 +28,11 @@ To do so:
The following example shows how to set a `MultipartConfigElement` on the Servlet registration:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@@ -44,8 +47,10 @@ The following example shows how to set a `MultipartConfigElement` on the Servlet
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
class AppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
@@ -59,6 +64,7 @@ The following example shows how to set a `MultipartConfigElement` on the Servlet
}
----
======
Once the Servlet multipart configuration is in place, you can add a bean of type
`StandardServletMultipartResolver` with a name of `multipartResolver`.

View File

@@ -15,8 +15,11 @@ include::partial$web/web-uris.adoc[leveloffset=+1]
You can use `ServletUriComponentsBuilder` to create URIs relative to the current request,
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpServletRequest request = ...
@@ -26,8 +29,10 @@ as the following example shows:
.replaceQueryParam("accountId", "{id}")
.build("123");
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val request: HttpServletRequest = ...
@@ -37,11 +42,15 @@ as the following example shows:
.replaceQueryParam("accountId", "{id}")
.build("123")
----
======
You can create URIs relative to the context path, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpServletRequest request = ...
@@ -52,8 +61,10 @@ You can create URIs relative to the context path, as the following example shows
.build()
.toUri();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val request: HttpServletRequest = ...
@@ -64,12 +75,16 @@ You can create URIs relative to the context path, as the following example shows
.build()
.toUri()
----
======
You can create URIs relative to a Servlet (for example, `/main/{asterisk}`),
as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
HttpServletRequest request = ...
@@ -80,8 +95,10 @@ as the following example shows:
.build()
.toUri();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val request: HttpServletRequest = ...
@@ -92,6 +109,7 @@ as the following example shows:
.build()
.toUri()
----
======
NOTE: As of 5.1, `ServletUriComponentsBuilder` ignores information from the `Forwarded` and
`X-Forwarded-*` headers, which specify the client-originated address. Consider using the
@@ -106,8 +124,11 @@ such headers.
Spring MVC provides a mechanism to prepare links to controller methods. For example,
the following MVC controller allows for link creation:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@Controller
@RequestMapping("/hotels/{hotel}")
@@ -119,8 +140,10 @@ the following MVC controller allows for link creation:
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@Controller
@RequestMapping("/hotels/{hotel}")
@@ -132,25 +155,32 @@ the following MVC controller allows for link creation:
}
}
----
======
You can prepare a link by referring to the method by name, as the following example shows:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val uriComponents = MvcUriComponentsBuilder
.fromMethodName(BookingController::class.java, "getBooking", 21).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
----
======
In the preceding example, we provide actual method argument values (in this case, the long value: `21`)
to be used as a path variable and inserted into the URL. Furthermore, we provide the
@@ -163,22 +193,28 @@ There are additional ways to use `MvcUriComponentsBuilder`. For example, you can
akin to mock testing through proxies to avoid referring to the controller method by name, as the following example shows
(the example assumes static import of `MvcUriComponentsBuilder.on`):
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val uriComponents = MvcUriComponentsBuilder
.fromMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
----
======
NOTE: Controller method signatures are limited in their design when they are supposed to be usable for
link creation with `fromMethodCall`. Aside from needing a proper parameter signature,
@@ -200,8 +236,11 @@ For such cases, you can use the static `fromXxx` overloaded methods that accept
with a base URL and then use the instance-based `withXxx` methods. For example, the
following listing uses `withMethodCall`:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en");
MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base);
@@ -209,8 +248,10 @@ following listing uses `withMethodCall`:
URI uri = uriComponents.encode().toUri();
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
val base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en")
val builder = MvcUriComponentsBuilder.relativeTo(base)
@@ -218,6 +259,7 @@ following listing uses `withMethodCall`:
val uri = uriComponents.encode().toUri()
----
======
NOTE: As of 5.1, `MvcUriComponentsBuilder` ignores information from the `Forwarded` and
`X-Forwarded-*` headers, which specify the client-originated address. Consider using the
@@ -234,8 +276,11 @@ by referring to the implicitly or explicitly assigned name for each request mapp
Consider the following example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
@RequestMapping("/people/{id}/addresses")
public class PersonAddressController {
@@ -244,8 +289,10 @@ Consider the following example:
public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... }
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
.Kotlin
----
@RequestMapping("/people/{id}/addresses")
class PersonAddressController {
@@ -254,6 +301,7 @@ Consider the following example:
fun getAddress(@PathVariable country: String): HttpEntity<PersonAddress> { ... }
}
----
======
Given the preceding controller, you can prepare a link from a JSP, as follows: