Spring WebFlux provides an annotation-based programming model, where the @Controller and @RestController components use annotations to express query mappings, query entry, exception handling, and more. Annotated controllers have flexible method signatures and do not necessarily need to extend base classes or implement specific interfaces.

The following listing shows a basic example:

Java
@RestController
public classHelloController {
@GetMapping("/hello")
public String handle (){
return "Hello WebFlux";
}
}
Kotlin
@RestController
class HelloController {
@GetMapping("/hello")
fun handle() = "Hello WebFlux"
}

In the previous example, the method returns a String to write to the response.

@Controller

You can define controller beans using the standard Spring bean definition. The @Controller stereotype allows for automatic discovery and is consistent with Spring's general support for discovering classes with the @Component annotation in the classpath and automatically registering bean definitions for them. It also acts as a stereotype for the annotated class, indicating its role as a web component.

To enable automatic detection of such beans with the @Controller annotation, you can add component scanning to the configuration Java, as shown in the following example:

Java
@Configuration
@ComponentScan("org.example.web")
public class WebConfig {
// ...
}
  1. Scanning the package org.example.web.
Kotlin
@Configuration
 @ComponentScan("org.example.web")
class WebConfig {
// ...
}
  1. Scan the package org.example.web.

@RestController is a composite an annotation that is itself meta-annotated with the @Controller and @ResponseBody annotations, indicating a controller whose each method inherits the @ResponseBody annotation at the type level and , therefore, writes directly to the response body instead of recognizing the representation and rendering using an HTML template.

Request Mapping

The @RequestMapping annotation is used to map requests to controller methods. It has various attributes to match by URL, HTTP method, request parameters, headers, and media types. You can use it at the class level to express general mappings, or at the method level to narrow it down to a specific endpoint mapping.

There are also HTTP method-specific options for shortening the @RequestMapping annotation:

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

The previous annotations are custom annotations that are specified because most controller methods will likely need to be mapped to defined by an HTTP method rather than using the @RequestMapping annotation, which is mapped to all HTTP methods by default. However, the @RequestMapping annotation is still required at the class level to express general mappings.

The following example uses type and method level mappings:

Java
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id)  {
// ...
}
@PostMapping
@ResponseStatus( HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
Kotlin
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
 // ...
}
}

URI templates

You can display queries using standard masks (aka search pattern (glob patterns) and wildcards:

Template Description Example

?

Matches a single character

"/pages/t?st.html" corresponds to "/pages/test.html" and "/pages/t3st.html"

*

Matches zero or more characters in the path segment

"/resources/*.png" corresponds to "/resources/file.png"

"/projects/*/versions" matches "/projects/spring/versions" but does not match "/projects/spring/boot/versions"

**

Matches zero or more path segments until the end of the path

"/resources/**" matches "/resources/file. png" and "/resources/images/file.png"

"/resources/**/file.png" is invalid , since ** is only allowed at the end of the path.

{name}

Matches a path segment and writes it to a variable called "name".

"/projects/ {project}/versions" matches "/projects/spring/versions" and captures project=spring

{name:[a-z]+}

Matches the expression "[a-z]+ " as a path variable named "name".

"/projects/{project:[a-z]+}/versions" matches "/projects/spring/versions" but not "/projects/spring1/versions"

{*path}

Matches zero or more path segments to the end of the path and writes it to a variable named "path" .

"/resources/{*file}" corresponds to "/resources/images/file.png" and captures file=/images/file.png

Captured URI variables can be accessed using an annotation @PathVariable, as shown in the following example:

Java
@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
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 level, as shown in the following example:

Java
@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {
@GetMapping("/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
 // ...
}
}
  1. Display a URI at the class level.
  2. Display Method-level URI.
Kotlin
@Controller
@RequestMapping("/owners/{ownerId}")
class OwnerController {
@GetMapping("/pets/{petId}")
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
// ...
}
}
  1. Display URIs at the class level.
  2. Mapping URIs at the method level.

URI variables are converted to the appropriate type, otherwise an error occurs TypeMismatchException. Simple types (int, long, Date, and so on) are supported by default, but you can register support for any other data type.

You can explicitly name URI variables (for example, @PathVariable("customId")), but you can also omit this information if the names are the same and your code compiles using debugging information or with the -parameters compiler flag in Java 8.

The {*varName} syntax declares a URI variable that matches zero or more remaining segments ways. For example, /resources/{*path} matches all files in the /resources/ directory, and the "path" variable reflects the full path in the /resources.

Syntax {varName:regex} declares a URI variable with a regular expression, which has the syntax: {varName:regex}. For example, given the URL /spring-web-3.0.5.jar, the following method retrieves the file name, version, and extension:

Java
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\ \.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String ext) {
// ...
}
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 contain inline placeholders ${...}, which are recognized when run with PropertySourcesPlaceholderConfigurer in local, system property sources, environment property sources, etc. You can use this, for example, to parameterize a base URL based on some external configuration.

Spring WebFlux uses PathPattern and PathPatternParser to support URI path mapping. Both classes are found in spring-web and are specifically designed for use with HTTP URL paths in web applications where a large number of URI path patterns are matched at runtime.

Spring WebFlux does not support suffix mapping to an image - unlike Spring MVC, where a mapping such as /person also matches /person.*. To negotiate content based on a URL, if necessary, we recommend using a query parameter that is simpler, more explicit, and less vulnerable to URL path-based exploits.

Pattern comparison

If multiple patterns match a URL, they must be compared to find the best match. This is done using PathPattern.SPECIFICITY_COMPARATOR, which looks for patterns that are more specific.

For each pattern, a score is calculated based on the number of URI identifier and wildcard variables, where URI -variable scores lower than wildcard. The sample with the lowest total score wins. If two samples have the same scores, the longer one is selected.

Catch-all samples (for example, **, {*varName}) are excluded from the count and is always sorted last. If two samples are both catch-all, the longer one is chosen.

Types of media consumed

You can narrow the query display based on the Content-Type of the query , as shown in the following example:

Java
@PostMapping(path = "/pets", consumes = "application /json")
public void addPet(@RequestBody Pet pet) {
// ...
}
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.

You can declare a generic consumes attribute at the class level. However, unlike most other request mapping attributes, when used at the class level, the consumes attribute at the method level overrides rather than extends the class-level declaration.

MediaType provides constants for commonly used media types, such as APPLICATION_JSON_VALUE and APPLICATION_XML_VALUE.

Produced Media Types

You can narrow the range of requests that are displayed based on the Accept request header and the list of content types that the controller method produces, as shown in the following example:

Java
@GetMapping(path = "/pets/{petId}", produces = "application/json" )
@ResponseBody
public Pet getPet(@PathVariable String petId)  {
// ...
}
Kotlin
@GetMapping("/pets/{petId}", produces = ["application/json"])
@ResponseBody
fun getPet(@PathVariable String petId): Pet {
// ...
}

The media type can determine the character set. Negable expressions are supported - for example, !text/plain means any content type other than text/plain.

You can declare a generic produces attribute at the class level. However, unlike most other request mapping attributes, when used at the class level, the produces attribute at the method level overrides rather than extends the class-level declaration.

MediaType provides constants for commonly used media types, such as APPLICATION_JSON_VALUE and APPLICATION_XML_VALUE.

Parameters and headers

You can narrow the display of queries based on the conditions of the query parameters. You can check for the presence of a query parameter myParam, its absence (!myParam) or the presence of a specific value (myParam=myValue). The following examples check for the presence of a parameter with a value:

Java
@GetMapping(path = "/pets/{petId} ", params = "myParam=myValue")
public void findPet (@PathVariable String petId) {
// ...
}
  1. Make sure myParam is equal to myValue.
Kotlin
@GetMapping("/pets/{petId}", params = ["myParam=myValue"])
fun findPet(@PathVariable petId: String) {
// ...
}
  1. Make sure myParam is equal to myValue.

The same can be used with request header conditions , as shown in the following example:

Java
@GetMapping(path = "/pets", headers = "myHeader=myValue")
public  void findPet(@PathVariable String petId) {
// ...
}
  1. Make sure that myHeader is equal to myValue.
Kotlin
@GetMapping("/pets", headers = ["myHeader=myValue"])
fun findPet(@PathVariable petId: String) {
// ... }
  1. Make sure myHeader is equal to myValue.

HTTP methods HEAD, OPTIONS

@GetMapping (and @RequestMapping(method=HttpMethod.GET)) transparently support the HTTP HEAD method for mapping requests. The controller methods do not need to be changed. The response wrapper function used in HttpHandler ensures that the Content-Length header is set to the number of bytes written (without actually writing to the response).

Default The HTTP OPTIONS method is handled by setting the Allow response header to the list of HTTP methods listed in all methods marked with the @RequestMapping annotation.

In the case of the annotation @RequestMapping without declaring HTTP methods, the Allow header is set to GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS. Controller methods should always declare supported HTTP methods (for example, using HTTP method-specific variants: @GetMapping, @PostMapping, and others).

You can explicitly map a method annotated with @RequestMapping to the HEAD HTTP method and the OPTIONS HTTP method, but in ordinary cases this is not necessary.

Custom annotations

Spring WebFlux supports the use of compound annotations to display queries. These are annotations that are themselves @RequestMapping meta-annotations and are composed to re-declar a subset (or all) of the attributes marked with the @RequestMapping annotation for a narrower, more specific purpose.

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping and @PatchMapping are examples of compound annotations. These are provided because it is likely that most controller methods will need to be mapped to a specific HTTP method instead of using the @RequestMapping annotation, which maps to all HTTP methods by default. If you need an example of compound annotations, take a look at how they are declared.

Spring WebFlux also supports custom query display attributes with custom query display logic. This is a more advanced option that requires subclassing RequestMappingHandlerMapping and overriding the getCustomMethodCondition method in which you can check the custom attribute and return your own RequestCondition.

Explicit registration

You can programmatically register handler methods, which can be used for dynamic registration or in more complex cases, such as if there are different instances of the same handler under different URLs. The following example shows how to do this:

Java
@Configuration
public class MyConfig {
@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) (1)
    throws NoSuchMethodException {
RequestMappingInfo info = RequestMappingInfo
        .paths("/user/{id}").methods(RequestMethod.GET).build();
Method method = UserHandler.class.getMethod("getUser", Long.class);
mapping.registerMapping(info, handler, method);
}
}
  1. Inject the target handler and displaying the handler for controllers.
  2. Prepare the request display metadata.
  3. Get the handler method.
  4. Add registration.
Kotlin
@Configuration
class MyConfig {
@Autowired
fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) {
val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build()
val method = UserHandler::class.java.getMethod("getUser", Long::class.java)
mapping.registerMapping(info, handler, method)
}
}
  1. Inject the target handler and handler mapping for controllers.
  2. Prepare the request mapping metadata.
  3. Get the handler method.
  4. Add registration .

Handler methods

Handler methods with the @RequestMapping annotation have a flexible signature and can be chosen from a number of supported controller method arguments and return values.

Method Arguments

The following table lists the supported controller method arguments.

Reactive types (Reactor, RxJava, or others) are supported in arguments that require blocking I/O to resolve (for example, reading the request body). This is noted in the "Description" column. Reactive types are not expected for arguments that do not require blocking.

The java.util.Optional argument from JDK 1.8 is supported as a method argument in combination with annotations that have a required attribute (for example, @RequestParam, @RequestHeader and others), and is equivalent to required=false.

Controller method argument Description

ServerWebExchange

Provides access to the full ServerWebExchange – container for HTTP request and response, request and session attributes, checkNotModified methods, etc.

ServerHttpRequest, ServerHttpResponse

Provides access to an HTTP request or response.

WebSession

Provides access to the session. This does not start a new session unless attributes are added. Supports reactive types.

java.security.Principal

The current authenticated user - possibly a concrete Principal implementation class, if known. Supports reactive types.

org.springframework.http.HttpMethod

HTTP request method.

java.util.Locale

The current locale of the request, determined by the most specific LocaleResolver available - essentially the configured LocaleResolver or LocaleContextResolver.

java.util.TimeZone + java.time.ZoneId

The time zone associated with the current request, as determined by the LocaleContextResolver.

@PathVariable

Designed to provide access to URI template variables.

@MatrixVariable

Designed to provide access to name-value pairs in path segments of URIs.

@RequestParam

Provides access to request parameters. Parameter values are converted to the declared type of the method's arguments.

Note that using the @RequestParam annotation is optional—for example, to set its attributes. See the "Any Other Argument" section later in this table.

@RequestHeader

Designed to provide access to request headers. Header values are converted to the declared type of the method argument.

@CookieValue

Designed to provide access to cookies. Cookie values are converted to the declared method argument type.

@RequestBody

Provides access to the HTTP request body. The body content is converted to the declared method argument type using HttpMessageReader instances. Supports reactive types.

HttpEntity<B>

Designed to provide access to request headers and body. The body is rendered using HttpMessageReader instances. Supports reactive types.

@RequestPart

Provides access to the component in the multipart/form-data request. Supports reactive types.

java.util.Map, org.springframework.ui.Model, and org.springframework.ui.ModelMap.

Designed to provide access to the model that is used in HTML controllers and displayed in templates as part of the view rendering.

@ModelAttribute

Intended to provide access to an existing attribute in the model (of which an instance is created if it is missing) with data binding and validation.

Note that using the @ModelAttribute annotation is optional - for example, setting its attributes. See the "Any Other Argument" section later in this table.

Errors, BindingResult

Provide access to validation and data binding errors for the command object, i.e. argument @ModelAttribute. The Errors or BindingResult argument must be declared immediately after the argument of the method being validated.

SessionStatus + class-level @SessionAttributes

Intended to mark completion of form processing, which causes session attributes declared through the @SessionAttributes annotation to be cleared at the class level. For more information, see the section on the @SessionAttributes annotation.

UriComponentsBuilder

Designed to prepare a URL associated with the host, port, scheme, and context path of the current request.

@SessionAttribute

Designed to provide access to any session attribute, as opposed to model attributes, stored in the session as a result of declaring the @SessionAttributes at the class level.

@RequestAttribute

Designed to provide access to request attributes.

Any other argument

If a method argument does not match any of the above, it resolves by default to @RequestParam if it is a simple type defined by BeanUtils#isSimplePropert, or to @ModelAttribute otherwise case.

Return Values

The following table lists the supported return values of a controller method. Note that reactive types from libraries such as Reactor, RxJava or others are generally supported for all return values.

Return value of the controller method Description

@ResponseBody

The return value is encoded through HttpMessageWriter instances and written to the response.

HttpEntity<B>, ResponseEntity<B>

The return value specifies that the full response, including HTTP headers, and body will be encoded through HttpMessageWriter instances and written into the response.

HttpHeaders

Designed to return a response with headers and no body.

String

The name of the view that should be recognized by ViewResolver instances and used in conjunction with an implicit model - defined through command objects and methods marked with the @ModelAttribute annotation. The handler method can also programmatically modify the model by declaring a Model argument.

View

A View instance intended for rendering along with an implicit model - defined through command objects and methods with the @ModelAttribute annotation. A handler method can also programmatically modify a model by declaring a Model argument.

java.util.Map, org.springframework.ui.Model

Attributes to add to the implicit model, with the view name implicitly determined based on the request path.

@ModelAttribute

An attribute added to the model when In this case, having views is implicitly determined based on the request path.

Note that the @ModelAttribute annotation is optional. See "Any other return value" later in this table.

Rendering

API for scripts for visualizing models and views.

void

A method with a return type of void, which is likely to be asynchronous (for example, Mono<Void>) is considered (or with a return value null), fully processed the response if it also has a ServerHttpResponse argument, a ServerWebExchange argument, or a @ResponseStatus annotation. The same is true if the controller has performed a positive check on the ETag or the lastModified timestamp.

If neither of the above values are true, the void return type may also indicate "no response body" for REST controllers or select a default view name for HTML controllers.

Flux<ServerSentEvent> ;, Observable<ServerSentEvent>, or other reactive type

Concerned with the generation of events sent by the server. The ServerSentEvent wrapper function can be omitted if only data needs to be written (however, text/event-stream must be requested or declared in the display via the produces).

Any other return value

If the return value does not match any of above, it is by default treated as the view name if it is a String or void (the default view name choice applies), or as a model attribute that must be added to the model, unless it is a simple type as defined by BeanUtils#isSimpleProperty, in which case it remains unresolved.

Type Conversion

Some annotated controller method arguments representing string-based request input (such as @RequestParam, @RequestHeader, @PathVariable, @MatrixVariable and @CookieValue)) may require a type conversion if the argument is not declared as a String.

In such cases, type conversion is applied automatically based on the configured converters. By default, simple types are supported (such as int, long, Date and others). Type conversion can be configured using WebDataBinder or by registering Formatters with FormattingConversionService.

A practical issue with type conversion is handling empty original string value. Such a value is considered missing if it becomes null as a result of a type conversion. This may be true for Long, UUID and other target types. If you need to allow null injection, then either use the required flag in the argument annotation, or declare the argument as @Nullable.

Matrix variables

In RFC 3986 pairs "name" are described -value" in path segments. In Spring WebFlux we call them "matrix variables", based on "old post" by Tim Berners-Lee, but they can also be called URI path parameters.

Matrix variables can appear in any path segment, with each variable separated by a semicolon and multiple values separated by commas - for example, "/cars;color=red,green;year=2012". Multiple values can also be specified through repeated variable names - for example, color=red;color=green;color=blue) .

Unlike Spring MVC, in WebFlux the presence or absence of matrix variables in the URL does not affect the rendering of requests. In other words, you don't have to use a URI variable to mask the contents of the variable. However, if you need to access matrix variables from a 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 this:

Java
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
Kotlin
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
fun findPet(@PathVariable petId: String, @MatrixVariable q: Int) {
// petId == 42
// q == 11
}

Considering that all path segments can contain matrix variables, sometimes you may need to define which path variable the matrix variable should be in, as shown in the following example:

Java
// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
Kotlin
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable(name = "q", pathVar = "ownerId") q1: Int,
@MatrixVariable(name = "q", pathVar = "petId") q2: Int) {
// q1 == 11
// q2 == 22
}

You can define a matrix variable as optional and provide a default value, as shown in the following example:

Java
// GET /pets/42
@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
Kotlin
// GET /pets/42
@GetMapping("/pets/{petId}")
fun findPet(@MatrixVariable(required = false, defaultValue = "1") q: Int) {
// q == 1
}

To get all the variables of a matrix, use MultiValueMap as shown in the following example:

Java
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable MultiValueMap<String, String> matrixVars,
@MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
Kotlin
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable matrixVars: MultiValueMap<String, String>,
@MatrixVariable(pathVar="petId") petMatrixVars: MultiValueMap<String, String>) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}

@RequestParam

You can use the @RequestParam annotation to bind request parameters to a method argument in the controller. The following code snippet demonstrates the usage:

Java
@Controller
@RequestMapping("/pets")
public class EditPetForm {
// ...
@GetMapping
public String setupForm(@RequestParam("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
// ...
}
  1. Using the @RequestParam annotation.
Kotlin
import org.springframework.ui.set
@Controller
@RequestMapping("/pets")
class EditPetForm {
// ...
@GetMapping
fun setupForm(@RequestParam("petId") petId: Int, model: Model): String {
val pet = clinic.loadPet(petId)
model["pet"] = pet
return "petForm"
}
// ...
}
  1. Using the @RequestParam annotation.
The concept of a "request parameter" in the Servlet API combines request parameters, form data, and multipart data into one. However, in WebFlux, each of them is accessed separately through ServerWebExchange. Although the @RequestParam annotation binds only to request parameters, you can use data binding to apply request parameters, form data, and multipart elements to a command object.

Method parameters that use the @RequestParam annotation are required by default, but you can also set a method parameter to be optional by setting the appropriate flag for @RequestParam to false or by declaring the argument with using the java.util.Optional wrapper.

If the target method parameter type is not String, type conversion is applied automatically.

If the @RequestParam annotation is declared for the Map<String, String> or MultiValueMap<String, String> argument, the Map is populated with all request parameters.

Note that using the @RequestParam annotation is optional - for example, to set its attributes. By default, any argument that is a simple value type (as defined by BeanUtils#isSimpleProperty) and is not resolved by any other argument resolver, is treated as if it were annotated with @RequestParam.

@RequestHeader

You can use the @RequestHeader annotation to bind a request header to a method argument in a controller.

The following example shows a request with headers:

Host                    localhost:8080
Accept                  text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language         fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding         gzip,deflate
Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive              300        

The following example code allows you to get the value of the Accept-headers Encoding and Keep-Alive:

Java
@GetMapping("/demo")
public void handle(
@RequestHeader("Accept-Encoding") String encoding, (1)
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
  1. Get the Accept-Encoding header values.
  2. Get the header values Keep-Alive.
Kotlin
@GetMapping("/demo")
fun handle(
@RequestHeader("Accept-Encoding") encoding: String, (1)
@RequestHeader("Keep-Alive") keepAlive: Long) {
//...
}
  1. Getting the values of the Accept-Encoding.
  2. Get the values of the Keep-Alive header.

If the type of the target method parameter is not String, type conversion is applied automatically.

If the @RequestHeader annotation is used in the Map<String, String> argument, MultiValueMap<String, String> or HttpHeaders, Map is filled with all header values.

Has a built-in support for converting a comma-separated string to an array or collection of strings or other types known to the type conversion system. For example, a method parameter annotated with @RequestHeader("Accept") can be of type String, as well as String[] or List<String>.

@CookieValue

You can use the @CookieValue annotation to bind an HTTP cookie value to method argument in the controller.

The following example shows a request with a cookie:

JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

The following code example shows how to get the cookie value:

Java
@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) {
//...
}
  1. Getting the cookie value.
Kotlin
@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) {
//...
}
  1. Getting the cookie value.

If the target method parameter type is not String, type conversion is applied automatically.

@ModelAttribute

You can use the @ModelAttribute annotation on a method argument to access the attribute from the model or create an instance of it if it is missing. The model attribute is also superimposed with the values of query parameters and form fields, the names of which coincide with the names of the fields. This is called data binding, and it eliminates the need to parse and transform individual query parameters and form fields. The following example shows how a Pet instance is bound:

Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { }
  1. Binding the Pet instance.
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { }
  1. Binding the Pet instance.

The Pet instance in the previous example is resolved as follows:

  • From the model, if it has already been added via Model.

  • From an HTTP session via the @SessionAttributes annotation.

  • From a call default constructor.

  • From a call to the "main constructor" with arguments corresponding to request parameters or form fields. Argument names are specified through the @ConstructorProperties annotation for JavaBeans or through run-time bytecode-stored parameter names.

After obtaining an instance of the model attribute, it is applied data binding. The WebExchangeDataBinder class maps query parameter and form field names to field names of the target Object. The corresponding fields are filled in after type conversion where necessary.

Data binding can lead to errors. By default, a WebExchangeBindException is thrown, but to check for such errors in a controller method, you can add a BindingResult argument directly next to the @ModelAttribute annotation, as shown in the following example :

Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
  1. Adding BindingResult.
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String {
if (result.hasErrors()) {
return "petForm"
}
// ...
}
  1. Adding BindingResult.

You can automatically apply validation after data binding by adding the javax.validation.Valid annotation or the @Validated annotation from Spring. The following example uses the @Valid annotation:

Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
  1. Using the @Valid annotation for the model attribute argument.
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String {
if (result.hasErrors()) {
return "petForm"
}
// ...
}
  1. Using the @Valid annotation for the model attribute argument.

Spring WebFlux, unlike Spring MVC, it supports reactive types in the model - for example, Mono<Account> or io.reactivex.Single<Account>. You can declare an argument marked with the @ModelAttribute annotation with or without a reactive type wrapper function, after which it will be resolved accordingly to the actual value if necessary. However, note that in order to use the BindingResult argument, you must declare the argument with the @ModelAttribute annotation before it, without a reactive type wrapper function, as shown earlier. In addition, you can handle any errors through a reactive type, as shown in the following example:

Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
return petMono
.flatMap(pet -> {
    // ...
})
.onErrorResume(ex -> {
    // ...
});
}
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") petMono: Mono<Pet>): Mono<String> {
return petMono
    .flatMap { pet ->
        // ...
    }
    .onErrorResume{ ex ->
        // ...
    }
}

Note that use the @ModelAttribute annotation is optional - for example, to set its attributes. By default, any argument that is not a simple value type (as defined by BeanUtils#isSimpleProperty) and is not resolved by any other argument resolver, is treated as if it were annotated with @ModelAttribute.

@SessionAttributes

@SessionAttributes is used to store model attributes in WebSession between requests. This is a type-level annotation that declares the session attributes used by a particular controller. This typically lists the model attribute names or model attribute types that should be transparently stored in the session for subsequent access requests.

Consider the following example:

Java
@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
}
  1. Using the @SessionAttributes annotation.
Kotlin
@Controller
@SessionAttributes("pet")
class EditPetForm {
// ...
}
  1. Using the @SessionAttributes annotation.

On the first request, if a model attribute named pet is added to the model, it is automatically promoted to and stored in the WebSession. It remains there until another controller method uses the SessionStatus method argument to clear the storage, as shown in the following example:

Java
@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors()) {
    // ...
}
    status.setComplete();
    // ...
}
}
}
  1. Using the annotation @SessionAttributes.
  2. Using the SessionStatus variable.
Kotlin
@Controller
@SessionAttributes("pet")
class EditPetForm {
// ...
@PostMapping("/pets/{id}")
fun handle(pet: Pet, errors: BindingResult, status: SessionStatus): String {
if (errors.hasErrors()) {
    // ...
}
status.setComplete()
// ...
}
}
  1. Using the @SessionAttributes annotation.
  2. Using the SessionStatus variable.

@SessionAttribute

If you need access to pre-existing session attributes that are managed globally (that is, outside the controller - e.g. filter) and may or may not be present, you can use the @SessionAttribute annotation on the method parameter, as shown in the following example:

Java
@GetMapping("/")
public String handle(@SessionAttribute User user) {
// ...
}
  1. Use the @SessionAttribute annotation.
Kotlin
@GetMapping("/")
fun handle(@SessionAttribute user: User): String {
// ...
}
  1. Use the @SessionAttribute annotation.

For use cases that require adding or removing session attributes, note the ability to inject WebSession into a controller method.

To temporarily store model attributes in a session as part of the controller workflow, consider using SessionAttributes.

@RequestAttribute

Similar to the @SessionAttribute annotation, you can use the @RequestAttribute annotation to access existing attributes request created earlier (for example, WebFilter), as shown in the following example:

Java
@GetMapping("/")
public String handle(@RequestAttribute Client client) {
// ...
}
  1. Using the @RequestAttribute annotation.
Kotlin
@GetMapping("/")
fun handle(@RequestAttribute client: Client): String {
// ...
}
  1. Using annotation @RequestAttribute.

Multipart content

ServerWebExchange provides access to multipart content. The best way to handle a file upload form (for example, from a browser) in a controller is to bind the data to a command object, as shown in the following example:

Java
class MyForm {
private String name;
private MultipartFile file;
// ...
}
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
// ...
}
}
Kotlin
class MyForm(
val name: String,
val file: MultipartFile)
@Controller
class FileUploadController {
@PostMapping("/form")
fun handleFormUpload(form: MyForm, errors: BindingResult): String {
// ...
}
}

You can also send multi-part requests from non-browser clients in scripts using RESTful services. The following example uses a file along with JSON:

POST /someUrl
Content-Type: multipart/mixed
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit
{
"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...

You can access individual components using the @RequestPart annotation, as shown in the following example:

Java
@PostMapping("/")
public String handle(@RequestPart("meta-data") Part metadata,
@RequestPart("file-data") FilePart file) {
// ...
}
  1. Usage @RequestPart annotation to get metadata.
  2. Use the @RequestPart annotation to get a file.
Kotlin
@PostMapping("/")
fun handle(@RequestPart("meta-data") Part metadata,
@RequestPart("file-data") FilePart file): String {
// ...
}
  1. Use the @RequestPart annotation to retrieve metadata.
  2. Use the @RequestPart annotation to retrieve a file.

To deserialize the raw content of a component (for example, in JSON - similar to the @RequestBody annotation), you can declare a specific target Object instead of a Part, as shown in the following example:

Java
@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata) {
// ...
}
  1. Use the @RequestPart annotation to retrieve metadata.
Kotlin
@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData): String {
// ...
}
  1. Using the @RequestPart annotation to obtain metadata.

You can use the @RequestPart annotation in combination with the annotation javax.validation.Valid or the @Validated annotation from Spring, which will cause the standard Bean Validation to be applied. Validation errors result in a WebExchangeBindException, resulting in a 400 response (BAD_REQUEST). The exception contains a BindingResult with error details and can also be handled in a controller method by declaring an argument with an asynchronous wrapper function and then use the statements associated with the error:

Java
@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
// use one of the onError*...
}
operators
Kotlin
@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData): String {
// ...
}

To access all multi-component data as a MultiValueMap, you can use the @RequestBody, as shown in the following example:

Java
@PostMapping("/")
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) {
// ...
}
  1. Using the @RequestBody annotation.
Kotlin
@PostMapping("/")
fun handle(@RequestBody parts: MultiValueMap<String, Part>): String {
// ...
}
  1. Using the @RequestBody annotation.

For sequential streaming access to multi-part data, you can use the @RequestBody annotation with Flux<Part> (or Flow<Part> in Kotlin), as shown in the following example:

Java
@PostMapping("/")
public String handle(@RequestBody Flux<Part> parts) {
// ...
}
  1. Using the @RequestBody annotation.
Kotlin
@PostMapping("/")
fun handle(@RequestBody parts: Flow<Part>): String {
// ...
}
  1. Using the @RequestBody annotation.

@RequestBody

Annotation @RequestBody can be used to read the request body and deserialize it into a object Object HttpMessageReader. The following example uses an argument annotated with @RequestBody:

Java
@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
// ...
}
Kotlin
@PostMapping("/accounts")
fun handle(@RequestBody account: Account) {
// ...
}

Unlike Spring MVC, in WebFlux the argument of a method marked with the @RequestBody annotation supports reactive types and fully non-blocking reading and (client-server) streaming.

Java
@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
// ...
}
Kotlin
@PostMapping("/accounts")
fun handle(@RequestBody accounts: Flow<Account>) {
// ...
}

You can use the HTTP message codecs option in the WebFlux configuration to configure or customize message readers.

The @RequestBody annotation can be used in combination with the javax.validation.Valid or the @Validated annotation from Spring, which causes the standard Bean Validation to be applied. Validation errors cause WebExchangeBindException, resulting in a 400 (BAD_REQUEST) response. The exception contains a BindingResult with error details and can also be handled in a controller method by declaring an argument with an asynchronous wrapper function and then using the error-related statements:

Java
@PostMapping("/accounts")
public void handle(@Valid @RequestBody Mono<Account> account) {
// используем один из операторов onError*...
}
Kotlin
@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Mono<Account>) {
// ...
}

HttpEntity

HttpEntity is more or less identical to the @RequestBody annotation in terms of usage, but is based on the entity -a container that opens the headers and body of the request. The following example uses HttpEntity:

Java
@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
// ...
}
Kotlin
@PostMapping("/accounts")
fun handle(entity: HttpEntity<Account>) {
// ...
}

@ResponseBody

The @ResponseBody annotation can be used in our method so that the return is serialized into the response body via HttpMessageWriter. The following example shows how to do this:

Java
@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
// ...
}
Kotlin
@GetMapping("/accounts/{id}")
@ResponseBody
fun handle(): Account {
// ...
}

@ResponseBody is also supported at the class level, in which case it is inherited by all controller methods. This is the effect of the @RestController annotation, which is nothing more than a meta annotation marked with the @Controller and @ResponseBody annotations.

@ResponseBody supports reactive types, which means you can return Reactor or RxJava types and receive the asynchronous values they create in response.

You can combine methods marked with the @ResponseBody annotation with JSON serialization views.

You can use the HTTP message codecs option in the WebFlux configuration to configure or customize message readers.

ResponseEntity

The ResponseEntity attribute is similar to the @ResponseBody annotation, but with a status and headers. For example:

Java
@GetMapping("/something")
public ResponseEntity<String> handle() {
String body = ... ;
String etag = ... ;
return ResponseEntity.ok().eTag(etag).body(body);
}
Kotlin
@GetMapping("/something")
fun handle(): ResponseEntity<String> {
val body: String = ...
val etag: String = ...
return ResponseEntity.ok().eTag(etag).build(body)
}

WebFlux supports the use of reactive type single-value for asynchronously creating ResponseEntity and/or single- and multi-value reactive types for the body. This allows you to use different asynchronous responses with ResponseEntity as follows:

  • ResponseEntity<Mono<T>> or ResponseEntity<Flux<T>>immediately communicates the status and headers of the response, while the response body is provided asynchronously at a later point. We use Mono if the body consists of 0..1 values, or Flux if it can create several values.

  • Mono<ResponseEntity<T>> provides all three parameters - response status, headers and body - asynchronously at a later point. This allows you to change the response status and headers depending on the results of asynchronous request processing.

  • Mono<ResponseEntity<Mono<T>>> or Mono<ResponseEntity<Flux<T>>> are another possible, although less common, alternative. They first provide the response status and headers asynchronously, and then secondly the response body, also asynchronously.

Jackson JSON

Spring provides library support Jackson JSON.

JSON Views

Spring WebFlux provides built-in support for serialization views from the Jackson library, which allows you to render exclusively a subset of all Object fields. To use it with controller methods annotated with @ResponseBody, or with the ResponseEntity class, you can leverage the @JsonView annotation from Jackson to activate the serialization view class, like shown in the following example:

Java
@RestController
public class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
@JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
Kotlin
@RestController
class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView::class)
fun getUser(): User {
return User("eric", "7!jd#h23")
}
}
class User(
@JsonView(WithoutPasswordView::class) val username: String,
@JsonView(WithPasswordView::class) val password: String
) {
interface WithoutPasswordView
interface WithPasswordView : WithoutPasswordView
}
Annotation @JsonView allows you to use an array of view classes, but you can only specify one per controller method. Use a composite interface if you need to activate multiple views.

Model

You can use the @ModelAttribute annotation:

  • As a method argument in methods with the @RequestMapping annotation to create or provide access to an object from the model and bind it to a request via WebDataBinder .

  • As a method-level annotation in classes marked with the @Controller or @ControllerAdvice annotations to help initialize the model before calling a method with the @RequestMapping annotation.

  • On a method with the @RequestMapping annotation to mark its return value as a model attribute .

This section describes methods annotated with @ModelAttribute, or the second item from the previous list. A controller can have any number of methods marked with the @ModelAttribute annotation. All such methods are called before methods marked with the @RequestMapping annotation in the same controller. A method with the @ModelAttribute annotation can also be shared between controllers through the @ControllerAdvice annotation.

Methods with the @ModelAttribute annotation have flexible method signatures. They support many of the same arguments as methods marked with the @RequestMapping annotation, except for the @ModelAttribute annotation itself or anything associated with the request body.

The following example uses a method marked with the @ModelAttribute annotation:

Java
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountRepository.findAccount(number));
// add more ...
}
Kotlin
@ModelAttribute
fun populateModel(@RequestParam number: String, model: Model) {
model.addAttribute(accountRepository.findAccount(number))
// add more ...
}

The following example adds only one attribute:

Java
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountRepository.findAccount(number);
}
Kotlin
@ModelAttribute
fun addAccount(@RequestParam number: String): Account {
return accountRepository.findAccount(number);
}
If the name is not explicitly specified, then the default name is chosen based on the type, as this is described in the javadoc at agreements. You can always assign an explicit name using the addAttribute overload or through the name attribute in the @ModelAttribute annotation (for the return value).

Spring WebFlux, unlike Spring MVC, explicitly supports reactive types in the model (for example, Mono<Account> or io.reactivex.Single<Account>). Such asynchronous model attributes can be transparently resolved (and the model updated) to their actual values during a call to @RequestMapping if the argument with the @ModelAttribute annotation is declared without a wrapper function, as shown in the following example:

Java
@ModelAttribute
public void addAccount(@RequestParam String number) {
Mono<Account> accountMono = accountRepository.findAccount(number);
model.addAttribute("account", accountMono);
}
@PostMapping("/accounts")
public String handle(@ModelAttribute Account account, BindingResult errors) {
// ...
}
Kotlin
import org.springframework.ui.set
@ModelAttribute
fun addAccount(@RequestParam number: String) {
val accountMono: Mono<Account> = accountRepository.findAccount(number)
model["account"] = accountMono
}
@PostMapping("/accounts")
fun handle(@ModelAttribute account: Account, errors: BindingResult): String {
// ...
}

In addition, any model attributes that have a reactive wrapper function are resolved to their actual values (and the model is updated) immediately before the view is rendered.

It is also possible to use the @ModelAttribute annotation as a method-level annotation for methods marked with the @RequestMapping annotation, in which case the return value of the method marked annotation @RequestMapping, interpreted as a model attribute. This is usually not required because this is the default behavior in HTML controllers unless the return value is a String, which would otherwise be interpreted as the name of the view. The @ModelAttribute annotation can also help you customize the model attribute name, as shown in the following example:

Java
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
public Account handle() {
// ...
return account;
}
Kotlin
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
fun handle(): Account {
// ...
return account
}

DataBinder

Classes with annotation @Controller or @ControllerAdvice may have methods annotated with @InitBinder to initialize WebDataBinder instances. Those, in turn, are used to:

  • Binding request parameters (that is, form or request data) to a model object.

  • Converts String-based request values (such as request parameters, path variables, headers, cookies, etc.) to the target type of controller method arguments.

  • Format model object values as String values when rendering HTML forms.

Methods with the @InitBinder annotation can register Controller-specific java.beans.PropertyEditor or Converter and Formatter components from Spring. You can also use the WebFlux Java configuration to register the Converter and Formatter types in a globally shared FormattingConversionService.

Methods marked with the @InitBinder annotation support many of the same arguments as methods with the @RequestMapping annotation, with the exception of arguments annotated with @ModelAttribute (command object). Typically they are declared with a WebDataBinder argument to register and a void return value. The following example uses the @InitBinder annotation:

Java
@Controller
public class FormController {
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
// ...
}
  1. Using the @InitBinder annotation.
Kotlin
@Controller
class FormController {
@InitBinder
fun initBinder(binder: WebDataBinder) {
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
dateFormat.isLenient = false
binder.registerCustomEditor(Date::class.java, CustomDateEditor(dateFormat, false))
}
// ...
}

Alternatively, when using Formatter based configuration via the generic FormattingConversionService, you can reuse the same approach and register Formatter instances code> specific to the controller, as shown in the following example:

Java
@Controller
public class FormController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
// ...
}
  1. Add a custom formatter (in this case DateFormatter).
Kotlin
@Controller
class FormController {
@InitBinder
fun initBinder(binder: WebDataBinder) {
binder.addCustomFormatter(DateFormatter("yyyy-MM-dd"))
}
// ...
}
  1. Adding a custom formatter (in this case DateFormatter).

Model structure

In the context of web applications, data binding involves binding HTTP request parameters (that is, form data or request parameters) to properties of a model object and its sub-objects.

Only public properties corresponding to are open for data binding JavaBeans naming conventions - for example, the public String getFirstName() and public void setFirstName(String) methods for the firstName property.

The model object and its nested object graph are also sometimes called the command object, base form object or POJO (“good old Java object”).

By default, Spring allows binding to all public properties in the model object graph. This means that you need to think carefully about what public properties the model has, since the client can target any public property, even those not intended for the use case.

For example, if you have an HTTP data endpoint -forms, a malicious client can pass values for properties that exist in the model's object graph but are not part of the HTML form presented in the browser. This may result in the model object and any of its sub-objects being set to data that is not expected to be updated.

The recommended approach is to use a specialized model object that opens only those properties that are relevant to form submission. For example, in a form to change a user's email address, the model object must declare a minimal set of properties, as in the following ChangeEmailForm.

public class ChangeEmailForm {
private String oldEmailAddress;
private String newEmailAddress;
public void setOldEmailAddress(String oldEmailAddress) {
this.oldEmailAddress = oldEmailAddress;
}
public String getOldEmailAddress() {
return this.oldEmailAddress;
}
public void setNewEmailAddress(String newEmailAddress) {
this.newEmailAddress = newEmailAddress;
}
public String getNewEmailAddress() {
return this.newEmailAddress;
}
}

If you are unable or unwilling to use a specialized model object for each use case of data binding, then mustrestrict the properties that are allowed for data binding. Ideally, this can be achieved by registering allowed field patterns using the setAllowedFields() method in WebDataBinder.

For example, to register valid field patterns in your application, you can implement a method marked with the @InitBinder annotation in a component with the @Controller or @ControllerAdvice annotation, as shown below:

@Controller
public class ChangeEmailController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
}
// @RequestMapping methods, etc.
}

In addition to registering valid field templates, you can also register disallowed field patterns using the setDisallowedFields() method in DataBinder and its subclasses. However, note that the "allow list" is safer than the "deny list". Therefore, using setAllowedFields() should be preferred over using setDisallowedFields().

Note that matching against allowed field patterns is case sensitive; while pattern matching of invalid fields is not. Additionally, a field that matches a pattern of invalid fields will not be accepted, even if it also matches a pattern in the list of valid fields.

It is very important to get it right configure valid and invalid field templates when directly opening the domain model for data binding. Otherwise, it will be a big security risk.

Additionally, it is strongly recommended not to use types from your domain model, such as JPA or Hibernate entities, as a model object in data binding scenarios.

Managing exceptions

Classes with the @Controller and @ControllerAdvice annotation can have methods marked with the @ExceptionHandler annotation, to handle exceptions from controller methods. The following example contains this handler method:

Java
@Controller
public class SimpleController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
  1. Declaration of the @ExceptionHandler annotation.
Kotlin
@Controller
class SimpleController {
// ...
@ExceptionHandler
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
}
  1. Annotation declaration @ExceptionHandler.

An exception can be a propagated high-level exception (for example, a directly thrown IOException ) or a nested cause inside a wrapper exception (for example, IOException wrapped inside IllegalStateException).

For matching exception types, it is preferable to declare the target exception as a method argument, like shown in the previous example. Additionally, the annotation declaration can narrow down the types of exceptions that must match. We generally recommend being as specific as possible in the argument signature and declaring the main root exception mappings to the @ControllerAdvice annotation in the appropriate order.

A method with the @ExceptionHandler annotation in WebFlux supports the same method arguments and return values as a method with the @RequestMapping annotation, except for the method arguments associated with the request body and with the @ModelAttribute annotation.

Support for methods annotated with @ExceptionHandler in Spring WebFlux is provided by the HandlerAdapter for methods with the @RequestMapping.

Handling exceptions in the REST API

A common requirement for REST-based services is to include error information in the response body. The Spring Framework does not do this automatically because the inclusion of error information in the response body is application-specific. However, the @RestController annotation can use methods marked with the @ExceptionHandler annotation with a ResponseEntity return value to set the response status and body. Such methods can also be declared in classes with the @ControllerAdvice annotation to apply them globally.

Note that that Spring WebFlux does not have an equivalent for ResponseEntityExceptionHandler from Spring MVC, since WebFlux only throws ResponseStatusException (or their subclasses) and they do not need to be converted to HTTP status code.

ControllerAdvice

Typically, methods marked with the @ExceptionHandler, @InitBinder and @ModelAttribute annotations are used in within the @Controller class (or class hierarchy) in which they are declared. If you want such methods to apply more globally (across all controllers), you can declare them in a class annotated with @ControllerAdvice or @RestControllerAdvice.

The @ControllerAdvice annotation is annotated with @Component, which means that such classes can be registered as Spring beans via component scanning. The @RestControllerAdvice annotation is a compound annotation that is marked with both the @ControllerAdvice annotation and the @ResponseBody annotation, which means that methods with the annotation @ExceptionHandler are rendered in the response body through message mapping (as opposed to view resolution or template rendering).

At startup, framework classes for methods annotated with @RequestMapping and @ExceptionHandler detect Spring beans annotated with @ControllerAdvice and then apply their methods at runtime. Global methods annotated with @ExceptionHandler (from the @ControllerAdvice annotation) are applied after local ones (from the @Controller annotation ). In contrast, global methods with the @ModelAttribute and @InitBinder annotations are applied before local ones.

By default, methods marked with the @ControllerAdvice apply to every request (that is, all controllers), but you can narrow down the controllers by using annotation attributes, as shown in the following example:

Java
// Target all controllers annotated with @RestController @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// Target all controllers in certain packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all controllers assigned to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}
Kotlin
// Target all controllers annotated with @RestController
@ControllerAdvice(annotations = [RestController::class])
public class ExampleAdvice1 {}
// Select all controllers in the target certain packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all controllers assigned to certain classes
@ControllerAdvice(assignableTypes = [ControllerInterface::class, AbstractController::class])
public class ExampleAdvice3 {}

The selectors in the previous example are evaluated during execution and can negatively impact performance when used extensively. For more information, see the javadoc annotation @ControllerAdvice.