You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4556 lines
164 KiB
4556 lines
164 KiB
[[mvc]] |
|
= Spring Web MVC |
|
:doc-spring-security: {doc-root}/spring-security/site/docs/current/reference |
|
|
|
|
|
|
|
|
|
[[mvc-introduction]] |
|
== Introduction |
|
|
|
Spring Web MVC is the original web framework built on the Servlet API and included |
|
in the Spring Framework from the very beginning. The formal name "Spring Web MVC" |
|
comes from the name of its source module |
|
https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc[spring-webmvc] |
|
but it is more commonly known as "Spring MVC". |
|
|
|
Parallel to Spring Web MVC, Spring Framework 5.0 introduced a reactive stack, web framework |
|
whose name Spring WebFlux is also based on its source module |
|
https://github.com/spring-projects/spring-framework/tree/master/spring-webflux[spring-webflux]. |
|
This section covers Spring Web MVC. The <<web-reactive.adoc#spring-web-reactive,next section>> |
|
covers Spring WebFlux. |
|
|
|
For baseline information and compatibility with Servlet container and Java EE version |
|
ranges please visit the Spring Framework |
|
https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions[Wiki]. |
|
|
|
|
|
[[mvc-servlet]] |
|
== DispatcherServlet |
|
[.small]#<<web-reactive.adoc#webflux-dispatcher-handler,Same in Spring WebFlux>># |
|
|
|
Spring MVC, like many other web frameworks, is designed around the front controller |
|
pattern where a central `Servlet`, the `DispatcherServlet`, provides a shared algorithm |
|
for request processing while actual work is performed by configurable, delegate components. |
|
This model is flexible and supports diverse workflows. |
|
|
|
The `DispatcherServlet`, as any `Servlet`, needs to be declared and mapped according |
|
to the Servlet specification using Java configuration or in `web.xml`. |
|
In turn the `DispatcherServlet` uses Spring configuration to discover |
|
the delegate components it needs for request mapping, view resolution, exception |
|
handling, <<mvc-servlet-special-bean-types,and more>>. |
|
|
|
Below is an example of the Java configuration that registers and initializes |
|
the `DispatcherServlet`. This class is auto-detected by the Servlet container |
|
(see <<mvc-container-config>>): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebApplicationInitializer implements WebApplicationInitializer { |
|
|
|
@Override |
|
public void onStartup(ServletContext servletCxt) { |
|
|
|
// Load Spring web application configuration |
|
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); |
|
ac.register(AppConfig.class); |
|
ac.refresh(); |
|
|
|
// Create and register the DispatcherServlet |
|
DispatcherServlet servlet = new DispatcherServlet(ac); |
|
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); |
|
registration.setLoadOnStartup(1); |
|
registration.addMapping("/app/*"); |
|
} |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
In addition to using the ServletContext API directly, you can also extend |
|
`AbstractAnnotationConfigDispatcherServletInitializer` and override specific methods |
|
(see example under <<mvc-servlet-context-hierarchy>>). |
|
==== |
|
|
|
Below is an example of `web.xml` configuration to register and initialize the `DispatcherServlet`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
|
|
<listener> |
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> |
|
</listener> |
|
|
|
<context-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>/WEB-INF/app-context.xml</param-value> |
|
</context-param> |
|
|
|
<servlet> |
|
<servlet-name>app</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
<init-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value></param-value> |
|
</init-param> |
|
<load-on-startup>1</load-on-startup> |
|
</servlet> |
|
|
|
<servlet-mapping> |
|
<servlet-name>app</servlet-name> |
|
<url-pattern>/app/*</url-pattern> |
|
</servlet-mapping> |
|
|
|
</web-app> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
Spring Boot follows a different initialization sequence. Rather than hooking into |
|
the lifecycle of the Servlet container, Spring Boot uses Spring configuration to |
|
bootstrap itself and the embedded Servlet container. `Filter` and `Servlet` declarations |
|
are detected in Spring configuration and registered with the Servlet container. |
|
For more details check the |
|
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container[Spring Boot docs]. |
|
==== |
|
|
|
|
|
|
|
[[mvc-servlet-context-hierarchy]] |
|
=== Context Hierarchy |
|
|
|
`DispatcherServlet` expects a `WebApplicationContext`, an extension of a plain |
|
`ApplicationContext`, for its own configuration. `WebApplicationContext` has a link to the |
|
`ServletContext` and `Servlet` it is associated with. It is also bound to the `ServletContext` |
|
such that applications can use static methods on `RequestContextUtils` to look up the |
|
`WebApplicationContext` if they need access to it. |
|
|
|
For many applications having a single `WebApplicationContext` is simple and sufficient. |
|
It is also possible to have a context hierarchy where one root `WebApplicationContext` |
|
is shared across multiple `DispatcherServlet` (or other `Servlet`) instances, each with |
|
its own child `WebApplicationContext` configuration. |
|
See <<core.adoc#context-introduction,Additional Capabilities of the ApplicationContext>> |
|
for more on the context hierarchy feature. |
|
|
|
The root `WebApplicationContext` typically contains infrastructure beans such as data repositories and |
|
business services that need to be shared across multiple `Servlet` instances. Those beans |
|
are effectively inherited and could be overridden (i.e. re-declared) in the Servlet-specific, |
|
child `WebApplicationContext` which typically contains beans local to the given `Servlet`: |
|
|
|
image::images/mvc-context-hierarchy.png[] |
|
|
|
Below is example configuration with a `WebApplicationContext` hierarchy: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
|
|
|
@Override |
|
protected Class<?>[] getRootConfigClasses() { |
|
return new Class<?>[] { RootConfig.class }; |
|
} |
|
|
|
@Override |
|
protected Class<?>[] getServletConfigClasses() { |
|
return new Class<?>[] { App1Config.class }; |
|
} |
|
|
|
@Override |
|
protected String[] getServletMappings() { |
|
return new String[] { "/app1/*" }; |
|
} |
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
If an application context hierarchy is not required, applications may return all |
|
configuration via `getRootConfigClasses()` and `null` from `getServletConfigClasses()`. |
|
==== |
|
|
|
And the `web.xml` equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<web-app> |
|
|
|
<listener> |
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> |
|
</listener> |
|
|
|
<context-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>/WEB-INF/root-context.xml</param-value> |
|
</context-param> |
|
|
|
<servlet> |
|
<servlet-name>app1</servlet-name> |
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> |
|
<init-param> |
|
<param-name>contextConfigLocation</param-name> |
|
<param-value>/WEB-INF/app1-context.xml</param-value> |
|
</init-param> |
|
<load-on-startup>1</load-on-startup> |
|
</servlet> |
|
|
|
<servlet-mapping> |
|
<servlet-name>app1</servlet-name> |
|
<url-pattern>/app1/*</url-pattern> |
|
</servlet-mapping> |
|
|
|
</web-app> |
|
---- |
|
|
|
[TIP] |
|
==== |
|
If an application context hierarchy is not required, applications may configure a |
|
"root" context only and leave the `contextConfigLocation` Servlet parameter empty. |
|
==== |
|
|
|
|
|
|
|
[[mvc-servlet-special-bean-types]] |
|
=== Special Bean Types |
|
[.small]#<<web-reactive.adoc#webflux-special-bean-types,Same in Spring WebFlux>># |
|
|
|
The `DispatcherServlet` delegates to special beans to process requests and render the |
|
appropriate responses. By "special beans" we mean Spring-managed, Object instances that |
|
implement WebFlux framework contracts. Those usually come with built-in contracts but |
|
you can customize their properties, extend or replace them. |
|
|
|
The table below lists the special beans detected by the `DispatcherHandler`: |
|
|
|
[[mvc-webappctx-special-beans-tbl]] |
|
[cols="1,2", options="header"] |
|
|=== |
|
| Bean type| Explanation |
|
|
|
| <<mvc-handlermapping,HandlerMapping>> |
|
| Map a request to a handler along with a list of |
|
<<mvc-handlermapping-interceptor, interceptors>> for pre- and post-processing. |
|
The mapping is based on some criteria the details of which vary by `HandlerMapping` |
|
implementation. |
|
|
|
The two main `HandlerMapping` implementations are `RequestMappingHandlerMapping` which |
|
supports `@RequestMapping` annotated methods and `SimpleUrlHandlerMapping` which |
|
maintains explicit registrations of URI path patterns to handlers. |
|
|
|
| HandlerAdapter |
|
| Help the `DispatcherServlet` to invoke a handler mapped to a request regardless of |
|
how the handler is actually invoked. For example, invoking an annotated controller |
|
requires resolving annotations. The main purpose of a `HandlerAdapter` is |
|
to shield the `DispatcherServlet` from such details. |
|
|
|
| <<mvc-exceptionhandlers,HandlerExceptionResolver>> |
|
| Strategy to resolve exceptions possibly mapping them to handlers, or to HTML error |
|
views, or other. See <<mvc-exceptionhandlers>>. |
|
|
|
| <<mvc-viewresolver,ViewResolver>> |
|
| Resolve logical String-based view names returned from a handler to an actual `View` |
|
to render to the response with. See <<mvc-viewresolver>> and <<mvc-view>>. |
|
|
|
| <<mvc-localeresolver,LocaleResolver>>, <<mvc-timezone,LocaleContextResolver>> |
|
| Resolve the `Locale` a client is using and possibly their time zone, in order to be able |
|
to offer internationalized views. See <<mvc-localeresolver>>. |
|
|
|
| <<mvc-themeresolver,ThemeResolver>> |
|
| Resolve themes your web application can use, for example, to offer personalized layouts. |
|
See <<mvc-themeresolver>>. |
|
|
|
| <<mvc-multipart,MultipartResolver>> |
|
| Abstraction for parsing a multi-part request (e.g. browser form file upload) with |
|
the help of some multipart parsing library. See <<mvc-multipart>>. |
|
|
|
| <<mvc-flash-attributes,FlashMapManager>> |
|
| Store and retrieve the "input" and the "output" `FlashMap` that can be used to pass |
|
attributes from one request to another, usually across a redirect. |
|
See <<mvc-flash-attributes>>. |
|
|=== |
|
|
|
|
|
[[mvc-servlet-config]] |
|
=== Web MVC Config |
|
[.small]#<<web-reactive.adoc#webflux-framework-config,Same in Spring WebFlux>># |
|
|
|
Applications can declare the infrastructure beans listed in <<mvc-special-bean-types>> |
|
that are required to process requests. The `DispatcherServlet` checks the |
|
`WebApplicationContext` for each special bean. If there are no matching bean types, it |
|
falls back on the default types listed in |
|
https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/resources/org/springframework/web/servlet/DispatcherServlet.properties[DispatcherServlet.properties]. |
|
|
|
In most cases the <<mvc-config>> is the best starting point. It declares the required |
|
beans in either Java or XML, and provides a higher level configuration callback API to |
|
customize it. |
|
|
|
[NOTE] |
|
==== |
|
Spring Boot relies on the MVC Java config to configure Spring MVC and also |
|
provides many extra convenient options. |
|
==== |
|
|
|
|
|
|
|
[[mvc-container-config]] |
|
=== Servlet Config |
|
|
|
In a Servlet 3.0+ environment, you have the option of configuring the Servlet container |
|
programmatically as an alternative or in combination with a `web.xml` file. Below is an |
|
example of registering a `DispatcherServlet`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
import org.springframework.web.WebApplicationInitializer; |
|
|
|
public class MyWebApplicationInitializer implements WebApplicationInitializer { |
|
|
|
@Override |
|
public void onStartup(ServletContext container) { |
|
XmlWebApplicationContext appContext = new XmlWebApplicationContext(); |
|
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); |
|
|
|
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext)); |
|
registration.setLoadOnStartup(1); |
|
registration.addMapping("/"); |
|
} |
|
} |
|
---- |
|
|
|
`WebApplicationInitializer` is an interface provided by Spring MVC that ensures your |
|
implementation is detected and automatically used to initialize any Servlet 3 container. |
|
An abstract base class implementation of `WebApplicationInitializer` named |
|
`AbstractDispatcherServletInitializer` makes it even easier to register the |
|
`DispatcherServlet` by simply overriding methods to specify the servlet mapping and the |
|
location of the `DispatcherServlet` configuration. |
|
|
|
This is recommended for applications that use Java-based Spring configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
|
|
|
@Override |
|
protected Class<?>[] getRootConfigClasses() { |
|
return null; |
|
} |
|
|
|
@Override |
|
protected Class<?>[] getServletConfigClasses() { |
|
return new Class<?>[] { MyWebConfig.class }; |
|
} |
|
|
|
@Override |
|
protected String[] getServletMappings() { |
|
return new String[] { "/" }; |
|
} |
|
} |
|
---- |
|
|
|
If using XML-based Spring configuration, you should extend directly from |
|
`AbstractDispatcherServletInitializer`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { |
|
|
|
@Override |
|
protected WebApplicationContext createRootApplicationContext() { |
|
return null; |
|
} |
|
|
|
@Override |
|
protected WebApplicationContext createServletApplicationContext() { |
|
XmlWebApplicationContext cxt = new XmlWebApplicationContext(); |
|
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml"); |
|
return cxt; |
|
} |
|
|
|
@Override |
|
protected String[] getServletMappings() { |
|
return new String[] { "/" }; |
|
} |
|
} |
|
---- |
|
|
|
`AbstractDispatcherServletInitializer` also provides a convenient way to add `Filter` |
|
instances and have them automatically mapped to the `DispatcherServlet`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { |
|
|
|
// ... |
|
|
|
@Override |
|
protected Filter[] getServletFilters() { |
|
return new Filter[] { |
|
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() }; |
|
} |
|
} |
|
---- |
|
|
|
Each filter is added with a default name based on its concrete type and automatically |
|
mapped to the `DispatcherServlet`. |
|
|
|
The `isAsyncSupported` protected method of `AbstractDispatcherServletInitializer` |
|
provides a single place to enable async support on the `DispatcherServlet` and all |
|
filters mapped to it. By default this flag is set to `true`. |
|
|
|
Finally, if you need to further customize the `DispatcherServlet` itself, you can |
|
override the `createDispatcherServlet` method. |
|
|
|
|
|
[[mvc-servlet-sequence]] |
|
=== Processing |
|
[.small]#<<web-reactive.adoc#webflux-dispatcher-handler-sequence,Same in Spring WebFlux>># |
|
|
|
The `DispatcherServlet` processes requests as follows: |
|
|
|
* The `WebApplicationContext` is searched for and bound in the request as an attribute |
|
that the controller and other elements in the process can use. It is bound by default |
|
under the key `DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE`. |
|
* The locale resolver is bound to the request to enable elements in the process to |
|
resolve the locale to use when processing the request (rendering the view, preparing |
|
data, and so on). If you do not need locale resolving, you do not need it. |
|
* The theme resolver is bound to the request to let elements such as views determine |
|
which theme to use. If you do not use themes, you can ignore it. |
|
* If you specify a multipart file resolver, the request is inspected for multiparts; if |
|
multiparts are found, the request is wrapped in a `MultipartHttpServletRequest` for |
|
further processing by other elements in the process. See <<mvc-multipart>> for further |
|
information about multipart handling. |
|
* An appropriate handler is searched for. If a handler is found, the execution chain |
|
associated with the handler (preprocessors, postprocessors, and controllers) is |
|
executed in order to prepare a model or rendering. Or alternatively for annotated |
|
controllers, the response may be rendered (within the `HandlerAdapter`) instead of |
|
returning a view. |
|
* If a model is returned, the view is rendered. If no model is returned, (may be due to |
|
a preprocessor or postprocessor intercepting the request, perhaps for security |
|
reasons), no view is rendered, because the request could already have been fulfilled. |
|
|
|
The `HandlerExceptionResolver` beans declared in the `WebApplicationContext` are used to |
|
resolve exceptions thrown during request processing. Those exception resolvers allow |
|
customizing the logic to address exceptions. See <<mvc-exceptionhandlers>> for more details. |
|
|
|
The Spring `DispatcherServlet` also supports the return of the |
|
__last-modification-date__, as specified by the Servlet API. The process of determining |
|
the last modification date for a specific request is straightforward: the |
|
`DispatcherServlet` looks up an appropriate handler mapping and tests whether the |
|
handler that is found implements the __LastModified__ interface. If so, the value of the |
|
`long getLastModified(request)` method of the `LastModified` interface is returned to |
|
the client. |
|
|
|
You can customize individual `DispatcherServlet` instances by adding Servlet |
|
initialization parameters ( `init-param` elements) to the Servlet declaration in the |
|
`web.xml` file. See the following table for the list of supported parameters. |
|
|
|
[[mvc-disp-servlet-init-params-tbl]] |
|
.DispatcherServlet initialization parameters |
|
|=== |
|
| Parameter| Explanation |
|
|
|
| `contextClass` |
|
| Class that implements `WebApplicationContext`, which instantiates the context used by |
|
this Servlet. By default, the `XmlWebApplicationContext` is used. |
|
|
|
| `contextConfigLocation` |
|
| String that is passed to the context instance (specified by `contextClass`) to |
|
indicate where context(s) can be found. The string consists potentially of multiple |
|
strings (using a comma as a delimiter) to support multiple contexts. In case of |
|
multiple context locations with beans that are defined twice, the latest location |
|
takes precedence. |
|
|
|
| `namespace` |
|
| Namespace of the `WebApplicationContext`. Defaults to `[servlet-name]-servlet`. |
|
|=== |
|
|
|
|
|
|
|
[[mvc-handlermapping-interceptor]] |
|
=== Interception |
|
|
|
All `HandlerMapping` implementations supports handler interceptors that are useful when |
|
you want to apply specific functionality to certain requests, for example, checking for |
|
a principal. Interceptors must implement `HandlerInterceptor` from the |
|
`org.springframework .web .servlet` package with three methods that should provide enough |
|
flexibility to do all kinds of pre-processing and post-processing: |
|
|
|
* `preHandle(..)` -- __before__ the actual handler is executed |
|
* `postHandle(..)` -- __after__ the handler is executed |
|
* `afterCompletion(..)` -- __after the complete request has finished__ |
|
|
|
The `preHandle(..)` method returns a boolean value. You can use this method to break or |
|
continue the processing of the execution chain. When this method returns `true`, the |
|
handler execution chain will continue; when it returns false, the `DispatcherServlet` |
|
assumes the interceptor itself has taken care of requests (and, for example, rendered an |
|
appropriate view) and does not continue executing the other interceptors and the actual |
|
handler in the execution chain. |
|
|
|
See <<mvc-config-interceptors>> in the section on MVC configuration for examples of how to |
|
configure interceptors. You can also register them directly via setters on individual |
|
`HandlerMapping` implementations. |
|
|
|
Note that `postHandle` is less useful with `@ResponseBody` and `ResponseEntity` methods for |
|
which a the response is written and committed within the `HandlerAdapter` and before |
|
`postHandle`. That means its too late to make any changes to the response such as adding |
|
an extra header. For such scenarios you can implement `ResponseBodyAdvice` and either |
|
declare it as an <<mvc-ann-controller-advice>> bean or configure it directly on |
|
`RequestMappingHandlerAdapter`. |
|
|
|
|
|
|
|
[[mvc-exceptionhandlers]] |
|
=== Exception Resolution |
|
|
|
If an exception occurs during the mapping or the invocation of a request handler (e.g. an |
|
`@Controller`), the `DispatcherServlet` delegates to a chain of `HandlerExceptionResolver` |
|
beans to try and resolve the exception and to provide alternative handling for it, which |
|
typically means preparing an error response whether an HTML error page, an error status, |
|
or both. |
|
|
|
The table below lists the available `HandlerExceptionResolver` implementations: |
|
|
|
[cols="1,2", options="header"] |
|
.HandlerExceptionResolver implementations |
|
|=== |
|
| HandlerExceptionResolver | Description |
|
|
|
| `SimpleMappingExceptionResolver` |
|
| A mapping between exception class names and error view names. Useful for rendering |
|
error pages in a browser application. |
|
|
|
| {api-spring-framework}/web/servlet/mvc/support/DefaultHandlerExceptionResolver.html[DefaultHandlerExceptionResolver] |
|
| Resolves exceptions raised by Spring MVC and maps them to HTTP status codes. |
|
Also see alternative `ResponseEntityExceptionHandler` and <<mvc-ann-rest-exceptions>>. |
|
|
|
| `ResponseStatusExceptionResolver` |
|
| Resolves exceptions with the `@ResponseStatus` annotation and maps them to HTTP status |
|
codes based on the value in the annotation. |
|
|
|
| `ExceptionHandlerExceptionResolver` |
|
| Resolves exceptions by invoking an `@ExceptionHandler` method in an `@Controller` or an |
|
`@ControllerAdvice` class. See <<mvc-ann-exceptionhandler>>. |
|
|=== |
|
|
|
|
|
[[mvc-excetionhandlers-handling]] |
|
==== Handling |
|
|
|
You chain exception resolvers by declaring more than one exception resolver beans and, |
|
if necessary, setting the `order` property to specify ordering. Remember, the higher the |
|
order property, the later the exception resolver is positioned in the chain. |
|
|
|
The contract of `HandlerExceptionResolver` specifies that it __can__ return: |
|
|
|
* `ModelAndView` that points to an error view. |
|
* Empty `ModelAndView` if the exception was handled within the resolver. |
|
* `null` if the exception remains unresolved, for subsequent resolvers to try; if the |
|
exception remains unresolved by any resolver, it is re-thrown and left to propagate to |
|
the Servlet container. |
|
|
|
To configure exception handling is as simple as adding `HandlerExceptionResolver` beans |
|
to your Spring configuration. The <<mvc-config>> automatically declares built-in |
|
resolvers for default Spring MVC exceptions, for `@ResponseStatus` annotated exceptions, |
|
and for support of `@ExceptionHandler` methods. You can customize that list or replace it. |
|
|
|
|
|
[[mvc-ann-customer-servlet-container-error-page]] |
|
==== Container error page |
|
|
|
If an exception remains unresolved by any `HandlerExceptionResolver` and is therefore |
|
left to propagate, or if the response status is set to an error status (i.e. 4xx, 5xx), |
|
Servlet containers may render a default error page in HTML. To customize the default |
|
error page of the container, you can declare an error page mapping in `web.xml`: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<error-page> |
|
<location>/error</location> |
|
</error-page> |
|
---- |
|
|
|
Given the above, when an exception bubbles up, or the response has an error status, the |
|
Servlet container makes an ERROR dispatch within the container to the configured URL |
|
(e.g. "/error"). This is then processed by the `DispatcherServlet`, possibly mapping it |
|
to an `@Controller` which could be implemented to return an error view name with a model |
|
or to render a JSON response as shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RestController |
|
public class ErrorController { |
|
|
|
@RequestMapping(path = "/error") |
|
public Map<String, Object> handle(HttpServletRequest request) { |
|
Map<String, Object> map = new HashMap<String, Object>(); |
|
map.put("status", request.getAttribute("javax.servlet.error.status_code")); |
|
map.put("reason", request.getAttribute("javax.servlet.error.message")); |
|
return map; |
|
} |
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
The Servlet API does not provide a way to create error page mappings in Java. You can |
|
however use both an `WebApplicationInitializer` and a minimal `web.xml`. |
|
==== |
|
|
|
|
|
|
|
|
|
[[mvc-viewresolver]] |
|
=== View Resolution |
|
[.small]#<<web-reactive.adoc#webflux-viewresolution,Same in Spring WebFlux>># |
|
|
|
Spring MVC defines the `ViewResolver` and `View` interfaces that enable you to render |
|
models in a browser without tying you to a specific view technology. `ViewResolver` |
|
provides a mapping between view names and actual views. `View` addresses the preparation |
|
of data before handing over to a specific view technology. |
|
|
|
The table below provides more details on the `ViewResolver` hierarchy: |
|
|
|
[[mvc-view-resolvers-tbl]] |
|
.ViewResolver implementations |
|
|=== |
|
| ViewResolver| Description |
|
|
|
| `AbstractCachingViewResolver` |
|
| Sub-classes of `AbstractCachingViewResolver` cache view instances that they resolve. |
|
Caching improves performance of certain view technologies. It's possible to turn off the |
|
cache by setting the `cache` property to `false`. Furthermore, if you must refresh a |
|
certain view at runtime (for example when a FreeMarker template is modified), you can use |
|
the `removeFromCache(String viewName, Locale loc)` method. |
|
|
|
| `XmlViewResolver` |
|
| Implementation of `ViewResolver` that accepts a configuration file written in XML with |
|
the same DTD as Spring's XML bean factories. The default configuration file is |
|
`/WEB-INF/views.xml`. |
|
|
|
| `ResourceBundleViewResolver` |
|
| Implementation of `ViewResolver` that uses bean definitions in a `ResourceBundle`, |
|
specified by the bundle base name, and for each view it is supposed to resolve, it uses |
|
the value of the property `[viewname].(class)` as the view class and the value of the |
|
property `[viewname].url` as the view url. Examples can be found in the chapter on |
|
<<mvc-view>>. |
|
|
|
| `UrlBasedViewResolver` |
|
| Simple implementation of the `ViewResolver` interface that effects the direct |
|
resolution of logical view names to URLs, without an explicit mapping definition. This |
|
is appropriate if your logical names match the names of your view resources in a |
|
straightforward manner, without the need for arbitrary mappings. |
|
|
|
| `InternalResourceViewResolver` |
|
| Convenient subclass of `UrlBasedViewResolver` that supports `InternalResourceView` (in |
|
effect, Servlets and JSPs) and subclasses such as `JstlView` and `TilesView`. You can |
|
specify the view class for all views generated by this resolver by using |
|
`setViewClass(..)`. See the `UrlBasedViewResolver` javadocs for details. |
|
|
|
| `FreeMarkerViewResolver` |
|
| Convenient subclass of `UrlBasedViewResolver` that supports `FreeMarkerView` and |
|
custom subclasses of them. |
|
|
|
| `ContentNegotiatingViewResolver` |
|
| Implementation of the `ViewResolver` interface that resolves a view based on the |
|
request file name or `Accept` header. See <<mvc-multiple-representations>>. |
|
|=== |
|
|
|
|
|
[[mvc-viewresolver-handling]] |
|
==== Handling |
|
[.small]#<<web-reactive.adoc#webflux-viewresolution-handling,Same in Spring WebFlux>># |
|
|
|
You chain view resolvers by declaring more than one resolver beans and, if necessary, by |
|
setting the `order` property to specify ordering. Remember, the higher the order property, |
|
the later the view resolver is positioned in the chain. |
|
|
|
The contract of a `ViewResolver` specifies that it __can__ return null to indicate the |
|
view could not be found. However in the case of JSPs, and `InternalResourceViewResolver`, |
|
the only way to figure out if a JSP exists is to perform a dispatch through |
|
`RequestDispatcher`. Therefore an `InternalResourceViewResolver` must always be configured |
|
to be last in the overall order of view resolvers. |
|
|
|
To configure view resolution is as simple as adding `ViewResolver` beans to your Spring |
|
configuration. The <<mvc-config>> provides provides a dedicated configuration API for |
|
<<mvc-config-view-resolvers>> and also for adding logic-less |
|
<<mvc-config-view-controller,View Controllers>> which are useful for HTML template |
|
rendering without controller logic. |
|
|
|
|
|
[[mvc-redirecting-redirect-prefix]] |
|
==== Redirecting |
|
[.small]#<<web-reactive.adoc#webflux-redirecting-redirect-prefix,Same in Spring WebFlux>># |
|
|
|
The special `redirect:` prefix in a view name allows you to perform a redirect. The |
|
`UrlBasedViewResolver` (and sub-classes) recognize this as an instruction that a |
|
redirect is needed. The rest of the view name is the redirect URL. |
|
|
|
The net effect is the same as if the controller had returned a `RedirectView`, but now |
|
the controller itself can simply operate in terms of logical view names. A logical view |
|
name such as `redirect:/myapp/some/resource` will redirect relative to the current |
|
Servlet context, while a name such as `redirect:http://myhost.com/some/arbitrary/path` |
|
will redirect to an absolute URL. |
|
|
|
Note that if a controller method is annotated with the `@ResponseStatus`, the annotation |
|
value takes precedence over the response status set by `RedirectView`. |
|
|
|
|
|
[[mvc-redirecting-forward-prefix]] |
|
==== Forwarding |
|
|
|
It is also possible to use a special `forward:` prefix for view names that are |
|
ultimately resolved by `UrlBasedViewResolver` and subclasses. This creates an |
|
`InternalResourceView` which does a `RequestDispatcher.forward()`. |
|
Therefore, this prefix is not useful with `InternalResourceViewResolver` and |
|
`InternalResourceView` (for JSPs) but it can be helpful if using another view |
|
technology, but still want to force a forward of a resource to be handled by the |
|
Servlet/JSP engine. Note that you may also chain multiple view resolvers, instead. |
|
|
|
|
|
[[mvc-multiple-representations]] |
|
==== Content negotiation |
|
[.small]#<<web-reactive.adoc#webflux-multiple-representations,Same in Spring WebFlux>># |
|
|
|
{api-spring-framework}/web/servlet/view/ContentNegotiatingViewResolver.html[ContentNegotiatingViewResolver] |
|
does not resolve views itself but rather delegates |
|
to other view resolvers, and selects the view that resembles the representation requested |
|
by the client. The representation can be determined from the `Accept` header or from a |
|
query parameter, e.g. `"/path?format=pdf"`. |
|
|
|
The `ContentNegotiatingViewResolver` selects an appropriate `View` to handle the request |
|
by comparing the request media type(s) with the media type (also known as |
|
`Content-Type`) supported by the `View` associated with each of its `ViewResolvers`. The |
|
first `View` in the list that has a compatible `Content-Type` returns the representation |
|
to the client. If a compatible view cannot be supplied by the `ViewResolver` chain, then |
|
the list of views specified through the `DefaultViews` property will be consulted. This |
|
latter option is appropriate for singleton `Views` that can render an appropriate |
|
representation of the current resource regardless of the logical view name. The `Accept` |
|
header may include wild cards, for example `text/{asterisk}`, in which case a `View` whose |
|
Content-Type was `text/xml` is a compatible match. |
|
|
|
See <<mvc-config-view-resolvers>> under <<mvc-config>> for configuration details. |
|
|
|
|
|
|
|
[[mvc-localeresolver]] |
|
=== Locale |
|
|
|
Most parts of Spring's architecture support internationalization, just as the Spring web |
|
MVC framework does. `DispatcherServlet` enables you to automatically resolve messages |
|
using the client's locale. This is done with `LocaleResolver` objects. |
|
|
|
When a request comes in, the `DispatcherServlet` looks for a locale resolver, and if it |
|
finds one it tries to use it to set the locale. Using the `RequestContext.getLocale()` |
|
method, you can always retrieve the locale that was resolved by the locale resolver. |
|
|
|
In addition to automatic locale resolution, you can also attach an interceptor to the |
|
handler mapping (see <<mvc-handlermapping-interceptor>> for more information on handler |
|
mapping interceptors) to change the locale under specific circumstances, for example, |
|
based on a parameter in the request. |
|
|
|
Locale resolvers and interceptors are defined in the |
|
`org.springframework.web.servlet.i18n` package and are configured in your application |
|
context in the normal way. Here is a selection of the locale resolvers included in |
|
Spring. |
|
|
|
|
|
[[mvc-timezone]] |
|
==== TimeZone |
|
|
|
In addition to obtaining the client's locale, it is often useful to know their time zone. |
|
The `LocaleContextResolver` interface offers an extension to `LocaleResolver` that allows |
|
resolvers to provide a richer `LocaleContext`, which may include time zone information. |
|
|
|
When available, the user's `TimeZone` can be obtained using the |
|
`RequestContext.getTimeZone()` method. Time zone information will automatically be used |
|
by Date/Time `Converter` and `Formatter` objects registered with Spring's |
|
`ConversionService`. |
|
|
|
|
|
[[mvc-localeresolver-acceptheader]] |
|
==== Header resolver |
|
|
|
This locale resolver inspects the `accept-language` header in the request that was sent |
|
by the client (e.g., a web browser). Usually this header field contains the locale of |
|
the client's operating system. __Note that this resolver does not support time zone |
|
information.__ |
|
|
|
|
|
[[mvc-localeresolver-cookie]] |
|
==== Cookie resolver |
|
|
|
This locale resolver inspects a `Cookie` that might exist on the client to see if a |
|
`Locale` or `TimeZone` is specified. If so, it uses the specified details. Using the |
|
properties of this locale resolver, you can specify the name of the cookie as well as the |
|
maximum age. Find below an example of defining a `CookieLocaleResolver`. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> |
|
|
|
<property name="cookieName" value="clientlanguage"/> |
|
|
|
<!-- in seconds. If set to -1, the cookie is not persisted (deleted when browser shuts down) --> |
|
<property name="cookieMaxAge" value="100000"/> |
|
|
|
</bean> |
|
---- |
|
|
|
[[mvc-cookie-locale-resolver-props-tbl]] |
|
.CookieLocaleResolver properties |
|
[cols="1,1,4"] |
|
|=== |
|
| Property | Default | Description |
|
|
|
| cookieName |
|
| classname + LOCALE |
|
| The name of the cookie |
|
|
|
| cookieMaxAge |
|
| Servlet container default |
|
| The maximum time a cookie will stay persistent on the client. If -1 is specified, the |
|
cookie will not be persisted; it will only be available until the client shuts down |
|
their browser. |
|
|
|
| cookiePath |
|
| / |
|
| Limits the visibility of the cookie to a certain part of your site. When cookiePath is |
|
specified, the cookie will only be visible to that path and the paths below it. |
|
|=== |
|
|
|
|
|
[[mvc-localeresolver-session]] |
|
==== Session resolver |
|
|
|
The `SessionLocaleResolver` allows you to retrieve `Locale` and `TimeZone` from the |
|
session that might be associated with the user's request. In contrast to |
|
`CookieLocaleResolver`, this strategy stores locally chosen locale settings in the |
|
Servlet container's `HttpSession`. As a consequence, those settings are just temporary |
|
for each session and therefore lost when each session terminates. |
|
|
|
Note that there is no direct relationship with external session management mechanisms |
|
such as the Spring Session project. This `SessionLocaleResolver` will simply evaluate and |
|
modify corresponding `HttpSession` attributes against the current `HttpServletRequest`. |
|
|
|
|
|
[[mvc-localeresolver-interceptor]] |
|
==== Locale interceptor |
|
|
|
You can enable changing of locales by adding the `LocaleChangeInterceptor` to one of the |
|
handler mappings (see <<mvc-handlermapping>>). It will detect a parameter in the request |
|
and change the locale. It calls `setLocale()` on the `LocaleResolver` that also exists |
|
in the context. The following example shows that calls to all `{asterisk}.view` resources |
|
containing a parameter named `siteLanguage` will now change the locale. So, for example, |
|
a request for the following URL, `http://www.sf.net/home.view?siteLanguage=nl` will |
|
change the site language to Dutch. |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<bean id="localeChangeInterceptor" |
|
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"> |
|
<property name="paramName" value="siteLanguage"/> |
|
</bean> |
|
|
|
<bean id="localeResolver" |
|
class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/> |
|
|
|
<bean id="urlMapping" |
|
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> |
|
<property name="interceptors"> |
|
<list> |
|
<ref bean="localeChangeInterceptor"/> |
|
</list> |
|
</property> |
|
<property name="mappings"> |
|
<value>/**/*.view=someController</value> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[mvc-themeresolver]] |
|
=== Themes |
|
|
|
You can apply Spring Web MVC framework themes to set the overall look-and-feel of your |
|
application, thereby enhancing user experience. A theme is a collection of static |
|
resources, typically style sheets and images, that affect the visual style of the |
|
application. |
|
|
|
|
|
[[mvc-themeresolver-defining]] |
|
==== Define a theme |
|
|
|
To use themes in your web application, you must set up an implementation of the |
|
`org.springframework.ui.context.ThemeSource` interface. The `WebApplicationContext` |
|
interface extends `ThemeSource` but delegates its responsibilities to a dedicated |
|
implementation. By default the delegate will be an |
|
`org.springframework.ui.context.support.ResourceBundleThemeSource` implementation that |
|
loads properties files from the root of the classpath. To use a custom `ThemeSource` |
|
implementation or to configure the base name prefix of the `ResourceBundleThemeSource`, |
|
you can register a bean in the application context with the reserved name `themeSource`. |
|
The web application context automatically detects a bean with that name and uses it. |
|
|
|
When using the `ResourceBundleThemeSource`, a theme is defined in a simple properties |
|
file. The properties file lists the resources that make up the theme. Here is an example: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
styleSheet=/themes/cool/style.css |
|
background=/themes/cool/img/coolBg.jpg |
|
---- |
|
|
|
The keys of the properties are the names that refer to the themed elements from view |
|
code. For a JSP, you typically do this using the `spring:theme` custom tag, which is |
|
very similar to the `spring:message` tag. The following JSP fragment uses the theme |
|
defined in the previous example to customize the look and feel: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> |
|
<html> |
|
<head> |
|
<link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/> |
|
</head> |
|
<body style="background=<spring:theme code='background'/>"> |
|
... |
|
</body> |
|
</html> |
|
---- |
|
|
|
By default, the `ResourceBundleThemeSource` uses an empty base name prefix. As a result, |
|
the properties files are loaded from the root of the classpath. Thus you would put the |
|
`cool.properties` theme definition in a directory at the root of the classpath, for |
|
example, in `/WEB-INF/classes`. The `ResourceBundleThemeSource` uses the standard Java |
|
resource bundle loading mechanism, allowing for full internationalization of themes. For |
|
example, we could have a `/WEB-INF/classes/cool_nl.properties` that references a special |
|
background image with Dutch text on it. |
|
|
|
|
|
[[mvc-themeresolver-resolving]] |
|
==== Resolve themes |
|
|
|
After you define themes, as in the preceding section, you decide which theme to use. The |
|
`DispatcherServlet` will look for a bean named `themeResolver` to find out which |
|
`ThemeResolver` implementation to use. A theme resolver works in much the same way as a |
|
`LocaleResolver`. It detects the theme to use for a particular request and can also |
|
alter the request's theme. The following theme resolvers are provided by Spring: |
|
|
|
[[mvc-theme-resolver-impls-tbl]] |
|
.ThemeResolver implementations |
|
[cols="1,4"] |
|
|=== |
|
| Class | Description |
|
|
|
| `FixedThemeResolver` |
|
| Selects a fixed theme, set using the `defaultThemeName` property. |
|
|
|
| `SessionThemeResolver` |
|
| The theme is maintained in the user's HTTP session. It only needs to be set once for |
|
each session, but is not persisted between sessions. |
|
|
|
| `CookieThemeResolver` |
|
| The selected theme is stored in a cookie on the client. |
|
|=== |
|
|
|
Spring also provides a `ThemeChangeInterceptor` that allows theme changes on every |
|
request with a simple request parameter. |
|
|
|
|
|
|
|
[[mvc-multipart]] |
|
=== Multipart resolver |
|
[.small]#<<web-reactive.adoc#webflux-multipart,Same in Spring WebFlux>># |
|
|
|
`MultipartResolver` from the `org.springframework.web.multipart` package is a strategy |
|
for parsing multipart requests including file uploads. There is one implementation |
|
based on http://jakarta.apache.org/commons/fileupload[__Commons FileUpload__] and another |
|
based on Servlet 3.0 multipart request parsing. |
|
|
|
To enable multipart handling, you need declare a `MultipartResolver` bean in your |
|
`DispatcherServlet` Spring configuration with the name "multipartResolver". |
|
The `DispatcherServlet` detects it and applies it to incoming request. When a POST with |
|
content-type of "multipart/form-data" is received, the resolver parses the content and |
|
wraps the current `HttpServletRequest` as `MultipartHttpServletRequest` in order to |
|
provide access to resolved parts in addition to exposing them as request parameters. |
|
|
|
|
|
[[mvc-multipart-resolver-commons]] |
|
==== Apache FileUpload |
|
|
|
To use Apache Commons FileUpload, simply configure a bean of type |
|
`CommonsMultipartResolver` with the name `multipartResolver`. Of course you also need to |
|
have `commons-fileupload` as a dependency on your classpath. |
|
|
|
|
|
[[mvc-multipart-resolver-standard]] |
|
==== Servlet 3.0 |
|
|
|
Servlet 3.0 multipart parsing needs to be enabled through Servlet container configuration: |
|
|
|
* in Java, set a `MultipartConfigElement` on the Servlet registration. |
|
* in `web.xml`, add a `"<multipart-config>"` section to the servlet declaration. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
|
|
|
// ... |
|
|
|
@Override |
|
protected void customizeRegistration(ServletRegistration.Dynamic registration) { |
|
|
|
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold |
|
registration.setMultipartConfig(new MultipartConfigElement("/tmp")); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Once the Servlet 3.0 configuration is in place, simply add a bean of type |
|
`StandardServletMultipartResolver` with the name `multipartResolver`. |
|
|
|
|
|
|
|
|
|
[[filters]] |
|
== Filters |
|
[.small]#<<web-reactive.adoc#webflux-filters,Same in Spring WebFlux>># |
|
|
|
The `spring-web` module provides some useful filters. |
|
|
|
|
|
|
|
[[filters-http-put]] |
|
=== HTTP PUT Form |
|
|
|
Browsers can only submit form data via HTTP GET or HTTP POST but non-browser clients can also |
|
use HTTP PUT and PATCH. The Servlet API requires `ServletRequest.getParameter{asterisk}()` |
|
methods to support form field access only for HTTP POST. |
|
|
|
The `spring-web` module provides `HttpPutFormContentFilter` that intercepts HTTP PUT and |
|
PATCH requests with content type `application/x-www-form-urlencoded`, reads the form data from |
|
the body of the request, and wraps the `ServletRequest` in order to make the form data |
|
available through the `ServletRequest.getParameter{asterisk}()` family of methods. |
|
|
|
|
|
|
|
[[webflux-filters-forwarded-headers]] |
|
=== Forwarded Headers |
|
[.small]#<<web-reactive.adoc#webflux-filters-forwarded-headers,Same in Spring WebFlux>># |
|
|
|
As a request goes through proxies such as load balancers the host, port, and |
|
scheme may change presenting a challenge for applications that need to create links |
|
to resources since the links should reflect the host, port, and scheme of the |
|
original request as seen from a client perspective. |
|
|
|
https://tools.ietf.org/html/rfc7239[RFC 7239] defines the "Forwarded" HTTP header |
|
for proxies to use to provide information about the original request. There are also |
|
other non-standard headers in use such as "X-Forwarded-Host", "X-Forwarded-Port", |
|
and "X-Forwarded-Proto". |
|
|
|
`ForwardedHeaderFilter` detects, extracts, and uses information from the "Forwarded" |
|
header, or from "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto". |
|
It wraps the request in order to overlay its host, port, and scheme and also "hides" |
|
the forwarded headers for subsequent processing. |
|
|
|
Note that there are security considerations when using forwarded headers as explained |
|
in Section 8 of RFC 7239. At the application level it is difficult to determine whether |
|
forwarded headers can be trusted or not. This is why the network upstream should be |
|
configured correctly to filter out untrusted forwarded headers from the outside. |
|
|
|
Applications that don't have a proxy and don't need to use forwarded headers can |
|
configure the `ForwardedHeaderFilter` to remove and ignore such headers. |
|
|
|
|
|
|
|
[[filters-shallow-etag]] |
|
=== Shallow ETag |
|
|
|
There is a `ShallowEtagHeaderFilter`. It is called shallow because it doesn't have any |
|
knowledge of the content. Instead it relies on buffering actual content written to the |
|
response and computing the ETag value at the end. |
|
|
|
See <<mvc-httpcaching-shallowetag>> for more details. |
|
|
|
|
|
|
|
[[filters-cors]] |
|
=== CORS |
|
|
|
Spring MVC provides fine-grained support for CORS configuration through annotations on |
|
controllers. However when used with Spring Security it is advisable to rely on the built-in |
|
`CorsFilter` that must be ordered ahead of Spring Security's chain of filters. |
|
|
|
See the section on <<mvc-cors>> and the <<mvc-cors-filter,CorsFilter>> for more details. |
|
|
|
|
|
|
|
|
|
[[mvc-controller]] |
|
== Annotated Controllers |
|
[.small]#<<web-reactive.adoc#webflux-controller,Same in Spring WebFlux>># |
|
|
|
Spring MVC provides an annotation-based programming model where `@Controller` and |
|
`@RestController` components use annotations to express request mappings, request input, |
|
exception handling, and more. Annotated controllers have flexible method signatures and |
|
do not have to extend base classes nor implement specific interfaces. |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class HelloController { |
|
|
|
@GetMapping("/hello") |
|
public String handle(Model model) { |
|
model.addAttribute("message", "Hello World!"); |
|
return "index"; |
|
} |
|
} |
|
---- |
|
|
|
In this particular example the method accepts a `Model` and returns a view name as a `String` |
|
but many other options exist and are explained further below in this chapter. |
|
|
|
[TIP] |
|
==== |
|
Guides and tutorials on https://spring.io/guides[spring.io] use the annotation-based |
|
programming model described in this section. |
|
==== |
|
|
|
|
|
|
|
[[mvc-ann-controller]] |
|
=== Declaration |
|
[.small]#<<web-reactive.adoc#webflux-ann-controller,Same in Spring WebFlux>># |
|
|
|
You can define controller beans using a standard Spring bean definition in the |
|
Servlet's `WebApplicationContext`. The `@Controller` stereotype allows for auto-detection, |
|
aligned with Spring general support for detecting `@Component` classes in the classpath |
|
and auto-registering bean definitions for them. It also acts as a stereotype for the |
|
annotated class, indicating its role as a web component. |
|
|
|
To enable auto-detection of such `@Controller` beans, you can add component scanning to |
|
your Java configuration: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@ComponentScan("org.example.web") |
|
public class WebConfig { |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
The XML configuration equivalent: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:p="http://www.springframework.org/schema/p" |
|
xmlns:context="http://www.springframework.org/schema/context" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
http://www.springframework.org/schema/context/spring-context.xsd"> |
|
|
|
<context:component-scan base-package="org.example.web"/> |
|
|
|
<!-- ... --> |
|
|
|
</beans> |
|
---- |
|
|
|
`@RestController` is a <<core.adoc#beans-meta-annotations,composed annotation>> that is |
|
itself meta-annotated with `@Controller` and `@ResponseBody` indicating a controller whose |
|
every method inherits the type-level `@ResponseBody` annotation and therefore writes |
|
directly to the response body vs view resolution and rendering with an HTML template. |
|
|
|
|
|
[[mvc-ann-requestmapping-proxying]] |
|
==== AOP proxies |
|
|
|
In some cases a controller may need to be decorated with an AOP proxy at runtime. |
|
One example is if you choose to have `@Transactional` annotations directly on the |
|
controller. When this is the case, for controllers specifically, we recommend |
|
using class-based proxying. This is typically the default choice with controllers. |
|
However if a controller must implement an interface that is not a Spring Context |
|
callback (e.g. `InitializingBean`, `*Aware`, etc), you may need to explicitly |
|
configure class-based proxying. For example with `<tx:annotation-driven/>`, |
|
change to `<tx:annotation-driven proxy-target-class="true"/>`. |
|
|
|
|
|
|
|
[[mvc-ann-requestmapping]] |
|
=== Request Mapping |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping,Same in Spring WebFlux>># |
|
|
|
The `@RequestMapping` annotation is used to map requests to controllers methods. It has |
|
various attributes to match by URL, HTTP method, request parameters, headers, and media |
|
types. It can be used at the class-level to express shared mappings or at the method level |
|
to narrow down to a specific endpoint mapping. |
|
|
|
There are also HTTP method specific shortcut variants of `@RequestMapping`: |
|
|
|
- `@GetMapping` |
|
- `@PostMapping` |
|
- `@PutMapping` |
|
- `@DeleteMapping` |
|
- `@PatchMapping` |
|
|
|
The above are <<mvc-ann-requestmapping-composed>> that are provided out of the box |
|
because arguably most controller methods should be mapped to a specific HTTP method vs |
|
using `@RequestMapping` which by default matches to all HTTP methods. At the same an |
|
`@RequestMapping` is still needed at the class level to express shared mappings. |
|
|
|
Below is an example with type and method level mappings: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RestController |
|
@RequestMapping("/persons") |
|
class PersonController { |
|
|
|
@GetMapping("/{id}") |
|
public Person getPerson(@PathVariable Long id) { |
|
// ... |
|
} |
|
|
|
@PostMapping |
|
@ResponseStatus(HttpStatus.CREATED) |
|
public void add(@RequestBody Person person) { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-requestmapping-uri-templates]] |
|
==== URI patterns |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-uri-templates,Same in Spring |
|
WebFlux>># |
|
|
|
You can map requests using glob patterns and wildcards: |
|
|
|
* `?` matches one character |
|
* `*` matches zero or more characters within a path segment |
|
* `**` match zero or more path segments |
|
|
|
You can also declare URI variables and access their values with `@PathVariable`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/owners/{ownerId}/pets/{petId}") |
|
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { |
|
// ... |
|
} |
|
---- |
|
|
|
URI variables can be declared at the class and method level: |
|
[source,java,intent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("/owners/{ownerId}") |
|
public class OwnerController { |
|
|
|
@GetMapping("/pets/{petId}") |
|
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
URI variables are automatically converted to the appropriate type or`TypeMismatchException` |
|
is raised. Simple types -- `int`, `long`, `Date`, are supported by default and you can |
|
register support for any other data type. |
|
See <<mvc-ann-typeconversion>> and <<mvc-ann-initbinder>>. |
|
|
|
URI variables can be named explicitly -- e.g. `@PathVariable("customId")`, but you can |
|
leave that detail out if the names are the same and your code is compiled with debugging |
|
information or with the `-parameters` compiler flag on Java 8. |
|
|
|
The syntax `{varName:regex}` declares a URI variable with a regular expressions with the |
|
syntax `{varName:regex}` -- e.g. given URL `"/spring-web-3.0.5 .jar"`, the below method |
|
extracts the name, version, and file extension: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}") |
|
public void handle(@PathVariable String version, @PathVariable String ext) { |
|
// ... |
|
} |
|
---- |
|
|
|
URI path patterns can also have embedded `${...}` placeholders that are resolved on startup |
|
via `PropertyPlaceHolderConfigurer` against local, system, environment, and other property |
|
sources. This can be used for example to parameterize a base URL based on some external |
|
configuration. |
|
|
|
[NOTE] |
|
==== |
|
Spring MVC uses the `PathMatcher` contract and the `AntPathMatcher` implementation from |
|
`spring-core` for URI path matching. |
|
==== |
|
|
|
|
|
[[mvc-ann-requestmapping-pattern-comparison]] |
|
==== Pattern comparison |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-pattern-comparison,Same in Spring |
|
WebFlux>># |
|
|
|
When multiple patterns match a URL, they must be compared to find the best match. This done |
|
via `AntPathMatcher.getPatternComparator(String path)` which looks for patterns that more |
|
specific. |
|
|
|
A pattern is less specific if it has a lower count of URI variables and single wildcards |
|
counted as 1 and double wildcards counted as 2. Given an equal score, the longer pattern is |
|
chosen. Given the same score and length, the pattern with more URI variables than wildcards |
|
is chosen. |
|
|
|
The default mapping pattern `/{asterisk}{asterisk}` is excluded from scoring and always |
|
sorted last. Also prefix patterns such as `/public/{asterisk}{asterisk}` are considered less |
|
specific than other pattern that don't have double wildcards. |
|
|
|
For the full details see `AntPatternComparator` in `AntPathMatcher` and also keep mind that |
|
the `PathMatcher` implementation used can be customized. See <<mvc-config-path-matching>> |
|
in the configuration section. |
|
|
|
|
|
[[mvc-ann-requestmapping-suffix-pattern-match]] |
|
==== Suffix match |
|
|
|
By default Spring MVC performs `".{asterisk}"` suffix pattern matching so that a |
|
controller mapped to `/person` is also implicitly mapped to `/person.{asterisk}`. |
|
The file extension is then used to interpret the requested content type to use for |
|
the response (i.e. instead of the "Accept" header), e.g. `/person.pdf`, |
|
`/person.xml`, etc. |
|
|
|
Using file extensions like this was necessary when browsers used to send Accept headers |
|
that were hard to interpret consistently. At present that is no longer a necessity and |
|
using the "Accept" header should be the preferred choice. |
|
|
|
Over time the use of file name extensions has proven problematic in a variety of ways. |
|
It can cause ambiguity when overlayed with the use of URI variables, path parameters, |
|
URI encoding, and it also makes it difficult to reason about URL-based authorization |
|
and security (see next section for more details). |
|
|
|
To completely disable the use of file extensions, you must set both of these: |
|
|
|
* `useSuffixPatternMatching(false)`, see <<mvc-config-path-matching,PathMatchConfigurer>> |
|
* `favorPathExtension(false)`, see <<mvc-config-content-negotiation,ContentNeogiationConfigurer>> |
|
|
|
URL-based content negotiation can still be useful, for example when typing a URL in a |
|
browser. To enable that we recommend a query parameter based strategy to avoid most of |
|
the issues that come with file extensions. Or if you must use file extensions, consider |
|
restricting them to a list of explicitly registered extensions through the |
|
`mediaTypes` property of <<mvc-config-content-negotiation,ContentNeogiationConfigurer>>. |
|
|
|
|
|
[[mvc-ann-requestmapping-rfd]] |
|
==== Suffix match and RFD |
|
|
|
Reflected file download (RFD) attack is similar to XSS in that it relies on request input, |
|
e.g. query parameter, URI variable, being reflected in the response. However instead of |
|
inserting JavaScript into HTML, an RFD attack relies on the browser switching to perform a |
|
download and treating the response as an executable script when double-clicked later. |
|
|
|
In Spring MVC `@ResponseBody` and `ResponseEntity` methods are at risk because |
|
they can render different content types which clients can request via URL path extensions. |
|
Disabling suffix pattern matching and the use of path extensions for content negotiation |
|
lower the risk but are not sufficient to prevent RFD attacks. |
|
|
|
To prevent RFD attacks, prior to rendering the response body Spring MVC adds a |
|
`Content-Disposition:inline;filename=f.txt` header to suggest a fixed and safe download |
|
file. This is done only if the URL path contains a file extension that is neither whitelisted |
|
nor explicitly registered for content negotiation purposes. However it may potentially have |
|
side effects when URLs are typed directly into a browser. |
|
|
|
Many common path extensions are whitelisted by default. Applications with custom |
|
`HttpMessageConverter` implementations can explicitly register file extensions for content |
|
negotiation to avoid having a `Content-Disposition` header added for those extensions. |
|
See <<mvc-config-content-negotiation>>. |
|
|
|
Check http://pivotal.io/security/cve-2015-5211[CVE-2015-5211] for additional |
|
recommendations related to RFD. |
|
|
|
|
|
|
|
|
|
[[mvc-ann-requestmapping-consumes]] |
|
==== Consumable media types |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-consumes,Same in Spring WebFlux>># |
|
|
|
You can narrow the request mapping based on the `Content-Type` of the request: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping(path = "/pets", **consumes = "application/json"**) |
|
public void addPet(@RequestBody Pet pet) { |
|
// ... |
|
} |
|
---- |
|
|
|
The consumes attribute also supports negation expressions -- e.g. `!text/plain` means any |
|
content type other than "text/plain". |
|
|
|
You can declare a shared consumes attribute at the class level. Unlike most other request |
|
mapping attributes however when used at the class level, a method-level consumes attribute |
|
will overrides rather than extend the class level declaration. |
|
|
|
[TIP] |
|
==== |
|
`MediaType` provides constants for commonly used media types -- e.g. |
|
`APPLICATION_JSON_VALUE`, `APPLICATION_JSON_UTF8_VALUE`. |
|
==== |
|
|
|
|
|
[[mvc-ann-requestmapping-produces]] |
|
==== Producible media types |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-produces,Same in Spring WebFlux>># |
|
|
|
You can narrow the request mapping based on the `Accept` request header and the list of |
|
content types that a controller method produces: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping(path = "/pets/{petId}", **produces = "application/json;charset=UTF-8"**) |
|
@ResponseBody |
|
public Pet getPet(@PathVariable String petId) { |
|
// ... |
|
} |
|
---- |
|
|
|
The media type can specify a character set. Negated expressions are supported -- e.g. |
|
`!text/plain` means any content type other than "text/plain". |
|
|
|
You can declare a shared produces attribute at the class level. Unlike most other request |
|
mapping attributes however when used at the class level, a method-level produces attribute |
|
will overrides rather than extend the class level declaration. |
|
|
|
[TIP] |
|
==== |
|
`MediaType` provides constants for commonly used media types -- e.g. |
|
`APPLICATION_JSON_VALUE`, `APPLICATION_JSON_UTF8_VALUE`. |
|
==== |
|
|
|
|
|
[[mvc-ann-requestmapping-params-and-headers]] |
|
==== Parameters, headers |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-params-and-headers,Same in Spring |
|
WebFlux>># |
|
|
|
You can narrow request mappings based on request parameter conditions. You can test for the |
|
presence of a request parameter (`"myParam"`), for the absence (`"!myParam"`), or for a |
|
specific value (`"myParam=myValue"`): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping(path = "/pets/{petId}", **params = "myParam=myValue"**) |
|
public void findPet(@PathVariable String petId) { |
|
// ... |
|
} |
|
---- |
|
|
|
You can also use the same with request header conditions: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping(path = "/pets", **headers = "myHeader=myValue"**) |
|
public void findPet(@PathVariable String petId) { |
|
// ... |
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
You can match `Content-Type` and `Accept` with the headers condition but it is better to use |
|
<<mvc-ann-requestmapping-consumes,consumes>> and <<mvc-ann-requestmapping-produces,produces>> |
|
instead. |
|
==== |
|
|
|
|
|
[[mvc-ann-requestmapping-head-options]] |
|
==== HTTP HEAD, OPTIONS |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-head-options,Same in Spring WebFlux>># |
|
|
|
`@GetMapping` -- and also `@RequestMapping(method=HttpMethod.GET)`, support HTTP HEAD |
|
transparently for request mapping purposes. Controller methods don't need to change. |
|
A response wrapper, applied in `javax.servlet.http.HttpServlet`, ensures a `"Content-Length"` |
|
header is set to the number of bytes written and without actually writing to the response. |
|
|
|
`@GetMapping` -- and also `@RequestMapping(method=HttpMethod.GET)`, are implicitly mapped to |
|
and also support HTTP HEAD. An HTTP HEAD request is processed as if it were HTTP GET except |
|
but instead of writing the body, the number of bytes are counted and the "Content-Length" |
|
header set. |
|
|
|
By default HTTP OPTIONS is handled by setting the "Allow" response header to the list of HTTP |
|
methods listed in all `@RequestMapping` methods with matching URL patterns. |
|
|
|
For a `@RequestMapping` without HTTP method declarations, the "Allow" header is set to |
|
`"GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"`. Controller methods should always declare the |
|
supported HTTP methods for example by using the HTTP method specific variants -- |
|
`@GetMapping`, `@PostMapping`, etc. |
|
|
|
`@RequestMapping` method can be explicitly mapped to HTTP HEAD and HTTP OPTIONS, but that |
|
is not necessary in the common case. |
|
|
|
|
|
[[mvc-ann-requestmapping-composed]] |
|
==== Custom Annotations |
|
[.small]#<<web-reactive.adoc#mvc-ann-requestmapping-head-options,Same in Spring WebFlux>># |
|
|
|
Spring MVC supports the use of <<core.adoc#beans-meta-annotations,composed annotations>> |
|
for request mapping. Those are annotations that are themselves meta-annotated with |
|
`@RequestMapping` and composed to redeclare a subset (or all) of the `@RequestMapping` |
|
attributes with a narrower, more specific purpose. |
|
|
|
`@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, and `@PatchMapping` are |
|
examples of composed annotations. They're provided out of the box because arguably most |
|
controller methods should be mapped to a specific HTTP method vs using `@RequestMapping` |
|
which by default matches to all HTTP methods. If you need an example of composed |
|
annotations, look at how those are declared. |
|
|
|
Spring MVC also supports custom request mapping attributes with custom request matching |
|
logic. This is a more advanced option that requires sub-classing |
|
`RequestMappingHandlerMapping` and overriding the `getCustomMethodCondition` method where |
|
you can check the custom attribute and return your own `RequestCondition`. |
|
|
|
|
|
|
|
[[mvc-ann-methods]] |
|
=== Handler Methods |
|
[.small]#<<web-reactive.adoc#webflux-ann-methods,Same in Spring WebFlux>># |
|
|
|
`@RequestMapping` handler methods have a flexible signature and can choose from a range of |
|
supported controller method arguments and return values. |
|
|
|
|
|
[[mvc-ann-arguments]] |
|
==== Method Arguments |
|
[.small]#<<web-reactive.adoc#webflux-ann-arguments,Same in Spring WebFlux>># |
|
|
|
The table below shows supported controller method arguments. Reactive types are not supported |
|
for any arguments. |
|
|
|
JDK 8's `java.util.Optional` is supported as a method argument in combination with |
|
annotations that have a `required` attribute -- e.g. `@RequestParam`, `@RequestHeader`, |
|
etc, and is equivalent to `required=false`. |
|
|
|
[cols="1,2", options="header"] |
|
|=== |
|
| Controller method argument | Description |
|
|
|
| `WebRequest`, `NativeWebRequest` |
|
| Generic access to request parameters, request & session attributes, without direct |
|
use of the Servlet API. |
|
|
|
| `javax.servlet.ServletRequest`, `javax.servlet.ServletResponse` |
|
| Choose any specific request or response type -- e.g. `ServletRequest`, `HttpServletRequest`, |
|
or Spring's `MultipartRequest`, `MultipartHttpServletRequest`. |
|
|
|
| `javax.servlet.http.HttpSession` |
|
| Enforces the presence of a session. As a consequence, such an argument is never `null`. + |
|
**Note:** Session access is not thread-safe. Consider setting the |
|
``RequestMappingHandlerAdapter``'s "synchronizeOnSession" flag to "true" if multiple |
|
requests are allowed to access a session concurrently. |
|
|
|
| `javax.servlet.http.PushBuilder` |
|
| Servlet 4.0 push builder API for programmatic HTTP/2 resource pushes. |
|
Note that per Servlet spec, the injected `PushBuilder` instance can be null if the client |
|
does not support that HTTP/2 feature. |
|
|
|
| `java.security.Principal` |
|
| Currently authenticated user; possibly a specific `Principal` implementation class if known. |
|
|
|
| `HttpMethod` |
|
| The HTTP method of the request. |
|
|
|
| `java.util.Locale` |
|
| The current request locale, determined by the most specific `LocaleResolver` available, in |
|
effect, the configured `LocaleResolver`/`LocaleContextResolver`. |
|
|
|
| `java.util.TimeZone` + `java.time.ZoneId` |
|
| The time zone associated with the current request, as determined by a `LocaleContextResolver`. |
|
|
|
| `java.io.InputStream`, `java.io.Reader` |
|
| For access to the raw request body as exposed by the Servlet API. |
|
|
|
| `java.io.OutputStream`, `java.io.Writer` |
|
| For access to the raw response body as exposed by the Servlet API. |
|
|
|
| `@PathVariable` |
|
| For access to URI template variables. See <<mvc-ann-requestmapping-uri-templates>>. |
|
|
|
| `@MatrixVariable` |
|
| For access to name-value pairs in URI path segments. See <<mvc-ann-matrix-variables>>. |
|
|
|
| `@RequestParam` |
|
| For access to Servlet request parameters. Parameter values are converted to the declared |
|
method argument type. See <<mvc-ann-requestparam>>. |
|
|
|
Note that use of `@RequestParam` is optional, e.g. to set its attributes. |
|
See "Any other argument" further below in this table. |
|
|
|
| `@RequestHeader` |
|
| For access to request headers. Header values are converted to the declared method argument |
|
type. See <<mvc-ann-requestheader>>. |
|
|
|
| `@CookieValue` |
|
| For access to cookies. Cookies values are converted to the declared method argument |
|
type. See <<mvc-ann-cookievalue>>. |
|
|
|
| `@RequestBody` |
|
| For access to the HTTP request body. Body content is converted to the declared method |
|
argument type using ``HttpMessageConverter``s. See <<mvc-ann-requestbody>>. |
|
|
|
| `HttpEntity<B>` |
|
| For access to request headers and body. The body is converted with ``HttpMessageConverter``s. |
|
See <<mvc-ann-httpentity>>. |
|
|
|
| `@RequestPart` |
|
| For access to a part in a "multipart/form-data" request. |
|
See <<mvc-multipart-forms>>. |
|
|
|
| `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap` |
|
| For access to the model that is used in HTML controllers and exposed to templates as |
|
part of view rendering. |
|
|
|
| `RedirectAttributes` |
|
| Specify attributes to use in case of a redirect -- i.e. to be appended to the query |
|
string, and/or flash attributes to be stored temporarily until the request after redirect. |
|
See <<mvc-redirecting-passing-data>> and <<mvc-flash-attributes>>. |
|
|
|
| `@ModelAttribute` |
|
| For access to an existing attribute in the model (instantiated if not present) with |
|
data binding and validation applied. See <<mvc-ann-modelattrib-method-args>> as well as |
|
<<mvc-ann-modelattrib-methods>> and <<mvc-ann-initbinder>>. |
|
|
|
Note that use of `@ModelAttribute` is optional, e.g. to set its attributes. |
|
See "Any other argument" further below in this table. |
|
|
|
| `Errors`, `BindingResult` |
|
| For access to errors from validation and data binding for a command object |
|
(i.e. `@ModelAttribute` argument), or errors from the validation of an `@RequestBody` or |
|
`@RequestPart` arguments; an `Errors`, or `BindingResult` argument must be declared |
|
immediately after the validated method argument. |
|
|
|
| `SessionStatus` + class-level `@SessionAttributes` |
|
| For marking form processing complete which triggers cleanup of session attributes |
|
declared through a class-level `@SessionAttributes` annotation. See |
|
<<mvc-ann-sessionattributes>> for more details. |
|
|
|
| `UriComponentsBuilder` |
|
| For preparing a URL relative to the current request's host, port, scheme, context path, and |
|
the literal part of the servlet mapping also taking into account `Forwarded` and |
|
`X-Forwarded-*` headers. See <<mvc-uri-building>>. |
|
|
|
| `@SessionAttribute` |
|
| For access to any session attribute; in contrast to model attributes stored in the session |
|
as a result of a class-level `@SessionAttributes` declaration. See |
|
<<mvc-ann-sessionattribute>> for more details. |
|
|
|
| `@RequestAttribute` |
|
| For access to request attributes. See <<mvc-ann-requestattrib>> for more details. |
|
|
|
| Any other argument |
|
| If a method argument is not matched to any of the above, by default it is resolved as |
|
an `@RequestParam` if it is a simple type, as determined by |
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty], |
|
or as an `@ModelAttribute` otherwise. |
|
|=== |
|
|
|
|
|
[[mvc-ann-return-types]] |
|
==== Return Values |
|
[.small]#<<web-reactive.adoc#webflux-ann-return-types,Same in Spring WebFlux>># |
|
|
|
The table below shows supported controller method return values. Reactive types are |
|
supported for all return values, see below for more details. |
|
|
|
[cols="1,2", options="header"] |
|
|=== |
|
| Controller method return value | Description |
|
|
|
| `@ResponseBody` |
|
| The return value is converted through ``HttpMessageConverter``s and written to the |
|
response. See <<mvc-ann-responsebody>>. |
|
|
|
| `HttpEntity<B>`, `ResponseEntity<B>` |
|
| The return value specifies the full response including HTTP headers and body be converted |
|
through ``HttpMessageConverter``s and written to the response. |
|
See <<mvc-ann-responseentity>>. |
|
|
|
| `HttpHeaders` |
|
| For returning a response with headers and no body. |
|
|
|
| `String` |
|
| A view name to be resolved with ``ViewResolver``'s and used together with the implicit |
|
model -- determined through command objects and `@ModelAttribute` methods. The handler |
|
method may also programmatically enrich the model by declaring a `Model` argument |
|
(see above). |
|
|
|
| `View` |
|
| A `View` instance to use for rendering together with the implicit model -- determined |
|
through command objects and `@ModelAttribute` methods. The handler method may also |
|
programmatically enrich the model by declaring a `Model` argument (see above). |
|
|
|
| `java.util.Map`, `org.springframework.ui.Model` |
|
| Attributes to be added to the implicit model with the view name implicitly determined |
|
through a `RequestToViewNameTranslator`. |
|
|
|
| `@ModelAttribute` |
|
| An attribute to be added to the model with the view name implicitly determined through |
|
a `RequestToViewNameTranslator`. |
|
|
|
Note that `@ModelAttribute` is optional. See "Any other return value" further below in |
|
this table. |
|
|
|
| `ModelAndView` object |
|
| The view and model attributes to use, and optionally a response status. |
|
|
|
| `void` |
|
| A method with a `void` return type (or `null` return value) is considered to have fully |
|
handled the response if it also has a `ServletResponse`, or an `OutputStream` argument, or |
|
an `@ResponseStatus` annotation. The same is true also if the controller has made a positive |
|
ETag or lastModified timestamp check (see <<mvc-caching-etag-lastmodified>> for details). |
|
|
|
If none of the above is true, a `void` return type may also indicate "no response body" for |
|
REST controllers, or default view name selection for HTML controllers. |
|
|
|
| `DeferredResult<V>` |
|
| Produce any of the above return values asynchronously from any thread -- e.g. possibly as a |
|
result of some event or callback. See <<mvc-ann-async>> and |
|
<<mvc-ann-async-deferredresult>>. |
|
|
|
| `Callable<V>` |
|
| Produce any of the above return values asynchronously in a Spring MVC managed thread. |
|
See <<mvc-ann-async>> and <<mvc-ann-async-callable>>. |
|
|
|
| `ListenableFuture<V>`, |
|
`java.util.concurrent.CompletionStage<V>`, |
|
`java.util.concurrent.CompletableFuture<V>` |
|
| Alternative to `DeferredResult` as a convenience for example when an underlying service |
|
returns one of those. |
|
|
|
| `ResponseBodyEmitter`, `SseEmitter` |
|
| Emit a stream of objects asynchronously to be written to the response with |
|
``HttpMessageConverter``'s; also supported as the body of a `ResponseEntity`. |
|
See <<mvc-ann-async>> and <<mvc-ann-async-http-streaming>>. |
|
|
|
| `StreamingResponseBody` |
|
| Write to the response `OutputStream` asynchronously; also supported as the body of a |
|
`ResponseEntity`. See <<mvc-ann-async>> and <<mvc-ann-async-http-streaming>>. |
|
|
|
| Reactive types -- Reactor, RxJava, or others via `ReactiveAdapterRegistry` |
|
| Alternative to ``DeferredResult` with multi-value streams (e.g. `Flux`, `Observable`) |
|
collected to a `List`. |
|
|
|
For streaming scenarios -- e.g. `text/event-stream`, `application/json+stream` -- |
|
`SseEmitter` and `ResponseBodyEmitter` are used instead, where `ServletOutputStream` |
|
blocking I/O is performed on a Spring MVC managed thread and back pressure applied |
|
against the completion of each write. |
|
|
|
See <<mvc-ann-async>> and <<mvc-ann-async-reactive-types>>. |
|
|
|
| Any other return value |
|
| If a return value is not matched to any of the above, by default it is treated as a view |
|
name, if it is `String` or `void` (default view name selection via |
|
`RequestToViewNameTranslator` applies); or as a model attribute to be added to the model, |
|
unless it is a simple type, as determined by |
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty] |
|
in which case it remains unresolved. |
|
|=== |
|
|
|
|
|
[[mvc-ann-typeconversion]] |
|
==== Type Conversion |
|
[.small]#<<web-reactive.adoc#webflux-ann-typeconversion,Same in Spring WebFlux>># |
|
|
|
Some annotated controller method arguments that represent String-based request input -- e.g. |
|
`@RequestParam`, `@RequestHeader`, `@PathVariable`, `@MatrixVariable`, and `@CookieValue`, |
|
may require type conversion if the argument is declared as something other than `String`. |
|
|
|
For such cases type conversion is automatically applied based on the configured converters. |
|
By default simple types such as `int`, `long`, `Date`, etc. are supported. Type conversion |
|
can be customized through a `WebDataBinder`, see <<mvc-ann-initbinder>>, or by registering |
|
`Formatters` with the `FormattingConversionService`, see |
|
<<core.adoc#format, Spring Field Formatting>>. |
|
|
|
|
|
[[mvc-ann-matrix-variables]] |
|
==== Matrix variables |
|
[.small]#<<web-reactive.adoc#webflux-ann-matrix-variables,Same in Spring WebFlux>># |
|
|
|
http://tools.ietf.org/html/rfc3986#section-3.3[RFC 3986] discusses name-value pairs in |
|
path segments. In Spring MVC we refer to those as "matrix variables" based on an |
|
http://www.w3.org/DesignIssues/MatrixURIs.html["old post"] by Tim Berners-Lee but they |
|
can be also be referred to as URI path parameters. |
|
|
|
Matrix variables can appear in any path segment, each variable separated by semicolon and |
|
multiple values separated by comma, e.g. `"/cars;color=red,green;year=2012"`. Multiple |
|
values can also be specified through repeated variable names, e.g. |
|
`"color=red;color=green;color=blue"`. |
|
|
|
If a URL is expected to contain matrix variables, the request mapping for a controller |
|
method must use a URI variable to mask that variable content and ensure the request can |
|
be matched successfully independent of matrix variable order and presence. |
|
Below is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// GET /pets/42;q=11;r=22 |
|
|
|
@GetMapping("/pets/{petId}") |
|
public void findPet(@PathVariable String petId, @MatrixVariable int q) { |
|
|
|
// petId == 42 |
|
// q == 11 |
|
} |
|
---- |
|
|
|
Given that all path segments may contain matrix variables, sometimes you may need to |
|
disambiguate which path variable the matrix variable is expected to be in. |
|
For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// 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 |
|
} |
|
---- |
|
|
|
A matrix variable may be defined as optional and a default value specified: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// GET /pets/42 |
|
|
|
@GetMapping("/pets/{petId}") |
|
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) { |
|
|
|
// q == 1 |
|
} |
|
---- |
|
|
|
To get all matrix variables, use a `MultiValueMap`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// 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] |
|
} |
|
---- |
|
|
|
Note that you need to enable the use of matrix variables. In the MVC Java config you need |
|
to set a `UrlPathHelper` with `removeSemicolonContent=false` via |
|
<<mvc-config-path-matching>>. In the MVC XML namespace, use |
|
`<mvc:annotation-driven enable-matrix-variables="true"/>`. |
|
|
|
|
|
[[mvc-ann-requestparam]] |
|
==== @RequestParam |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestparam,Same in Spring WebFlux>># |
|
|
|
Use the `@RequestParam` annotation to bind Servlet request parameters (i.e. query |
|
parameters or form data) to a method argument in a controller. |
|
|
|
The following code snippet shows the usage: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@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"; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
Method parameters using this annotation are required by default, but you can specify that |
|
a method parameter is optional by setting ``@RequestParam``'s `required` flag to `false` |
|
or by declaring the argument with an `java.util.Optional` wrapper. |
|
|
|
Type conversion is applied automatically if the target method parameter type is not |
|
`String`. See <<mvc-ann-typeconversion>>. |
|
|
|
When an `@RequestParam` annotation is declared as `Map<String, String>` or |
|
`MultiValueMap<String, String>` argument, the map is populated with all request |
|
parameters. |
|
|
|
Note that use of `@RequestParam` is optional, e.g. to set its attributes. |
|
By default any argument that is a simple value type, as determined by |
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty], |
|
and is not resolved by any other argument resolver, is treated as if it was annotated |
|
with `@RequestParam`. |
|
|
|
|
|
[[mvc-ann-requestheader]] |
|
==== @RequestHeader |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestheader,Same in Spring WebFlux>># |
|
|
|
Use the `@RequestHeader` annotation to bind a request header to a method argument in a |
|
controller. |
|
|
|
Given request with headers: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
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 gets the value of the `Accept-Encoding` and `Keep-Alive` headers: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/demo") |
|
public void handle( |
|
**@RequestHeader("Accept-Encoding")** String encoding, |
|
**@RequestHeader("Keep-Alive")** long keepAlive) { |
|
//... |
|
} |
|
---- |
|
|
|
Type conversion is applied automatically if the target method parameter type is not |
|
`String`. See <<mvc-ann-typeconversion>>. |
|
|
|
When an `@RequestHeader` annotation is used on a `Map<String, String>`, |
|
`MultiValueMap<String, String>`, or `HttpHeaders` argument, the map is populated |
|
with all header values. |
|
|
|
[TIP] |
|
==== |
|
Built-in support is available for converting a comma-separated string into an |
|
array/collection of strings or other types known to the type conversion system. For |
|
example a method parameter annotated with `@RequestHeader("Accept")` may be of type |
|
`String` but also `String[]` or `List<String>`. |
|
==== |
|
|
|
|
|
[[mvc-ann-cookievalue]] |
|
==== @CookieValue |
|
[.small]#<<web-reactive.adoc#webflux-ann-cookievalue,Same in Spring WebFlux>># |
|
|
|
Use the `@CookieValue` annotation to bind the value of an HTTP cookie to a method argument |
|
in a controller. |
|
|
|
Given request with the following cookie: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 |
|
---- |
|
|
|
The following code sample demonstrates how to get the cookie value: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/demo") |
|
public void handle(**@CookieValue("JSESSIONID")** String cookie) { |
|
//... |
|
} |
|
---- |
|
|
|
Type conversion is applied automatically if the target method parameter type is not |
|
`String`. See <<mvc-ann-typeconversion>>. |
|
|
|
|
|
[[mvc-ann-modelattrib-method-args]] |
|
==== @ModelAttribute |
|
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-method-args,Same in Spring WebFlux>># |
|
|
|
Use the `@ModelAttribute` annotation on a method argument to access an attribute from the |
|
model, or have it instantiated if not present. The model attribute is also overlaid with |
|
values from HTTP Servlet request parameters whose names match to field names. This is |
|
referred to as data binding and it saves you from having to deal with parsing and |
|
converting individual query parameters and form fields. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
public String processSubmit(**@ModelAttribute Pet pet**) { } |
|
---- |
|
|
|
The `Pet` instance above is resolved as follows: |
|
|
|
* From the model if already added via <<mvc-ann-modelattrib-methods>>. |
|
* From the HTTP session via <<mvc-ann-sessionattributes>>. |
|
* From a URI path variable passed through a `Converter` (example below). |
|
* From the invocation of a default constructor. |
|
* From the invocation of a "primary constructor" with arguments matching to Servlet |
|
request parameters; argument names are determined via JavaBeans |
|
`@ConstructorProperties` or via runtime-retained parameter names in the bytecode. |
|
|
|
While it is common to use a <<mvc-ann-modelattrib-methods>> to populate the model with |
|
attributes, one other alternative is to rely on a `Converter<String, T>` in combination |
|
with a URI path variable convention. In the example below the model attribute name |
|
"account" matches the URI path variable "account" and the `Account` is loaded by passing |
|
the `String` account number through a registered `Converter<String, Account>`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PutMapping("/accounts/{account}") |
|
public String save(@ModelAttribute("account") Account account) { |
|
// ... |
|
} |
|
---- |
|
|
|
After the model attribute instance is obtained, data binding is applied. The |
|
`WebDataBinder` class matches Servlet request parameter names (query parameters and form |
|
fields) to field names on the target Object. Matching fields are populated after type |
|
conversion is applied where necessary. For more on data binding (and validation) see |
|
<<core.adoc#validation, Validation>>. For more on customizing data binding see |
|
<<mvc-ann-initbinder>>. |
|
|
|
Data binding may result in errors. By default a `BindException` is raised but to check |
|
for such errors in the controller method, add a `BindingResult` argument immediately next |
|
to the `@ModelAttribute` as shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { |
|
if (result.hasErrors()) { |
|
return "petForm"; |
|
} |
|
// ... |
|
} |
|
---- |
|
|
|
In some cases you may want access to a model attribute without data binding. For such |
|
cases you can inject the `Model` into the controller and access it directly or |
|
alternatively set `@ModelAttribute(binding=false)` as shown below: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ModelAttribute |
|
public AccountForm setUpForm() { |
|
return new AccountForm(); |
|
} |
|
|
|
@ModelAttribute |
|
public Account findAccount(@PathVariable String accountId) { |
|
return accountRepository.findOne(accountId); |
|
} |
|
|
|
@PostMapping("update") |
|
public String update(@Valid AccountUpdateForm form, BindingResult result, |
|
**@ModelAttribute(binding=false)** Account account) { |
|
// ... |
|
} |
|
---- |
|
|
|
Validation can be applied automatically after data binding by adding the |
|
`javax.validation.Valid` annotation or Spring's `@Validated` annotation (also see |
|
<<core.adoc#validation-beanvalidation, Bean validation>> and |
|
<<core.adoc#validation, Spring validation>>). For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) { |
|
if (result.hasErrors()) { |
|
return "petForm"; |
|
} |
|
// ... |
|
} |
|
---- |
|
|
|
Note that use of `@ModelAttribute` is optional, e.g. to set its attributes. |
|
By default any argument that is not a simple value type, as determined by |
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty], |
|
and is not resolved by any other argument resolver, is treated as if it was annotated |
|
with `@ModelAttribute`. |
|
|
|
|
|
|
|
[[mvc-ann-sessionattributes]] |
|
==== @SessionAttributes |
|
[.small]#<<web-reactive.adoc#webflux-ann-sessionattributes,Same in Spring WebFlux>># |
|
|
|
`@SessionAttributes` is used to store model attributes in the HTTP Servlet session between |
|
requests. It is a type-level annotation that declares session attributes used by a |
|
specific controller. This will typically list the names of model attributes or types of |
|
model attributes which should be transparently stored in the session for subsequent |
|
requests to access. |
|
|
|
For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
**@SessionAttributes("pet")** |
|
public class EditPetForm { |
|
// ... |
|
} |
|
---- |
|
|
|
On the first request when a model attribute with the name "pet" is added to the model, |
|
it is automatically promoted to and saved in the HTTP Servlet session. It remains there |
|
until another controller method uses a `SessionStatus` method argument to clear the |
|
storage: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
**@SessionAttributes("pet")** |
|
public class EditPetForm { |
|
|
|
// ... |
|
|
|
@PostMapping("/pets/{id}") |
|
public String handle(Pet pet, BindingResult errors, SessionStatus status) { |
|
if (errors.hasErrors) { |
|
// ... |
|
} |
|
status.setComplete(); |
|
// ... |
|
} |
|
} |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-sessionattribute]] |
|
==== @SessionAttribute |
|
[.small]#<<web-reactive.adoc#webflux-ann-sessionattribute,Same in Spring WebFlux>># |
|
|
|
If you need access to pre-existing session attributes that are managed globally, |
|
i.e. outside the controller (e.g. by a filter), and may or may not be present |
|
use the `@SessionAttribute` annotation on a method parameter: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/") |
|
public String handle(**@SessionAttribute** User user) { |
|
// ... |
|
} |
|
---- |
|
|
|
For use cases that require adding or removing session attributes consider injecting |
|
`org.springframework.web.context.request.WebRequest` or |
|
`javax.servlet.http.HttpSession` into the controller method. |
|
|
|
For temporary storage of model attributes in the session as part of a controller |
|
workflow consider using `SessionAttributes` as described in |
|
<<mvc-ann-sessionattributes>>. |
|
|
|
|
|
[[mvc-ann-requestattrib]] |
|
==== @RequestAttribute |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestattrib,Same in Spring WebFlux>># |
|
|
|
Similar to `@SessionAttribute` the `@RequestAttribute` annotation can be used to |
|
access pre-existing request attributes created earlier, e.g. by a Servlet `Filter` |
|
or `HandlerInterceptor`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/") |
|
public String handle(**@RequestAttribute** Client client) { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
[[mvc-redirecting-passing-data]] |
|
==== Redirect attributes |
|
|
|
By default all model attributes are considered to be exposed as URI template variables in |
|
the redirect URL. Of the remaining attributes those that are primitive types or |
|
collections/arrays of primitive types are automatically appended as query parameters. |
|
|
|
Appending primitive type attributes as query parameters may be the desired result if a |
|
model instance was prepared specifically for the redirect. However, in annotated |
|
controllers the model may contain additional attributes added for rendering purposes (e.g. |
|
drop-down field values). To avoid the possibility of having such attributes appear in the |
|
URL, an `@RequestMapping` method can declare an argument of type `RedirectAttributes` and |
|
use it to specify the exact attributes to make available to `RedirectView`. If the method |
|
does redirect, the content of `RedirectAttributes` is used. Otherwise the content of the |
|
model is used. |
|
|
|
The `RequestMappingHandlerAdapter` provides a flag called |
|
`"ignoreDefaultModelOnRedirect"` that can be used to indicate the content of the default |
|
`Model` should never be used if a controller method redirects. Instead the controller |
|
method should declare an attribute of type `RedirectAttributes` or if it doesn't do so |
|
no attributes should be passed on to `RedirectView`. Both the MVC namespace and the MVC |
|
Java config keep this flag set to `false` in order to maintain backwards compatibility. |
|
However, for new applications we recommend setting it to `true` |
|
|
|
Note that URI template variables from the present request are automatically made |
|
available when expanding a redirect URL and do not need to be added explicitly neither |
|
through `Model` nor `RedirectAttributes`. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/files/{path}") |
|
public String upload(...) { |
|
// ... |
|
return "redirect:files/{path}"; |
|
} |
|
---- |
|
|
|
Another way of passing data to the redirect target is via __Flash Attributes__. Unlike |
|
other redirect attributes, flash attributes are saved in the HTTP session (and hence do |
|
not appear in the URL). See <<mvc-flash-attributes>> for more information. |
|
|
|
|
|
[[mvc-flash-attributes]] |
|
==== Flash attributes |
|
|
|
Flash attributes provide a way for one request to store attributes intended for use in |
|
another. This is most commonly needed when redirecting -- for example, the |
|
__Post/Redirect/Get__ pattern. Flash attributes are saved temporarily before the |
|
redirect (typically in the session) to be made available to the request after the |
|
redirect and removed immediately. |
|
|
|
Spring MVC has two main abstractions in support of flash attributes. `FlashMap` is used |
|
to hold flash attributes while `FlashMapManager` is used to store, retrieve, and manage |
|
`FlashMap` instances. |
|
|
|
Flash attribute support is always "on" and does not need to enabled explicitly although |
|
if not used, it never causes HTTP session creation. On each request there is an "input" |
|
`FlashMap` with attributes passed from a previous request (if any) and an "output" |
|
`FlashMap` with attributes to save for a subsequent request. Both `FlashMap` instances |
|
are accessible from anywhere in Spring MVC through static methods in |
|
`RequestContextUtils`. |
|
|
|
Annotated controllers typically do not need to work with `FlashMap` directly. Instead an |
|
`@RequestMapping` method can accept an argument of type `RedirectAttributes` and use it |
|
to add flash attributes for a redirect scenario. Flash attributes added via |
|
`RedirectAttributes` are automatically propagated to the "output" FlashMap. Similarly, |
|
after the redirect, attributes from the "input" `FlashMap` are automatically added to the |
|
`Model` of the controller serving the target URL. |
|
|
|
.Matching requests to flash attributes |
|
**** |
|
The concept of flash attributes exists in many other Web frameworks and has proven to be |
|
exposed sometimes to concurrency issues. This is because by definition flash attributes |
|
are to be stored until the next request. However the very "next" request may not be the |
|
intended recipient but another asynchronous request (e.g. polling or resource requests) |
|
in which case the flash attributes are removed too early. |
|
|
|
To reduce the possibility of such issues, `RedirectView` automatically "stamps" |
|
`FlashMap` instances with the path and query parameters of the target redirect URL. In |
|
turn the default `FlashMapManager` matches that information to incoming requests when |
|
looking up the "input" `FlashMap`. |
|
|
|
This does not eliminate the possibility of a concurrency issue entirely but nevertheless |
|
reduces it greatly with information that is already available in the redirect URL. |
|
Therefore the use of flash attributes is recommended mainly for redirect scenarios . |
|
**** |
|
|
|
|
|
[[mvc-multipart-forms]] |
|
==== Multipart |
|
[.small]#<<web-reactive.adoc#webflux-multipart-forms,Same in Spring WebFlux>># |
|
|
|
After a `MultipartResolver` has been <<mvc-multipart,enabled>>, the content of POST |
|
requests with "multipart/form-data" is parsed and accessible as regular request |
|
parameters. In the example below we access one regular form field and one uploaded |
|
file: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@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"; |
|
} |
|
|
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
When using Servlet 3.0 multipart parsing you can also use `javax.servlet.http.Part` as |
|
a method argument instead of Spring's `MultipartFile`. |
|
==== |
|
|
|
Multipart content can also be used as part of data binding to a |
|
<<mvc-ann-modelattrib-method-args,command object>>. For example the above form field |
|
and file could have been fields on a form object: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
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"; |
|
} |
|
|
|
} |
|
---- |
|
|
|
Multipart requests can also be submitted from non-browser clients in a RESTful service |
|
scenario. For example a file along with JSON: |
|
|
|
[literal] |
|
[subs="verbatim,quotes"] |
|
---- |
|
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 "meta-data" part with `@RequestParam` as a `String` but you'll |
|
probably want it deserialized from JSON (similar to `@RequestBody`). Use the |
|
`@RequestPart` annotation to access a multipart after converting it with an |
|
<<integration.adoc#rest-message-conversion,HttpMessageConverter>>: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/") |
|
public String handle(**@RequestPart("meta-data") MetaData metadata, |
|
@RequestPart("file-data") MultipartFile file**) { |
|
// ... |
|
} |
|
---- |
|
|
|
`@RequestPart` can be used in combination with `javax.validation.Valid`, or Spring's |
|
`@Validated` annotation, which causes Standard Bean Validation to be applied. |
|
By default validation errors cause a `MethodArgumentNotValidException` which is turned |
|
into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally |
|
within the controller through an `Errors` or `BindingResult` argument: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/") |
|
public String handle(**@Valid** @RequestPart("meta-data") MetaData metadata, |
|
**BindingResult result**) { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-requestbody]] |
|
==== @RequestBody |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestbody,Same in Spring WebFlux>># |
|
|
|
Use the `@RequestBody` annotation to have the request body read and deserialized into an |
|
Object through an <<integration.adoc#rest-message-conversion,HttpMessageConverter>>. |
|
Below is an example with an `@RequestBody` argument: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/accounts") |
|
public void handle(@RequestBody Account account) { |
|
// ... |
|
} |
|
---- |
|
|
|
You can use the <<mvc-config-message-converters>> option of the <<mvc-config>> to |
|
configure or customize message conversion. |
|
|
|
`@RequestBody` can be used in combination with `javax.validation.Valid`, or Spring's |
|
`@Validated` annotation, which causes Standard Bean Validation to be applied. |
|
By default validation errors cause a `MethodArgumentNotValidException` which is turned |
|
into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally |
|
within the controller through an `Errors` or `BindingResult` argument: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/accounts") |
|
public void handle(@Valid @RequestBody Account account, BindingResult result) { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-httpentity]] |
|
==== HttpEntity |
|
[.small]#<<web-reactive.adoc#webflux-ann-httpentity,Same in Spring WebFlux>># |
|
|
|
`HttpEntity` is more or less identical to using <<mvc-ann-requestbody>> but based on a |
|
container object that exposes request headers and body. Below is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/accounts") |
|
public void handle(HttpEntity<Account> entity) { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-responsebody]] |
|
==== @ResponseBody |
|
[.small]#<<web-reactive.adoc#webflux-ann-responsebody,Same in Spring WebFlux>># |
|
|
|
Use the `@ResponseBody` annotation on a method to have the return serialized to the |
|
response body through an |
|
<<integration.adoc#rest-message-conversion,HttpMessageConverter>>. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/accounts/{id}") |
|
@ResponseBody |
|
public Account handle() { |
|
// ... |
|
} |
|
---- |
|
|
|
`@ResponseBody` is also supported at the class level in which case it is inherited by |
|
all controller methods. This is the effect of `@RestController` which is nothing more |
|
than a meta-annotation marked with `@Controller` and `@ResponseBody`. |
|
|
|
`@ResponseBody` may be used with reactive types. |
|
See <<mvc-ann-async>> and <<mvc-ann-async-reactive-types>> for more details. |
|
|
|
You can use the <<mvc-config-message-converters>> option of the <<mvc-config>> to |
|
configure or customize message conversion. |
|
|
|
`@ResponseBody` methods can be combined with JSON serialization views. |
|
See <<mvc-ann-jackson>> for details. |
|
|
|
|
|
[[mvc-ann-responseentity]] |
|
==== ResponseEntity |
|
[.small]#<<web-reactive.adoc#webflux-ann-responseentity,Same in Spring WebFlux>># |
|
|
|
`ResponseEntity` is more or less identical to using <<mvc-ann-responsebody>> but based |
|
on a container object that specifies request headers and body. Below is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping("/something") |
|
public ResponseEntity<String> handle() { |
|
// ... |
|
URI location = ... |
|
return new ResponseEntity.created(location).build(); |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-jackson]] |
|
==== Jackson JSON |
|
|
|
[[mvc-ann-jsonview]] |
|
===== Jackson serialization views |
|
[.small]#<<web-reactive.adoc#webflux-ann-jsonview,Same in Spring WebFlux>># |
|
|
|
Spring MVC provides built-in support for |
|
http://wiki.fasterxml.com/JacksonJsonViews[Jackson's Serialization Views] |
|
which allows rendering only a subset of all fields in an Object. To use it with |
|
`@ResponseBody` or `ResponseEntity` controller methods, use Jackson's |
|
`@JsonView` annotation to activate a serialization view class: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RestController |
|
public class UserController { |
|
|
|
@GetMapping("/user") |
|
@JsonView(User.WithoutPasswordView.class) |
|
public User getUser() { |
|
return new User("eric", "7!jd#h23"); |
|
} |
|
} |
|
|
|
public class User { |
|
|
|
public interface WithoutPasswordView {}; |
|
public interface WithPasswordView extends WithoutPasswordView {}; |
|
|
|
private String username; |
|
private String password; |
|
|
|
public User() { |
|
} |
|
|
|
public User(String username, String password) { |
|
this.username = username; |
|
this.password = password; |
|
} |
|
|
|
@JsonView(WithoutPasswordView.class) |
|
public String getUsername() { |
|
return this.username; |
|
} |
|
|
|
@JsonView(WithPasswordView.class) |
|
public String getPassword() { |
|
return this.password; |
|
} |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
`@JsonView` allows an array of view classes but you can only specify only one per |
|
controller method. Use a composite interface if you need to activate multiple views. |
|
==== |
|
|
|
For controllers relying on view resolution, simply add the serialization view class |
|
to the model: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@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"; |
|
} |
|
} |
|
---- |
|
|
|
[[mvc-ann-jsonp]] |
|
===== Jackson JSONP |
|
|
|
In order to enable http://en.wikipedia.org/wiki/JSONP[JSONP] support for `@ResponseBody` |
|
and `ResponseEntity` methods, declare an `@ControllerAdvice` bean that extends |
|
`AbstractJsonpResponseBodyAdvice` as shown below where the constructor argument indicates |
|
the JSONP query parameter name(s): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ControllerAdvice |
|
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { |
|
|
|
public JsonpAdvice() { |
|
super("callback"); |
|
} |
|
} |
|
---- |
|
|
|
For controllers relying on view resolution, JSONP is automatically enabled when the |
|
request has a query parameter named `jsonp` or `callback`. Those names can be |
|
customized through `jsonpParameterNames` property. |
|
|
|
|
|
|
|
[[mvc-ann-modelattrib-methods]] |
|
=== Model Methods |
|
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-methods,Same in Spring WebFlux>># |
|
|
|
The `@ModelAttribute` annotation can be used on `@RequestMapping` |
|
<<mvc-ann-modelattrib-method-args,method arguments>> to create or access an Object |
|
from the model and bind it to the request. `@ModelAttribute` can also be used as a |
|
method-level annotation on controller methods whose purpose is not to handle requests |
|
but to add commonly needed model attributes prior to request handling. |
|
|
|
A controller can have any number of `@ModelAttribute` methods. All such methods are |
|
invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute` |
|
method can also be shared across controllers via `@ControllerAdvice`. See the section on |
|
<<mvc-ann-controller-advice>> for more details. |
|
|
|
`@ModelAttribute` methods have flexible method signatures. They support many of the same |
|
arguments as `@RequestMapping` methods except for `@ModelAttribute` itself nor anything |
|
related to the request body. |
|
|
|
An example `@ModelAttribute` method: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ModelAttribute |
|
public void populateModel(@RequestParam String number, Model model) { |
|
model.addAttribute(accountRepository.findAccount(number)); |
|
// add more ... |
|
} |
|
---- |
|
|
|
To add one attribute only: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@ModelAttribute |
|
public Account addAccount(@RequestParam String number) { |
|
return accountRepository.findAccount(number); |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
When a name is not explicitly specified, a default name is chosen based on the Object |
|
type as explained in the Javadoc for |
|
{api-spring-framework}/core/Conventions.html[Conventions]. |
|
You can always assign an explicit name by using the overloaded `addAttribute` method or |
|
through the name attribute on `@ModelAttribute` (for a return value). |
|
==== |
|
|
|
`@ModelAttribute` can also be used as a method-level annotation on `@RequestMapping` |
|
methods in which case the return value of the `@RequestMapping` method is interpreted as a |
|
model attribute. This is typically not required, as it is the default behavior in HTML |
|
controllers, unless the return value is a `String` which would otherwise be interpreted |
|
as a view name (also see <<mvc-coc-r2vnt>>). `@ModelAttribute` can also help to customize |
|
the model attribute name: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/accounts/{id}") |
|
@ModelAttribute("myAccount") |
|
public Account handle() { |
|
// ... |
|
return account; |
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
|
|
[[mvc-ann-initbinder]] |
|
=== Binder Methods |
|
[.small]#<<web-reactive.adoc#webflux-ann-initbinder,Same in Spring WebFlux>># |
|
|
|
`@InitBinder` methods in an `@Controller` or `@ControllerAdvice` class can be used to |
|
customize type conversion for method arguments that represent String-based request values |
|
(e.g. request parameters, path variables, headers, cookies, and others). Type conversion |
|
also applies during data binding of request parameters onto `@ModelAttribute` arguments |
|
(i.e. command objects). |
|
|
|
`@InitBinder` methods can register controller-specific `java.bean.PropertyEditor`, or |
|
Spring `Converter` and `Formatter` components. In addition, the |
|
<<mvc-config-conversion,MVC config>> can be used to register `Converter` and `Formatter` |
|
types in a globally shared `FormattingConversionService`. |
|
|
|
|
|
`@InitBinder` methods support many of the same arguments that a `@RequestMapping` methods |
|
do, except for `@ModelAttribute` (command object) arguments. Typically they're are declared |
|
with a `WebDataBinder` argument, for registrations, and a `void` return value. |
|
Below is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@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)); |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
Alternatively when using a `Formatter`-based setup through a shared |
|
`FormattingConversionService`, you could re-use the same approach and register |
|
controller-specific ``Formatter``'s: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class FormController { |
|
|
|
**@InitBinder** |
|
protected void initBinder(WebDataBinder binder) { |
|
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-ann-exceptionhandler]] |
|
=== Exception Methods |
|
|
|
`@ExceptionHandler` methods in an `@Controller` can be used to handle exceptions during |
|
request handling from the same controller. An `@ExceptionHandler` can also be declared |
|
in an <<mvc-ann-controller-advice,@ControllerAdvice class>> to apply across controllers. |
|
Support for `@ExceptionHandler` methods in Spring MVC is provided through the |
|
<<mvc-exceptionhandlers,HandlerExceptionResolver>> mechanism. |
|
|
|
Below is an example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class SimpleController { |
|
|
|
// ... |
|
|
|
@ExceptionHandler(IOException.class) |
|
public ResponseEntity<String> handle(IOException ex) { |
|
// ... |
|
} |
|
|
|
} |
|
---- |
|
|
|
The value of the `@ExceptionHandler` annotation can be set to an array of Exception types |
|
to match to. Or if the annotation value is not set, then the exception type declared in |
|
the method signature is used instead. `@ExceptionHandler` methods can declare other |
|
arguments too, e.g. the `HttpServletRequest`. The return value type can be a `String`, |
|
which is interpreted as a view name, a `ModelAndView` object, a `ResponseEntity`, or you |
|
can also add the `@ResponseBody` annotation. |
|
|
|
For `@ExceptionHandler` methods, a root exception match will be preferred to just |
|
matching a cause of the current exception, among the handler methods of a particular |
|
controller or advice bean. However, a cause match on a higher-priority `@ControllerAdvice` |
|
will still be preferred to a any match (whether root or cause level) on a lower-priority |
|
advice bean. As a consequence, when using a multi-advice arrangement, please declare your |
|
primary root exception mappings on a prioritized advice bean with a corresponding order! |
|
|
|
[[mvc-ann-rest-exceptions]] |
|
==== REST API exceptions |
|
|
|
A common requirement for REST services is to include error details in the body of the |
|
response. The Spring Framework does not automatically do this because the representation |
|
of error details in the response body is application specific. However a |
|
`@RestController` may use `@ExceptionHandler` methods with a `ResponseEntity` return |
|
value to set the status and the body of the response. Such methods may also be declared |
|
in `@ControllerAdvice` classes to apply them globally. |
|
|
|
Applications that implement global exception handling with error details in the response |
|
body should consider extending |
|
{api-spring-framework}/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.html[ResponseEntityExceptionHandler] |
|
which provides handling for exceptions that Spring MVC raises along with hooks to |
|
customize the response body. To make use of this, create a sub-class of |
|
`ResponseEntityExceptionHandler`, annotate with `@ControllerAdvice`, override the |
|
necessary methods, and declare it as a Spring bean. |
|
|
|
|
|
|
|
[[mvc-ann-controller-advice]] |
|
=== Controller Advice |
|
[.small]#<<web-reactive.adoc#webflux-ann-controller-advice,Same in Spring WebFlux>># |
|
|
|
Typically `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply within |
|
the `@Controller` class (or class hierarchy) they are declared in. If you want such |
|
methods to apply more globally, across controllers, you can declare them in a class |
|
marked with `@ControllerAdvice` or `@RestControllerAdvice`. |
|
|
|
`@ControllerAdvice` is marked with `@Component` which means such classes can be registered |
|
as Spring beans via <<core.adoc#beans-java-instantiating-container-scan,component scanning>>. |
|
`@RestControllerAdvice` is also a meta-annotation marked with both `@ControllerAdvice` and |
|
`@ResponseBody` which essentially means `@ExceptionHandler` methods are rendered to the |
|
response body via message conversion (vs view resolution/template rendering). |
|
|
|
On startup, the infrastructure classes for `@RequestMapping` and `@ExceptionHandler` methods |
|
detect Spring beans of type `@ControllerAdvice`, and then apply their methods at runtime. |
|
Global `@ExceptionHandler` methods (from an `@ControllerAdvice`) are applied *after* local |
|
ones (from the `@Controller`). By contrast global `@ModelAttribute` and `@InitBinder` |
|
methods are applied *before* local ones. |
|
|
|
By default `@ControllerAdvice` methods apply to every request, i.e. all controllers, but |
|
you can narrow that down to a subset of controllers via attributes on the annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Target all Controllers annotated with @RestController |
|
@ControllerAdvice(annotations = RestController.class) |
|
public class ExampleAdvice1 {} |
|
|
|
// Target all Controllers within specific packages |
|
@ControllerAdvice("org.example.controllers") |
|
public class ExampleAdvice2 {} |
|
|
|
// Target all Controllers assignable to specific classes |
|
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) |
|
public class ExampleAdvice3 {} |
|
---- |
|
|
|
Keep in mind the above selectors are evaluated at runtime and may negatively impact |
|
performance if used extensively. See the |
|
{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[@ControllerAdvice] |
|
Javadoc for more details. |
|
|
|
|
|
|
|
|
|
[[mvc-uri-building]] |
|
== URI Links |
|
[.small]#<<web-reactive.adoc#mvc-uri-building,Same in Spring WebFlux>># |
|
|
|
This section describes various options available in the Spring Framework to prepare URIs. |
|
|
|
|
|
|
|
include::web-uris.adoc[leveloffset=+2] |
|
|
|
|
|
|
|
[[mvc-servleturicomponentsbuilder]] |
|
=== Servlet request relative |
|
|
|
You can use `ServletUriComponentsBuilder` to create URIs relative to the current request: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
HttpServletRequest request = ... |
|
|
|
// Re-uses host, scheme, port, path and query string... |
|
|
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromRequest(request) |
|
.replaceQueryParam("accountId", "{id}").build() |
|
.expand("123") |
|
.encode(); |
|
---- |
|
|
|
You can create URIs relative to the context path: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Re-uses host, port and context path... |
|
|
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request) |
|
.path("/accounts").build() |
|
---- |
|
|
|
You can create URIs relative to a Servlet (e.g. `/main/{asterisk}`): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Re-uses host, port, context path, and Servlet prefix... |
|
|
|
ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request) |
|
.path("/accounts").build() |
|
---- |
|
|
|
[CAUTION] |
|
==== |
|
`ServletUriComponentsBuilder` detects and uses information from the "Forwarded", |
|
"X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, so the resulting |
|
links reflect the original request. You need to ensure that your application is behind |
|
a trusted proxy which filters out such headers coming from outside. Also consider using |
|
the <<filters-forwarded-headers,ForwardedHeaderFilter>> which processes such headers once |
|
per request, and also provides an option to remove and ignore such headers. |
|
==== |
|
|
|
|
|
|
|
[[mvc-links-to-controllers]] |
|
=== Links to controllers |
|
|
|
Spring MVC provides a mechanism to prepare links to controller methods. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
@RequestMapping("/hotels/{hotel}") |
|
public class BookingController { |
|
|
|
@GetMapping("/bookings/{booking}") |
|
public String getBooking(@PathVariable Long booking) { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
You can prepare a link by referring to the method by name: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = MvcUriComponentsBuilder |
|
.fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42); |
|
|
|
URI uri = uriComponents.encode().toUri(); |
|
---- |
|
|
|
In the above example we provided actual method argument values, in this case the long value 21, |
|
to be used as a path variable and inserted into the URL. Furthermore, we provided the |
|
value 42 in order to fill in any remaining URI variables such as the "hotel" variable inherited |
|
from the type-level request mapping. If the method had more arguments you can supply null for |
|
arguments not needed for the URL. In general only `@PathVariable` and `@RequestParam` arguments |
|
are relevant for constructing the URL. |
|
|
|
There are additional ways to use `MvcUriComponentsBuilder`. For example you can use a technique |
|
akin to mock testing through proxies to avoid referring to the controller method by name |
|
(the example assumes static import of `MvcUriComponentsBuilder.on`): |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponents uriComponents = MvcUriComponentsBuilder |
|
.fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42); |
|
|
|
URI uri = uriComponents.encode().toUri(); |
|
---- |
|
|
|
The above examples use static methods in `MvcUriComponentsBuilder`. Internally they rely |
|
on `ServletUriComponentsBuilder` to prepare a base URL from the scheme, host, port, |
|
context path and servlet path of the current request. This works well in most cases, |
|
however sometimes it may be insufficient. For example you may be outside the context of |
|
a request (e.g. a batch process that prepares links) or perhaps you need to insert a path |
|
prefix (e.g. a locale prefix that was removed from the request path and needs to be |
|
re-inserted into links). |
|
|
|
For such cases you can use the static "fromXxx" overloaded methods that accept a |
|
`UriComponentsBuilder` to use base URL. Or you can create an instance of `MvcUriComponentsBuilder` |
|
with a base URL and then use the instance-based "withXxx" methods. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en"); |
|
MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base); |
|
builder.withMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42); |
|
|
|
URI uri = uriComponents.encode().toUri(); |
|
---- |
|
|
|
[CAUTION] |
|
==== |
|
`MvcUriComponentsBuilder` detects and uses information from the "Forwarded", |
|
"X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers, so the resulting |
|
links reflect the original request. You need to ensure that your application is behind |
|
a trusted proxy which filters out such headers coming from outside. Also consider using |
|
the <<filters-forwarded-headers,ForwardedHeaderFilter>> which processes such headers once |
|
per request, and also provides an option to remove and ignore such headers. |
|
==== |
|
|
|
|
|
|
|
|
|
[[mvc-links-to-controllers-from-views]] |
|
=== Links in views |
|
|
|
You can also build links to annotated controllers from views such as JSP, Thymeleaf, |
|
FreeMarker. This can be done using the `fromMappingName` method in `MvcUriComponentsBuilder` |
|
which refers to mappings by name. |
|
|
|
Every `@RequestMapping` is assigned a default name based on the capital letters of the |
|
class and the full method name. For example, the method `getFoo` in class `FooController` |
|
is assigned the name "FC#getFoo". This strategy can be replaced or customized by creating |
|
an instance of `HandlerMethodMappingNamingStrategy` and plugging it into your |
|
`RequestMappingHandlerMapping`. The default strategy implementation also looks at the |
|
name attribute on `@RequestMapping` and uses that if present. That means if the default |
|
mapping name assigned conflicts with another (e.g. overloaded methods) you can assign |
|
a name explicitly on the `@RequestMapping`. |
|
|
|
[NOTE] |
|
==== |
|
The assigned request mapping names are logged at TRACE level on startup. |
|
==== |
|
|
|
The Spring JSP tag library provides a function called `mvcUrl` that can be used to |
|
prepare links to controller methods based on this mechanism. |
|
|
|
For example given: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping("/people/{id}/addresses") |
|
public class PersonAddressController { |
|
|
|
@RequestMapping("/{country}") |
|
public HttpEntity getAddress(@PathVariable String country) { ... } |
|
} |
|
---- |
|
|
|
You can prepare a link from a JSP as follows: |
|
|
|
[source,jsp,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %> |
|
... |
|
<a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">Get Address</a> |
|
---- |
|
|
|
The above example relies on the `mvcUrl` JSP function declared in the Spring tag library |
|
(i.e. META-INF/spring.tld). For more advanced cases (e.g. a custom base URL as explained |
|
in the previous section), it is easy to define your own function, or use a custom tag file, |
|
in order to use a specific instance of `MvcUriComponentsBuilder` with a custom base URL. |
|
|
|
|
|
|
|
|
|
[[mvc-ann-async]] |
|
== Async Requests |
|
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>># |
|
|
|
Spring MVC has an extensive integration with Servlet 3.0 asynchronous request |
|
<<mvc-ann-async-processing,processing>>: |
|
|
|
* <<mvc-ann-async-deferredresult>> and <<mvc-ann-async-callable>> return values in |
|
controller method provide basic support for a single asynchronous return value. |
|
* Controllers can <<mvc-ann-async-http-streaming,stream>> multiple values including |
|
<<mvc-ann-async-sse,SSE>> and <<mvc-ann-async-output-stream,raw data>>. |
|
* Controllers can use reactive clients and return |
|
<<mvc-ann-async-reactive-types,reactive types>> for response handling. |
|
|
|
|
|
|
|
[[mvc-ann-async-deferredresult]] |
|
=== `DeferredResult` |
|
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>># |
|
|
|
Once the asynchronous request processing feature is |
|
<<mvc-ann-async-configuration,enabled>> in the Servlet container, controller methods can |
|
wrap any supported controller method return value with `DeferredResult`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/quotes") |
|
@ResponseBody |
|
public DeferredResult<String> quotes() { |
|
DeferredResult<String> deferredResult = new DeferredResult<String>(); |
|
// Save the deferredResult somewhere.. |
|
return deferredResult; |
|
} |
|
|
|
// From some other thread... |
|
deferredResult.setResult(data); |
|
---- |
|
|
|
The controller can produce the return value asynchronously, from a different thread, for |
|
example in response to an external event (JMS message), a scheduled task, or other. |
|
|
|
|
|
|
|
[[mvc-ann-async-callable]] |
|
=== `Callable` |
|
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>># |
|
|
|
A controller may also wrap any supported return value with `java.util.concurrent.Callable`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@PostMapping |
|
public Callable<String> processUpload(final MultipartFile file) { |
|
|
|
return new Callable<String>() { |
|
public String call() throws Exception { |
|
// ... |
|
return "someView"; |
|
} |
|
}; |
|
|
|
} |
|
---- |
|
|
|
The return value will then be obtained by executing the the given task through the |
|
<<mvc-ann-async-configuration-spring-mvc,configured>> `TaskExecutor`. |
|
|
|
|
|
|
|
[[mvc-ann-async-processing]] |
|
=== Processing |
|
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>># |
|
|
|
Here is a very concise overview of Servlet asynchronous request processing: |
|
|
|
* A `ServletRequest` can be put in asynchronous mode by calling `request.startAsync()`. |
|
The main effect of doing so is that the Servlet, as well as any Filters, can exit but |
|
the response will remain open to allow processing to complete later. |
|
* The call to `request.startAsync()` returns `AsyncContext` which can be used for |
|
further control over async processing. For example it provides the method `dispatch`, |
|
that is similar to a forward from the Servlet API except it allows an |
|
application to resume request processing on a Servlet container thread. |
|
* The `ServletRequest` provides access to the current `DispatcherType` that can |
|
be used to distinguish between processing the initial request, an async |
|
dispatch, a forward, and other dispatcher types. |
|
|
|
`DeferredResult` processing: |
|
|
|
* Controller returns a `DeferredResult` and saves it in some in-memory |
|
queue or list where it can be accessed. |
|
* Spring MVC calls `request.startAsync()`. |
|
* Meanwhile the `DispatcherServlet` and all configured Filter's exit the request |
|
processing thread but the response remains open. |
|
* The application sets the `DeferredResult` from some thread and Spring MVC |
|
dispatches the request back to the Servlet container. |
|
* The `DispatcherServlet` is invoked again and processing resumes with the |
|
asynchronously produced return value. |
|
|
|
`Callable` processing: |
|
|
|
* Controller returns a `Callable`. |
|
* Spring MVC calls `request.startAsync()` and submits the `Callable` to |
|
a `TaskExecutor` for processing in a separate thread. |
|
* Meanwhile the `DispatcherServlet` and all Filter's exit the Servlet container thread |
|
but the response remains open. |
|
* Eventually the `Callable` produces a result and Spring MVC dispatches the request back |
|
to the Servlet container to complete processing. |
|
* The `DispatcherServlet` is invoked again and processing resumes with the |
|
asynchronously produced return value from the `Callable`. |
|
|
|
For further background and context you can also read |
|
https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[the |
|
blog posts] that introduced asynchronous request processing support in Spring MVC 3.2. |
|
|
|
|
|
[[mvc-ann-async-exceptions]] |
|
==== Exception handling |
|
|
|
When using a `DeferredResult` you can choose whether to call `setResult` or |
|
`setErrorResult` with an exception. In both cases Spring MVC dispatches the request back |
|
to the Servlet container to complete processing. It is then treated either as if the |
|
controller method returned the given value, or as if it produced the given exception. |
|
The exception then goes through the regular exception handling mechanism, e.g. invoking |
|
`@ExceptionHandler` methods. |
|
|
|
When using `Callable`, similar processing logic follows. The main difference being that |
|
the result is returned from the `Callable` or an exception is raised by it. |
|
|
|
|
|
[[mvc-ann-async-interception]] |
|
==== Interception |
|
|
|
``HandlerInterceptor``'s can also be `AsyncHandlerInterceptor` in order to receive the |
|
`afterConcurrentHandlingStarted` callback on the initial request that starts asynchronous |
|
processing instead of `postHandle` and `afterCompletion`. |
|
|
|
``HandlerInterceptor``'s can also register a `CallableProcessingInterceptor` |
|
or a `DeferredResultProcessingInterceptor` in order to integrate more deeply with the |
|
lifecycle of an asynchronous request for example to handle a timeout event. See |
|
{api-spring-framework}/web/servlet/AsyncHandlerInterceptor.html[AsyncHandlerInterceptor] |
|
for more details. |
|
|
|
`DeferredResult` provides `onTimeout(Runnable)` and `onCompletion(Runnable)` callbacks. |
|
See the Javadoc of `DeferredResult` for more details. `Callable` can be substituted for |
|
`WebAsyncTask` that exposes additional methods for timeout and completion callbacks. |
|
|
|
|
|
[[mvc-ann-async-vs-webflux]] |
|
==== Compared to WebFlux |
|
|
|
The Servlet API was originally built for making a single pass through the Filter-Servlet |
|
chain. Asynchronous request processing, added in Servlet 3.0, allows applications to exit |
|
the Filter-Servlet chain but leave the response open for further processing. The Spring MVC |
|
async support is built around that mechanism. When a controller returns a `DeferredResult`, |
|
the Filter-Servlet chain is exited and the Servlet container thread is released. Later when |
|
the `DeferredResult` is set, an ASYNC dispatch (to the same URL) is made during which the |
|
controller is mapped again but rather than invoking it, the `DeferredResult` value is used |
|
(as if the controller returned it) to resume processing. |
|
|
|
By contrast Spring WebFlux is neither built on the Servlet API, nor does it need such an |
|
asynchronous request processing feature because it is asynchronous by design. Asynchronous |
|
handling is built into all framework contracts and is intrinsically supported through :: |
|
stages of request processing. |
|
|
|
From a programming model perspective, both Spring MVC and Spring WebFlux support |
|
asynchronous and <<mvc-ann-async-reactive-types>> as return values in controller methods. |
|
Spring MVC even supports streaming, including reactive back pressure. However individual |
|
writes to the response remain blocking (and performed on a separate thread) unlike WebFlux |
|
that relies on non-blocking I/O and does not need an extra thread for each write. |
|
|
|
Another fundamental difference is that Spring MVC does not support asynchronous or |
|
reactive types in controller method arguments, e.g. `@RequestBody`, `@RequestPart`, and |
|
others, nor does it have any explicit support for asynchronous and reactive types as |
|
model attributes. Spring WebFlux does support all that. |
|
|
|
|
|
|
|
[[mvc-ann-async-http-streaming]] |
|
=== HTTP Streaming |
|
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>># |
|
|
|
`DeferredResult` and `Callable` can be used for a single asynchronous return value. |
|
What if you want to produce multiple asynchronous values and have those written to the |
|
response? |
|
|
|
|
|
[[mvc-ann-async-objects]] |
|
==== Objects |
|
|
|
The `ResponseBodyEmitter` return value can be used to produce a stream of Objects, where |
|
each Object sent is serialized with an |
|
<<integration.adoc#rest-message-conversion,HttpMessageConverter>> and written to the |
|
response. For example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/events") |
|
public ResponseBodyEmitter handle() { |
|
ResponseBodyEmitter emitter = new ResponseBodyEmitter(); |
|
// Save the emitter somewhere.. |
|
return emitter; |
|
} |
|
|
|
// In some other thread |
|
emitter.send("Hello once"); |
|
|
|
// and again later on |
|
emitter.send("Hello again"); |
|
|
|
// and done at some point |
|
emitter.complete(); |
|
---- |
|
|
|
`ResponseBodyEmitter` can also be used as the body in a `ResponseEntity` allowing you to |
|
customize the status and headers of the response. |
|
|
|
When an `emitter` throws an `IOException` (e.g. if the remote client went away) applications |
|
are not responsible for cleaning up the connection, and should not invoke `emitter.complete` |
|
or `emitter.completeWithError`. Instead the servlet container automatically initiates an |
|
`AsyncListener` error notification in which Spring MVC makes a `completeWithError` call, |
|
which in turn performs one a final ASYNC dispatch to the application during which Spring MVC |
|
invokes the configured exception resolvers and completes the request. |
|
|
|
|
|
[[mvc-ann-async-sse]] |
|
==== SSE |
|
|
|
`SseEmitter` is a sub-class of `ResponseBodyEmitter` that provides support for |
|
http://www.w3.org/TR/eventsource/[Server-Sent Events] where events sent from the server |
|
are formatted according to the W3C SSE specification. In order to produce an SSE |
|
stream from a controller simply return `SseEmitter`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE) |
|
public SseEmitter handle() { |
|
SseEmitter emitter = new SseEmitter(); |
|
// Save the emitter somewhere.. |
|
return emitter; |
|
} |
|
|
|
// In some other thread |
|
emitter.send("Hello once"); |
|
|
|
// and again later on |
|
emitter.send("Hello again"); |
|
|
|
// and done at some point |
|
emitter.complete(); |
|
---- |
|
|
|
While SSE is the main option for streaming into browsers, note that Internet Explorer |
|
does not support Server-Sent Events. Consider using Spring's |
|
<<web.adoc#websocket,WebSocket messaging>> with |
|
<<web.adoc#websocket-fallback,SockJS fallback>> transports (including SSE) that target |
|
a wide range of browsers. |
|
|
|
Also see <<mvc-ann-async-objects,previous section>> for notes on exception handling. |
|
|
|
|
|
[[mvc-ann-async-output-stream]] |
|
==== Raw data |
|
|
|
Sometimes it is useful to bypass message conversion and stream directly to the response |
|
`OutputStream` for example for a file download. Use the of the `StreamingResponseBody` |
|
return value type to do that: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/download") |
|
public StreamingResponseBody handle() { |
|
return new StreamingResponseBody() { |
|
@Override |
|
public void writeTo(OutputStream outputStream) throws IOException { |
|
// write... |
|
} |
|
}; |
|
} |
|
---- |
|
|
|
`StreamingResponseBody` can be used as the body in a `ResponseEntity` allowing you to |
|
customize the status and headers of the response. |
|
|
|
|
|
|
|
[[mvc-ann-async-reactive-types]] |
|
=== Reactive types |
|
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>># |
|
|
|
Spring MVC supports use of reactive client libraries in a controller. This includes the |
|
`WebClient` from `spring-webflux` and others such as Spring Data reactive data |
|
repositories. In such scenarios it is convenient to be able to return reactive types |
|
from the controller method . |
|
|
|
Reactive return values are handled as follows: |
|
|
|
* A single-value promise is adapted to, and similar to using `DeferredResult`. Examples |
|
include `Mono` (Reactor) or `Single` (RxJava). |
|
* A multi-value stream, with a streaming media type such as `"application/stream+json"` |
|
or `"text/event-stream"`, is adapted to, and similar to using `ResponseBodyEmitter` or |
|
`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava). |
|
Applications can also return `Flux<ServerSentEvent>` or `Observable<ServerSentEvent>`. |
|
* A multi-value stream, with any other media type (e.g. "application/json"), is adapted |
|
to, and similar to using `DeferredResult<List<?>>`. |
|
|
|
[TIP] |
|
==== |
|
Spring MVC supports Reactor and RxJava through the |
|
{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] from |
|
`spring-core` which allows it to adapt from multiple reactive libraries. |
|
==== |
|
|
|
When streaming to the response via reactive types, Spring MVC supports reactive back |
|
pressure, but still needs to use blocking I/O to perform actual writes. This is done |
|
through the <<mvc-ann-async-configuration-spring-mvc,configured>> MVC `TaskExecutor` on |
|
a separate thread in order to avoid blocking the upstream source (e.g. a `Flux` returned |
|
from the `WebClient`). By default a `SyncTaskExecutor` is used which is not suitable for |
|
production. https://jira.spring.io/browse/SPR-16203[SPR-16203] will provide better |
|
defaults in Spring Framework 5.1. In the mean time please configure the executor through |
|
the <<mvc-ann-async-configuration-spring-mvc,MVC config>>. |
|
|
|
|
|
|
|
[[mvc-ann-async-disconnects]] |
|
=== Disconnects |
|
[.small]#<<web-reactive.adoc#webflux-codecs-streaming,Same in Spring WebFlux>># |
|
|
|
The Servlet API does not provide any notification when a remote client goes away. |
|
Therefore while streaming to the response, whether via <<mvc-ann-async-sse,SseEmitter>> or |
|
<<mvc-ann-async-reactive-types,reactive types>, it is important to send data periodically, |
|
since the write would fail if the client has disconnected. The send could take the form |
|
of an empty (comment-only) SSE event, or any other data that the other side would have to |
|
to interpret as a heartbeat and ignore. |
|
|
|
Alternatively consider using web messaging solutions such as |
|
<<websocket-stomp,STOMP over WebSocket>> or WebSocket with <<websocket-fallback,SockJS>> |
|
that have a built-in heartbeat mechanism. |
|
|
|
|
|
|
|
[[mvc-ann-async-configuration]] |
|
=== Configuration |
|
[.small]#<<mvc-ann-async-vs-webflux,Compared to WebFlux>># |
|
|
|
The async request processing feature must be enabled at the Servlet container level. |
|
The MVC config also exposes several options for asynchronous requests. |
|
|
|
|
|
[[mvc-ann-async-configuration-servlet3]] |
|
==== Servlet container |
|
|
|
Filter and Servlet declarations have an `asyncSupported` that needs to be set to true |
|
in order enable asynchronous request processing. In addition, Filter mappings should be |
|
declared to handle the ASYNC `javax.servlet.DispatchType`. |
|
|
|
In Java configuration, when you use `AbstractAnnotationConfigDispatcherServletInitializer` |
|
to initialize the Servlet container, this is done automatically. |
|
|
|
In `web.xml` configuration, add `<async-supported>true</async-supported>` to the |
|
`DispatcherServlet` and to `Filter` declarations, and also add |
|
`<dispatcher>ASYNC</dispatcher>` to filter mappings. |
|
|
|
|
|
[[mvc-ann-async-configuration-spring-mvc]] |
|
==== Spring MVC |
|
|
|
The MVC config exposes options related to async request processing: |
|
|
|
* Java config -- use the `configureAsyncSupport` callback on `WebMvcConfigurer`. |
|
* XML namespace -- use the `<async-support>` element under `<mvc:annotation-driven>`. |
|
|
|
You can configure the following: |
|
|
|
* Default timeout value for async requests, which if not set, depends |
|
on the underlying Servlet container (e.g. 10 seconds on Tomcat). |
|
* `AsyncTaskExecutor` to use for blocking writes when streaming with |
|
<<mvc-ann-async-reactive-types>>, and also for executing ``Callable``'s returned from |
|
controller methods. It is highly recommended to configure this property if you're |
|
streaming with reactive types or have controller methods that return `Callable` since |
|
by default it is a `SimpleAsyncTaskExecutor`. |
|
* ``DeferredResultProcessingInterceptor``'s and ``CallableProcessingInterceptor``'s. |
|
|
|
Note that the default timeout value can also be set on a `DeferredResult`, |
|
`ResponseBodyEmitter` and `SseEmitter`. For a `Callable`, use `WebAsyncTask` to provide |
|
a timeout value. |
|
|
|
|
|
|
|
|
|
include::webmvc-cors.adoc[leveloffset=+1] |
|
|
|
|
|
|
|
|
|
[[mvc-web-security]] |
|
== Web Security |
|
[.small]#<<web-reactive.adoc#webflux-web-security,Same in Spring WebFlux>># |
|
|
|
The http://projects.spring.io/spring-security/[Spring Security] project provides support |
|
for protecting web applications from malicious exploits. Check out the Spring Security |
|
reference documentation including: |
|
|
|
* {doc-spring-security}/html5/#mvc[Spring MVC Security] |
|
* {doc-spring-security}/html5/#test-mockmvc[Spring MVC Test Support] |
|
* {doc-spring-security}/html5/#csrf[CSRF protection] |
|
* {doc-spring-security}/html5/#headers[Security Response Headers] |
|
|
|
http://hdiv.org/[HDIV] is another web security framework that integrates with Spring MVC. |
|
|
|
|
|
|
|
|
|
[[mvc-caching]] |
|
== HTTP Caching |
|
|
|
A good HTTP caching strategy can significantly improve the performance of a web application |
|
and the experience of its clients. The `'Cache-Control'` HTTP response header is mostly |
|
responsible for this, along with conditional headers such as `'Last-Modified'` and `'ETag'`. |
|
|
|
The `'Cache-Control'` HTTP response header advises private caches (e.g. browsers) and |
|
public caches (e.g. proxies) on how they can cache HTTP responses for further reuse. |
|
|
|
An http://en.wikipedia.org/wiki/HTTP_ETag[ETag] (entity tag) is an HTTP response header |
|
returned by an HTTP/1.1 compliant web server used to determine change in content at a |
|
given URL. It can be considered to be the more sophisticated successor to the |
|
`Last-Modified` header. When a server returns a representation with an ETag header, the |
|
client can use this header in subsequent GETs, in an `If-None-Match` header. If the |
|
content has not changed, the server returns `304: Not Modified`. |
|
|
|
This section describes the different choices available to configure HTTP caching in a |
|
Spring Web MVC application. |
|
|
|
|
|
|
|
[[mvc-caching-cachecontrol]] |
|
=== Cache-Control |
|
|
|
Spring Web MVC supports many use cases and ways to configure "Cache-Control" headers for |
|
an application. While https://tools.ietf.org/html/rfc7234#section-5.2.2[RFC 7234 Section 5.2.2] |
|
completely describes that header and its possible directives, there are several ways to |
|
address the most common cases. |
|
|
|
Spring Web MVC uses a configuration convention in several of its APIs: |
|
`setCachePeriod(int seconds)`: |
|
|
|
* A `-1` value won't generate a `'Cache-Control'` response header. |
|
* A `0` value will prevent caching using the `'Cache-Control: no-store'` directive. |
|
* An `n > 0` value will cache the given response for `n` seconds using the |
|
`'Cache-Control: max-age=n'` directive. |
|
|
|
The {api-spring-framework}/http/CacheControl.html[`CacheControl`] builder |
|
class simply describes the available "Cache-Control" directives and makes it easier to |
|
build your own HTTP caching strategy. Once built, a `CacheControl` instance can then be |
|
accepted as an argument in several Spring Web MVC APIs. |
|
|
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
// Cache for an hour - "Cache-Control: max-age=3600" |
|
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS); |
|
|
|
// Prevent caching - "Cache-Control: no-store" |
|
CacheControl ccNoStore = CacheControl.noStore(); |
|
|
|
// Cache for ten days in public and private caches, |
|
// public caches should not transform the response |
|
// "Cache-Control: max-age=864000, public, no-transform" |
|
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS) |
|
.noTransform().cachePublic(); |
|
---- |
|
|
|
|
|
|
|
[[mvc-caching-static-resources]] |
|
=== Static resources |
|
|
|
Static resources should be served with appropriate `'Cache-Control'` and conditional |
|
headers for optimal performance. |
|
<<mvc-config-static-resources,Configuring a `ResourceHttpRequestHandler`>> for serving |
|
static resources not only natively writes `'Last-Modified'` headers by reading a file's |
|
metadata, but also `'Cache-Control'` headers if properly configured. |
|
|
|
You can set the `cachePeriod` attribute on a `ResourceHttpRequestHandler` or use |
|
a `CacheControl` instance, which supports more specific directives: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public-resources/") |
|
.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
And in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<mvc:resources mapping="/resources/**" location="/public-resources/"> |
|
<mvc:cache-control max-age="3600" cache-public="true"/> |
|
</mvc:resources> |
|
---- |
|
|
|
|
|
|
|
[[mvc-caching-etag-lastmodified]] |
|
=== @Controller caching |
|
|
|
Controllers can support `'Cache-Control'`, `'ETag'`, and/or `'If-Modified-Since'` HTTP requests; |
|
this is indeed recommended if a `'Cache-Control'` header is to be set on the response. |
|
This involves calculating a lastModified `long` and/or an Etag value for a given request, |
|
comparing it against the `'If-Modified-Since'` request header value, and potentially returning |
|
a response with status code 304 (Not Modified). |
|
|
|
As described in <<mvc-ann-httpentity>>, controllers can interact with the request/response using |
|
`HttpEntity` types. Controllers returning `ResponseEntity` can include HTTP caching information |
|
in responses like this: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@GetMapping("/book/{id}") |
|
public ResponseEntity<Book> showBook(@PathVariable Long id) { |
|
|
|
Book book = findBook(id); |
|
String version = book.getVersion(); |
|
|
|
return ResponseEntity |
|
.ok() |
|
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS)) |
|
.eTag(version) // lastModified is also available |
|
.body(book); |
|
} |
|
---- |
|
|
|
Doing this will not only include `'ETag'` and `'Cache-Control'` headers in the response, it will **also convert the |
|
response to an `HTTP 304 Not Modified` response with an empty body** if the conditional headers sent by the client |
|
match the caching information set by the Controller. |
|
|
|
An `@RequestMapping` method may also wish to support the same behavior. |
|
This can be achieved as follows: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@RequestMapping |
|
public String myHandleMethod(WebRequest webRequest, Model model) { |
|
|
|
long lastModified = // 1. application-specific calculation |
|
|
|
if (request.checkNotModified(lastModified)) { |
|
// 2. shortcut exit - no further processing necessary |
|
return null; |
|
} |
|
|
|
// 3. or otherwise further request processing, actually preparing content |
|
model.addAttribute(...); |
|
return "myViewName"; |
|
} |
|
---- |
|
|
|
There are two key elements here: calling `request.checkNotModified(lastModified)` and |
|
returning `null`. The former sets the appropriate response status and headers |
|
before it returns `true`. |
|
The latter, in combination with the former, causes Spring MVC to do no further |
|
processing of the request. |
|
|
|
Note that there are 3 variants for this: |
|
|
|
* `request.checkNotModified(lastModified)` compares lastModified with the |
|
`'If-Modified-Since'` or `'If-Unmodified-Since'` request header |
|
* `request.checkNotModified(eTag)` compares eTag with the `'If-None-Match'` request header |
|
* `request.checkNotModified(eTag, lastModified)` does both, meaning that both |
|
conditions should be valid |
|
|
|
When receiving conditional `'GET'`/`'HEAD'` requests, `checkNotModified` will check |
|
that the resource has not been modified and if so, it will result in a `HTTP 304 Not Modified` |
|
response. In case of conditional `'POST'`/`'PUT'`/`'DELETE'` requests, `checkNotModified` |
|
will check that the resource has not been modified and if it has been, it will result in a |
|
`HTTP 409 Precondition Failed` response to prevent concurrent modifications. |
|
|
|
|
|
|
|
[[mvc-httpcaching-shallowetag]] |
|
=== ETag Filter |
|
|
|
Support for ETags is provided by the Servlet filter `ShallowEtagHeaderFilter`. It is a |
|
plain Servlet Filter, and thus can be used in combination with any web framework. The |
|
`ShallowEtagHeaderFilter` filter creates so-called shallow ETags by caching the content |
|
written to the response and generating an MD5 hash over that to send as an ETag header. |
|
The next time a client sends a request for the same resource, it uses that hash as the |
|
`If-None-Match` value. The filter detects this, lets the request be processed as usual, and |
|
at the end compares the two hashes. If they are equal, a `304` is returned. |
|
|
|
Note that this strategy saves network bandwidth but not CPU, as the full response must be |
|
computed for each request. Other strategies at the controller level, described above, can |
|
avoid computation. |
|
|
|
This filter has a `writeWeakETag` parameter that configures the filter to write Weak ETags, |
|
like this: `W/"02a2d595e6ed9a0b24f027f2b63b134d6"`, as defined in |
|
https://tools.ietf.org/html/rfc7232#section-2.3[RFC 7232 Section 2.3]. |
|
|
|
|
|
|
|
|
|
include::webmvc-view.adoc[leveloffset=+1] |
|
|
|
|
|
|
|
|
|
[[mvc-config]] |
|
== MVC Config |
|
[.small]#<<web-reactive.adoc#webflux-config,Same in Spring WebFlux>># |
|
|
|
The MVC Java config and the MVC XML namespace provide default configuration suitable for most |
|
applications along with a configuration API to customize it. |
|
|
|
For more advanced customizations, not available in the configuration API, see |
|
<<mvc-config-advanced-java>> and <<mvc-config-advanced-xml>>. |
|
|
|
You do not need to understand the underlying beans created by the MVC Java config and the |
|
MVC namespace but if you want to learn more, see <<mvc-servlet-special-bean-types>> and |
|
<<mvc-servlet-config>>. |
|
|
|
|
|
[[mvc-config-enable]] |
|
=== Enable MVC Config |
|
[.small]#<<web-reactive.adoc#webflux-config-enable,Same in Spring WebFlux>># |
|
|
|
In Java config use the `@EnableWebMvc` annotation: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig { |
|
} |
|
---- |
|
|
|
In XML use the `<mvc:annotation-driven>` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven/> |
|
|
|
</beans> |
|
---- |
|
|
|
The above registers a number of Spring MVC |
|
<<mvc-servlet-special-bean-types,infrastructure beans>> also adapting to dependencies |
|
available on the classpath: e.g. payload converters for JSON, XML, etc. |
|
|
|
|
|
|
|
[[mvc-config-customize]] |
|
=== MVC Config API |
|
[.small]#<<web-reactive.adoc#webflux-config-customize,Same in Spring WebFlux>># |
|
|
|
In Java config implement `WebMvcConfigurer` interface: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
// Implement configuration methods... |
|
} |
|
---- |
|
|
|
In XML check attributes and sub-elements of `<mvc:annotation-driven/>`. You can view the |
|
http://schema.spring.io/mvc/spring-mvc.xsd[Spring MVC XML schema] or use the code |
|
completion feature of your IDE to discover what attributes and sub-elements are |
|
available. |
|
|
|
|
|
|
|
[[mvc-config-conversion]] |
|
=== Type conversion |
|
[.small]#<<web-reactive.adoc#webflux-config-conversion,Same in Spring WebFlux>># |
|
|
|
By default formatters for `Number` and `Date` types are installed, including support for |
|
the `@NumberFormat` and `@DateTimeFormat` annotations. Full support for the Joda Time |
|
formatting library is also installed if Joda Time is present on the classpath. |
|
|
|
In Java config, register custom formatters and converters: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addFormatters(FormatterRegistry registry) { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
In XML, the same: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven conversion-service="conversionService"/> |
|
|
|
<bean id="conversionService" |
|
class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> |
|
<property name="converters"> |
|
<set> |
|
<bean class="org.example.MyConverter"/> |
|
</set> |
|
</property> |
|
<property name="formatters"> |
|
<set> |
|
<bean class="org.example.MyFormatter"/> |
|
<bean class="org.example.MyAnnotationFormatterFactory"/> |
|
</set> |
|
</property> |
|
<property name="formatterRegistrars"> |
|
<set> |
|
<bean class="org.example.MyFormatterRegistrar"/> |
|
</set> |
|
</property> |
|
</bean> |
|
|
|
</beans> |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
See <<core.adoc#format-FormatterRegistrar-SPI,FormatterRegistrar SPI>> |
|
and the `FormattingConversionServiceFactoryBean` for more information on when to use FormatterRegistrars. |
|
==== |
|
|
|
|
|
|
|
[[mvc-config-validation]] |
|
=== Validation |
|
[.small]#<<web-reactive.adoc#webflux-config-validation,Same in Spring WebFlux>># |
|
|
|
By default if <<core.adoc#validation-beanvalidation-overview,Bean Validation>> is present |
|
on the classpath -- e.g. Hibernate Validator, the `LocalValidatorFactoryBean` is registered |
|
as a global <<core.adoc#validator,Validator>> for use with `@Valid` and `Validated` on |
|
controller method arguments. |
|
|
|
In Java config, you can customize the global `Validator` instance: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public Validator getValidator(); { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
In XML, the same: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:mvc="http://www.springframework.org/schema/mvc" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xsi:schemaLocation=" |
|
http://www.springframework.org/schema/beans |
|
http://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven validator="globalValidator"/> |
|
|
|
</beans> |
|
---- |
|
|
|
Note that you can also register ``Validator``'s locally: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Controller |
|
public class MyController { |
|
|
|
@InitBinder |
|
protected void initBinder(WebDataBinder binder) { |
|
binder.addValidators(new FooValidator()); |
|
} |
|
|
|
} |
|
---- |
|
|
|
[TIP] |
|
==== |
|
If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and |
|
mark it with `@Primary` in order to avoid conflict with the one declared in the MVC config. |
|
==== |
|
|
|
|
|
|
|
[[mvc-config-interceptors]] |
|
=== Interceptors |
|
|
|
In Java config, register interceptors to apply to incoming requests: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addInterceptors(InterceptorRegistry registry) { |
|
registry.addInterceptor(new LocaleInterceptor()); |
|
registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**"); |
|
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*"); |
|
} |
|
} |
|
---- |
|
|
|
In XML, the same: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<mvc:interceptors> |
|
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/> |
|
<mvc:interceptor> |
|
<mvc:mapping path="/**"/> |
|
<mvc:exclude-mapping path="/admin/**"/> |
|
<bean class="org.springframework.web.servlet.theme.ThemeChangeInterceptor"/> |
|
</mvc:interceptor> |
|
<mvc:interceptor> |
|
<mvc:mapping path="/secure/*"/> |
|
<bean class="org.example.SecurityInterceptor"/> |
|
</mvc:interceptor> |
|
</mvc:interceptors> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-content-negotiation]] |
|
=== Content Types |
|
[.small]#<<web-reactive.adoc#webflux-config-content-negotiation,Same in Spring WebFlux>># |
|
|
|
You can configure how Spring MVC determines the requested media types from the request -- |
|
e.g. `Accept` header, URL path extension, query parameter, etc. |
|
|
|
By default the URL path extension is checked first -- with `json`, `xml`, `rss`, and `atom` |
|
registered as known extensions depending on classpath dependencies, and the "Accept" header |
|
is checked second. |
|
|
|
Consider changing those defaults to `Accept` header only and if you must use URL-based |
|
content type resolution consider the query parameter strategy over the path extensions. See |
|
<<mvc-ann-requestmapping-suffix-pattern-match>> and <<mvc-ann-requestmapping-rfd>> for |
|
more details. |
|
|
|
In Java config, customize requested content type resolution: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { |
|
configurer.mediaType("json", MediaType.APPLICATION_JSON); |
|
} |
|
} |
|
---- |
|
|
|
In XML, the same: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager"/> |
|
|
|
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> |
|
<property name="mediaTypes"> |
|
<value> |
|
json=application/json |
|
xml=application/xml |
|
</value> |
|
</property> |
|
</bean> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-message-converters]] |
|
=== Message Converters |
|
[.small]#<<web-reactive.adoc#webflux-config-message-codecs,Same in Spring WebFlux>># |
|
|
|
Customization of `HttpMessageConverter` can be achieved in Java config by overriding |
|
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`] |
|
if you want to replace the default converters created by Spring MVC, or by overriding |
|
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#extendMessageConverters-java.util.List-[`extendMessageConverters()`] |
|
if you just want to customize them or add additional converters to the default ones. |
|
|
|
Below is an example that adds Jackson JSON and XML converters with a customized |
|
`ObjectMapper` instead of default ones: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfiguration implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { |
|
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() |
|
.indentOutput(true) |
|
.dateFormat(new SimpleDateFormat("yyyy-MM-dd")) |
|
.modulesToInstall(new ParameterNamesModule()); |
|
converters.add(new MappingJackson2HttpMessageConverter(builder.build())); |
|
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build())); |
|
} |
|
} |
|
---- |
|
|
|
In this example, |
|
{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[Jackson2ObjectMapperBuilder] |
|
is used to create a common configuration for both `MappingJackson2HttpMessageConverter` and |
|
`MappingJackson2XmlHttpMessageConverter` with indentation enabled, a customized date format |
|
and the registration of |
|
https://github.com/FasterXML/jackson-module-parameter-names[jackson-module-parameter-names] |
|
that adds support for accessing parameter names (feature added in Java 8). |
|
|
|
This builder customizes Jackson's default properties with the following ones: |
|
|
|
. http://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/DeserializationFeature.html#FAIL_ON_UNKNOWN_PROPERTIES[`DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES`] is disabled. |
|
. http://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/MapperFeature.html#DEFAULT_VIEW_INCLUSION[`MapperFeature.DEFAULT_VIEW_INCLUSION`] is disabled. |
|
|
|
It also automatically registers the following well-known modules if they are detected on the classpath: |
|
|
|
. https://github.com/FasterXML/jackson-datatype-jdk7[jackson-datatype-jdk7]: support for Java 7 types like `java.nio.file.Path`. |
|
. https://github.com/FasterXML/jackson-datatype-joda[jackson-datatype-joda]: support for Joda-Time types. |
|
. https://github.com/FasterXML/jackson-datatype-jsr310[jackson-datatype-jsr310]: support for Java 8 Date & Time API types. |
|
. https://github.com/FasterXML/jackson-datatype-jdk8[jackson-datatype-jdk8]: support for other Java 8 types like `Optional`. |
|
|
|
[NOTE] |
|
==== |
|
Enabling indentation with Jackson XML support requires |
|
http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.codehaus.woodstox%22%20AND%20a%3A%22woodstox-core-asl%22[`woodstox-core-asl`] |
|
dependency in addition to http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22jackson-dataformat-xml%22[`jackson-dataformat-xml`] one. |
|
==== |
|
|
|
Other interesting Jackson modules are available: |
|
|
|
. https://github.com/zalando/jackson-datatype-money[jackson-datatype-money]: support for `javax.money` types (unofficial module) |
|
. https://github.com/FasterXML/jackson-datatype-hibernate[jackson-datatype-hibernate]: support for Hibernate specific types and properties (including lazy-loading aspects) |
|
|
|
It is also possible to do the same in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:annotation-driven> |
|
<mvc:message-converters> |
|
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> |
|
<property name="objectMapper" ref="objectMapper"/> |
|
</bean> |
|
<bean class="org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter"> |
|
<property name="objectMapper" ref="xmlMapper"/> |
|
</bean> |
|
</mvc:message-converters> |
|
</mvc:annotation-driven> |
|
|
|
<bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" |
|
p:indentOutput="true" |
|
p:simpleDateFormat="yyyy-MM-dd" |
|
p:modulesToInstall="com.fasterxml.jackson.module.paramnames.ParameterNamesModule"/> |
|
|
|
<bean id="xmlMapper" parent="objectMapper" p:createXmlMapper="true"/> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-view-controller]] |
|
=== View Controllers |
|
|
|
This is a shortcut for defining a `ParameterizableViewController` that immediately |
|
forwards to a view when invoked. Use it in static cases when there is no Java controller |
|
logic to execute before the view generates the response. |
|
|
|
An example of forwarding a request for `"/"` to a view called `"home"` in Java: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addViewControllers(ViewControllerRegistry registry) { |
|
registry.addViewController("/").setViewName("home"); |
|
} |
|
} |
|
---- |
|
|
|
And the same in XML use the `<mvc:view-controller>` element: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:view-controller path="/" view-name="home"/> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-view-resolvers]] |
|
=== View Resolvers |
|
[.small]#<<web-reactive.adoc#webflux-config-view-resolvers,Same in Spring WebFlux>># |
|
|
|
The MVC config simplifies the registration of view resolvers. |
|
|
|
The following is a Java config example that configures content negotiation view |
|
resolution using FreeMarker HTML templates and Jackson as a default `View` for |
|
JSON rendering: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureViewResolvers(ViewResolverRegistry registry) { |
|
registry.enableContentNegotiation(new MappingJackson2JsonView()); |
|
registry.jsp(); |
|
} |
|
} |
|
---- |
|
|
|
And the same in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:view-resolvers> |
|
<mvc:content-negotiation> |
|
<mvc:default-views> |
|
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/> |
|
</mvc:default-views> |
|
</mvc:content-negotiation> |
|
<mvc:jsp/> |
|
</mvc:view-resolvers> |
|
---- |
|
|
|
Note however that FreeMarker, Tiles, Groovy Markup and script templates also require |
|
configuration of the underlying view technology. |
|
|
|
The MVC namespace provides dedicated elements. For example with FreeMarker: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
|
|
<mvc:view-resolvers> |
|
<mvc:content-negotiation> |
|
<mvc:default-views> |
|
<bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/> |
|
</mvc:default-views> |
|
</mvc:content-negotiation> |
|
<mvc:freemarker cache="false"/> |
|
</mvc:view-resolvers> |
|
|
|
<mvc:freemarker-configurer> |
|
<mvc:template-loader-path location="/freemarker"/> |
|
</mvc:freemarker-configurer> |
|
|
|
---- |
|
|
|
In Java config simply add the respective "Configurer" bean: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureViewResolvers(ViewResolverRegistry registry) { |
|
registry.enableContentNegotiation(new MappingJackson2JsonView()); |
|
registry.freeMarker().cache(false); |
|
} |
|
|
|
@Bean |
|
public FreeMarkerConfigurer freeMarkerConfigurer() { |
|
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); |
|
configurer.setTemplateLoaderPath("/WEB-INF/"); |
|
return configurer; |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-static-resources]] |
|
=== Static Resources |
|
[.small]#<<web-reactive.adoc#webflux-config-static-resources,Same in Spring WebFlux>># |
|
|
|
This option provides a convenient way to serve static resources from a list of |
|
{api-spring-framework}/core/io/Resource.html[Resource]-based locations. |
|
|
|
In the example below, given a request that starts with `"/resources"`, the relative path is |
|
used to find and serve static resources relative to "/public" under the web application |
|
root or on the classpath under `"/static"`. The resources are served with a 1-year future |
|
expiration to ensure maximum use of the browser cache and a reduction in HTTP requests |
|
made by the browser. The `Last-Modified` header is also evaluated and if present a `304` |
|
status code is returned. |
|
|
|
In Java config: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public", "classpath:/static/") |
|
.setCachePeriod(31556926); |
|
} |
|
} |
|
---- |
|
|
|
In XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:resources mapping="/resources/**" |
|
location="/public, classpath:/static/" |
|
cache-period="31556926" /> |
|
---- |
|
|
|
See also |
|
<<mvc-caching-static-resources, HTTP caching support for static resources>>. |
|
|
|
The resource handler also supports a chain of |
|
{api-spring-framework}/web/servlet/resource/ResourceResolver.html[ResourceResolver]'s and |
|
{api-spring-framework}/web/servlet/resource/ResourceTransformer.html[ResourceTransformer]'s. |
|
which can be used to create a toolchain for working with optimized resources. |
|
|
|
The `VersionResourceResolver` can be used for versioned resource URLs based on an MD5 hash |
|
computed from the content, a fixed application version, or other. A |
|
`ContentVersionStrategy` (MD5 hash) is a good choice with some notable exceptions such as |
|
JavaScript resources used with a module loader. |
|
|
|
For example in Java config; |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public/") |
|
.resourceChain(true) |
|
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); |
|
} |
|
} |
|
---- |
|
|
|
In XML, the same: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim"] |
|
---- |
|
<mvc:resources mapping="/resources/**" location="/public/"> |
|
<mvc:resource-chain> |
|
<mvc:resource-cache/> |
|
<mvc:resolvers> |
|
<mvc:version-resolver> |
|
<mvc:content-version-strategy patterns="/**"/> |
|
</mvc:version-resolver> |
|
</mvc:resolvers> |
|
</mvc:resource-chain> |
|
</mvc:resources> |
|
---- |
|
|
|
You can use `ResourceUrlProvider` to rewrite URLs and apply the full chain of resolvers and |
|
transformers -- e.g. to insert versions. The MVC config provides a `ResourceUrlProvider` |
|
bean so it can be injected into others. You can also make the rewrite transparent with the |
|
`ResourceUrlEncodingFilter` for Thymeleaf, JSPs, FreeMarker, and others with URL tags that |
|
rely on `HttpServletResponse#encodeURL`. |
|
|
|
http://www.webjars.org/documentation[WebJars] is also supported via `WebJarsResourceResolver` |
|
and automatically registered when `"org.webjars:webjars-locator"` is present on the |
|
classpath. The resolver can re-write URLs to include the version of the jar and can also |
|
match to incoming URLs without versions -- e.g. `"/jquery/jquery.min.js"` to |
|
`"/jquery/1.2.0/jquery.min.js"`. |
|
|
|
|
|
|
|
[[mvc-default-servlet-handler]] |
|
=== Default Servlet |
|
|
|
This allows for mapping the `DispatcherServlet` to "/" (thus overriding the mapping |
|
of the container's default Servlet), while still allowing static resource requests to be |
|
handled by the container's default Servlet. It configures a |
|
`DefaultServletHttpRequestHandler` with a URL mapping of "/**" and the lowest priority |
|
relative to other URL mappings. |
|
|
|
This handler will forward all requests to the default Servlet. Therefore it is important |
|
that it remains last in the order of all other URL `HandlerMappings`. That will be the |
|
case if you use `<mvc:annotation-driven>` or alternatively if you are setting up your |
|
own customized `HandlerMapping` instance be sure to set its `order` property to a value |
|
lower than that of the `DefaultServletHttpRequestHandler`, which is `Integer.MAX_VALUE`. |
|
|
|
To enable the feature using the default setup use: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { |
|
configurer.enable(); |
|
} |
|
} |
|
---- |
|
|
|
Or in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:default-servlet-handler/> |
|
---- |
|
|
|
The caveat to overriding the "/" Servlet mapping is that the `RequestDispatcher` for the |
|
default Servlet must be retrieved by name rather than by path. The |
|
`DefaultServletHttpRequestHandler` will attempt to auto-detect the default Servlet for |
|
the container at startup time, using a list of known names for most of the major Servlet |
|
containers (including Tomcat, Jetty, GlassFish, JBoss, Resin, WebLogic, and WebSphere). |
|
If the default Servlet has been custom configured with a different name, or if a |
|
different Servlet container is being used where the default Servlet name is unknown, |
|
then the default Servlet's name must be explicitly provided as in the following example: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { |
|
configurer.enable("myCustomDefaultServlet"); |
|
} |
|
|
|
} |
|
---- |
|
|
|
Or in XML: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-path-matching]] |
|
=== Path Matching |
|
[.small]#<<web-reactive.adoc#webflux-config-path-matching,Same in Spring WebFlux>># |
|
|
|
This allows customizing options related to URL matching and treatment of the URL. |
|
For details on the individual options check out the |
|
{api-spring-framework}/web/servlet/config/annotation/PathMatchConfigurer.html[PathMatchConfigurer] API. |
|
|
|
Example in Java config: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configurePathMatch(PathMatchConfigurer configurer) { |
|
configurer |
|
.setUseSuffixPatternMatch(true) |
|
.setUseTrailingSlashMatch(false) |
|
.setUseRegisteredSuffixPatternMatch(true) |
|
.setPathMatcher(antPathMatcher()) |
|
.setUrlPathHelper(urlPathHelper()); |
|
} |
|
|
|
@Bean |
|
public UrlPathHelper urlPathHelper() { |
|
//... |
|
} |
|
|
|
@Bean |
|
public PathMatcher antPathMatcher() { |
|
//... |
|
} |
|
|
|
} |
|
---- |
|
|
|
In XML, the same: |
|
|
|
[source,xml,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
<mvc:annotation-driven> |
|
<mvc:path-matching |
|
suffix-pattern="true" |
|
trailing-slash="false" |
|
registered-suffixes-only="true" |
|
path-helper="pathHelper" |
|
path-matcher="pathMatcher"/> |
|
</mvc:annotation-driven> |
|
|
|
<bean id="pathHelper" class="org.example.app.MyPathHelper"/> |
|
<bean id="pathMatcher" class="org.example.app.MyPathMatcher"/> |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-advanced-java]] |
|
=== Advanced Java Config |
|
[.small]#<<web-reactive.adoc#webflux-config-advanced-java,Same in Spring WebFlux>># |
|
|
|
`@EnableWebMvc` imports `DelegatingWebMvcConfiguration` that (1) provides default Spring |
|
configuration for Spring MVC applications and (2) detects and delegates to |
|
``WebMvcConfigurer``'s to customize that configuration. |
|
|
|
For advanced mode, remove `@EnableWebMvc` and extend directly from |
|
`DelegatingWebMvcConfiguration` instead of implementing `WebMvcConfigurer`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Configuration |
|
public class WebConfig extends DelegatingWebMvcConfiguration { |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
|
|
You can keep existing methods in `WebConfig` but you can now also override bean declarations |
|
from the base class and you can still have any number of other ``WebMvcConfigurer``'s on |
|
the classpath. |
|
|
|
|
|
|
|
[[mvc-config-advanced-xml]] |
|
=== Advanced XML Config |
|
|
|
The MVC namespace does not have an advanced mode. If you need to customize a property on |
|
a bean that you can't change otherwise, you can use the `BeanPostProcessor` lifecycle |
|
hook of the Spring `ApplicationContext`: |
|
|
|
[source,java,indent=0] |
|
[subs="verbatim,quotes"] |
|
---- |
|
@Component |
|
public class MyPostProcessor implements BeanPostProcessor { |
|
|
|
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
Note that `MyPostProcessor` needs to be declared as a bean either explicitly in XML or |
|
detected through a `<component scan/>` declaration. |
|
|
|
|
|
|
|
|
|
[[mvc-http2]] |
|
== HTTP/2 |
|
[.small]#<<web-reactive.adoc#webflux-http2,Same in Spring WebFlux>># |
|
|
|
Servlet 4 containers are required to support HTTP/2 and Spring Framework 5 is compatible |
|
with Servlet API 4. From a programming model perspective there is nothing specific that |
|
applications need to do. However there are considerations related to server configuration. |
|
For more details please check out the |
|
https://github.com/spring-projects/spring-framework/wiki/HTTP-2-support[HTTP/2 wiki page]. |
|
|
|
The Servlet API does expose one construct related to HTTP/2. The |
|
`javax.servlet.http.PushBuilder` can used to proactively push resources to clients and it |
|
is supported as a <<mvc-ann-arguments,method argument>> to `@RequestMapping` methods.
|
|
|