Handler methods

Module 5. Spring
Level 9 , Lesson 3
Available

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

Method arguments

The following table describes supported controller method arguments. Reactive types are not supported for any arguments.

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

Controller method argument Description

WebRequest, NativeWebRequest

Provides typed access to request parameters and request and session attributes, without directly using the Servlet API.

javax.servlet. ServletRequest, javax.servlet.ServletResponse

Intended to select any specific type of request or response - for example, ServletRequest, HttpServletRequest or MultipartRequest, MultipartHttpServletRequest from Spring.

javax.servlet.http.HttpSession

Ensures that a session exists. As a consequence, such an argument is never null. Please note that session access is not thread safe. Consider setting the synchronizeOnSession flag of the RequestMappingHandlerAdapter instance to true if multiple requests are allowed to access the session simultaneously.

javax.servlet.http.PushBuilder

Servlet 4.0 build pusher API for programmatic push ) HTTP/2 resources. Note that, according to the Servlet specification, the injected PushBuilder instance may be empty if the client does not support this HTTP/2 feature.

java.security.Principal

The current authenticated user - possibly a concrete implementation class of Principal, if it is known.

Note that this argument is not accelerated recognized if it has an annotation designed to allow a custom resolver to recognize it before falling back to default recognition via HttpServletRequest#getUserPrincipal. For example, Authentication from Spring Security implements Principal and will be injected as such via HttpServletRequest#getUserPrincipal unless it is also annotated with @AuthenticationPrincipal , otherwise in this case it will be resolved by the custom Spring Security resolver via Authentication#getPrincipal.

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.

java.io.InputStream, java.io.Reader

Designed to provide access to the raw request body as it is presented in the Servlet API.

java.io. OutputStream, java.io.Writer

Designed to provide access to the raw response body exposed by the Servlet API.

@PathVariable

Designed to provide access to URI pattern variables.

@MatrixVariable

Designed to provide access to pairs "name-value" in URI path segments.

@RequestParam

Designed to provide access to servlet request parameters, including multipart files. Parameter values are converted to the declared type of the method arguments.

Note that the use of the @RequestParam annotation is optional for simple parameter values. See "Any other argument" at the end of 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

To access the HTTP request body. The body content is converted to the declared method argument type using HttpMessageConverter implementations.

HttpEntity<B>

Designed to provide access to request headers and body. The body is converted using HttpMessageConverter.

@RequestPart

Designed to provide access to a component in a multipart/form-data request by converting the body of that component using HttpMessageConverter.

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

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

RedirectAttributes

Specifies the attributes that will be used in the event of a redirect (that is, will be added to request line), flash attributes that will be temporarily stored until the request arrives after a redirect.

@ModelAttribute

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

Note that use the @ModelAttribute annotation is optional (for example, to set its attributes). See "Any other argument" at the end of this table.

Errors, BindingResult

Designed to provide access to validation and data binding errors for a command object (that is, an argument with the @ModelAttribute annotation) or argument validation errors with annotations @RequestBody or @RequestPart. The argument Errors or BindingResult must be declared immediately after the argument of the validated method.

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.

UriComponentsBuilder

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

@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 annotation at the class level.

@RequestAttribute

Designed to provide access to request attributes.

Any other argument

If argument method does not match any of the previous values in this table and is a simple type (as defined by BeanUtils#isSimpleProperty), it resolves to @RequestParam. Otherwise, it resolves to @ModelAttribute.

Return values

In the following The table describes the supported return values of the controller method. Reactive types are supported for all return values.

Return value of the controller method Description

@ResponseBody

The return value is converted through the HttpMessageConverter implementation and written to the response.

HttpEntity<B>, ResponseEntity<B>

Return value specifying the full response (including HTTP headers and body), converted through the HttpMessageConverter implementation and written to the response.

HttpHeaders

Designed to return a response with headers and no body.

String

The name of the view to be recognized by ViewResolver implementations and used in conjunction with the implicit model - defined through command objects and annotated methods @ModelAttribute. The handler method can also programmatically customize the model by declaring a Model argument.

View

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

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

Attributes added to the implicit model, with the view name implicitly defined via RequestToViewNameTranslator .

@ModelAttribute

Attribute , added to the model whose view name is implicitly specified via RequestToViewNameTranslator.

Note that the @ModelAttribute annotation is optional. See the "Any Other Return Value" section at the end of this table.

ModelAndView object

The view and model attributes to use and, if necessary, the response status.

void

A method with a return type void (or a return value null) is considered to have fully processed the response if it also has ServletResponse, the OutputStream argument, or the @ResponseStatus annotation. The same is true if the controller has performed a positive ETag or lastModified timestamp check.

If neither of the above is true, the return type is void can also indicate "no response body" for REST controllers or a default view name choice for HTML controllers.

DeferredResult<V>

Produces any of the previous return values asynchronously from any thread - for example, as a result of an event or callback.

Callable<V>

Produces any of the above returned values asynchronously in a thread managed by Spring MVC.

ListenableFuture<V>, java.util.concurrent .CompletionStage<V>, java.util.concurrent.CompletableFuture<V>

A more convenient alternative to DeferredResult (for example, if the main service returns one of them).

ResponseBodyEmitter, SseEmitter

Asynchronously spawns a stream of objects to write to the response using HttpMessageConverter implementations. Also supported as a body for ResponseEntity.

StreamingResponseBody

Asynchronous writing to OutputStream of the response. Also supported as a body for ResponseEntity.

Reactive types - Reactor, RxJava, or others via ReactiveAdapterRegistry

Alternative to DeferredResult with multi-valued streams (e.g. Flux, Observable) , collected in List.

In streaming scenarios (for example, text/event-stream, application/json+stream ) instead, SseEmitter and ResponseBodyEmitter are used, where the blocking I/O ServletOutputStream is executed on a Spring MVC-managed thread and the callback occurs upon completion of each records.

Any other return value

Any return value that does not match any of previous values in this table and is a String or void, treated as the view name (the default view name selection via RequestToViewNameTranslator is applied) if it is not a simple type as defined by BeanUtils#isSimpleProperty. Values that are simple types remain unresolved.

Type conversion

Some annotated controller method arguments representing request input based on String (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, the type conversion is applied automatically based on the configured converters. By default, simple types are supported (int, long, Date and others). You can configure type conversion via WebDataBinder or by registering Formatters with the FormattingConversionService.

A practical issue with type conversion is handling empty the original value of the string. 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.

Starting in version 5.3, non-null arguments will be honored even after type conversion. If the handler method is expected to accept a null value, then either declare the argument as @Nullable, or mark it as required=false in the appropriate @RequestParam etc. This is the best practice and recommended solution for regressions encountered when upgrading to version 5.3.

In addition, you can specially handle, for example, the MissingPathVariableException exception that is raised if the annotation @PathVariable is required. A Null value after conversion will be treated as an empty original value, so the corresponding Missing...Exception exception will be generated.

Matrix Variables

In RFC 3986 describes name-value pairs in path segments. In Spring MVC we call them "matrix variables", based on "old post" by Tim Berners-Lee, but 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 a comma (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).

If the URL is expected to contain matrix variables, then the request mapping for a controller method must use a URI variable to mask the contents of such a matrix variable and ensure successful request mapping regardless of the order or presence of the matrix variables. The following example uses a matrix variable:

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
}

Given that all path segments can contain matrix variables, sometimes it can you will need to determine which path variable the matrix variable should be in. The following example shows how to do this:

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

// GET /owners/42;q=11/pets/21;q=22
@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
}

A matrix variable can be defined as optional and have 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 matrix variables, you can 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]
}

Please note that the use of matrix variables must be enabled. In MVC configuration in Java, you need to set UrlPathHelper when removeSemicolonContent=false via Path Matching. In the MVC namespace on XML you can set <mvc:annotation-driven enable-matrix-variables="true"/>.

@RequestParam

The @RequestParam annotation can be used to bind servlet request parameters (that is, request parameters or form data) to a method argument in the controller.

In the following example shows how to do this:

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 for bindings petId.
Kotlin

import org.springframework.ui.set
@Controller
@RequestMapping("/pets" )
class EditPetForm {
    // ...
    @GetMapping
    fun setupForm(@RequestParam("petId") petId: Int, model: Model): String { 
        val pet = this.clinic.loadPet(petId);
        model["pet"] = pet
        return "petForm"
    }
    // ...
}
  1. Using the @RequestParam annotation to bind the petId.

By default, method parameters that use this annotation are required, but you can specify that the method parameter is optional by setting the required flag of the @RequestParam annotation to false or by declaring the argument using the java.util.Optional wrapper function.

Type conversion is applied automatically if the target method's parameter type is not String.

Declaring the argument type as an array or list allows multiple parameter values for the same parameter name.

If the @RequestParam annotation is declared as Map<String, String> or MultiValueMap<String , String> without specifying a parameter name in the annotation, then the Map is populated with the request parameter values for each given parameter name.

Note that using the @RequestParam annotation is optional (for example, to set 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 the request header to a method argument in the controller.

Consider the following 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 shows how to get the value of the Accept-Encoding headers and Keep-Alive:

Java

@GetMapping("/demo")
public void handle(
        @RequestHeader(" Accept-Encoding") String encoding, 
        @RequestHeader("Keep-Alive") long keepAlive) { 
    //...
}
  1. We get the values of the Accept-Encoding header.
  2. We get the values of the Keep-Alive header.
Kotlin

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

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

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

There is 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

The @CookieValue annotation can be used to bind HTTP data type values cookie to a method argument in the controller.

Consider a request with the following cookie:

JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

The following example shows how to get the cookie value:

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

@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) { 
    //...
}
  1. Get the value of the JSESSIONID cookie.

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

@ModelAttribute

The @ModelAttribute annotation can be used 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 overlaid with values from the servlet request parameters via HTTP, 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 to do this:

Java

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) {
    // method logic...
}
Kotlin

@PostMapping( "/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String {
    // method logic...
}

The above Pet instance is obtained in one of the following ways:

  • Retrieved from the model where it may have been added by a method with the @ModelAttribute annotation.

  • Retrieved from the HTTP session if the model attribute was specified in the @SessionAttributes annotation at the class level.

  • Received through some kind of Converter, where the name of the model attribute is the same as the name of the request value, for example, a path variable or request parameter (see. following example).

  • Created using the default constructor.

  • Created via the "primary constructor" with arguments corresponding to the parameters servlet request. Argument names are specified through the @ConstructorProperties annotation of JavaBeans classes or through run-time bytecode-stored parameter names.

An alternative to using a method with the @ModelAttribute annotation to provide it, or to call the framework to create a model attribute, is to use Converter<String, T> to provide an instance. This is applicable if the model attribute name is the same as the name of a query value, such as a path variable or query parameter, and there is a Converter from String to the model attribute type. In the following example, the model attribute name is account, which corresponds to the URI path variable account, and there is also a registered Converter<String, Account>. which can load Account from the data store:

Java

@PutMapping("/accounts/{account}")
public String save( @ModelAttribute("account") Account account) {
    // ...
}
Kotlin

@PutMapping ("/accounts/{account}")
fun save(@ModelAttribute("account") account: Account): String {
    // ...
}

After receiving the model attribute instance, data binding is applied. The WebDataBinder class maps servlet request parameter names (query parameters and form fields) to target object field names. Matched fields are populated after applying type conversion where necessary.

Data binding can cause errors. By default, a BindException exception is thrown. However, 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 next to the annotation @ModelAttribute.
Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/ edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { 
    if (result.hasErrors ()) {
        return "petForm"
    }
    // ...
}
  1. Adding BindingResult next to the @ModelAttribute annotation.

In some cases, you may need to access a model attribute without data binding. For such cases, you can inject a Model into the controller and access it directly, or alternatively set @ModelAttribute(binding=false), as shown in the following example:

Java

@ModelAttribute
public AccountForm setUpForm() {
    return new AccountForm();
}
@ModelAttribute
public Account findAccount(@PathVariable String accountId) {
    return accountRepository.findOne(accountId);
}
@PostMapping("update")
public String update(@Valid AccountForm form, BindingResult result,
        @ModelAttribute(binding=false) Account account) { 
    // ...
}
  1. Setting @ModelAttribute(binding=false) .
Kotlin

@ModelAttribute
fun setUpForm(): AccountForm {
    return AccountForm()
}
@ModelAttribute
fun findAccount(@PathVariable accountId : String): Account {
    return accountRepository.findOne(accountId)
}
@PostMapping("update")
fun update(@Valid form: AccountForm, result: BindingResult,
           @ModelAttribute(binding = false) account: Account): String { 
    // ...
}
  1. Setting @ModelAttribute(binding=false).

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

Java

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { 
    if (result.hasErrors()) {
        return "petForm";
    }
    // ...
}
  1. Validate the Pet instance.
Kotlin

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

Note that using the @ModelAttribute annotation is optional (for example, to set 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

The @SessionAttributes annotation is used to store model attributes in a servlet HTTP session 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.

The following example uses the @SessionAttributes annotation:

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, when a model attribute named pet is added to the model, it is automatically promoted and stored in the servlet's HTTP session. 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. Storing the Pet value in the servlet session.
  2. Clearing the Pet value from the servlet session.
Kotlin

@Controller
@SessionAttributes("pet") 
class EditPetForm {
    // ...
    @PostMapping("/pets/{id}")
    fun handle(pet: Pet, errors: BindingResult, status: SessionStatus): String {
        if (errors.hasErrors()) {
            // ...
        }
        status.setComplete() 
        // ...
    }
}
  1. Storing the Pet value in the servlet session.
  2. Clearing the Pet value from the servlet session.

@SessionAttribute

If you need access to pre-existing session attributes that are managed globally (that is, outside the controller - for example, by a filter) and can present or absent, you can use the @SessionAttribute annotation on the method parameter, as shown in the following example:

Java

@RequestMapping("/")
public String handle(@SessionAttribute User user) { 
    // ...
}
  1. Using the @SessionAttribute annotation.
Kotlin

@RequestMapping("/")
fun handle(@SessionAttribute user: User): String { 
    // ..
}

For use cases that require adding or removing session attributes, consider implementing org.springframework.web.context.request.WebRequest or javax.servlet.http.HttpSession into a controller method.

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

@RequestAttribute

Similar to the @SessionAttribute annotation, you can use the @RequestAttribute to provide access to existing request attributes created earlier (for example, Filter or HandlerInterceptor servlets):

Java

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

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

Redirect Attributes

By default, all model attributes are considered to be in the form of a URI pattern in the redirect URL. Of the remaining attributes, those that are primitive types or collections or arrays of primitive types are automatically added as query parameters.

Adding primitive type attributes as query parameters may be the desired result if the model instance was prepared specifically for redirection However, in annotated controllers, the model may contain additional attributes added for visualization purposes (for example, dropdown field values). To avoid such attributes appearing in the URL, a method marked with the @RequestMapping annotation can declare an argument of type RedirectAttributes and use it to specify the exact attributes that should be available RedirectView. If the method performs a redirect, the contents of RedirectAttributes are used. Otherwise, the contents of the model are used.

RequestMappingHandlerAdapter provides a ignoreDefaultModelOnRedirect flag that can be used to indicate that the contents of the default Model should never be used in a redirect controller method. Instead, the controller method must declare an attribute of type RedirectAttributes, or, if it does not, then no attributes should be passed to RedirectView. In both the MVC namespace and the Java MVC configuration, this flag is set to false to maintain backward compatibility. However, for new applications, we recommend setting this to true.

Note that the URI template variables from this request are automatically made available when the redirect URL is expanded, so add them explicitly via Model or RedirectAttributes is not required. The following example shows how to define a redirect:

Java

@PostMapping("/files/{path}")
public String upload(...) {
    // ...
    return "redirect:files/{path}";
}
Kotlin

@PostMapping("/files/{path}")
fun upload(... ): String {
    // ...
    return "redirect:files/{path}"
}

Another way to pass data to the redirect target is to use flash attributes . Unlike other redirect attributes, flash attributes are temporarily stored in the HTTP session (and therefore are not displayed in the URL).

Flash attributes

Flash attributes provide the ability to store attributes one request, intended for use in another request. Most often this is needed when redirecting - for example, the Post-Redirect-Get pattern. Flash attributes are temporarily stored before a redirect (usually in a session) so that they are available for request after the redirect, and are then immediately removed.

Spring MVC has two main abstractions for supporting flash attributes. FlashMap is used to store flash attributes, and FlashMapManager is used to store, retrieve, and manage instances of FlashMap.

Support for flash attributes is always "on" and does not require explicit activation. However, if it is not used, it will never create an HTTP session. With each request, there is an "input" FlashMap with the attributes passed in from the previous request (if any), and an "output" FlashMap with the attributes that need to be saved for the subsequent request. Both FlashMap instances are accessible from anywhere in Spring MVC via static methods in RequestContextUtils.

Annotated controllers generally do not need to work directly with FlashMap. Instead, a method annotated with @RequestMapping can take an argument of type RedirectAttributes and use it to add flash attributes for the redirection script. Flash attributes added via RedirectAttributes are automatically propagated to the "output" FlashMap. Likewise, after a redirect, attributes from the "input" FlashMap are automatically added to the Model of the controller serving the target URL.

Mapping requests to flash attributes

The concept of flash attributes exists in many other web frameworks and, as it turns out, sometimes prone to concurrency problems. This is because flash attributes, by definition, must be stored until the next request. However, the "next" request itself may not be from the intended recipient, but from another asynchronous request (such as a polling or resource allocation request), in which case flash attributes will be removed too early.

To reduce the likelihood When problems like these occur, RedirectView automatically "tags" FlashMap instances with the path and query parameters of the target redirect URL. In turn, FlashMapManager by default matches this information with incoming requests when looking for an "input" FlashMap.

This does not completely eliminate the possibility of the problem with parallelism, but reduces it significantly when using information that is already available in the redirect URL. Therefore, we recommend using flash attributes primarily for redirection scenarios.

Multipart

After activating the MultipartResolver the contents of POST requests with multipart/form-data is parsed and becomes available as normal request parameters. The following example accesses one regular form field and one uploaded file:

Java

@Controller
public class FileUploadController {
    @PostMapping("/form")
    public String handleFormUpload(@RequestParam("name") String name,
            @RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            byte[] bytes = file.getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}
Kotlin

@Controller
class FileUploadController {
    @PostMapping("/form")
    fun handleFormUpload(@RequestParam("name") name: String,
                        @RequestParam("file") file: MultipartFile): String {
        if (!file.isEmpty) {
            val bytes = file.bytes
            // store the bytes somewhere
            return "redirect:uploadSuccess"
        }
        return "redirect:uploadFailure"
    }
}

Declaring the argument type as List<MultipartFile> allows you to allow multiple files for one and same parameter name.

If the @RequestParam annotation is declared as Map<String, MultipartFile> or MultiValueMap<String, MultipartFile> without a parameter name specified in the annotation, then the Map is filled with multi-part files for each given parameter name.

When multi-part parsing on Servlet 3.0, you can also declare javax.servlet.http.Part instead of MultipartFile from Spring as a method argument or collection value type.

You can also use multipart content as a data binding element to the command object. For example, the form field and file from the previous example could be form object fields, 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) {
        if (!form.getFile().isEmpty()) {
            byte[] bytes = form.getFile().getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}
Kotlin

class MyForm(val name: String, val file: MultipartFile, ...)
@Controller
class FileUploadController {
    @PostMapping("/form")
    fun handleFormUpload(form: MyForm, errors: BindingResult): String {
        if (!form.file.isEmpty) {
            val bytes = form.file.bytes
            // store the bytes somewhere
            return "redirect:uploadSuccess"
        }
        return "redirect:uploadFailure"
    }
}

Multi-part requests can also be sent from non-browser clients in a RESTful service scenario. The following example shows a JSON file:

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

You can access the " metadata" with the @RequestParam annotation as a String, but you'll probably want it to be deserialized from JSON (similar to the @RequestBody annotation code>). Use the @RequestPart annotation to access a multi-part file after it has been converted using HttpMessageConverter:

Java

@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata,
        @RequestPart("file-data") MultipartFile file) {
    // ...
}
Kotlin

@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData,
        @RequestPart("file-data") file: MultipartFile): String {
    // ...
}

You can use the @RequestPart annotation in combination with javax.validation.Valid or use the @Validated annotation from Spring, which causes the standard Bean Validation to be applied. By default, validation errors result in a MethodArgumentNotValidException exception, which turns into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation errors locally in the controller via the Errors or BindingResult argument, as shown in the following example:

Java

@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") MetaData metadata,
        BindingResult result) {
    // ...
}
Kotlin

@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData,
        result: BindingResult): String {
    // ...
}

@RequestBody

Annotation @RequestBody can be used so that the body of the request can be read and deserialized into a Object via the HttpMessageConverter. The following example uses an argument with the @RequestBody annotation:

Java

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

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

The Message Converters option in the MVC configuration can be used to configure or configure message conversion.

You can use the @RequestBody annotation in combination with javax.validation.Valid or the @Validated annotation from Spring, which trigger the application standard Bean Validation. By default, validation errors result in a MethodArgumentNotValidException exception, which turns into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation errors locally in the controller via the Errors or BindingResult argument, as shown in the following example:

Java

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

@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Account, result: BindingResult) {
    // ...
}

HttpEntity

The HttpEntity class is more or less identical in terms of using the @RequestBody annotation, but is based on a container object that exposes the request headers and body. The following listing is an example:

Java

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

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

@ResponseBody

Annotation @ResponseBodycan be used for a method to have the return serialized into the response body via HttpMessageConverter. The following listing is an example:

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 methods controller. This is the effect of the @RestController annotation, which is nothing more than a meta annotation marked with the @Controller and @ResponseBody annotations.

You can use @ResponseBody with reactive types.

You can use the Message Converters option in the MVC configuration to configure or customize message conversion.

You can combine methods marked with the @ResponseBody annotation, with serialization representations in JSON format.

ResponseEntity

The ResponseEntity class is similar to the @ResponseBody annotation, but with status and headings. 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 = ...
    val etag = ...
    return ResponseEntity.ok().eTag(etag).build(body)
}

Spring MVC supports the use of a single-value reactive type to asynchronously create a ResponseEntity, and/or single- and multi-value reactive types for the body. This allows the following types of asynchronous responses:

  • ResponseEntity<Mono<T>> or ResponseEntity<Flux<T>> report the response status and headers immediately, while the response body is provided asynchronously at a later point. Use Mono if the body consists of 0..1 values, or Flux if it can create multiple 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.

Jackson JSON

Spring provides support for the Jackson JSON library.

JSON Views

Spring MVC provides native support for view serialization Jackson's Serialization Views, which allows you to display only a subset of all fields of an object. 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("eric", "7!jd#h23")
}
class User(
        @JsonView(WithoutPasswordView::class) val username: String,
        @JsonView(WithPasswordView::class) val password: String) {
    interface WithoutPasswordView
    interface WithPasswordView : WithoutPasswordView
}
@JsonView allows you to use an array of view classes, but you can only specify one per controller method. If you need to activate multiple views, you can use a composite interface.

If you want to do the above programmatically, instead of declaring the @JsonView annotation wrap the return value with MappingJacksonValue and use it to provide a serialization view:

Java

@RestController
public class UserController {
    @GetMapping("/user")
    public MappingJacksonValue getUser() {
        User user = new User("eric", "7!jd#h23");
        MappingJacksonValue value = new MappingJacksonValue(user);
        value.setSerializationView(User.WithoutPasswordView.class);
        return value;
    }
}
Kotlin

@RestController
class UserController {
    @GetMapping("/user")
    fun getUser(): MappingJacksonValue {
        val value = MappingJacksonValue(User("eric", "7!jd#h23"))
        value.serializationView = User.WithoutPasswordView::class.java
        return value
    }
}

For controllers that use view recognition, you can add a serialization view class to the model, as shown in the following example:

Java

@Controller
public class UserController extends AbstractController {
    @GetMapping("/user")
    public String getUser(Model model) {
        model.addAttribute("user", new User("eric", "7!jd#h23"));
        model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class);
        return "userView";
    }
}
Kotlin

import org.springframework.ui.set
@Controller
class UserController : AbstractController() {
    @GetMapping("/user")
    fun getUser(model: Model): String {
        model["user"] = User("eric", "7!jd#h23")
        model[JsonView::class.qualifiedName] = User.WithoutPasswordView::class.java
        return "userView"
    }
}
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION