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.
6369 lines
229 KiB
6369 lines
229 KiB
[[mvc]] |
|
= Spring Web MVC |
|
:doc-spring-security: {doc-root}/spring-security/reference |
|
|
|
Spring Web MVC is the original web framework built on the Servlet API and has been included |
|
in the Spring Framework from the very beginning. The formal name, "`Spring Web MVC,`" |
|
comes from the name of its source module |
|
({spring-framework-main-code}/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 |
|
({spring-framework-main-code}/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 Jakarta EE version |
|
ranges, see 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, WebFlux>># |
|
|
|
Spring MVC, as 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 by 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>>. |
|
|
|
The following example of the Java configuration registers and initializes |
|
the `DispatcherServlet`, which is auto-detected by the Servlet container |
|
(see <<mvc-container-config>>): |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
public class MyWebApplicationInitializer implements WebApplicationInitializer { |
|
|
|
@Override |
|
public void onStartup(ServletContext servletContext) { |
|
|
|
// Load Spring web application configuration |
|
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); |
|
context.register(AppConfig.class); |
|
|
|
// Create and register the DispatcherServlet |
|
DispatcherServlet servlet = new DispatcherServlet(context); |
|
ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); |
|
registration.setLoadOnStartup(1); |
|
registration.addMapping("/app/*"); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class MyWebApplicationInitializer : WebApplicationInitializer { |
|
|
|
override fun onStartup(servletContext: ServletContext) { |
|
|
|
// Load Spring web application configuration |
|
val context = AnnotationConfigWebApplicationContext() |
|
context.register(AppConfig::class.java) |
|
|
|
// Create and register the DispatcherServlet |
|
val servlet = DispatcherServlet(context) |
|
val registration = servletContext.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 the example under <<mvc-servlet-context-hierarchy>>). |
|
|
|
NOTE: For programmatic use cases, a `GenericWebApplicationContext` can be used as an |
|
alternative to `AnnotationConfigWebApplicationContext`. See the |
|
{api-spring-framework}/web/context/support/GenericWebApplicationContext.html[`GenericWebApplicationContext`] |
|
javadoc for details. |
|
|
|
The following example of `web.xml` configuration registers and initializes 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, see the |
|
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container[Spring Boot documentation]. |
|
|
|
|
|
|
|
[[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 the `Servlet` with which it is associated. 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 suffices. |
|
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 can be overridden (that is, re-declared) in the Servlet-specific |
|
child `WebApplicationContext`, which typically contains beans local to the given `Servlet`. |
|
The following image shows this relationship: |
|
|
|
image::images/mvc-context-hierarchy.png[] |
|
|
|
The following example configures a `WebApplicationContext` hierarchy: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
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/*" }; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() { |
|
|
|
override fun getRootConfigClasses(): Array<Class<*>> { |
|
return arrayOf(RootConfig::class.java) |
|
} |
|
|
|
override fun getServletConfigClasses(): Array<Class<*>> { |
|
return arrayOf(App1Config::class.java) |
|
} |
|
|
|
override fun getServletMappings(): Array<String> { |
|
return arrayOf("/app1/*") |
|
} |
|
} |
|
---- |
|
|
|
TIP: If an application context hierarchy is not required, applications can return all |
|
configuration through `getRootConfigClasses()` and `null` from `getServletConfigClasses()`. |
|
|
|
The following example shows 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, 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 framework contracts. Those usually come with built-in contracts, but |
|
you can customize their properties and extend or replace them. |
|
|
|
The following table lists the special beans detected by the `DispatcherServlet`: |
|
|
|
[[mvc-webappctx-special-beans-tbl]] |
|
[cols="1,2", options="header"] |
|
|=== |
|
| Bean type| Explanation |
|
|
|
| `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, to HTML error |
|
views, or other targets. See <<mvc-exceptionhandlers>>. |
|
|
|
| <<mvc-viewresolver, `ViewResolver`>> |
|
| Resolve logical `String`-based view names returned from a handler to an actual `View` |
|
with which to render to the response. 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 (for example, 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, WebFlux>># |
|
|
|
Applications can declare the infrastructure beans listed in <<mvc-servlet-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 |
|
{spring-framework-main-code}/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 configuration to configure Spring MVC and |
|
provides many extra convenient options. |
|
|
|
|
|
|
|
[[mvc-container-config]] |
|
=== Servlet Config |
|
|
|
In a Servlet environment, you have the option of configuring the Servlet container |
|
programmatically as an alternative or in combination with a `web.xml` file. |
|
The following example registers a `DispatcherServlet`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
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("/"); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
import org.springframework.web.WebApplicationInitializer |
|
|
|
class MyWebApplicationInitializer : WebApplicationInitializer { |
|
|
|
override fun onStartup(container: ServletContext) { |
|
val appContext = XmlWebApplicationContext() |
|
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml") |
|
|
|
val registration = container.addServlet("dispatcher", 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 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, as the |
|
following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
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[] { "/" }; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() { |
|
|
|
override fun getRootConfigClasses(): Array<Class<*>>? { |
|
return null |
|
} |
|
|
|
override fun getServletConfigClasses(): Array<Class<*>>? { |
|
return arrayOf(MyWebConfig::class.java) |
|
} |
|
|
|
override fun getServletMappings(): Array<String> { |
|
return arrayOf("/") |
|
} |
|
} |
|
---- |
|
|
|
If you use XML-based Spring configuration, you should extend directly from |
|
`AbstractDispatcherServletInitializer`, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
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[] { "/" }; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class MyWebAppInitializer : AbstractDispatcherServletInitializer() { |
|
|
|
override fun createRootApplicationContext(): WebApplicationContext? { |
|
return null |
|
} |
|
|
|
override fun createServletApplicationContext(): WebApplicationContext { |
|
return XmlWebApplicationContext().apply { |
|
setConfigLocation("/WEB-INF/spring/dispatcher-config.xml") |
|
} |
|
} |
|
|
|
override fun getServletMappings(): Array<String> { |
|
return arrayOf("/") |
|
} |
|
} |
|
---- |
|
|
|
`AbstractDispatcherServletInitializer` also provides a convenient way to add `Filter` |
|
instances and have them be automatically mapped to the `DispatcherServlet`, as the |
|
following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer { |
|
|
|
// ... |
|
|
|
@Override |
|
protected Filter[] getServletFilters() { |
|
return new Filter[] { |
|
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() }; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class MyWebAppInitializer : AbstractDispatcherServletInitializer() { |
|
|
|
// ... |
|
|
|
override fun getServletFilters(): Array<Filter> { |
|
return arrayOf(HiddenHttpMethodFilter(), 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, 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 `DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE` key. |
|
* The locale resolver is bound to the request to let elements in the process |
|
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 the locale resolver. |
|
* 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 |
|
run to prepare a model for rendering. Alternatively, for annotated |
|
controllers, the response can be rendered (within the `HandlerAdapter`) instead of |
|
returning a view. |
|
* If a model is returned, the view is rendered. If no model is returned (maybe 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. |
|
|
|
For HTTP caching support, handlers can use the `checkNotModified` methods of `WebRequest`, |
|
along with further options for annotated controllers as described in |
|
<<mvc-caching-etag-lastmodified,HTTP Caching for Controllers>>. |
|
|
|
You can customize individual `DispatcherServlet` instances by adding Servlet |
|
initialization parameters (`init-param` elements) to the Servlet declaration in the |
|
`web.xml` file. The following table lists the supported parameters: |
|
|
|
[[mvc-disp-servlet-init-params-tbl]] |
|
.DispatcherServlet initialization parameters |
|
|=== |
|
| Parameter| Explanation |
|
|
|
| `contextClass` |
|
| Class that implements `ConfigurableWebApplicationContext`, to be instantiated and |
|
locally configured by this Servlet. By default, `XmlWebApplicationContext` is used. |
|
|
|
| `contextConfigLocation` |
|
| String that is passed to the context instance (specified by `contextClass`) to |
|
indicate where contexts can be found. The string consists potentially of multiple |
|
strings (using a comma as a delimiter) to support multiple contexts. In the 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`. |
|
|
|
| `throwExceptionIfNoHandlerFound` |
|
| Whether to throw a `NoHandlerFoundException` when no handler was found for a request. |
|
The exception can then be caught with a `HandlerExceptionResolver` (for example, by using an |
|
`@ExceptionHandler` controller method) and handled as any others. |
|
|
|
By default, this is set to `false`, in which case the `DispatcherServlet` sets the |
|
response status to 404 (NOT_FOUND) without raising an exception. |
|
|
|
Note that, if <<mvc-default-servlet-handler,default servlet handling>> is |
|
also configured, unresolved requests are always forwarded to the default servlet |
|
and a 404 is never raised. |
|
|=== |
|
|
|
|
|
|
|
[[mvc-handlermapping-path]] |
|
=== Path Matching |
|
|
|
The Servlet API exposes the full request path as `requestURI` and further sub-divides it |
|
into `contextPath`, `servletPath`, and `pathInfo` whose values vary depending on how a |
|
Servlet is mapped. From these inputs, Spring MVC needs to determine the lookup path to |
|
use for mapping handlers, which should exclude the `contextPath` and any `servletMapping` |
|
prefix, if applicable. |
|
|
|
The `servletPath` and `pathInfo` are decoded and that makes them impossible to compare |
|
directly to the full `requestURI` in order to derive the lookupPath and that makes it |
|
necessary to decode the `requestURI`. However this introduces its own issues because the |
|
path may contain encoded reserved characters such as `"/"` or `";"` that can in turn |
|
alter the structure of the path after they are decoded which can also lead to security |
|
issues. In addition, Servlet containers may normalize the `servletPath` to varying |
|
degrees which makes it further impossible to perform `startsWith` comparisons against |
|
the `requestURI`. |
|
|
|
This is why it is best to avoid reliance on the `servletPath` which comes with the |
|
prefix-based `servletPath` mapping type. If the `DispatcherServlet` is mapped as the |
|
default Servlet with `"/"` or otherwise without a prefix with `"/*"` and the Servlet |
|
container is 4.0+ then Spring MVC is able to detect the Servlet mapping type and avoid |
|
use of the `servletPath` and `pathInfo` altogether. On a 3.1 Servlet container, |
|
assuming the same Servlet mapping types, the equivalent can be achieved by providing |
|
a `UrlPathHelper` with `alwaysUseFullPath=true` via <<mvc-config-path-matching>> in |
|
the MVC config. |
|
|
|
Fortunately the default Servlet mapping `"/"` is a good choice. However, there is still |
|
an issue in that the `requestURI` needs to be decoded to make it possible to compare to |
|
controller mappings. This is again undesirable because of the potential to decode |
|
reserved characters that alter the path structure. If such characters are not expected, |
|
then you can reject them (like the Spring Security HTTP firewall), or you can configure |
|
`UrlPathHelper` with `urlDecode=false` but controller mappings will need to match to the |
|
encoded path which may not always work well. Furthermore, sometimes the |
|
`DispatcherServlet` needs to share the URL space with another Servlet and may need to |
|
be mapped by prefix. |
|
|
|
The above issues are addressed when using `PathPatternParser` and parsed patterns, as |
|
an alternative to String path matching with `AntPathMatcher`. The `PathPatternParser` has |
|
been available for use in Spring MVC from version 5.3, and is enabled by default from |
|
version 6.0. Unlike `AntPathMatcher` which needs either the lookup path decoded or the |
|
controller mapping encoded, a parsed `PathPattern` matches to a parsed representation |
|
of the path called `RequestPath`, one path segment at a time. This allows decoding and |
|
sanitizing path segment values individually without the risk of altering the structure |
|
of the path. Parsed `PathPattern` also supports the use of `servletPath` prefix mapping |
|
as long as a Servlet path mapping is used and the prefix is kept simple, i.e. it has no |
|
encoded characters. For pattern syntax details and comparison, see |
|
<<mvc-ann-requestmapping-pattern-comparison>>. |
|
|
|
|
|
|
|
|
|
[[mvc-handlermapping-interceptor]] |
|
=== Interception |
|
|
|
All `HandlerMapping` implementations support 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 run |
|
* `postHandle(..)`: After the handler is run |
|
* `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 continues. 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 by using setters on individual |
|
`HandlerMapping` implementations. |
|
|
|
`postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for |
|
which the response is written and committed within the `HandlerAdapter` and before |
|
`postHandle`. That means it is 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]] |
|
=== Exceptions |
|
[.small]#<<web-reactive.adoc#webflux-dispatcher-exceptions, WebFlux>># |
|
|
|
If an exception occurs during request mapping or is thrown from a request handler (such as |
|
a `@Controller`), the `DispatcherServlet` delegates to a chain of `HandlerExceptionResolver` |
|
beans to resolve the exception and provide alternative handling, which is typically an |
|
error response. |
|
|
|
The following table 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. |
|
See also 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 a `@Controller` or a |
|
`@ControllerAdvice` class. See <<mvc-ann-exceptionhandler, @ExceptionHandler methods>>. |
|
|=== |
|
|
|
|
|
[[mvc-exceptionhandlers-handling]] |
|
==== Chain of Resolvers |
|
|
|
You can form an exception resolver chain by declaring multiple `HandlerExceptionResolver` |
|
beans in your Spring configuration and setting their `order` properties as needed. |
|
The higher the order property, the later the exception resolver is positioned. |
|
|
|
The contract of `HandlerExceptionResolver` specifies that it can return: |
|
|
|
* a `ModelAndView` that points to an error view. |
|
* An empty `ModelAndView` if the exception was handled within the resolver. |
|
* `null` if the exception remains unresolved, for subsequent resolvers to try, and, if the |
|
exception remains at the end, it is allowed to bubble up to the Servlet container. |
|
|
|
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 (that is, 4xx, 5xx), |
|
Servlet containers can 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`. |
|
The following example shows how to do so: |
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"] |
|
---- |
|
<error-page> |
|
<location>/error</location> |
|
</error-page> |
|
---- |
|
|
|
Given the preceding example, 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 |
|
(for example, `/error`). This is then processed by the `DispatcherServlet`, possibly mapping it |
|
to a `@Controller`, which could be implemented to return an error view name with a model |
|
or to render a JSON response, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@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("jakarta.servlet.error.status_code")); |
|
map.put("reason", request.getAttribute("jakarta.servlet.error.message")); |
|
return map; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RestController |
|
class ErrorController { |
|
|
|
@RequestMapping(path = ["/error"]) |
|
fun handle(request: HttpServletRequest): Map<String, Any> { |
|
val map = HashMap<String, Any>() |
|
map["status"] = request.getAttribute("jakarta.servlet.error.status_code") |
|
map["reason"] = request.getAttribute("jakarta.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 a `WebApplicationInitializer` and a minimal `web.xml`. |
|
|
|
|
|
|
|
[[mvc-viewresolver]] |
|
=== View Resolution |
|
[.small]#<<web-reactive.adoc#webflux-viewresolution, WebFlux>># |
|
|
|
Spring MVC defines the `ViewResolver` and `View` interfaces that let you 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 following table provides more details on the `ViewResolver` hierarchy: |
|
|
|
[[mvc-view-resolvers-tbl]] |
|
.ViewResolver implementations |
|
|=== |
|
| ViewResolver| Description |
|
|
|
| `AbstractCachingViewResolver` |
|
| Subclasses of `AbstractCachingViewResolver` cache view instances that they resolve. |
|
Caching improves performance of certain view technologies. You can 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. |
|
|
|
| `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 {api-spring-framework}/web/reactive/result/view/UrlBasedViewResolver.html[`UrlBasedViewResolver`] |
|
javadoc 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>>. |
|
|
|
| `BeanNameViewResolver` |
|
| Implementation of the `ViewResolver` interface that interprets a view name as a |
|
bean name in the current application context. This is a very flexible variant which |
|
allows for mixing and matching different view types based on distinct view names. |
|
Each such `View` can be defined as a bean e.g. in XML or in configuration classes. |
|
|=== |
|
|
|
|
|
[[mvc-viewresolver-handling]] |
|
==== Handling |
|
[.small]#<<web-reactive.adoc#webflux-viewresolution-handling, WebFlux>># |
|
|
|
You can chain view resolvers by declaring more than one resolver bean 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 that 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, you must always configure an `InternalResourceViewResolver` |
|
to be last in the overall order of view resolvers. |
|
|
|
Configuring view resolution is as simple as adding `ViewResolver` beans to your Spring |
|
configuration. The <<mvc-config>> provides a dedicated configuration API for |
|
<<mvc-config-view-resolvers>> and 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, WebFlux>># |
|
|
|
The special `redirect:` prefix in a view name lets you perform a redirect. The |
|
`UrlBasedViewResolver` (and its subclasses) 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 operate in terms of logical view names. A logical view |
|
name (such as `redirect:/myapp/some/resource`) redirects relative to the current |
|
Servlet context, while a name such as `redirect:https://myhost.com/some/arbitrary/path` |
|
redirects 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 |
|
|
|
You can also 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 you use 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, 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 (for example, `"/path?format=pdf"`). |
|
|
|
The `ContentNegotiatingViewResolver` selects an appropriate `View` to handle the request |
|
by comparing the request media types 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, |
|
the list of views specified through the `DefaultViews` property is 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 can include wildcards (for example `text/{asterisk}`), in which case a `View` whose |
|
`Content-Type` is `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, as the Spring web |
|
MVC framework does. `DispatcherServlet` lets you automatically resolve messages |
|
by 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. By 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. The following selection of locale resolvers is included in |
|
Spring. |
|
|
|
* <<mvc-timezone>> |
|
* <<mvc-localeresolver-acceptheader>> |
|
* <<mvc-localeresolver-cookie>> |
|
* <<mvc-localeresolver-session>> |
|
* <<mvc-localeresolver-interceptor>> |
|
|
|
|
|
[[mvc-timezone]] |
|
==== Time Zone |
|
|
|
In addition to obtaining the client's locale, it is often useful to know its time zone. |
|
The `LocaleContextResolver` interface offers an extension to `LocaleResolver` that lets |
|
resolvers provide a richer `LocaleContext`, which may include time zone information. |
|
|
|
When available, the user's `TimeZone` can be obtained by using the |
|
`RequestContext.getTimeZone()` method. Time zone information is automatically used |
|
by any Date/Time `Converter` and `Formatter` objects that are 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 (for example, 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. By using the |
|
properties of this locale resolver, you can specify the name of the cookie as well as the |
|
maximum age. The following example defines 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> |
|
---- |
|
|
|
The following table describes the properties `CookieLocaleResolver`: |
|
|
|
[[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 persists on the client. If `-1` is specified, the |
|
cookie will not be persisted. It is available only until the client shuts down |
|
the browser. |
|
|
|
| `cookiePath` |
|
| / |
|
| Limits the visibility of the cookie to a certain part of your site. When `cookiePath` is |
|
specified, the cookie is visible only to that path and the paths below it. |
|
|=== |
|
|
|
|
|
[[mvc-localeresolver-session]] |
|
==== Session Resolver |
|
|
|
The `SessionLocaleResolver` lets you 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 temporary |
|
for each session and are, therefore, lost when each session ends. |
|
|
|
Note that there is no direct relationship with external session management mechanisms, |
|
such as the Spring Session project. This `SessionLocaleResolver` evaluates and |
|
modifies the 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 |
|
`HandlerMapping` definitions. It detects a parameter in the request and changes the locale |
|
accordingly, calling the `setLocale` method on the `LocaleResolver` in the dispatcher's |
|
application context. The next example shows that calls to all `{asterisk}.view` resources |
|
that contain a parameter named `siteLanguage` now changes the locale. So, for example, |
|
a request for the URL, `https://www.sf.net/home.view?siteLanguage=nl`, changes the site |
|
language to Dutch. The following example shows how to intercept the locale: |
|
|
|
[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. |
|
|
|
WARNING: as of 6.0 support for themes has been deprecated theme in favor of using CSS, |
|
and without any special support on the server side. |
|
|
|
|
|
[[mvc-themeresolver-defining]] |
|
==== Defining 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 is 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 you use the `ResourceBundleThemeSource`, a theme is defined in a simple properties |
|
file. The properties file lists the resources that make up the theme, as the following example shows: |
|
|
|
[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]] |
|
==== Resolving Themes |
|
|
|
After you define themes, as described in the <<mvc-themeresolver-defining, preceding section>>, |
|
you decide which theme to use. The `DispatcherServlet` looks 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 table describes the theme resolvers provided by Spring: |
|
|
|
[[mvc-theme-resolver-impls-tbl]] |
|
.ThemeResolver implementations |
|
[cols="1,4"] |
|
|=== |
|
| Class | Description |
|
|
|
| `FixedThemeResolver` |
|
| Selects a fixed theme, set by using the `defaultThemeName` property. |
|
|
|
| `SessionThemeResolver` |
|
| The theme is maintained in the user's HTTP session. It needs to be set only 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 lets theme changes on every |
|
request with a simple request parameter. |
|
|
|
|
|
|
|
[[mvc-multipart]] |
|
=== Multipart Resolver |
|
[.small]#<<web-reactive.adoc#webflux-multipart, 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 https://commons.apache.org/proper/commons-fileupload[Commons FileUpload] and |
|
another based on Servlet multipart request parsing. |
|
|
|
To enable multipart handling, you need to declare a `MultipartResolver` bean in your |
|
`DispatcherServlet` Spring configuration with a name of `multipartResolver`. |
|
The `DispatcherServlet` detects it and applies it to the incoming request. When a POST |
|
with a content type of `multipart/form-data` is received, the resolver parses the |
|
content wraps the current `HttpServletRequest` as a `MultipartHttpServletRequest` to |
|
provide access to resolved files in addition to exposing parts as request parameters. |
|
|
|
|
|
[[mvc-multipart-resolver-commons]] |
|
==== Apache Commons `FileUpload` |
|
|
|
To use Apache Commons `FileUpload`, you can configure a bean of type |
|
`CommonsMultipartResolver` with a name of `multipartResolver`. You also need to have |
|
the `commons-fileupload` jar as a dependency on your classpath. |
|
|
|
This resolver variant delegates to a local library within the application, providing |
|
maximum portability across Servlet containers. As an alternative, consider standard |
|
Servlet multipart resolution through the container's own parser as discussed below. |
|
|
|
[NOTE] |
|
==== |
|
Commons FileUpload traditionally applies to POST requests only but accepts any |
|
`multipart/` content type. See the |
|
{api-spring-framework}/web/multipart/commons/CommonsMultipartResolver.html[`CommonsMultipartResolver`] |
|
javadoc for details and configuration options. |
|
==== |
|
|
|
|
|
[[mvc-multipart-resolver-standard]] |
|
==== Servlet Multipart Parsing |
|
|
|
Servlet multipart parsing needs to be enabled through Servlet container configuration. |
|
To do so: |
|
|
|
* In Java, set a `MultipartConfigElement` on the Servlet registration. |
|
* In `web.xml`, add a `"<multipart-config>"` section to the servlet declaration. |
|
|
|
The following example shows how to set a `MultipartConfigElement` on the Servlet registration: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { |
|
|
|
// ... |
|
|
|
@Override |
|
protected void customizeRegistration(ServletRegistration.Dynamic registration) { |
|
|
|
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold |
|
registration.setMultipartConfig(new MultipartConfigElement("/tmp")); |
|
} |
|
|
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class AppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() { |
|
|
|
// ... |
|
|
|
override fun customizeRegistration(registration: ServletRegistration.Dynamic) { |
|
|
|
// Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold |
|
registration.setMultipartConfig(MultipartConfigElement("/tmp")) |
|
} |
|
|
|
} |
|
---- |
|
|
|
Once the Servlet multipart configuration is in place, you can add a bean of type |
|
`StandardServletMultipartResolver` with a name of `multipartResolver`. |
|
|
|
[NOTE] |
|
==== |
|
This resolver variant uses your Servlet container's multipart parser as-is, |
|
potentially exposing the application to container implementation differences. |
|
By default, it will try to parse any `multipart/` content type with any HTTP |
|
method but this may not be supported across all Servlet containers. See the |
|
{api-spring-framework}/web/multipart/support/StandardServletMultipartResolver.html[`StandardServletMultipartResolver`] |
|
javadoc for details and configuration options. |
|
==== |
|
|
|
|
|
|
|
[[mvc-logging]] |
|
=== Logging |
|
[.small]#<<web-reactive.adoc#webflux-logging, WebFlux>># |
|
|
|
DEBUG-level logging in Spring MVC is designed to be compact, minimal, and |
|
human-friendly. It focuses on high-value bits of information that are useful over and |
|
over again versus others that are useful only when debugging a specific issue. |
|
|
|
TRACE-level logging generally follows the same principles as DEBUG (and, for example, also |
|
should not be a fire hose) but can be used for debugging any issue. In addition, some log |
|
messages may show a different level of detail at TRACE versus DEBUG. |
|
|
|
Good logging comes from the experience of using the logs. If you spot anything that does |
|
not meet the stated goals, please let us know. |
|
|
|
|
|
[[mvc-logging-sensitive-data]] |
|
==== Sensitive Data |
|
[.small]#<<web-reactive.adoc#webflux-logging-sensitive-data, WebFlux>># |
|
|
|
DEBUG and TRACE logging may log sensitive information. This is why request parameters and |
|
headers are masked by default and their logging in full must be enabled explicitly |
|
through the `enableLoggingRequestDetails` property on `DispatcherServlet`. |
|
|
|
The following example shows how to do so by using Java configuration: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
public class MyInitializer |
|
extends AbstractAnnotationConfigDispatcherServletInitializer { |
|
|
|
@Override |
|
protected Class<?>[] getRootConfigClasses() { |
|
return ... ; |
|
} |
|
|
|
@Override |
|
protected Class<?>[] getServletConfigClasses() { |
|
return ... ; |
|
} |
|
|
|
@Override |
|
protected String[] getServletMappings() { |
|
return ... ; |
|
} |
|
|
|
@Override |
|
protected void customizeRegistration(ServletRegistration.Dynamic registration) { |
|
registration.setInitParameter("enableLoggingRequestDetails", "true"); |
|
} |
|
|
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class MyInitializer : AbstractAnnotationConfigDispatcherServletInitializer() { |
|
|
|
override fun getRootConfigClasses(): Array<Class<*>>? { |
|
return ... |
|
} |
|
|
|
override fun getServletConfigClasses(): Array<Class<*>>? { |
|
return ... |
|
} |
|
|
|
override fun getServletMappings(): Array<String> { |
|
return ... |
|
} |
|
|
|
override fun customizeRegistration(registration: ServletRegistration.Dynamic) { |
|
registration.setInitParameter("enableLoggingRequestDetails", "true") |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
|
|
[[filters]] |
|
== Filters |
|
[.small]#<<web-reactive.adoc#webflux-filters, WebFlux>># |
|
|
|
The `spring-web` module provides some useful filters: |
|
|
|
* <<filters-http-put>> |
|
* <<filters-forwarded-headers>> |
|
* <<filters-shallow-etag>> |
|
* <<filters-cors>> |
|
|
|
|
|
|
|
[[filters-http-put]] |
|
=== Form Data |
|
|
|
Browsers can submit form data only through HTTP GET or HTTP POST but non-browser clients can also |
|
use HTTP PUT, PATCH, and DELETE. The Servlet API requires `ServletRequest.getParameter{asterisk}()` |
|
methods to support form field access only for HTTP POST. |
|
|
|
The `spring-web` module provides `FormContentFilter` to intercept HTTP PUT, PATCH, and DELETE |
|
requests with a content type of `application/x-www-form-urlencoded`, read the form data from |
|
the body of the request, and wrap the `ServletRequest` to make the form data |
|
available through the `ServletRequest.getParameter{asterisk}()` family of methods. |
|
|
|
|
|
|
|
[[filters-forwarded-headers]] |
|
=== Forwarded Headers |
|
[.small]#<<web-reactive.adoc#webflux-forwarded-headers, WebFlux>># |
|
|
|
As a request goes through proxies (such as load balancers) the host, port, and |
|
scheme may change, and that makes it a challenge to create links that point to the correct |
|
host, port, and scheme from a client perspective. |
|
|
|
https://tools.ietf.org/html/rfc7239[RFC 7239] defines the `Forwarded` HTTP header |
|
that proxies can use to provide information about the original request. There are other |
|
non-standard headers, too, including `X-Forwarded-Host`, `X-Forwarded-Port`, |
|
`X-Forwarded-Proto`, `X-Forwarded-Ssl`, and `X-Forwarded-Prefix`. |
|
|
|
`ForwardedHeaderFilter` is a Servlet filter that modifies the request in order to |
|
a) change the host, port, and scheme based on `Forwarded` headers, and b) to remove those |
|
headers to eliminate further impact. The filter relies on wrapping the request, and |
|
therefore it must be ordered ahead of other filters, such as `RequestContextFilter`, that |
|
should work with the modified and not the original request. |
|
|
|
There are security considerations for forwarded headers since an application cannot know |
|
if the headers were added by a proxy, as intended, or by a malicious client. This is why |
|
a proxy at the boundary of trust should be configured to remove untrusted `Forwarded` |
|
headers that come from the outside. You can also configure the `ForwardedHeaderFilter` |
|
with `removeOnly=true`, in which case it removes but does not use the headers. |
|
|
|
In order to support <<mvc-ann-async,asynchronous requests>> and error dispatches this |
|
filter should be mapped with `DispatcherType.ASYNC` and also `DispatcherType.ERROR`. |
|
If using Spring Framework's `AbstractAnnotationConfigDispatcherServletInitializer` |
|
(see <<mvc-container-config>>) all filters are automatically registered for all dispatch |
|
types. However if registering the filter via `web.xml` or in Spring Boot via a |
|
`FilterRegistrationBean` be sure to include `DispatcherType.ASYNC` and |
|
`DispatcherType.ERROR` in addition to `DispatcherType.REQUEST`. |
|
|
|
|
|
|
|
[[filters-shallow-etag]] |
|
=== Shallow ETag |
|
|
|
The `ShallowEtagHeaderFilter` filter creates a "`shallow`" ETag by caching the content |
|
written to the response and computing an MD5 hash from it. The next time a client sends, |
|
it does the same, but it also compares the computed value against the `If-None-Match` |
|
request header and, if the two are equal, returns a 304 (NOT_MODIFIED). |
|
|
|
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 earlier, can avoid |
|
the computation. See <<mvc-caching>>. |
|
|
|
This filter has a `writeWeakETag` parameter that configures the filter to write weak ETags |
|
similar to the following: `W/"02a2d595e6ed9a0b24f027f2b63b134d6"` (as defined in |
|
https://tools.ietf.org/html/rfc7232#section-2.3[RFC 7232 Section 2.3]). |
|
|
|
In order to support <<mvc-ann-async,asynchronous requests>> this filter must be mapped |
|
with `DispatcherType.ASYNC` so that the filter can delay and successfully generate an |
|
ETag to the end of the last async dispatch. If using Spring Framework's |
|
`AbstractAnnotationConfigDispatcherServletInitializer` (see <<mvc-container-config>>) |
|
all filters are automatically registered for all dispatch types. However if registering |
|
the filter via `web.xml` or in Spring Boot via a `FilterRegistrationBean` be sure to include |
|
`DispatcherType.ASYNC`. |
|
|
|
|
|
|
|
[[filters-cors]] |
|
=== CORS |
|
[.small]#<<web-reactive.adoc#webflux-filters-cors, WebFlux>># |
|
|
|
Spring MVC provides fine-grained support for CORS configuration through annotations on |
|
controllers. However, when used with Spring Security, we advise relying on the built-in |
|
`CorsFilter` that must be ordered ahead of Spring Security's chain of filters. |
|
|
|
See the sections on <<mvc-cors>> and the <<mvc-cors-filter>> for more details. |
|
|
|
|
|
|
|
|
|
[[mvc-controller]] |
|
== Annotated Controllers |
|
[.small]#<<web-reactive.adoc#webflux-controller, 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. |
|
The following example shows a controller defined by annotations: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
public class HelloController { |
|
|
|
@GetMapping("/hello") |
|
public String handle(Model model) { |
|
model.addAttribute("message", "Hello World!"); |
|
return "index"; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
import org.springframework.ui.set |
|
|
|
@Controller |
|
class HelloController { |
|
|
|
@GetMapping("/hello") |
|
fun handle(model: Model): String { |
|
model["message"] = "Hello World!" |
|
return "index" |
|
} |
|
} |
|
---- |
|
|
|
In the preceding example, the method accepts a `Model` and returns a view name as a `String`, |
|
but many other options exist and are explained later in this chapter. |
|
|
|
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, WebFlux>># |
|
|
|
You can define controller beans by 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, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@ComponentScan("org.example.web") |
|
public class WebConfig { |
|
|
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@ComponentScan("org.example.web") |
|
class WebConfig { |
|
|
|
// ... |
|
} |
|
---- |
|
|
|
The following example shows the XML configuration equivalent of the preceding example: |
|
|
|
[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 |
|
https://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/context |
|
https://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` to indicate a controller whose |
|
every method inherits the type-level `@ResponseBody` annotation and, therefore, writes |
|
directly to the response body versus view resolution and rendering with an HTML template. |
|
|
|
|
|
[[mvc-ann-requestmapping-proxying]] |
|
==== AOP Proxies |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-proxying, WebFlux>># |
|
|
|
In some cases, you may need to decorate a controller 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 automatically the case with such annotations |
|
directly on the controller. |
|
|
|
If the controller implements an interface, and needs AOP proxying, you may need to |
|
explicitly configure class-based proxying. For example, with `@EnableTransactionManagement` |
|
you can change to `@EnableTransactionManagement(proxyTargetClass = true)`, and with |
|
`<tx:annotation-driven/>` you can change to `<tx:annotation-driven proxy-target-class="true"/>`. |
|
|
|
NOTE: Keep in mind that as of 6.0, with interface proxying, Spring MVC no longer detects |
|
controllers based solely on a type-level `@RequestMapping` annotation on the interface. |
|
Please, enable class based proxying, or otherwise the interface must also have an |
|
`@Controller` annotation. |
|
|
|
|
|
|
|
[[mvc-ann-requestmapping]] |
|
=== Request Mapping |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping, WebFlux>># |
|
|
|
You can use the `@RequestMapping` annotation to map requests to controllers methods. It has |
|
various attributes to match by URL, HTTP method, request parameters, headers, and media |
|
types. You can use it at the class level to express 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 shortcuts are <<mvc-ann-requestmapping-composed>> that are provided because, |
|
arguably, most controller methods should be mapped to a specific HTTP method versus |
|
using `@RequestMapping`, which, by default, matches to all HTTP methods. |
|
A `@RequestMapping` is still needed at the class level to express shared mappings. |
|
|
|
The following example has type and method level mappings: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@RestController |
|
@RequestMapping("/persons") |
|
class PersonController { |
|
|
|
@GetMapping("/{id}") |
|
public Person getPerson(@PathVariable Long id) { |
|
// ... |
|
} |
|
|
|
@PostMapping |
|
@ResponseStatus(HttpStatus.CREATED) |
|
public void add(@RequestBody Person person) { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RestController |
|
@RequestMapping("/persons") |
|
class PersonController { |
|
|
|
@GetMapping("/{id}") |
|
fun getPerson(@PathVariable id: Long): Person { |
|
// ... |
|
} |
|
|
|
@PostMapping |
|
@ResponseStatus(HttpStatus.CREATED) |
|
fun add(@RequestBody person: Person) { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-ann-requestmapping-uri-templates]] |
|
==== URI patterns |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-uri-templates, WebFlux>># |
|
|
|
`@RequestMapping` methods can be mapped using URL patterns. There are two alternatives: |
|
|
|
* `PathPattern` -- a pre-parsed pattern matched against the URL path also pre-parsed as |
|
`PathContainer`. Designed for web use, this solution deals effectively with encoding and |
|
path parameters, and matches efficiently. |
|
* `AntPathMatcher` -- match String patterns against a String path. This is the original |
|
solution also used in Spring configuration to select resources on the classpath, on the |
|
filesystem, and other locations. It is less efficient and the String path input is a |
|
challenge for dealing effectively with encoding and other issues with URLs. |
|
|
|
`PathPattern` is the recommended solution for web applications and it is the only choice in |
|
Spring WebFlux. It was enabled for use in Spring MVC from version 5.3 and is enabled by |
|
default from version 6.0. See <<mvc-config-path-matching, MVC config>> for |
|
customizations of path matching options. |
|
|
|
`PathPattern` supports the same pattern syntax as `AntPathMatcher`. In addition, it also |
|
supports the capturing pattern, e.g. `+{*spring}+`, for matching 0 or more path segments |
|
at the end of a path. `PathPattern` also restricts the use of `+**+` for matching multiple |
|
path segments such that it's only allowed at the end of a pattern. This eliminates many |
|
cases of ambiguity when choosing the best matching pattern for a given request. |
|
For full pattern syntax please refer to |
|
{api-spring-framework}/web/util/pattern/PathPattern.html[PathPattern] and |
|
{api-spring-framework}/util/AntPathMatcher.html[AntPathMatcher]. |
|
|
|
Some example patterns: |
|
|
|
* `+"/resources/ima?e.png"+` - match one character in a path segment |
|
* `+"/resources/*.png"+` - match zero or more characters in a path segment |
|
* `+"/resources/**"+` - match multiple path segments |
|
* `+"/projects/{project}/versions"+` - match a path segment and capture it as a variable |
|
* `+"/projects/{project:[a-z]+}/versions"+` - match and capture a variable with a regex |
|
|
|
Captured URI variables can be accessed with `@PathVariable`. For example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/owners/{ownerId}/pets/{petId}") |
|
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/owners/{ownerId}/pets/{petId}") |
|
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
You can declare URI variables at the class and method levels, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
@RequestMapping("/owners/{ownerId}") |
|
public class OwnerController { |
|
|
|
@GetMapping("/pets/{petId}") |
|
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
@RequestMapping("/owners/{ownerId}") |
|
class OwnerController { |
|
|
|
@GetMapping("/pets/{petId}") |
|
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
URI variables are automatically converted to the appropriate type, or `TypeMismatchException` |
|
is raised. Simple types (`int`, `long`, `Date`, and so on) are supported by default and you can |
|
register support for any other data type. |
|
See <<mvc-ann-typeconversion>> and <<mvc-ann-initbinder>>. |
|
|
|
You can explicitly name URI variables (for example, `@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 expression that has |
|
syntax of `{varName:regex}`. For example, given URL `"/spring-web-3.0.5.jar"`, the following method |
|
extracts the name, version, and file extension: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}") |
|
public void handle(@PathVariable String name, @PathVariable String version, @PathVariable String ext) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}") |
|
fun handle(@PathVariable name: String, @PathVariable version: String, @PathVariable ext: String) { |
|
// ... |
|
} |
|
---- |
|
|
|
URI path patterns can also have embedded `${...}` placeholders that are resolved on startup |
|
by using `PropertySourcesPlaceholderConfigurer` against local, system, environment, and |
|
other property sources. You can use this, for example, to parameterize a base URL based on |
|
some external configuration. |
|
|
|
|
|
|
|
[[mvc-ann-requestmapping-pattern-comparison]] |
|
==== Pattern Comparison |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-pattern-comparison, WebFlux>># |
|
|
|
When multiple patterns match a URL, the best match must be selected. This is done with |
|
one of the following depending on whether use of parsed `PathPattern` is enabled for use or not: |
|
|
|
* {api-spring-framework}/web/util/pattern/PathPattern.html#SPECIFICITY_COMPARATOR[`PathPattern.SPECIFICITY_COMPARATOR`] |
|
* {api-spring-framework}/util/AntPathMatcher.html#getPatternComparator-java.lang.String-[`AntPathMatcher.getPatternComparator(String path)`] |
|
|
|
Both help to sort patterns with more specific ones on top. A pattern is less specific if |
|
it has a lower count of URI variables (counted as 1), 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 do not have double wildcards. |
|
|
|
For the full details, follow the above links to the pattern Comparators. |
|
|
|
|
|
[[mvc-ann-requestmapping-suffix-pattern-match]] |
|
==== Suffix Match |
|
|
|
Starting in 5.3, by default Spring MVC no longer performs `.{asterisk}` suffix pattern |
|
matching where a controller mapped to `/person` is also implicitly mapped to |
|
`/person.{asterisk}`. As a consequence path extensions are no longer used to interpret |
|
the requested content type for the response -- for example, `/person.pdf`, `/person.xml`, |
|
and so on. |
|
|
|
Using file extensions in this way 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 overlain with the use of URI variables, path parameters, and |
|
URI encoding. Reasoning about URL-based authorization |
|
and security (see next section for more details) also becomes more difficult. |
|
|
|
To completely disable the use of path extensions in versions prior to 5.3, set the following: |
|
|
|
* `useSuffixPatternMatching(false)`, see <<mvc-config-path-matching, PathMatchConfigurer>> |
|
* `favorPathExtension(false)`, see <<mvc-config-content-negotiation, ContentNegotiationConfigurer>> |
|
|
|
Having a way to request content types other than through the `"Accept"` header can still |
|
be useful, e.g. when typing a URL in a browser. A safe alternative to path extensions is |
|
to use the query parameter strategy. 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,ContentNegotiationConfigurer>>. |
|
|
|
|
|
[[mvc-ann-requestmapping-rfd]] |
|
==== Suffix Match and RFD |
|
|
|
A reflected file download (RFD) attack is similar to XSS in that it relies on request input |
|
(for example, a query parameter and a 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 through URL path extensions. |
|
Disabling suffix pattern matching and using 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 |
|
allowed as safe nor explicitly registered for content negotiation. However, it can |
|
potentially have side effects when URLs are typed directly into a browser. |
|
|
|
Many common path extensions are allowed as safe 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>>. |
|
|
|
See https://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, WebFlux>># |
|
|
|
You can narrow the request mapping based on the `Content-Type` of the request, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping(path = "/pets", consumes = "application/json") // <1> |
|
public void addPet(@RequestBody Pet pet) { |
|
// ... |
|
} |
|
---- |
|
<1> Using a `consumes` attribute to narrow the mapping by the content type. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/pets", consumes = ["application/json"]) // <1> |
|
fun addPet(@RequestBody pet: Pet) { |
|
// ... |
|
} |
|
---- |
|
<1> Using a `consumes` attribute to narrow the mapping by the content type. |
|
|
|
The `consumes` attribute also supports negation expressions -- for example, `!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 |
|
overrides rather than extends the class-level declaration. |
|
|
|
TIP: `MediaType` provides constants for commonly used media types, such as |
|
`APPLICATION_JSON_VALUE` and `APPLICATION_XML_VALUE`. |
|
|
|
|
|
[[mvc-ann-requestmapping-produces]] |
|
==== Producible Media Types |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-produces, WebFlux>># |
|
|
|
You can narrow the request mapping based on the `Accept` request header and the list of |
|
content types that a controller method produces, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping(path = "/pets/{petId}", produces = "application/json") // <1> |
|
@ResponseBody |
|
public Pet getPet(@PathVariable String petId) { |
|
// ... |
|
} |
|
---- |
|
<1> Using a `produces` attribute to narrow the mapping by the content type. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/pets/{petId}", produces = ["application/json"]) // <1> |
|
@ResponseBody |
|
fun getPet(@PathVariable petId: String): Pet { |
|
// ... |
|
} |
|
---- |
|
<1> Using a `produces` attribute to narrow the mapping by the content type. |
|
|
|
The media type can specify a character set. Negated expressions are supported -- for example, |
|
`!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 |
|
overrides rather than extends the class-level declaration. |
|
|
|
TIP: `MediaType` provides constants for commonly used media types, such as |
|
`APPLICATION_JSON_VALUE` and `APPLICATION_XML_VALUE`. |
|
|
|
|
|
[[mvc-ann-requestmapping-params-and-headers]] |
|
==== Parameters, headers |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-params-and-headers, 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 of one (`!myParam`), or for a |
|
specific value (`myParam=myValue`). The following example shows how to test for a specific value: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") // <1> |
|
public void findPet(@PathVariable String petId) { |
|
// ... |
|
} |
|
---- |
|
<1> Testing whether `myParam` equals `myValue`. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) // <1> |
|
fun findPet(@PathVariable petId: String) { |
|
// ... |
|
} |
|
---- |
|
<1> Testing whether `myParam` equals `myValue`. |
|
|
|
You can also use the same with request header conditions, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping(path = "/pets", headers = "myHeader=myValue") // <1> |
|
public void findPet(@PathVariable String petId) { |
|
// ... |
|
} |
|
---- |
|
<1> Testing whether `myHeader` equals `myValue`. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/pets", headers = ["myHeader=myValue"]) // <1> |
|
fun findPet(@PathVariable petId: String) { |
|
// ... |
|
} |
|
---- |
|
|
|
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, WebFlux>># |
|
|
|
`@GetMapping` (and `@RequestMapping(method=HttpMethod.GET)`) support HTTP HEAD |
|
transparently for request mapping. Controller methods do not need to change. |
|
A response wrapper, applied in `jakarta.servlet.http.HttpServlet`, ensures a `Content-Length` |
|
header is set to the number of bytes written (without actually writing to the response). |
|
|
|
`@GetMapping` (and `@RequestMapping(method=HttpMethod.GET)`) are implicitly mapped to |
|
and support HTTP HEAD. An HTTP HEAD request is processed as if it were HTTP GET except |
|
that, instead of writing the body, the number of bytes are counted and the `Content-Length` |
|
header is set. |
|
|
|
By default, HTTP OPTIONS is handled by setting the `Allow` response header to the list of HTTP |
|
methods listed in all `@RequestMapping` methods that have 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`, and others). |
|
|
|
You can explicitly map the `@RequestMapping` method 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, 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 are provided because, arguably, most |
|
controller methods should be mapped to a specific HTTP method versus 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 subclassing |
|
`RequestMappingHandlerMapping` and overriding the `getCustomMethodCondition` method, where |
|
you can check the custom attribute and return your own `RequestCondition`. |
|
|
|
|
|
[[mvc-ann-requestmapping-registration]] |
|
==== Explicit Registrations |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestmapping-registration, WebFlux>># |
|
|
|
You can programmatically register handler methods, which you can use for dynamic |
|
registrations or for advanced cases, such as different instances of the same handler |
|
under different URLs. The following example registers a handler method: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
public class MyConfig { |
|
|
|
@Autowired |
|
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) // <1> |
|
throws NoSuchMethodException { |
|
|
|
RequestMappingInfo info = RequestMappingInfo |
|
.paths("/user/{id}").methods(RequestMethod.GET).build(); // <2> |
|
|
|
Method method = UserHandler.class.getMethod("getUser", Long.class); // <3> |
|
|
|
mapping.registerMapping(info, handler, method); // <4> |
|
} |
|
} |
|
---- |
|
<1> Inject the target handler and the handler mapping for controllers. |
|
<2> Prepare the request mapping meta data. |
|
<3> Get the handler method. |
|
<4> Add the registration. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
class MyConfig { |
|
|
|
@Autowired |
|
fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { // <1> |
|
val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() // <2> |
|
val method = UserHandler::class.java.getMethod("getUser", Long::class.java) // <3> |
|
mapping.registerMapping(info, handler, method) // <4> |
|
} |
|
} |
|
---- |
|
<1> Inject the target handler and the handler mapping for controllers. |
|
<2> Prepare the request mapping meta data. |
|
<3> Get the handler method. |
|
<4> Add the registration. |
|
|
|
|
|
|
|
[[mvc-ann-methods]] |
|
=== Handler Methods |
|
[.small]#<<web-reactive.adoc#webflux-ann-methods, 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, WebFlux>># |
|
|
|
The next table describes the 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 (for example, `@RequestParam`, `@RequestHeader`, |
|
and others) and is equivalent to `required=false`. |
|
|
|
[cols="1,2", options="header"] |
|
|=== |
|
| Controller method argument | Description |
|
|
|
| `WebRequest`, `NativeWebRequest` |
|
| Generic access to request parameters and request and session attributes, without direct |
|
use of the Servlet API. |
|
|
|
| `jakarta.servlet.ServletRequest`, `jakarta.servlet.ServletResponse` |
|
| Choose any specific request or response type -- for example, `ServletRequest`, `HttpServletRequest`, |
|
or Spring's `MultipartRequest`, `MultipartHttpServletRequest`. |
|
|
|
| `jakarta.servlet.http.HttpSession` |
|
| Enforces the presence of a session. As a consequence, such an argument is never `null`. |
|
Note that session access is not thread-safe. Consider setting the |
|
`RequestMappingHandlerAdapter` instance's `synchronizeOnSession` flag to `true` if multiple |
|
requests are allowed to concurrently access a session. |
|
|
|
| `jakarta.servlet.http.PushBuilder` |
|
| Servlet 4.0 push builder API for programmatic HTTP/2 resource pushes. |
|
Note that, per the Servlet specification, 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. |
|
|
|
Note that this argument is not resolved eagerly, if it is annotated in order to allow a custom resolver to resolve it |
|
before falling back on default resolution via `HttpServletRequest#getUserPrincipal`. |
|
For example, the Spring Security `Authentication` implements `Principal` and would be injected as such via |
|
`HttpServletRequest#getUserPrincipal`, unless it is also annotated with `@AuthenticationPrincipal` in which case it |
|
is resolved by a custom Spring Security resolver through `Authentication#getPrincipal`. |
|
|
|
| `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` or `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 the Servlet request parameters, including multipart files. Parameter values |
|
are converted to the declared method argument type. See <<mvc-ann-requestparam>> as well |
|
as <<mvc-multipart-forms>>. |
|
|
|
Note that use of `@RequestParam` is optional for simple parameter values. |
|
See "`Any other argument`", at the end of 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 by using `HttpMessageConverter` implementations. See <<mvc-ann-requestbody>>. |
|
|
|
| `HttpEntity<B>` |
|
| For access to request headers and body. The body is converted with an `HttpMessageConverter`. |
|
See <<mvc-ann-httpentity>>. |
|
|
|
| `@RequestPart` |
|
| For access to a part in a `multipart/form-data` request, converting the part's body |
|
with an `HttpMessageConverter`. 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 (that is, to be appended to the query |
|
string) and 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 (for example, to set its attributes). |
|
See "`Any other argument`" at the end of this table. |
|
|
|
| `Errors`, `BindingResult` |
|
| For access to errors from validation and data binding for a command object |
|
(that is, a `@ModelAttribute` argument) or errors from the validation of a `@RequestBody` or |
|
`@RequestPart` arguments. You must declare an `Errors`, or `BindingResult` argument |
|
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. 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 earlier values in this table and it is |
|
a simple type (as determined by |
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]), |
|
it is resolved as a `@RequestParam`. Otherwise, it is resolved as a `@ModelAttribute`. |
|
|=== |
|
|
|
|
|
[[mvc-ann-return-types]] |
|
==== Return Values |
|
[.small]#<<web-reactive.adoc#webflux-ann-return-types, WebFlux>># |
|
|
|
The next table describes the supported controller method return values. Reactive types are |
|
supported for all return values. |
|
|
|
[cols="1,2", options="header"] |
|
|=== |
|
| Controller method return value | Description |
|
|
|
| `@ResponseBody` |
|
| The return value is converted through `HttpMessageConverter` implementations and written to the |
|
response. See <<mvc-ann-responsebody>>. |
|
|
|
| `HttpEntity<B>`, `ResponseEntity<B>` |
|
| The return value that specifies the full response (including HTTP headers and body) is to be converted |
|
through `HttpMessageConverter` implementations and written to the response. |
|
See <<mvc-ann-responseentity>>. |
|
|
|
| `HttpHeaders` |
|
| For returning a response with headers and no body. |
|
|
|
| `ErrorResponse` |
|
| To render an RFC 7807 error response with details in the body, |
|
see <<mvc-ann-rest-exceptions>> |
|
|
|
| `ProblemDetail` |
|
| To render an RFC 7807 error response with details in the body, |
|
see <<mvc-ann-rest-exceptions>> |
|
|
|
| `String` |
|
| A view name to be resolved with `ViewResolver` implementations and used together with the implicit |
|
model -- determined through command objects and `@ModelAttribute` methods. The handler |
|
method can also programmatically enrich the model by declaring a `Model` argument |
|
(see <<mvc-ann-requestmapping-registration>>). |
|
|
|
| `View` |
|
| A `View` instance to use for rendering together with the implicit model -- determined |
|
through command objects and `@ModelAttribute` methods. The handler method can also |
|
programmatically enrich the model by declaring a `Model` argument |
|
(see <<mvc-ann-requestmapping-registration>>). |
|
|
|
| `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" at the end of |
|
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`, an `OutputStream` argument, or |
|
an `@ResponseStatus` annotation. The same is also true 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 can also indicate "`no response body`" for |
|
REST controllers or a default view name selection for HTML controllers. |
|
|
|
| `DeferredResult<V>` |
|
| Produce any of the preceding return values asynchronously from any thread -- for example, 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` implementations. 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 through `ReactiveAdapterRegistry` |
|
| Alternative to `DeferredResult` with multi-value streams (for example, `Flux`, `Observable`) |
|
collected to a `List`. |
|
|
|
For streaming scenarios (for example, `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 is applied |
|
against the completion of each write. |
|
|
|
See <<mvc-ann-async>> and <<mvc-ann-async-reactive-types>>. |
|
|
|
| Any other return value |
|
| Any return value that does not match any of the earlier values in this table and that |
|
is a `String` or `void` is treated as a view name (default view name selection through |
|
`RequestToViewNameTranslator` applies), provided it is not a simple type, as determined by |
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]. |
|
Values that are simple types remain unresolved. |
|
|=== |
|
|
|
|
|
[[mvc-ann-typeconversion]] |
|
==== Type Conversion |
|
[.small]#<<web-reactive.adoc#webflux-ann-typeconversion, WebFlux>># |
|
|
|
Some annotated controller method arguments that represent `String`-based request input (such as |
|
`@RequestParam`, `@RequestHeader`, `@PathVariable`, `@MatrixVariable`, and `@CookieValue`) |
|
can 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 (`int`, `long`, `Date`, and others) are supported. You can customize |
|
type conversion through a `WebDataBinder` (see <<mvc-ann-initbinder>>) or by registering |
|
`Formatters` with the `FormattingConversionService`. |
|
See <<core.adoc#format, Spring Field Formatting>>. |
|
|
|
A practical issue in type conversion is the treatment of an empty String source value. |
|
Such a value is treated as missing if it becomes `null` as a result of type conversion. |
|
This can be the case for `Long`, `UUID`, and other target types. If you want to allow `null` |
|
to be injected, either use the `required` flag on the argument annotation, or declare the |
|
argument as `@Nullable`. |
|
|
|
[NOTE] |
|
==== |
|
As of 5.3, non-null arguments will be enforced even after type conversion. If your handler |
|
method intends to accept a null value as well, either declare your argument as `@Nullable` |
|
or mark it as `required=false` in the corresponding `@RequestParam`, etc. annotation. This is |
|
a best practice and the recommended solution for regressions encountered in a 5.3 upgrade. |
|
|
|
Alternatively, you may specifically handle e.g. the resulting `MissingPathVariableException` |
|
in the case of a required `@PathVariable`. A null value after conversion will be treated like |
|
an empty original value, so the corresponding `Missing...Exception` variants will be thrown. |
|
==== |
|
|
|
|
|
[[mvc-ann-matrix-variables]] |
|
==== Matrix Variables |
|
[.small]#<<web-reactive.adoc#webflux-ann-matrix-variables, WebFlux>># |
|
|
|
https://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 |
|
https://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, with each variable separated by a semicolon and |
|
multiple values separated by comma (for example, `/cars;color=red,green;year=2012`). Multiple |
|
values can also be specified through repeated variable names (for example, |
|
`color=red;color=green;color=blue`). |
|
|
|
If 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. |
|
The following example uses a matrix variable: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// GET /pets/42;q=11;r=22 |
|
|
|
@GetMapping("/pets/{petId}") |
|
public void findPet(@PathVariable String petId, @MatrixVariable int q) { |
|
|
|
// petId == 42 |
|
// q == 11 |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// GET /pets/42;q=11;r=22 |
|
|
|
@GetMapping("/pets/{petId}") |
|
fun findPet(@PathVariable petId: String, @MatrixVariable q: Int) { |
|
|
|
// petId == 42 |
|
// q == 11 |
|
} |
|
---- |
|
|
|
Given that all path segments may contain matrix variables, you may sometimes need to |
|
disambiguate which path variable the matrix variable is expected to be in. |
|
The following example shows how to do so: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// GET /owners/42;q=11/pets/21;q=22 |
|
|
|
@GetMapping("/owners/{ownerId}/pets/{petId}") |
|
public void findPet( |
|
@MatrixVariable(name="q", pathVar="ownerId") int q1, |
|
@MatrixVariable(name="q", pathVar="petId") int q2) { |
|
|
|
// q1 == 11 |
|
// q2 == 22 |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// GET /owners/42;q=11/pets/21;q=22 |
|
|
|
@GetMapping("/owners/{ownerId}/pets/{petId}") |
|
fun findPet( |
|
@MatrixVariable(name = "q", pathVar = "ownerId") q1: Int, |
|
@MatrixVariable(name = "q", pathVar = "petId") q2: Int) { |
|
|
|
// q1 == 11 |
|
// q2 == 22 |
|
} |
|
---- |
|
|
|
A matrix variable may be defined as optional and a default value specified, as the |
|
following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// GET /pets/42 |
|
|
|
@GetMapping("/pets/{petId}") |
|
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) { |
|
|
|
// q == 1 |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// GET /pets/42 |
|
|
|
@GetMapping("/pets/{petId}") |
|
fun findPet(@MatrixVariable(required = false, defaultValue = "1") q: Int) { |
|
|
|
// q == 1 |
|
} |
|
---- |
|
|
|
To get all matrix variables, you can use a `MultiValueMap`, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23 |
|
|
|
@GetMapping("/owners/{ownerId}/pets/{petId}") |
|
public void findPet( |
|
@MatrixVariable MultiValueMap<String, String> matrixVars, |
|
@MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) { |
|
|
|
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] |
|
// petMatrixVars: ["q" : 22, "s" : 23] |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23 |
|
|
|
@GetMapping("/owners/{ownerId}/pets/{petId}") |
|
fun findPet( |
|
@MatrixVariable matrixVars: MultiValueMap<String, String>, |
|
@MatrixVariable(pathVar="petId") petMatrixVars: MultiValueMap<String, String>) { |
|
|
|
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] |
|
// petMatrixVars: ["q" : 22, "s" : 23] |
|
} |
|
---- |
|
|
|
Note that you need to enable the use of matrix variables. In the MVC Java configuration, |
|
you need to set a `UrlPathHelper` with `removeSemicolonContent=false` through |
|
<<mvc-config-path-matching>>. In the MVC XML namespace, you can set |
|
`<mvc:annotation-driven enable-matrix-variables="true"/>`. |
|
|
|
|
|
[[mvc-ann-requestparam]] |
|
==== `@RequestParam` |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestparam, WebFlux>># |
|
|
|
You can use the `@RequestParam` annotation to bind Servlet request parameters (that is, |
|
query parameters or form data) to a method argument in a controller. |
|
|
|
The following example shows how to do so: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
@RequestMapping("/pets") |
|
public class EditPetForm { |
|
|
|
// ... |
|
|
|
@GetMapping |
|
public String setupForm(@RequestParam("petId") int petId, Model model) { <1> |
|
Pet pet = this.clinic.loadPet(petId); |
|
model.addAttribute("pet", pet); |
|
return "petForm"; |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
<1> Using `@RequestParam` to bind `petId`. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
import org.springframework.ui.set |
|
|
|
@Controller |
|
@RequestMapping("/pets") |
|
class EditPetForm { |
|
|
|
// ... |
|
|
|
@GetMapping |
|
fun setupForm(@RequestParam("petId") petId: Int, model: Model): String { // <1> |
|
val pet = this.clinic.loadPet(petId); |
|
model["pet"] = pet |
|
return "petForm" |
|
} |
|
|
|
// ... |
|
|
|
} |
|
---- |
|
<1> Using `@RequestParam` to bind `petId`. |
|
|
|
By default, method parameters that use this annotation are required, but you can specify that |
|
a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to |
|
`false` or by declaring the argument with an `java.util.Optional` wrapper. |
|
|
|
Type conversion is automatically applied if the target method parameter type is not |
|
`String`. See <<mvc-ann-typeconversion>>. |
|
|
|
Declaring the argument type as an array or list allows for resolving multiple parameter |
|
values for the same parameter name. |
|
|
|
When an `@RequestParam` annotation is declared as a `Map<String, String>` or |
|
`MultiValueMap<String, String>`, without a parameter name specified in the annotation, |
|
then the map is populated with the request parameter values for each given parameter name. |
|
|
|
Note that use of `@RequestParam` is optional (for example, 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 were annotated |
|
with `@RequestParam`. |
|
|
|
|
|
[[mvc-ann-requestheader]] |
|
==== `@RequestHeader` |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestheader, WebFlux>># |
|
|
|
You can use the `@RequestHeader` annotation to bind a request header to a method argument in a |
|
controller. |
|
|
|
Consider the following 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 example gets the value of the `Accept-Encoding` and `Keep-Alive` headers: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/demo") |
|
public void handle( |
|
@RequestHeader("Accept-Encoding") String encoding, // <1> |
|
@RequestHeader("Keep-Alive") long keepAlive) { // <2> |
|
//... |
|
} |
|
---- |
|
<1> Get the value of the `Accept-Encoding` header. |
|
<2> Get the value of the `Keep-Alive` header. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/demo") |
|
fun handle( |
|
@RequestHeader("Accept-Encoding") encoding: String, // <1> |
|
@RequestHeader("Keep-Alive") keepAlive: Long) { // <2> |
|
//... |
|
} |
|
---- |
|
<1> Get the value of the `Accept-Encoding` header. |
|
<2> Get the value of the `Keep-Alive` header. |
|
|
|
If the target method parameter type is not |
|
`String`, type conversion is automatically applied. 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 or collection of strings or other types known to the type conversion system. For |
|
example, a method parameter annotated with `@RequestHeader("Accept")` can be of type |
|
`String` but also `String[]` or `List<String>`. |
|
|
|
|
|
[[mvc-ann-cookievalue]] |
|
==== `@CookieValue` |
|
[.small]#<<web-reactive.adoc#webflux-ann-cookievalue, WebFlux>># |
|
|
|
You can use the `@CookieValue` annotation to bind the value of an HTTP cookie to a method argument |
|
in a controller. |
|
|
|
Consider a request with the following cookie: |
|
|
|
[literal,subs="verbatim,quotes"] |
|
---- |
|
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 |
|
---- |
|
|
|
The following example shows how to get the cookie value: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/demo") |
|
public void handle(@CookieValue("JSESSIONID") String cookie) { <1> |
|
//... |
|
} |
|
---- |
|
<1> Get the value of the `JSESSIONID` cookie. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/demo") |
|
fun handle(@CookieValue("JSESSIONID") cookie: String) { // <1> |
|
//... |
|
} |
|
---- |
|
<1> Get the value of the `JSESSIONID` cookie. |
|
|
|
If the target method parameter type is not `String`, type conversion is applied automatically. |
|
See <<mvc-ann-typeconversion>>. |
|
|
|
|
|
[[mvc-ann-modelattrib-method-args]] |
|
==== `@ModelAttribute` |
|
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-method-args, WebFlux>># |
|
|
|
You can use the `@ModelAttribute` annotation on a method argument to access an attribute from |
|
the model or have it be instantiated if not present. The model attribute is also overlain 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. The following example shows how to do so: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
public String processSubmit(@ModelAttribute Pet pet) { |
|
// method logic... |
|
} |
|
---- |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
fun processSubmit(@ModelAttribute pet: Pet): String { |
|
// method logic... |
|
} |
|
---- |
|
|
|
The `Pet` instance above is sourced in one of the following ways: |
|
|
|
* Retrieved from the model where it may have been added by a |
|
<<mvc-ann-modelattrib-methods,@ModelAttribute method>>. |
|
* Retrieved from the HTTP session if the model attribute was listed in |
|
the class-level <<mvc-ann-sessionattributes>> annotation. |
|
* Obtained through a `Converter` where the model attribute name matches the name of a |
|
request value such as a path variable or a request parameter (see next example). |
|
* Instantiated using its default constructor. |
|
* Instantiated through a "`primary constructor`" with arguments that match to Servlet |
|
request parameters. Argument names are determined through JavaBeans |
|
`@ConstructorProperties` or through runtime-retained parameter names in the bytecode. |
|
|
|
One alternative to using a <<mvc-ann-modelattrib-methods,@ModelAttribute method>> to |
|
supply it or relying on the framework to create the model attribute, is to have a |
|
`Converter<String, T>` to provide the instance. This is applied when the model attribute |
|
name matches to the name of a request value such as a path variable or a request |
|
parameter, and there is a `Converter` from `String` to the model attribute type. |
|
In the following example, the model attribute name is `account` which matches the URI |
|
path variable `account`, and there is a registered `Converter<String, Account>` which |
|
could load the `Account` from a data store: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PutMapping("/accounts/{account}") |
|
public String save(@ModelAttribute("account") Account account) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PutMapping("/accounts/{account}") |
|
fun save(@ModelAttribute("account") account: Account): String { |
|
// ... |
|
} |
|
---- |
|
|
|
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 can result in errors. By default, a `BindException` is raised. However, to check |
|
for such errors in the controller method, you can add a `BindingResult` argument immediately next |
|
to the `@ModelAttribute`, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { // <1> |
|
if (result.hasErrors()) { |
|
return "petForm"; |
|
} |
|
// ... |
|
} |
|
---- |
|
<1> Adding a `BindingResult` next to the `@ModelAttribute`. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1> |
|
if (result.hasErrors()) { |
|
return "petForm" |
|
} |
|
// ... |
|
} |
|
---- |
|
<1> Adding a `BindingResult` next to the `@ModelAttribute`. |
|
|
|
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 the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ModelAttribute |
|
public AccountForm setUpForm() { |
|
return new AccountForm(); |
|
} |
|
|
|
@ModelAttribute |
|
public Account findAccount(@PathVariable String accountId) { |
|
return accountRepository.findOne(accountId); |
|
} |
|
|
|
@PostMapping("update") |
|
public String update(@Valid AccountForm form, BindingResult result, |
|
@ModelAttribute(binding=false) Account account) { // <1> |
|
// ... |
|
} |
|
---- |
|
<1> Setting `@ModelAttribute(binding=false)`. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ModelAttribute |
|
fun setUpForm(): AccountForm { |
|
return AccountForm() |
|
} |
|
|
|
@ModelAttribute |
|
fun findAccount(@PathVariable accountId: String): Account { |
|
return accountRepository.findOne(accountId) |
|
} |
|
|
|
@PostMapping("update") |
|
fun update(@Valid form: AccountForm, result: BindingResult, |
|
@ModelAttribute(binding = false) account: Account): String { // <1> |
|
// ... |
|
} |
|
---- |
|
<1> Setting `@ModelAttribute(binding=false)`. |
|
|
|
You can automatically apply validation after data binding by adding the |
|
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation |
|
(<<core.adoc#validation-beanvalidation, Bean Validation>> and |
|
<<core.adoc#validation, Spring validation>>). The following example shows how to do so: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { // <1> |
|
if (result.hasErrors()) { |
|
return "petForm"; |
|
} |
|
// ... |
|
} |
|
---- |
|
<1> Validate the `Pet` instance. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") |
|
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1> |
|
if (result.hasErrors()) { |
|
return "petForm" |
|
} |
|
// ... |
|
} |
|
---- |
|
|
|
Note that using `@ModelAttribute` is optional (for example, 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 were annotated |
|
with `@ModelAttribute`. |
|
|
|
|
|
[[mvc-ann-sessionattributes]] |
|
==== `@SessionAttributes` |
|
[.small]#<<web-reactive.adoc#webflux-ann-sessionattributes, WebFlux>># |
|
|
|
`@SessionAttributes` is used to store model attributes in the HTTP Servlet session between |
|
requests. It is a type-level annotation that declares the session attributes used by a |
|
specific controller. This typically lists the names of model attributes or types of |
|
model attributes that should be transparently stored in the session for subsequent |
|
requests to access. |
|
|
|
The following example uses the `@SessionAttributes` annotation: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
@SessionAttributes("pet") // <1> |
|
public class EditPetForm { |
|
// ... |
|
} |
|
---- |
|
<1> Using the `@SessionAttributes` annotation. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
@SessionAttributes("pet") // <1> |
|
class EditPetForm { |
|
// ... |
|
} |
|
---- |
|
<1> Using the `@SessionAttributes` annotation. |
|
|
|
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, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
@SessionAttributes("pet") // <1> |
|
public class EditPetForm { |
|
|
|
// ... |
|
|
|
@PostMapping("/pets/{id}") |
|
public String handle(Pet pet, BindingResult errors, SessionStatus status) { |
|
if (errors.hasErrors) { |
|
// ... |
|
} |
|
status.setComplete(); // <2> |
|
// ... |
|
} |
|
} |
|
---- |
|
<1> Storing the `Pet` value in the Servlet session. |
|
<2> Clearing the `Pet` value from the Servlet session. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
@SessionAttributes("pet") // <1> |
|
class EditPetForm { |
|
|
|
// ... |
|
|
|
@PostMapping("/pets/{id}") |
|
fun handle(pet: Pet, errors: BindingResult, status: SessionStatus): String { |
|
if (errors.hasErrors()) { |
|
// ... |
|
} |
|
status.setComplete() // <2> |
|
// ... |
|
} |
|
} |
|
---- |
|
<1> Storing the `Pet` value in the Servlet session. |
|
<2> Clearing the `Pet` value from the Servlet session. |
|
|
|
|
|
[[mvc-ann-sessionattribute]] |
|
==== `@SessionAttribute` |
|
[.small]#<<web-reactive.adoc#webflux-ann-sessionattribute, WebFlux>># |
|
|
|
If you need access to pre-existing session attributes that are managed globally |
|
(that is, outside the controller -- for example, by a filter) and may or may not be present, |
|
you can use the `@SessionAttribute` annotation on a method parameter, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@RequestMapping("/") |
|
public String handle(@SessionAttribute User user) { <1> |
|
// ... |
|
} |
|
---- |
|
<1> Using a `@SessionAttribute` annotation. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RequestMapping("/") |
|
fun handle(@SessionAttribute user: User): String { // <1> |
|
// ... |
|
} |
|
---- |
|
|
|
For use cases that require adding or removing session attributes, consider injecting |
|
`org.springframework.web.context.request.WebRequest` or |
|
`jakarta.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, WebFlux>># |
|
|
|
Similar to `@SessionAttribute`, you can use the `@RequestAttribute` annotations to |
|
access pre-existing request attributes created earlier (for example, by a Servlet `Filter` |
|
or `HandlerInterceptor`): |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/") |
|
public String handle(@RequestAttribute Client client) { // <1> |
|
// ... |
|
} |
|
---- |
|
<1> Using the `@RequestAttribute` annotation. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/") |
|
fun handle(@RequestAttribute client: Client): String { // <1> |
|
// ... |
|
} |
|
---- |
|
<1> Using the `@RequestAttribute` annotation. |
|
|
|
|
|
[[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 or arrays of primitive types are automatically appended as query parameters. |
|
|
|
Appending primitive type attributes as query parameters can be the desired result if a |
|
model instance was prepared specifically for the redirect. However, in annotated |
|
controllers, the model can contain additional attributes added for rendering purposes (for example, |
|
drop-down field values). To avoid the possibility of having such attributes appear in the |
|
URL, a `@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`, which you can use to indicate that 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 does not do so, |
|
no attributes should be passed on to `RedirectView`. Both the MVC namespace and the MVC |
|
Java configuration keep this flag set to `false`, 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 you don't need to explicitly add them |
|
through `Model` or `RedirectAttributes`. The following example shows how to define a redirect: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/files/{path}") |
|
public String upload(...) { |
|
// ... |
|
return "redirect:files/{path}"; |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/files/{path}") |
|
fun upload(...): String { |
|
// ... |
|
return "redirect:files/{path}" |
|
} |
|
---- |
|
|
|
Another way of passing data to the redirect target is by using flash attributes. Unlike |
|
other redirect attributes, flash attributes are saved in the HTTP session (and, hence, do |
|
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 that are 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 are 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 be enabled explicitly. |
|
However, 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, a |
|
`@RequestMapping` method can accept an argument of type `RedirectAttributes` and use it |
|
to add flash attributes for a redirect scenario. Flash attributes added through |
|
`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 that serves the target URL. |
|
|
|
.Matching requests to flash attributes |
|
**** |
|
The concept of flash attributes exists in many other web frameworks and has proven to sometimes |
|
be exposed 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 (for example, 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 |
|
it looks up the "`input`" `FlashMap`. |
|
|
|
This does not entirely eliminate the possibility of a concurrency issue but |
|
reduces it greatly with information that is already available in the redirect URL. |
|
Therefore, we recommend that you use flash attributes mainly for redirect scenarios. |
|
**** |
|
|
|
|
|
[[mvc-multipart-forms]] |
|
==== Multipart |
|
[.small]#<<web-reactive.adoc#webflux-multipart-forms, 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. The following example accesses one regular form field and one uploaded |
|
file: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
public class FileUploadController { |
|
|
|
@PostMapping("/form") |
|
public String handleFormUpload(@RequestParam("name") String name, |
|
@RequestParam("file") MultipartFile file) { |
|
|
|
if (!file.isEmpty()) { |
|
byte[] bytes = file.getBytes(); |
|
// store the bytes somewhere |
|
return "redirect:uploadSuccess"; |
|
} |
|
return "redirect:uploadFailure"; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
class FileUploadController { |
|
|
|
@PostMapping("/form") |
|
fun handleFormUpload(@RequestParam("name") name: String, |
|
@RequestParam("file") file: MultipartFile): String { |
|
|
|
if (!file.isEmpty) { |
|
val bytes = file.bytes |
|
// store the bytes somewhere |
|
return "redirect:uploadSuccess" |
|
} |
|
return "redirect:uploadFailure" |
|
} |
|
} |
|
---- |
|
|
|
Declaring the argument type as a `List<MultipartFile>` allows for resolving multiple |
|
files for the same parameter name. |
|
|
|
When the `@RequestParam` annotation is declared as a `Map<String, MultipartFile>` or |
|
`MultiValueMap<String, MultipartFile>`, without a parameter name specified in the annotation, |
|
then the map is populated with the multipart files for each given parameter name. |
|
|
|
NOTE: With Servlet multipart parsing, you may also declare `jakarta.servlet.http.Part` |
|
instead of Spring's `MultipartFile`, as a method argument or collection value type. |
|
|
|
You can also use multipart content as part of data binding to a |
|
<<mvc-ann-modelattrib-method-args, command object>>. For example, the form field |
|
and file from the preceding example could be fields on a form object, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
class MyForm { |
|
|
|
private String name; |
|
|
|
private MultipartFile file; |
|
|
|
// ... |
|
} |
|
|
|
@Controller |
|
public class FileUploadController { |
|
|
|
@PostMapping("/form") |
|
public String handleFormUpload(MyForm form, BindingResult errors) { |
|
if (!form.getFile().isEmpty()) { |
|
byte[] bytes = form.getFile().getBytes(); |
|
// store the bytes somewhere |
|
return "redirect:uploadSuccess"; |
|
} |
|
return "redirect:uploadFailure"; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
class MyForm(val name: String, val file: MultipartFile, ...) |
|
|
|
@Controller |
|
class FileUploadController { |
|
|
|
@PostMapping("/form") |
|
fun handleFormUpload(form: MyForm, errors: BindingResult): String { |
|
if (!form.file.isEmpty) { |
|
val bytes = form.file.bytes |
|
// store the bytes somewhere |
|
return "redirect:uploadSuccess" |
|
} |
|
return "redirect:uploadFailure" |
|
} |
|
} |
|
---- |
|
|
|
|
|
Multipart requests can also be submitted from non-browser clients in a RESTful service |
|
scenario. The following example shows a file 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",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/") |
|
public String handle(@RequestPart("meta-data") MetaData metadata, |
|
@RequestPart("file-data") MultipartFile file) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/") |
|
fun handle(@RequestPart("meta-data") metadata: MetaData, |
|
@RequestPart("file-data") file: MultipartFile): String { |
|
// ... |
|
} |
|
---- |
|
|
|
You can use `@RequestPart` in combination with `jakarta.validation.Valid` or use Spring's |
|
`@Validated` annotation, both of which cause Standard Bean Validation to be applied. |
|
By default, validation errors cause a `MethodArgumentNotValidException`, which is turned |
|
into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation errors locally |
|
within the controller through an `Errors` or `BindingResult` argument, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/") |
|
public String handle(@Valid @RequestPart("meta-data") MetaData metadata, |
|
BindingResult result) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/") |
|
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData, |
|
result: BindingResult): String { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-ann-requestbody]] |
|
==== `@RequestBody` |
|
[.small]#<<web-reactive.adoc#webflux-ann-requestbody, WebFlux>># |
|
|
|
You can use the `@RequestBody` annotation to have the request body read and deserialized into an |
|
`Object` through an <<integration.adoc#rest-message-conversion, `HttpMessageConverter`>>. |
|
The following example uses a `@RequestBody` argument: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/accounts") |
|
public void handle(@RequestBody Account account) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/accounts") |
|
fun handle(@RequestBody account: Account) { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
You can use the <<mvc-config-message-converters>> option of the <<mvc-config>> to |
|
configure or customize message conversion. |
|
|
|
You can use `@RequestBody` in combination with `jakarta.validation.Valid` or Spring's |
|
`@Validated` annotation, both of which cause Standard Bean Validation to be applied. |
|
By default, validation errors cause a `MethodArgumentNotValidException`, which is turned |
|
into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation errors locally |
|
within the controller through an `Errors` or `BindingResult` argument, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/accounts") |
|
public void handle(@Valid @RequestBody Account account, BindingResult result) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/accounts") |
|
fun handle(@Valid @RequestBody account: Account, result: BindingResult) { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
[[mvc-ann-httpentity]] |
|
==== HttpEntity |
|
[.small]#<<web-reactive.adoc#webflux-ann-httpentity, WebFlux>># |
|
|
|
`HttpEntity` is more or less identical to using <<mvc-ann-requestbody>> but is based on a |
|
container object that exposes request headers and body. The following listing shows an example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping("/accounts") |
|
public void handle(HttpEntity<Account> entity) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping("/accounts") |
|
fun handle(entity: HttpEntity<Account>) { |
|
// ... |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-ann-responsebody]] |
|
==== `@ResponseBody` |
|
[.small]#<<web-reactive.adoc#webflux-ann-responsebody, WebFlux>># |
|
|
|
You can use the `@ResponseBody` annotation on a method to have the return serialized |
|
to the response body through an |
|
<<integration.adoc#rest-message-conversion, HttpMessageConverter>>. |
|
The following listing shows an example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/accounts/{id}") |
|
@ResponseBody |
|
public Account handle() { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/accounts/{id}") |
|
@ResponseBody |
|
fun handle(): Account { |
|
// ... |
|
} |
|
---- |
|
|
|
`@ResponseBody` is also supported at the class level, in which case it is inherited by |
|
all controller methods. This is the effect of `@RestController`, which is nothing more |
|
than a meta-annotation marked with `@Controller` and `@ResponseBody`. |
|
|
|
You can use `@ResponseBody` 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. |
|
|
|
You can combine `@ResponseBody` methods with JSON serialization views. |
|
See <<mvc-ann-jackson>> for details. |
|
|
|
|
|
[[mvc-ann-responseentity]] |
|
==== ResponseEntity |
|
[.small]#<<web-reactive.adoc#webflux-ann-responseentity, WebFlux>># |
|
|
|
`ResponseEntity` is like <<mvc-ann-responsebody>> but with status and headers. For example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/something") |
|
public ResponseEntity<String> handle() { |
|
String body = ... ; |
|
String etag = ... ; |
|
return ResponseEntity.ok().eTag(etag).body(body); |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/something") |
|
fun handle(): ResponseEntity<String> { |
|
val body = ... |
|
val etag = ... |
|
return ResponseEntity.ok().eTag(etag).build(body) |
|
} |
|
---- |
|
|
|
Spring MVC supports using a single value <<mvc-ann-async-reactive-types, reactive type>> |
|
to produce the `ResponseEntity` asynchronously, and/or single and multi-value reactive |
|
types for the body. This allows the following types of async responses: |
|
|
|
* `ResponseEntity<Mono<T>>` or `ResponseEntity<Flux<T>>` make the response status and |
|
headers known immediately while the body is provided asynchronously at a later point. |
|
Use `Mono` if the body consists of 0..1 values or `Flux` if it can produce multiple values. |
|
* `Mono<ResponseEntity<T>>` provides all three -- response status, headers, and body, |
|
asynchronously at a later point. This allows the response status and headers to vary |
|
depending on the outcome of asynchronous request handling. |
|
|
|
|
|
[[mvc-ann-jackson]] |
|
==== Jackson JSON |
|
|
|
Spring offers support for the Jackson JSON library. |
|
|
|
[[mvc-ann-jsonview]] |
|
===== JSON Views |
|
[.small]#<<web-reactive.adoc#webflux-ann-jsonview, WebFlux>># |
|
|
|
Spring MVC provides built-in support for |
|
https://www.baeldung.com/jackson-json-view-annotation[Jackson's Serialization Views], |
|
which allow rendering only a subset of all fields in an `Object`. To use it with |
|
`@ResponseBody` or `ResponseEntity` controller methods, you can use Jackson's |
|
`@JsonView` annotation to activate a serialization view class, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@RestController |
|
public class UserController { |
|
|
|
@GetMapping("/user") |
|
@JsonView(User.WithoutPasswordView.class) |
|
public User getUser() { |
|
return new User("eric", "7!jd#h23"); |
|
} |
|
} |
|
|
|
public class User { |
|
|
|
public interface WithoutPasswordView {}; |
|
public interface WithPasswordView extends WithoutPasswordView {}; |
|
|
|
private String username; |
|
private String password; |
|
|
|
public User() { |
|
} |
|
|
|
public User(String username, String password) { |
|
this.username = username; |
|
this.password = password; |
|
} |
|
|
|
@JsonView(WithoutPasswordView.class) |
|
public String getUsername() { |
|
return this.username; |
|
} |
|
|
|
@JsonView(WithPasswordView.class) |
|
public String getPassword() { |
|
return this.password; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RestController |
|
class UserController { |
|
|
|
@GetMapping("/user") |
|
@JsonView(User.WithoutPasswordView::class) |
|
fun getUser() = User("eric", "7!jd#h23") |
|
} |
|
|
|
class User( |
|
@JsonView(WithoutPasswordView::class) val username: String, |
|
@JsonView(WithPasswordView::class) val password: String) { |
|
|
|
interface WithoutPasswordView |
|
interface WithPasswordView : WithoutPasswordView |
|
} |
|
---- |
|
|
|
NOTE: `@JsonView` allows an array of view classes, but you can specify only one per |
|
controller method. If you need to activate multiple views, you can use a composite interface. |
|
|
|
If you want to do the above programmatically, instead of declaring an `@JsonView` annotation, |
|
wrap the return value with `MappingJacksonValue` and use it to supply the serialization view: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@RestController |
|
public class UserController { |
|
|
|
@GetMapping("/user") |
|
public MappingJacksonValue getUser() { |
|
User user = new User("eric", "7!jd#h23"); |
|
MappingJacksonValue value = new MappingJacksonValue(user); |
|
value.setSerializationView(User.WithoutPasswordView.class); |
|
return value; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RestController |
|
class UserController { |
|
|
|
@GetMapping("/user") |
|
fun getUser(): MappingJacksonValue { |
|
val value = MappingJacksonValue(User("eric", "7!jd#h23")) |
|
value.serializationView = User.WithoutPasswordView::class.java |
|
return value |
|
} |
|
} |
|
---- |
|
|
|
For controllers that rely on view resolution, you can add the serialization view class |
|
to the model, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
public class UserController extends AbstractController { |
|
|
|
@GetMapping("/user") |
|
public String getUser(Model model) { |
|
model.addAttribute("user", new User("eric", "7!jd#h23")); |
|
model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class); |
|
return "userView"; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
import org.springframework.ui.set |
|
|
|
@Controller |
|
class UserController : AbstractController() { |
|
|
|
@GetMapping("/user") |
|
fun getUser(model: Model): String { |
|
model["user"] = User("eric", "7!jd#h23") |
|
model[JsonView::class.qualifiedName] = User.WithoutPasswordView::class.java |
|
return "userView" |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-ann-modelattrib-methods]] |
|
=== Model |
|
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-methods, WebFlux>># |
|
|
|
You can use the `@ModelAttribute` annotation: |
|
|
|
* On a <<mvc-ann-modelattrib-method-args,method argument>> in `@RequestMapping` methods |
|
to create or access an `Object` from the model and to bind it to the request through a |
|
`WebDataBinder`. |
|
* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes that help |
|
to initialize the model prior to any `@RequestMapping` method invocation. |
|
* On a `@RequestMapping` method to mark its return value is a model attribute. |
|
|
|
This section discusses `@ModelAttribute` methods -- the second item in the preceding list. |
|
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 through `@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 or anything |
|
related to the request body. |
|
|
|
The following example shows a `@ModelAttribute` method: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ModelAttribute |
|
public void populateModel(@RequestParam String number, Model model) { |
|
model.addAttribute(accountRepository.findAccount(number)); |
|
// add more ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ModelAttribute |
|
fun populateModel(@RequestParam number: String, model: Model) { |
|
model.addAttribute(accountRepository.findAccount(number)) |
|
// add more ... |
|
} |
|
---- |
|
|
|
The following example adds only one attribute: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ModelAttribute |
|
public Account addAccount(@RequestParam String number) { |
|
return accountRepository.findAccount(number); |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ModelAttribute |
|
fun addAccount(@RequestParam number: String): Account { |
|
return accountRepository.findAccount(number) |
|
} |
|
---- |
|
|
|
|
|
NOTE: When a name is not explicitly specified, a default name is chosen based on the `Object` |
|
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). |
|
|
|
You can also use `@ModelAttribute` 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` that would otherwise be interpreted as a view name. |
|
`@ModelAttribute` can also customize the model attribute name, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/accounts/{id}") |
|
@ModelAttribute("myAccount") |
|
public Account handle() { |
|
// ... |
|
return account; |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/accounts/{id}") |
|
@ModelAttribute("myAccount") |
|
fun handle(): Account { |
|
// ... |
|
return account |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-ann-initbinder]] |
|
=== `DataBinder` |
|
[.small]#<<web-reactive.adoc#webflux-ann-initbinder, WebFlux>># |
|
|
|
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods that |
|
initialize instances of `WebDataBinder`, and those, in turn, can: |
|
|
|
* Bind request parameters (that is, form or query data) to a model object. |
|
* Convert String-based request values (such as request parameters, path variables, |
|
headers, cookies, and others) to the target type of controller method arguments. |
|
* Format model object values as `String` values when rendering HTML forms. |
|
|
|
`@InitBinder` methods can register controller-specific `java.beans.PropertyEditor` or |
|
Spring `Converter` and `Formatter` components. In addition, you can use the |
|
<<mvc-config-conversion,MVC config>> to register `Converter` and `Formatter` |
|
types in a globally shared `FormattingConversionService`. |
|
|
|
`@InitBinder` methods support many of the same arguments that `@RequestMapping` methods |
|
do, except for `@ModelAttribute` (command object) arguments. Typically, they are declared |
|
with a `WebDataBinder` argument (for registrations) and a `void` return value. |
|
The following listing shows an example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
public class FormController { |
|
|
|
@InitBinder // <1> |
|
public void initBinder(WebDataBinder binder) { |
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
|
dateFormat.setLenient(false); |
|
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
<1> Defining an `@InitBinder` method. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
class FormController { |
|
|
|
@InitBinder // <1> |
|
fun initBinder(binder: WebDataBinder) { |
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd") |
|
dateFormat.isLenient = false |
|
binder.registerCustomEditor(Date::class.java, CustomDateEditor(dateFormat, false)) |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
<1> Defining an `@InitBinder` method. |
|
|
|
Alternatively, when you use a `Formatter`-based setup through a shared |
|
`FormattingConversionService`, you can re-use the same approach and register |
|
controller-specific `Formatter` implementations, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
public class FormController { |
|
|
|
@InitBinder // <1> |
|
protected void initBinder(WebDataBinder binder) { |
|
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
<1> Defining an `@InitBinder` method on a custom formatter. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
class FormController { |
|
|
|
@InitBinder // <1> |
|
protected fun initBinder(binder: WebDataBinder) { |
|
binder.addCustomFormatter(DateFormatter("yyyy-MM-dd")) |
|
} |
|
|
|
// ... |
|
} |
|
---- |
|
<1> Defining an `@InitBinder` method on a custom formatter. |
|
|
|
[[mvc-ann-initbinder-model-design]] |
|
==== Model Design |
|
[.small]#<<web-reactive.adoc#webflux-ann-initbinder-model-design, WebFlux>># |
|
|
|
include::web-data-binding-model-design.adoc[] |
|
|
|
|
|
[[mvc-ann-exceptionhandler]] |
|
=== Exceptions |
|
[.small]#<<web-reactive.adoc#webflux-ann-controller-exceptions, WebFlux>># |
|
|
|
`@Controller` and <<mvc-ann-controller-advice, @ControllerAdvice>> classes can have |
|
`@ExceptionHandler` methods to handle exceptions from controller methods, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
public class SimpleController { |
|
|
|
// ... |
|
|
|
@ExceptionHandler |
|
public ResponseEntity<String> handle(IOException ex) { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
class SimpleController { |
|
|
|
// ... |
|
|
|
@ExceptionHandler |
|
fun handle(ex: IOException): ResponseEntity<String> { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
The exception may match against a top-level exception being propagated (e.g. a direct |
|
`IOException` being thrown) or against a nested cause within a wrapper exception (e.g. |
|
an `IOException` wrapped inside an `IllegalStateException`). As of 5.3, this can match |
|
at arbitrary cause levels, whereas previously only an immediate cause was considered. |
|
|
|
For matching exception types, preferably declare the target exception as a method argument, |
|
as the preceding example shows. When multiple exception methods match, a root exception match is |
|
generally preferred to a cause exception match. More specifically, the `ExceptionDepthComparator` |
|
is used to sort exceptions based on their depth from the thrown exception type. |
|
|
|
Alternatively, the annotation declaration may narrow the exception types to match, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExceptionHandler({FileSystemException.class, RemoteException.class}) |
|
public ResponseEntity<String> handle(IOException ex) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExceptionHandler(FileSystemException::class, RemoteException::class) |
|
fun handle(ex: IOException): ResponseEntity<String> { |
|
// ... |
|
} |
|
---- |
|
|
|
You can even use a list of specific exception types with a very generic argument signature, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@ExceptionHandler({FileSystemException.class, RemoteException.class}) |
|
public ResponseEntity<String> handle(Exception ex) { |
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@ExceptionHandler(FileSystemException::class, RemoteException::class) |
|
fun handle(ex: Exception): ResponseEntity<String> { |
|
// ... |
|
} |
|
---- |
|
|
|
[NOTE] |
|
==== |
|
The distinction between root and cause exception matching can be surprising. |
|
|
|
In the `IOException` variant shown earlier, the method is typically called with |
|
the actual `FileSystemException` or `RemoteException` instance as the argument, |
|
since both of them extend from `IOException`. However, if any such matching |
|
exception is propagated within a wrapper exception which is itself an `IOException`, |
|
the passed-in exception instance is that wrapper exception. |
|
|
|
The behavior is even simpler in the `handle(Exception)` variant. This is |
|
always invoked with the wrapper exception in a wrapping scenario, with the |
|
actually matching exception to be found through `ex.getCause()` in that case. |
|
The passed-in exception is the actual `FileSystemException` or |
|
`RemoteException` instance only when these are thrown as top-level exceptions. |
|
==== |
|
|
|
We generally recommend that you be as specific as possible in the argument signature, |
|
reducing the potential for mismatches between root and cause exception types. |
|
Consider breaking a multi-matching method into individual `@ExceptionHandler` |
|
methods, each matching a single specific exception type through its signature. |
|
|
|
In a multi-`@ControllerAdvice` arrangement, we recommend declaring your primary root exception |
|
mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root |
|
exception match is preferred to a cause, this is defined among the methods of a given |
|
controller or `@ControllerAdvice` class. This means a cause match on a higher-priority |
|
`@ControllerAdvice` bean is preferred to any match (for example, root) on a lower-priority |
|
`@ControllerAdvice` bean. |
|
|
|
Last but not least, an `@ExceptionHandler` method implementation can choose to back |
|
out of dealing with a given exception instance by rethrowing it in its original form. |
|
This is useful in scenarios where you are interested only in root-level matches or in |
|
matches within a specific context that cannot be statically determined. A rethrown |
|
exception is propagated through the remaining resolution chain, as though |
|
the given `@ExceptionHandler` method would not have matched in the first place. |
|
|
|
Support for `@ExceptionHandler` methods in Spring MVC is built on the `DispatcherServlet` |
|
level, <<mvc-exceptionhandlers, HandlerExceptionResolver>> mechanism. |
|
|
|
|
|
[[mvc-ann-exceptionhandler-args]] |
|
==== Method Arguments |
|
[.small]#<<webflux.adoc#webflux-ann-exceptionhandler-args, WebFlux>># |
|
|
|
`@ExceptionHandler` methods support the following arguments: |
|
|
|
[cols="1,2", options="header"] |
|
|=== |
|
| Method argument | Description |
|
|
|
| Exception type |
|
| For access to the raised exception. |
|
|
|
| `HandlerMethod` |
|
| For access to the controller method that raised the exception. |
|
|
|
| `WebRequest`, `NativeWebRequest` |
|
| Generic access to request parameters and request and session attributes without direct |
|
use of the Servlet API. |
|
|
|
| `jakarta.servlet.ServletRequest`, `jakarta.servlet.ServletResponse` |
|
| Choose any specific request or response type (for example, `ServletRequest` or |
|
`HttpServletRequest` or Spring's `MultipartRequest` or `MultipartHttpServletRequest`). |
|
|
|
| `jakarta.servlet.http.HttpSession` |
|
| Enforces the presence of a session. As a consequence, such an argument is never `null`. + |
|
Note that session access is not thread-safe. Consider setting the |
|
`RequestMappingHandlerAdapter` instance's `synchronizeOnSession` flag to `true` if multiple |
|
requests are allowed to access a session concurrently. |
|
|
|
| `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` or `LocaleContextResolver`. |
|
|
|
| `java.util.TimeZone`, `java.time.ZoneId` |
|
| The time zone associated with the current request, as determined by a `LocaleContextResolver`. |
|
|
|
| `java.io.OutputStream`, `java.io.Writer` |
|
| For access to the raw response body, as exposed by the Servlet API. |
|
|
|
| `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap` |
|
| For access to the model for an error response. Always empty. |
|
|
|
| `RedirectAttributes` |
|
| Specify attributes to use in case of a redirect -- (that is to be appended to the query |
|
string) and flash attributes to be stored temporarily until the request after the redirect. |
|
See <<mvc-redirecting-passing-data>> and <<mvc-flash-attributes>>. |
|
|
|
| `@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. |
|
|
|
|=== |
|
|
|
|
|
[[mvc-ann-exceptionhandler-return-values]] |
|
==== Return Values |
|
[.small]#<<webflux.adoc#webflux-ann-exceptionhandler-return-values, WebFlux>># |
|
|
|
`@ExceptionHandler` methods support the following return values: |
|
|
|
[cols="1,2", options="header"] |
|
|=== |
|
| Return value | Description |
|
|
|
| `@ResponseBody` |
|
| The return value is converted through `HttpMessageConverter` instances and written to the |
|
response. See <<mvc-ann-responsebody>>. |
|
|
|
| `HttpEntity<B>`, `ResponseEntity<B>` |
|
| The return value specifies that the full response (including the HTTP headers and the body) |
|
be converted through `HttpMessageConverter` instances and written to the response. |
|
See <<mvc-ann-responseentity>>. |
|
|
|
| `ErrorResponse` |
|
| To render an RFC 7807 error response with details in the body, |
|
see <<mvc-ann-rest-exceptions>> |
|
|
|
| `ProblemDetail` |
|
| To render an RFC 7807 error response with details in the body, |
|
see <<mvc-ann-rest-exceptions>> |
|
|
|
| `String` |
|
| A view name to be resolved with `ViewResolver` implementations and used together with the |
|
implicit model -- determined through command objects and `@ModelAttribute` methods. |
|
The handler method can also programmatically enrich the model by declaring a `Model` |
|
argument (described earlier). |
|
|
|
| `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 (descried earlier). |
|
|
|
| `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`" at the end of |
|
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` an `OutputStream` argument, or |
|
a `@ResponseStatus` annotation. The same is also true 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 can also indicate "`no response body`" for |
|
REST controllers or default view name selection for HTML controllers. |
|
|
|
| Any other return value |
|
| If a return value is not matched to any of the above and is not a simple type (as determined by |
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]), |
|
by default, it is treated as a model attribute to be added to the model. If it is a simple type, |
|
it remains unresolved. |
|
|=== |
|
|
|
|
|
|
|
[[mvc-ann-controller-advice]] |
|
=== Controller Advice |
|
[.small]#<<web-reactive.adoc#webflux-ann-controller-advice, WebFlux>># |
|
|
|
`@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply only to the |
|
`@Controller` class, or class hierarchy, in which they are declared. If, instead, they |
|
are declared in an `@ControllerAdvice` or `@RestControllerAdvice` class, then they apply |
|
to any controller. Moreover, as of 5.3, `@ExceptionHandler` methods in `@ControllerAdvice` |
|
can be used to handle exceptions from any `@Controller` or any other handler. |
|
|
|
`@ControllerAdvice` is meta-annotated with `@Component` and therefore can be registered as |
|
a Spring bean through <<core.adoc#beans-java-instantiating-container-scan, |
|
component scanning>>. `@RestControllerAdvice` is meta-annotated with `@ControllerAdvice` |
|
and `@ResponseBody`, and that means `@ExceptionHandler` methods will have their return |
|
value rendered via response body message conversion, rather than via HTML views. |
|
|
|
On startup, `RequestMappingHandlerMapping` and `ExceptionHandlerExceptionResolver` detect |
|
controller advice beans and apply them 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. |
|
|
|
The `@ControllerAdvice` annotation has attributes that let you narrow the set of controllers |
|
and handlers that they apply to. For example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// 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 {} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Target all Controllers annotated with @RestController |
|
@ControllerAdvice(annotations = [RestController::class]) |
|
class ExampleAdvice1 |
|
|
|
// Target all Controllers within specific packages |
|
@ControllerAdvice("org.example.controllers") |
|
class ExampleAdvice2 |
|
|
|
// Target all Controllers assignable to specific classes |
|
@ControllerAdvice(assignableTypes = [ControllerInterface::class, AbstractController::class]) |
|
class ExampleAdvice3 |
|
---- |
|
|
|
The selectors in the preceding example are evaluated at runtime and may negatively impact |
|
performance if used extensively. See the |
|
{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[`@ControllerAdvice`] |
|
javadoc for more details. |
|
|
|
include::webmvc-functional.adoc[leveloffset=+1] |
|
|
|
|
|
|
|
[[mvc-uri-building]] |
|
== URI Links |
|
[.small]#<<web-reactive.adoc#webflux-uri-building, WebFlux>># |
|
|
|
This section describes various options available in the Spring Framework to work with URI's. |
|
|
|
include::web-uris.adoc[leveloffset=+2] |
|
|
|
|
|
|
|
[[mvc-servleturicomponentsbuilder]] |
|
=== Relative Servlet Requests |
|
|
|
You can use `ServletUriComponentsBuilder` to create URIs relative to the current request, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
HttpServletRequest request = ... |
|
|
|
// Re-uses scheme, host, port, path, and query string... |
|
|
|
URI uri = ServletUriComponentsBuilder.fromRequest(request) |
|
.replaceQueryParam("accountId", "{id}") |
|
.build("123"); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
val request: HttpServletRequest = ... |
|
|
|
// Re-uses scheme, host, port, path, and query string... |
|
|
|
val uri = ServletUriComponentsBuilder.fromRequest(request) |
|
.replaceQueryParam("accountId", "{id}") |
|
.build("123") |
|
---- |
|
|
|
You can create URIs relative to the context path, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
HttpServletRequest request = ... |
|
|
|
// Re-uses scheme, host, port, and context path... |
|
|
|
URI uri = ServletUriComponentsBuilder.fromContextPath(request) |
|
.path("/accounts") |
|
.build() |
|
.toUri(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
val request: HttpServletRequest = ... |
|
|
|
// Re-uses scheme, host, port, and context path... |
|
|
|
val uri = ServletUriComponentsBuilder.fromContextPath(request) |
|
.path("/accounts") |
|
.build() |
|
.toUri() |
|
---- |
|
|
|
You can create URIs relative to a Servlet (for example, `/main/{asterisk}`), |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
HttpServletRequest request = ... |
|
|
|
// Re-uses scheme, host, port, context path, and Servlet mapping prefix... |
|
|
|
URI uri = ServletUriComponentsBuilder.fromServletMapping(request) |
|
.path("/accounts") |
|
.build() |
|
.toUri(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
val request: HttpServletRequest = ... |
|
|
|
// Re-uses scheme, host, port, context path, and Servlet mapping prefix... |
|
|
|
val uri = ServletUriComponentsBuilder.fromServletMapping(request) |
|
.path("/accounts") |
|
.build() |
|
.toUri() |
|
---- |
|
|
|
NOTE: As of 5.1, `ServletUriComponentsBuilder` ignores information from the `Forwarded` and |
|
`X-Forwarded-*` headers, which specify the client-originated address. Consider using the |
|
<<filters-forwarded-headers, `ForwardedHeaderFilter`>> to extract and use or to discard |
|
such headers. |
|
|
|
|
|
|
|
[[mvc-links-to-controllers]] |
|
=== Links to Controllers |
|
|
|
Spring MVC provides a mechanism to prepare links to controller methods. For example, |
|
the following MVC controller allows for link creation: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
@RequestMapping("/hotels/{hotel}") |
|
public class BookingController { |
|
|
|
@GetMapping("/bookings/{booking}") |
|
public ModelAndView getBooking(@PathVariable Long booking) { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
@RequestMapping("/hotels/{hotel}") |
|
class BookingController { |
|
|
|
@GetMapping("/bookings/{booking}") |
|
fun getBooking(@PathVariable booking: Long): ModelAndView { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
You can prepare a link by referring to the method by name, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
UriComponents uriComponents = MvcUriComponentsBuilder |
|
.fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42); |
|
|
|
URI uri = uriComponents.encode().toUri(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
val uriComponents = MvcUriComponentsBuilder |
|
.fromMethodName(BookingController::class.java, "getBooking", 21).buildAndExpand(42) |
|
|
|
val uri = uriComponents.encode().toUri() |
|
---- |
|
|
|
In the preceding example, we provide actual method argument values (in this case, the long value: `21`) |
|
to be used as a path variable and inserted into the URL. Furthermore, we provide the |
|
value, `42`, 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, we could 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, as the following example shows |
|
(the example assumes static import of `MvcUriComponentsBuilder.on`): |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
UriComponents uriComponents = MvcUriComponentsBuilder |
|
.fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42); |
|
|
|
URI uri = uriComponents.encode().toUri(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
val uriComponents = MvcUriComponentsBuilder |
|
.fromMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42) |
|
|
|
val uri = uriComponents.encode().toUri() |
|
---- |
|
|
|
NOTE: Controller method signatures are limited in their design when they are supposed to be usable for |
|
link creation with `fromMethodCall`. Aside from needing a proper parameter signature, |
|
there is a technical limitation on the return type (namely, generating a runtime proxy |
|
for link builder invocations), so the return type must not be `final`. In particular, |
|
the common `String` return type for view names does not work here. You should use `ModelAndView` |
|
or even plain `Object` (with a `String` return value) instead. |
|
|
|
The earlier 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 can be insufficient. For example, you may be outside the context of |
|
a request (such as a batch process that prepares links) or perhaps you need to insert a path |
|
prefix (such as 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 a base URL. Alternatively, you can create an instance of `MvcUriComponentsBuilder` |
|
with a base URL and then use the instance-based `withXxx` methods. For example, the |
|
following listing uses `withMethodCall`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
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(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
val base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en") |
|
val builder = MvcUriComponentsBuilder.relativeTo(base) |
|
builder.withMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42) |
|
|
|
val uri = uriComponents.encode().toUri() |
|
---- |
|
|
|
NOTE: As of 5.1, `MvcUriComponentsBuilder` ignores information from the `Forwarded` and |
|
`X-Forwarded-*` headers, which specify the client-originated address. Consider using the |
|
<<filters-forwarded-headers, ForwardedHeaderFilter>> to extract and use or to discard |
|
such headers. |
|
|
|
|
|
|
|
[[mvc-links-to-controllers-from-views]] |
|
=== Links in Views |
|
|
|
In views such as Thymeleaf, FreeMarker, or JSP, you can build links to annotated controllers |
|
by referring to the implicitly or explicitly assigned name for each request mapping. |
|
|
|
Consider the following example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@RequestMapping("/people/{id}/addresses") |
|
public class PersonAddressController { |
|
|
|
@RequestMapping("/{country}") |
|
public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... } |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RequestMapping("/people/{id}/addresses") |
|
class PersonAddressController { |
|
|
|
@RequestMapping("/{country}") |
|
fun getAddress(@PathVariable country: String): HttpEntity<PersonAddress> { ... } |
|
} |
|
---- |
|
|
|
Given the preceding controller, 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 preceding example relies on the `mvcUrl` function declared in the Spring tag library |
|
(that is, META-INF/spring.tld), but it is easy to define your own function or prepare a |
|
similar one for other templating technologies. |
|
|
|
Here is how this works. On startup, every `@RequestMapping` is assigned a default name |
|
through `HandlerMethodMappingNamingStrategy`, whose default implementation uses the |
|
capital letters of the class and the method name (for example, the `getThing` method in |
|
`ThingController` becomes "TC#getThing"). If there is a name clash, you can use |
|
`@RequestMapping(name="..")` to assign an explicit name or implement your own |
|
`HandlerMethodMappingNamingStrategy`. |
|
|
|
|
|
|
|
|
|
[[mvc-ann-async]] |
|
== Asynchronous Requests |
|
[.small]#<<mvc-ann-async-vs-webflux, Compared to WebFlux>># |
|
|
|
Spring MVC has an extensive integration with Servlet asynchronous request |
|
<<mvc-ann-async-processing,processing>>: |
|
|
|
* <<mvc-ann-async-deferredresult, `DeferredResult`>> and <<mvc-ann-async-callable, `Callable`>> |
|
return values in controller methods 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`, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@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(result); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/quotes") |
|
@ResponseBody |
|
fun quotes(): DeferredResult<String> { |
|
val deferredResult = DeferredResult<String>() |
|
// Save the deferredResult somewhere.. |
|
return deferredResult |
|
} |
|
|
|
// From some other thread... |
|
deferredResult.setResult(result) |
|
---- |
|
|
|
The controller can produce the return value asynchronously, from a different thread -- for |
|
example, in response to an external event (JMS message), a scheduled task, or other event. |
|
|
|
|
|
|
|
[[mvc-ann-async-callable]] |
|
=== `Callable` |
|
[.small]#<<mvc-ann-async-vs-webflux, Compared to WebFlux>># |
|
|
|
A controller can wrap any supported return value with `java.util.concurrent.Callable`, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@PostMapping |
|
public Callable<String> processUpload(final MultipartFile file) { |
|
|
|
return new Callable<String>() { |
|
public String call() throws Exception { |
|
// ... |
|
return "someView"; |
|
} |
|
}; |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@PostMapping |
|
fun processUpload(file: MultipartFile) = Callable<String> { |
|
// ... |
|
"someView" |
|
} |
|
---- |
|
|
|
The return value can then be obtained by running 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 remains open to let processing complete later. |
|
* The call to `request.startAsync()` returns `AsyncContext`, which you can use for |
|
further control over asynchronous processing. For example, it provides the `dispatch` method, |
|
which is similar to a forward from the Servlet API, except that it lets an |
|
application resume request processing on a Servlet container thread. |
|
* The `ServletRequest` provides access to the current `DispatcherType`, which you can |
|
use to distinguish between processing the initial request, an asynchronous |
|
dispatch, a forward, and other dispatcher types. |
|
|
|
`DeferredResult` processing works as follows: |
|
|
|
* The 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 filters 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 works as follows: |
|
|
|
* The 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 filters 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 you use 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 (for example, invoking |
|
`@ExceptionHandler` methods). |
|
|
|
When you use `Callable`, similar processing logic occurs, 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` instances can be of type `AsyncHandlerInterceptor`, to receive the |
|
`afterConcurrentHandlingStarted` callback on the initial request that starts asynchronous |
|
processing (instead of `postHandle` and `afterCompletion`). |
|
|
|
`HandlerInterceptor` implementations can also register a `CallableProcessingInterceptor` |
|
or a `DeferredResultProcessingInterceptor`, 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 {api-spring-framework}/web/context/request/async/DeferredResult.html[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 lets applications exit the Filter-Servlet chain |
|
but leave the response open for further processing. The Spring MVC asynchronous 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 all |
|
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 are performed on a separate thread), unlike WebFlux, |
|
which 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 (for example, `@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, WebFlux>># |
|
|
|
You can use `DeferredResult` and `Callable` for a single asynchronous return value. |
|
What if you want to produce multiple asynchronous values and have those written to the |
|
response? This section describes how to do so. |
|
|
|
|
|
[[mvc-ann-async-objects]] |
|
==== Objects |
|
|
|
You can use the `ResponseBodyEmitter` return value to produce a stream of objects, where |
|
each object is serialized with an |
|
<<integration.adoc#rest-message-conversion, `HttpMessageConverter`>> and written to the |
|
response, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@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(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/events") |
|
fun handle() = ResponseBodyEmitter().apply { |
|
// Save the emitter somewhere.. |
|
} |
|
|
|
// In some other thread |
|
emitter.send("Hello once") |
|
|
|
// and again later on |
|
emitter.send("Hello again") |
|
|
|
// and done at some point |
|
emitter.complete() |
|
---- |
|
|
|
You can also use `ResponseBodyEmitter` as the body in a `ResponseEntity`, letting you |
|
customize the status and headers of the response. |
|
|
|
When an `emitter` throws an `IOException` (for example, 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. |
|
This call, in turn, performs one 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` (a subclass of `ResponseBodyEmitter`) provides support for |
|
https://www.w3.org/TR/eventsource/[Server-Sent Events], where events sent from the server |
|
are formatted according to the W3C SSE specification. To produce an SSE |
|
stream from a controller, return `SseEmitter`, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@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(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/events", produces = [MediaType.TEXT_EVENT_STREAM_VALUE]) |
|
fun handle() = SseEmitter().apply { |
|
// Save the emitter somewhere.. |
|
} |
|
|
|
// 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. |
|
|
|
See also <<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). You can use the `StreamingResponseBody` |
|
return value type to do so, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@GetMapping("/download") |
|
public StreamingResponseBody handle() { |
|
return new StreamingResponseBody() { |
|
@Override |
|
public void writeTo(OutputStream outputStream) throws IOException { |
|
// write... |
|
} |
|
}; |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/download") |
|
fun handle() = StreamingResponseBody { |
|
// write... |
|
} |
|
---- |
|
|
|
You can use `StreamingResponseBody` as the body in a `ResponseEntity` to |
|
customize the status and headers of the response. |
|
|
|
|
|
|
|
[[mvc-ann-async-reactive-types]] |
|
=== Reactive Types |
|
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, WebFlux>># |
|
|
|
Spring MVC supports use of reactive client libraries in a controller (also read |
|
<<web-reactive.adoc#webflux-reactive-libraries, Reactive Libraries>> in the WebFlux section). |
|
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, similar to using `DeferredResult`. Examples |
|
include `Mono` (Reactor) or `Single` (RxJava). |
|
* A multi-value stream with a streaming media type (such as `application/x-ndjson` |
|
or `text/event-stream`) is adapted to, 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 (such as `application/json`) is adapted |
|
to, 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 lets it adapt from multiple reactive libraries. |
|
|
|
For streaming to the response, reactive back pressure is supported, but writes to the |
|
response are still blocking and are run on a separate thread through the |
|
<<mvc-ann-async-configuration-spring-mvc, configured>> `TaskExecutor`, to avoid |
|
blocking the upstream source (such as a `Flux` returned from `WebClient`). |
|
By default, `SimpleAsyncTaskExecutor` is used for the blocking writes, but that is not |
|
suitable under load. If you plan to stream with a reactive type, you should use the |
|
<<mvc-ann-async-configuration-spring-mvc, MVC configuration>> to configure a task executor. |
|
|
|
|
|
|
|
[[mvc-ann-async-context-propagation]] |
|
=== Context Propagation |
|
|
|
It is common to propagate context via `java.lang.ThreadLocal`. This works transparently |
|
for handling on the same thread, but requires additional work for asynchronous handling |
|
across multiple threads. The Micrometer |
|
https://github.com/micrometer-metrics/context-propagation#context-propagation-library[Context Propagation] |
|
library simplifies context propagation across threads, and across context mechanisms such |
|
as `ThreadLocal` values, |
|
Reactor https://projectreactor.io/docs/core/release/reference/#context[context], |
|
GraphQL Java https://www.graphql-java.com/documentation/concerns/#context-objects[context], |
|
and others. |
|
|
|
If Micrometer Context Propagation is present on the classpath, when a controller method |
|
returns a <<mvc-ann-async-reactive-types,reactive type>> such as `Flux` or `Mono`, all |
|
`ThreadLocal` values, for which there is a registered `io.micrometer.ThreadLocalAccessor`, |
|
are written to the Reactor `Context` as key-value pairs, using the key assigned by the |
|
`ThreadLocalAccessor`. |
|
|
|
For other asynchronous handling scenarios, you can use the Context Propagation library |
|
directly. For example: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// Capture ThreadLocal values from the main thread ... |
|
ContextSnapshot snapshot = ContextSnapshot.captureAll(); |
|
|
|
// On a different thread: restore ThreadLocal values |
|
try (ContextSnapshot.Scope scoped = snapshot.setThreadLocals()) { |
|
// ... |
|
} |
|
---- |
|
|
|
For more details, see the |
|
https://micrometer.io/docs/contextPropagation[documentation] of the Micrometer Context |
|
Propagation library. |
|
|
|
|
|
|
|
[[mvc-ann-async-disconnects]] |
|
=== Disconnects |
|
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, WebFlux>># |
|
|
|
The Servlet API does not provide any notification when a remote client goes away. |
|
Therefore, while streaming to the response, whether through <<mvc-ann-async-sse, SseEmitter>> |
|
or <<mvc-ann-async-reactive-types, reactive types>>, it is important to send data periodically, |
|
since the write fails 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 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 asynchronous request processing feature must be enabled at the Servlet container level. |
|
The MVC configuration also exposes several options for asynchronous requests. |
|
|
|
|
|
[[mvc-ann-async-configuration-servlet3]] |
|
==== Servlet Container |
|
|
|
Filter and Servlet declarations have an `asyncSupported` flag that needs to be set to `true` |
|
to enable asynchronous request processing. In addition, Filter mappings should be |
|
declared to handle the `ASYNC` `jakarta.servlet.DispatchType`. |
|
|
|
In Java configuration, when you use `AbstractAnnotationConfigDispatcherServletInitializer` |
|
to initialize the Servlet container, this is done automatically. |
|
|
|
In `web.xml` configuration, you can add `<async-supported>true</async-supported>` to the |
|
`DispatcherServlet` and to `Filter` declarations and add |
|
`<dispatcher>ASYNC</dispatcher>` to filter mappings. |
|
|
|
|
|
[[mvc-ann-async-configuration-spring-mvc]] |
|
==== Spring MVC |
|
|
|
The MVC configuration exposes the following options related to asynchronous request processing: |
|
|
|
* Java configuration: 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. |
|
* `AsyncTaskExecutor` to use for blocking writes when streaming with |
|
<<mvc-ann-async-reactive-types>> and for executing `Callable` instances returned from |
|
controller methods. We highly recommended configuring this property if you |
|
stream with reactive types or have controller methods that return `Callable`, since |
|
by default, it is a `SimpleAsyncTaskExecutor`. |
|
* `DeferredResultProcessingInterceptor` implementations and `CallableProcessingInterceptor` implementations. |
|
|
|
Note that you can also set the default timeout value on a `DeferredResult`, |
|
a `ResponseBodyEmitter`, and an `SseEmitter`. For a `Callable`, you can use |
|
`WebAsyncTask` to provide a timeout value. |
|
|
|
|
|
include::webmvc-cors.adoc[leveloffset=+1] |
|
|
|
|
|
[[mvc-ann-rest-exceptions]] |
|
== Error Responses |
|
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions, WebFlux>># |
|
|
|
A common requirement for REST services is to include details in the body of error |
|
responses. The Spring Framework supports the "Problem Details for HTTP APIs" |
|
specification, https://www.rfc-editor.org/rfc/rfc7807.html[RFC 7807]. |
|
|
|
The following are the main abstractions for this support: |
|
|
|
- `ProblemDetail` -- representation for an RFC 7807 problem detail; a simple container |
|
for both standard fields defined in the spec, and for non-standard ones. |
|
- `ErrorResponse` -- contract to expose HTTP error response details including HTTP |
|
status, response headers, and a body in the format of RFC 7807; this allows exceptions to |
|
encapsulate and expose the details of how they map to an HTTP response. All Spring MVC |
|
exceptions implement this. |
|
- `ErrorResponseException` -- basic `ErrorResponse` implementation that others |
|
can use as a convenient base class. |
|
- `ResponseEntityExceptionHandler` -- convenient base class for an |
|
<<mvc-ann-controller-advice,@ControllerAdvice>> that handles all Spring MVC exceptions, |
|
and any `ErrorResponseException`, and renders an error response with a body. |
|
|
|
|
|
|
|
[[mvc-ann-rest-exceptions-render]] |
|
=== Render |
|
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-render, WebFlux>># |
|
|
|
You can return `ProblemDetail` or `ErrorResponse` from any `@ExceptionHandler` or from |
|
any `@RequestMapping` method to render an RFC 7807 response. This is processed as follows: |
|
|
|
- The `status` property of `ProblemDetail` determines the HTTP status. |
|
- The `instance` property of `ProblemDetail` is set from the current URL path, if not |
|
already set. |
|
- For content negotiation, the Jackson `HttpMessageConverter` prefers |
|
"application/problem+json" over "application/json" when rendering a `ProblemDetail`, |
|
and also falls back on it if no compatible media type is found. |
|
|
|
To enable RFC 7807 responses for Spring WebFlux exceptions and for any |
|
`ErrorResponseException`, extend `ResponseEntityExceptionHandler` and declare it as an |
|
<<mvc-ann-controller-advice,@ControllerAdvice>> in Spring configuration. The handler |
|
has an `@ExceptionHandler` method that handles any `ErrorResponse` exception, which |
|
includes all built-in web exceptions. You can add more exception handling methods, and |
|
use a protected method to map any exception to a `ProblemDetail`. |
|
|
|
|
|
|
|
[[mvc-ann-rest-exceptions-non-standard]] |
|
=== Non-Standard Fields |
|
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-non-standard, WebFlux>># |
|
|
|
You can extend an RFC 7807 response with non-standard fields in one of two ways. |
|
|
|
One, insert into the "properties" `Map` of `ProblemDetail`. When using the Jackson |
|
library, the Spring Framework registers `ProblemDetailJacksonMixin` that ensures this |
|
"properties" `Map` is unwrapped and rendered as top level JSON properties in the |
|
response, and likewise any unknown property during deserialization is inserted into |
|
this `Map`. |
|
|
|
You can also extend `ProblemDetail` to add dedicated non-standard properties. |
|
The copy constructor in `ProblemDetail` allows a subclass to make it easy to be created |
|
from an existing `ProblemDetail`. This could be done centrally, e.g. from an |
|
`@ControllerAdvice` such as `ResponseEntityExceptionHandler` that re-creates the |
|
`ProblemDetail` of an exception into a subclass with the additional non-standard fields. |
|
|
|
|
|
|
|
[[mvc-ann-rest-exceptions-i18n]] |
|
=== Internationalization |
|
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-i18n, WebFlux>># |
|
|
|
It is a common requirement to internationalize error response details, and good practice |
|
to customize the problem details for Spring MVC exceptions. This is supported as follows: |
|
|
|
- Each `ErrorResponse` exposes a message code and arguments to resolve the "detail" field |
|
through a <<core.adoc#context-functionality-messagesource,MessageSource>>. |
|
The actual message code value is parameterized with placeholders, e.g. |
|
`"HTTP method {0} not supported"` to be expanded from the arguments. |
|
- Each `ErrorResponse` also exposes a message code to resolve the "title" field. |
|
- `ResponseEntityExceptionHandler` uses the message code and arguments to resolve the |
|
"detail" and the "title" fields. |
|
|
|
By default, the message code for the "detail" field is "problemDetail." + the fully |
|
qualified exception class name. Some exceptions may expose additional message codes in |
|
which case a suffix is added to the default message code. The table below lists message |
|
arguments and codes for Spring MVC exceptions: |
|
|
|
[[mvc-ann-rest-exceptions-codes]] |
|
[cols="1,1,2", options="header"] |
|
|=== |
|
| Exception | Message Code | Message Code Arguments |
|
|
|
| `AsyncRequestTimeoutException` |
|
| (default) |
|
| |
|
|
|
| `ConversionNotSupportedException` |
|
| (default) |
|
| `{0}` property name, `{1}` property value |
|
|
|
| `HttpMediaTypeNotAcceptableException` |
|
| (default) |
|
| `{0}` list of supported media types |
|
|
|
| `HttpMediaTypeNotAcceptableException` |
|
| (default) + ".parseError" |
|
| |
|
|
|
| `HttpMediaTypeNotSupportedException` |
|
| (default) |
|
| `{0}` the media type that is not supported, `{1}` list of supported media types |
|
|
|
| `HttpMediaTypeNotSupportedException` |
|
| (default) + ".parseError" |
|
| |
|
|
|
| `HttpMessageNotReadableException` |
|
| (default) |
|
| |
|
|
|
| `HttpMessageNotWritableException` |
|
| (default) |
|
| |
|
|
|
| `HttpRequestMethodNotSupportedException` |
|
| (default) |
|
| `{0}` the current HTTP method, `{1}` the list of supported HTTP methods |
|
|
|
| `MethodArgumentNotValidException` |
|
| (default) |
|
| `{0}` the list of global errors, `{1}` the list of field errors. |
|
Message codes and arguments for each error within the `BindingResult` are also resolved |
|
via `MessageSource`. |
|
|
|
| `MissingRequestHeaderException` |
|
| (default) |
|
| `{0}` the header name |
|
|
|
| `MissingServletRequestParameterException` |
|
| (default) |
|
| `{0}` the request parameter name |
|
|
|
| `MissingMatrixVariableException` |
|
| (default) |
|
| `{0}` the matrix variable name |
|
|
|
| `MissingPathVariableException` |
|
| (default) |
|
| `{0}` the path variable name |
|
|
|
| `MissingRequestCookieException` |
|
| (default) |
|
| `{0}` the cookie name |
|
|
|
| `MissingServletRequestPartException` |
|
| (default) |
|
| `{0}` the part name |
|
|
|
| `NoHandlerFoundException` |
|
| (default) |
|
| |
|
|
|
| `TypeMismatchException` |
|
| (default) |
|
| `{0}` property name, `{1}` property value |
|
|
|
| `UnsatisfiedServletRequestParameterException` |
|
| (default) |
|
| `{0}` the list of parameter conditions |
|
|
|
|=== |
|
|
|
By default, the message code for the "title" field is "problemDetail.title." + the fully |
|
qualified exception class name. |
|
|
|
|
|
|
|
[[mvc-ann-rest-exceptions-client]] |
|
=== Client Handling |
|
[.small]#<<web-reactive.adoc#webflux-ann-rest-exceptions-client, WebFlux>># |
|
|
|
A client application can catch `WebClientResponseException`, when using the `WebClient`, |
|
or `RestClientResponseException` when using the `RestTemplate`, and use their |
|
`getResponseBodyAs` methods to decode the error response body to any target type such as |
|
`ProblemDetail`, or a subclass of `ProblemDetail`. |
|
|
|
|
|
|
|
[[mvc-web-security]] |
|
== Web Security |
|
[.small]#<<web-reactive.adoc#webflux-web-security, WebFlux>># |
|
|
|
The https://spring.io/projects/spring-security[Spring Security] project provides support |
|
for protecting web applications from malicious exploits. See the Spring Security |
|
reference documentation, including: |
|
|
|
* {doc-spring-security}/servlet/integrations/mvc.html[Spring MVC Security] |
|
* {doc-spring-security}/servlet/test/mockmvc/setup.html[Spring MVC Test Support] |
|
* {doc-spring-security}/features/exploits/csrf.html#csrf-protection[CSRF protection] |
|
* {doc-spring-security}/features/exploits/headers.html[Security Response Headers] |
|
|
|
https://hdiv.org/[HDIV] is another web security framework that integrates with Spring MVC. |
|
|
|
|
|
|
|
|
|
[[mvc-caching]] |
|
== HTTP Caching |
|
[.small]#<<web-reactive.adoc#webflux-caching, WebFlux>># |
|
|
|
HTTP caching can significantly improve the performance of a web application. HTTP caching |
|
revolves around the `Cache-Control` response header and, subsequently, conditional request |
|
headers (such as `Last-Modified` and `ETag`). `Cache-Control` advises private (for example, browser) |
|
and public (for example, proxy) caches on how to cache and re-use responses. An `ETag` header is used |
|
to make a conditional request that may result in a 304 (NOT_MODIFIED) without a body, |
|
if the content has not changed. `ETag` can be seen as a more sophisticated successor to |
|
the `Last-Modified` header. |
|
|
|
This section describes the HTTP caching-related options that are available in Spring Web MVC. |
|
|
|
|
|
|
|
[[mvc-caching-cachecontrol]] |
|
=== `CacheControl` |
|
[.small]#<<web-reactive.adoc#webflux-caching-cachecontrol, WebFlux>># |
|
|
|
{api-spring-framework}/http/CacheControl.html[`CacheControl`] provides support for |
|
configuring settings related to the `Cache-Control` header and is accepted as an argument |
|
in a number of places: |
|
|
|
* {api-spring-framework}/web/servlet/mvc/WebContentInterceptor.html[`WebContentInterceptor`] |
|
* {api-spring-framework}/web/servlet/support/WebContentGenerator.html[`WebContentGenerator`] |
|
* <<mvc-caching-etag-lastmodified>> |
|
* <<mvc-caching-static-resources>> |
|
|
|
While https://tools.ietf.org/html/rfc7234#section-5.2.2[RFC 7234] describes all possible |
|
directives for the `Cache-Control` response header, the `CacheControl` type takes a |
|
use case-oriented approach that focuses on the common scenarios: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
// Cache for an hour - "Cache-Control: max-age=3600" |
|
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS); |
|
|
|
// 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(); |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
// Cache for an hour - "Cache-Control: max-age=3600" |
|
val ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS) |
|
|
|
// Prevent caching - "Cache-Control: no-store" |
|
val 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" |
|
val ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic() |
|
---- |
|
|
|
`WebContentGenerator` also accepts a simpler `cachePeriod` property (defined in seconds) that |
|
works as follows: |
|
|
|
* A `-1` value does not generate a `Cache-Control` response header. |
|
* A `0` value prevents caching by using the `'Cache-Control: no-store'` directive. |
|
* An `n > 0` value caches the given response for `n` seconds by using the |
|
`'Cache-Control: max-age=n'` directive. |
|
|
|
|
|
|
|
[[mvc-caching-etag-lastmodified]] |
|
=== Controllers |
|
[.small]#<<web-reactive.adoc#webflux-caching-etag-lastmodified, WebFlux>># |
|
|
|
Controllers can add explicit support for HTTP caching. We recommended doing so, since the |
|
`lastModified` or `ETag` value for a resource needs to be calculated before it can be compared |
|
against conditional request headers. A controller can add an `ETag` header and `Cache-Control` |
|
settings to a `ResponseEntity`, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@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); |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@GetMapping("/book/{id}") |
|
fun showBook(@PathVariable id: Long): ResponseEntity<Book> { |
|
|
|
val book = findBook(id); |
|
val version = book.getVersion() |
|
|
|
return ResponseEntity |
|
.ok() |
|
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS)) |
|
.eTag(version) // lastModified is also available |
|
.body(book) |
|
} |
|
---- |
|
|
|
The preceding example sends a 304 (NOT_MODIFIED) response with an empty body if the comparison |
|
to the conditional request headers indicates that the content has not changed. Otherwise, the |
|
`ETag` and `Cache-Control` headers are added to the response. |
|
|
|
You can also make the check against conditional request headers in the controller, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@RequestMapping |
|
public String myHandleMethod(WebRequest request, Model model) { |
|
|
|
long eTag = ... // <1> |
|
|
|
if (request.checkNotModified(eTag)) { |
|
return null; // <2> |
|
} |
|
|
|
model.addAttribute(...); // <3> |
|
return "myViewName"; |
|
} |
|
---- |
|
<1> Application-specific calculation. |
|
<2> The response has been set to 304 (NOT_MODIFIED) -- no further processing. |
|
<3> Continue with the request processing. |
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@RequestMapping |
|
fun myHandleMethod(request: WebRequest, model: Model): String? { |
|
|
|
val eTag: Long = ... // <1> |
|
|
|
if (request.checkNotModified(eTag)) { |
|
return null // <2> |
|
} |
|
|
|
model[...] = ... // <3> |
|
return "myViewName" |
|
} |
|
---- |
|
<1> Application-specific calculation. |
|
<2> The response has been set to 304 (NOT_MODIFIED) -- no further processing. |
|
<3> Continue with the request processing. |
|
|
|
|
|
There are three variants for checking conditional requests against `eTag` values, `lastModified` |
|
values, or both. For conditional `GET` and `HEAD` requests, you can set the response to |
|
304 (NOT_MODIFIED). For conditional `POST`, `PUT`, and `DELETE`, you can instead set the response |
|
to 412 (PRECONDITION_FAILED), to prevent concurrent modification. |
|
|
|
|
|
|
|
[[mvc-caching-static-resources]] |
|
=== Static Resources |
|
[.small]#<<web-reactive.adoc#webflux-caching-static-resources, WebFlux>># |
|
|
|
You should serve static resources with a `Cache-Control` and conditional response headers |
|
for optimal performance. See the section on configuring <<mvc-config-static-resources>>. |
|
|
|
|
|
|
|
[[mvc-httpcaching-shallowetag]] |
|
=== `ETag` Filter |
|
|
|
You can use the `ShallowEtagHeaderFilter` to add "`shallow`" `eTag` values that are computed from the |
|
response content and, thus, save bandwidth but not CPU time. See <<filters-shallow-etag>>. |
|
|
|
include::webmvc-view.adoc[leveloffset=+1] |
|
|
|
|
|
|
|
|
|
[[mvc-config]] |
|
== MVC Config |
|
[.small]#<<web-reactive.adoc#webflux-config, WebFlux>># |
|
|
|
The MVC Java configuration and the MVC XML namespace provide default configuration |
|
suitable for most applications and a configuration API to customize it. |
|
|
|
For more advanced customizations, which are 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 configuration |
|
and the MVC namespace. If you want to learn more, see <<mvc-servlet-special-bean-types>> |
|
and <<mvc-servlet-config>>. |
|
|
|
|
|
|
|
[[mvc-config-enable]] |
|
=== Enable MVC Configuration |
|
[.small]#<<web-reactive.adoc#webflux-config-enable, WebFlux>># |
|
|
|
In Java configuration, you can use the `@EnableWebMvc` annotation to enable MVC |
|
configuration, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig { |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig |
|
---- |
|
|
|
In XML configuration, you can use the `<mvc:annotation-driven>` element to enable MVC |
|
configuration, as the following example shows: |
|
|
|
[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 |
|
https://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
https://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven/> |
|
|
|
</beans> |
|
---- |
|
|
|
The preceding example registers a number of Spring MVC |
|
<<mvc-servlet-special-bean-types, infrastructure beans>> and adapts to dependencies |
|
available on the classpath (for example, payload converters for JSON, XML, and others). |
|
|
|
|
|
|
|
[[mvc-config-customize]] |
|
=== MVC Config API |
|
[.small]#<<web-reactive.adoc#webflux-config-customize, WebFlux>># |
|
|
|
In Java configuration, you can implement the `WebMvcConfigurer` interface, as the |
|
following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
// Implement configuration methods... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
// Implement configuration methods... |
|
} |
|
---- |
|
|
|
|
|
In XML, you can check attributes and sub-elements of `<mvc:annotation-driven/>`. You can |
|
view the https://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, WebFlux>># |
|
|
|
By default, formatters for various number and date types are installed, along with support |
|
for customization via `@NumberFormat` and `@DateTimeFormat` on fields. |
|
|
|
To register custom formatters and converters in Java config, use the following: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addFormatters(FormatterRegistry registry) { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun addFormatters(registry: FormatterRegistry) { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
To do the same in XML config, use the following: |
|
|
|
[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 |
|
https://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
https://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> |
|
---- |
|
|
|
By default Spring MVC considers the request Locale when parsing and formatting date |
|
values. This works for forms where dates are represented as Strings with "input" form |
|
fields. For "date" and "time" form fields, however, browsers use a fixed format defined |
|
in the HTML spec. For such cases date and time formatting can be customized as follows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addFormatters(FormatterRegistry registry) { |
|
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); |
|
registrar.setUseIsoFormat(true); |
|
registrar.registerFormatters(registry); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun addFormatters(registry: FormatterRegistry) { |
|
val registrar = DateTimeFormatterRegistrar() |
|
registrar.setUseIsoFormat(true) |
|
registrar.registerFormatters(registry) |
|
} |
|
} |
|
---- |
|
|
|
NOTE: See <<core.adoc#format-FormatterRegistrar-SPI, the `FormatterRegistrar` SPI>> |
|
and the `FormattingConversionServiceFactoryBean` for more information on when to use |
|
FormatterRegistrar implementations. |
|
|
|
|
|
|
|
[[mvc-config-validation]] |
|
=== Validation |
|
[.small]#<<web-reactive.adoc#webflux-config-validation, WebFlux>># |
|
|
|
By default, if <<core.adoc#validation-beanvalidation-overview, Bean Validation>> is present |
|
on the classpath (for example, 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 configuration, you can customize the global `Validator` instance, as the |
|
following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public Validator getValidator() { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun getValidator(): Validator { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
The following example shows how to achieve the same configuration in XML: |
|
|
|
[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 |
|
https://www.springframework.org/schema/beans/spring-beans.xsd |
|
http://www.springframework.org/schema/mvc |
|
https://www.springframework.org/schema/mvc/spring-mvc.xsd"> |
|
|
|
<mvc:annotation-driven validator="globalValidator"/> |
|
|
|
</beans> |
|
---- |
|
|
|
Note that you can also register `Validator` implementations locally, as the following |
|
example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Controller |
|
public class MyController { |
|
|
|
@InitBinder |
|
protected void initBinder(WebDataBinder binder) { |
|
binder.addValidators(new FooValidator()); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Controller |
|
class MyController { |
|
|
|
@InitBinder |
|
protected fun initBinder(binder: WebDataBinder) { |
|
binder.addValidators(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 configuration. |
|
|
|
|
|
|
|
[[mvc-config-interceptors]] |
|
=== Interceptors |
|
|
|
In Java configuration, you can register interceptors to apply to incoming requests, as |
|
the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addInterceptors(InterceptorRegistry registry) { |
|
registry.addInterceptor(new LocaleChangeInterceptor()); |
|
registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**"); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun addInterceptors(registry: InterceptorRegistry) { |
|
registry.addInterceptor(LocaleChangeInterceptor()) |
|
registry.addInterceptor(ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**") |
|
} |
|
} |
|
---- |
|
|
|
The following example shows how to achieve the same configuration in XML: |
|
|
|
[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:interceptors> |
|
---- |
|
|
|
NOTE: Mapped interceptors are not ideally suited as a security layer due to the potential |
|
for a mismatch with annotated controller path matching, which can also match trailing |
|
slashes and path extensions transparently, along with other path matching options. Many |
|
of these options have been deprecated but the potential for a mismatch remains. |
|
Generally, we recommend using Spring Security which includes a dedicated |
|
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher] |
|
to align with Spring MVC path matching and also has a security firewall that blocks many |
|
unwanted characters in URL paths. |
|
|
|
|
|
|
|
|
|
[[mvc-config-content-negotiation]] |
|
=== Content Types |
|
[.small]#<<web-reactive.adoc#webflux-config-content-negotiation, WebFlux>># |
|
|
|
You can configure how Spring MVC determines the requested media types from the request |
|
(for example, `Accept` header, URL path extension, query parameter, and others). |
|
|
|
By default, only the `Accept` header is checked. |
|
|
|
If you must use URL-based content type resolution, consider using the query parameter |
|
strategy over path extensions. See |
|
<<mvc-ann-requestmapping-suffix-pattern-match>> and <<mvc-ann-requestmapping-rfd>> for |
|
more details. |
|
|
|
In Java configuration, you can customize requested content type resolution, as the |
|
following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { |
|
configurer.mediaType("json", MediaType.APPLICATION_JSON); |
|
configurer.mediaType("xml", MediaType.APPLICATION_XML); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun configureContentNegotiation(configurer: ContentNegotiationConfigurer) { |
|
configurer.mediaType("json", MediaType.APPLICATION_JSON) |
|
configurer.mediaType("xml", MediaType.APPLICATION_XML) |
|
} |
|
} |
|
---- |
|
|
|
|
|
The following example shows how to achieve the same configuration in XML: |
|
|
|
[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, WebFlux>># |
|
|
|
You can customize `HttpMessageConverter` in Java configuration by overriding |
|
{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`] |
|
(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()`] |
|
(to customize the default converters or add additional converters to the default ones). |
|
|
|
The following example adds XML and Jackson JSON converters with a customized |
|
`ObjectMapper` instead of the default ones: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@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.createXmlMapper(true).build())); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfiguration : WebMvcConfigurer { |
|
|
|
override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) { |
|
val builder = Jackson2ObjectMapperBuilder() |
|
.indentOutput(true) |
|
.dateFormat(SimpleDateFormat("yyyy-MM-dd")) |
|
.modulesToInstall(ParameterNamesModule()) |
|
converters.add(MappingJackson2HttpMessageConverter(builder.build())) |
|
converters.add(MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build())) |
|
---- |
|
|
|
In the preceding example, |
|
{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`] |
|
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`], |
|
Which adds support for accessing parameter names (a feature added in Java 8). |
|
|
|
This builder customizes Jackson's default properties as follows: |
|
|
|
* https://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. |
|
* https://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-joda[jackson-datatype-joda]: Support for Joda-Time types. |
|
* https://github.com/FasterXML/jackson-datatype-jsr310[jackson-datatype-jsr310]: Support for Java 8 Date and Time API types. |
|
* https://github.com/FasterXML/jackson-datatype-jdk8[jackson-datatype-jdk8]: Support for other Java 8 types, such as `Optional`. |
|
* https://github.com/FasterXML/jackson-module-kotlin[`jackson-module-kotlin`]: Support for Kotlin classes and data classes. |
|
|
|
NOTE: Enabling indentation with Jackson XML support requires |
|
https://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 https://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). |
|
|
|
The following example shows how to achieve the same configuration 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. You can use it in static cases when there is no Java controller |
|
logic to run before the view generates the response. |
|
|
|
The following example of Java configuration forwards a request for `/` to a view called `home`: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addViewControllers(ViewControllerRegistry registry) { |
|
registry.addViewController("/").setViewName("home"); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun addViewControllers(registry: ViewControllerRegistry) { |
|
registry.addViewController("/").setViewName("home") |
|
} |
|
} |
|
---- |
|
|
|
The following example achieves the same thing as the preceding example, but with XML, by |
|
using the `<mvc:view-controller>` element: |
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"] |
|
---- |
|
<mvc:view-controller path="/" view-name="home"/> |
|
---- |
|
|
|
If an `@RequestMapping` method is mapped to a URL for any HTTP method then a view |
|
controller cannot be used to handle the same URL. This is because a match by URL to an |
|
annotated controller is considered a strong enough indication of endpoint ownership so |
|
that a 405 (METHOD_NOT_ALLOWED), a 415 (UNSUPPORTED_MEDIA_TYPE), or similar response can |
|
be sent to the client to help with debugging. For this reason it is recommended to avoid |
|
splitting URL handling across an annotated controller and a view controller. |
|
|
|
|
|
|
|
[[mvc-config-view-resolvers]] |
|
=== View Resolvers |
|
[.small]#<<web-reactive.adoc#webflux-config-view-resolvers, WebFlux>># |
|
|
|
The MVC configuration simplifies the registration of view resolvers. |
|
|
|
The following Java configuration example configures content negotiation view |
|
resolution by using JSP and Jackson as a default `View` for JSON rendering: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureViewResolvers(ViewResolverRegistry registry) { |
|
registry.enableContentNegotiation(new MappingJackson2JsonView()); |
|
registry.jsp(); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun configureViewResolvers(registry: ViewResolverRegistry) { |
|
registry.enableContentNegotiation(MappingJackson2JsonView()) |
|
registry.jsp() |
|
} |
|
} |
|
---- |
|
|
|
|
|
The following example shows how to achieve the same configuration 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. The following example works 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 configuration, you can add the respective `Configurer` bean, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@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("/freemarker"); |
|
return configurer; |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun configureViewResolvers(registry: ViewResolverRegistry) { |
|
registry.enableContentNegotiation(MappingJackson2JsonView()) |
|
registry.freeMarker().cache(false) |
|
} |
|
|
|
@Bean |
|
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply { |
|
setTemplateLoaderPath("/freemarker") |
|
} |
|
} |
|
---- |
|
|
|
|
|
|
|
[[mvc-config-static-resources]] |
|
=== Static Resources |
|
[.small]#<<web-reactive.adoc#webflux-config-static-resources, 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 next example, 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 one-year future |
|
expiration to ensure maximum use of the browser cache and a reduction in HTTP requests |
|
made by the browser. The `Last-Modified` information is deduced from `Resource#lastModified` |
|
so that HTTP conditional requests are supported with `"Last-Modified"` headers. |
|
|
|
The following listing shows how to do so with Java configuration: |
|
|
|
[source,java,indent=0,subs="verbatim",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public", "classpath:/static/") |
|
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365))); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun addResourceHandlers(registry: ResourceHandlerRegistry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public", "classpath:/static/") |
|
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365))) |
|
} |
|
} |
|
---- |
|
|
|
The following example shows how to achieve the same configuration 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`] implementations and |
|
{api-spring-framework}/web/servlet/resource/ResourceTransformer.html[`ResourceTransformer`] implementations, |
|
which you can use to create a toolchain for working with optimized resources. |
|
|
|
You can use the `VersionResourceResolver` 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. |
|
|
|
The following example shows how to use `VersionResourceResolver` in Java configuration: |
|
|
|
[source,java,indent=0,subs="verbatim",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void addResourceHandlers(ResourceHandlerRegistry registry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public/") |
|
.resourceChain(true) |
|
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun addResourceHandlers(registry: ResourceHandlerRegistry) { |
|
registry.addResourceHandler("/resources/**") |
|
.addResourceLocations("/public/") |
|
.resourceChain(true) |
|
.addResolver(VersionResourceResolver().addContentVersionStrategy("/**")) |
|
} |
|
} |
|
---- |
|
|
|
The following example shows how to achieve the same configuration in XML: |
|
|
|
[source,xml,indent=0,subs="verbatim"] |
|
---- |
|
<mvc:resources mapping="/resources/**" location="/public/"> |
|
<mvc:resource-chain resource-cache="true"> |
|
<mvc:resolvers> |
|
<mvc:version-resolver> |
|
<mvc:content-version-strategy patterns="/**"/> |
|
</mvc:version-resolver> |
|
</mvc:resolvers> |
|
</mvc:resource-chain> |
|
</mvc:resources> |
|
---- |
|
|
|
You can then use `ResourceUrlProvider` to rewrite URLs and apply the full chain of resolvers and |
|
transformers -- for example, to insert versions. The MVC configuration provides a `ResourceUrlProvider` |
|
bean so that 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`. |
|
|
|
Note that, when using both `EncodedResourceResolver` (for example, for serving gzipped or |
|
brotli-encoded resources) and `VersionResourceResolver`, you must register them in this order. |
|
That ensures content-based versions are always computed reliably, based on the unencoded file. |
|
|
|
For https://www.webjars.org/documentation[WebJars], versioned URLs like |
|
`/webjars/jquery/1.2.0/jquery.min.js` are the recommended and most efficient way to use them. |
|
The related resource location is configured out of the box with Spring Boot (or can be configured |
|
manually via `ResourceHandlerRegistry`) and does not require to add the |
|
`org.webjars:webjars-locator-core` dependency. |
|
|
|
Version-less URLs like `/webjars/jquery/jquery.min.js` are supported through the |
|
`WebJarsResourceResolver` which is automatically registered when the |
|
`org.webjars:webjars-locator-core` library is present on the classpath, at the cost of a |
|
classpath scanning that could slow down application startup. The resolver can re-write URLs to |
|
include the version of the jar and can also match against incoming URLs without versions |
|
-- for example, from `/webjars/jquery/jquery.min.js` to `/webjars/jquery/1.2.0/jquery.min.js`. |
|
|
|
TIP: The Java configuration based on `ResourceHandlerRegistry` provides further options |
|
for fine-grained control, e.g. last-modified behavior and optimized resource resolution. |
|
|
|
|
|
|
|
[[mvc-default-servlet-handler]] |
|
=== Default Servlet |
|
|
|
Spring MVC 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 forwards all requests to the default Servlet. Therefore, it must |
|
remain last in the order of all other URL `HandlerMappings`. That is the |
|
case if you use `<mvc:annotation-driven>`. Alternatively, if you set 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`. |
|
|
|
The following example shows how to enable the feature by using the default setup: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { |
|
configurer.enable(); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) { |
|
configurer.enable() |
|
} |
|
} |
|
---- |
|
|
|
The following example shows how to achieve the same configuration 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` tries 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 you must explicitly provide the default Servlet's name, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { |
|
configurer.enable("myCustomDefaultServlet"); |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) { |
|
configurer.enable("myCustomDefaultServlet") |
|
} |
|
} |
|
---- |
|
|
|
|
|
The following example shows how to achieve the same configuration 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, WebFlux>># |
|
|
|
You can customize options related to path matching and treatment of the URL. |
|
For details on the individual options, see the |
|
{api-spring-framework}/web/servlet/config/annotation/PathMatchConfigurer.html[`PathMatchConfigurer`] javadoc. |
|
|
|
The following example shows how to customize path matching in Java configuration: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
public class WebConfig implements WebMvcConfigurer { |
|
|
|
@Override |
|
public void configurePathMatch(PathMatchConfigurer configurer) { |
|
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class)); |
|
} |
|
|
|
private PathPatternParser patternParser() { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
@EnableWebMvc |
|
class WebConfig : WebMvcConfigurer { |
|
|
|
override fun configurePathMatch(configurer: PathMatchConfigurer) { |
|
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController::class.java)) |
|
} |
|
|
|
fun patternParser(): PathPatternParser { |
|
//... |
|
} |
|
} |
|
---- |
|
|
|
The following example shows how to customize path matching in XML configuration: |
|
|
|
[source,xml,indent=0,subs="verbatim,quotes"] |
|
---- |
|
<mvc:annotation-driven> |
|
<mvc:path-matching |
|
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, WebFlux>># |
|
|
|
`@EnableWebMvc` imports `DelegatingWebMvcConfiguration`, which: |
|
|
|
* Provides default Spring configuration for Spring MVC applications |
|
* Detects and delegates to `WebMvcConfigurer` implementations to customize that configuration. |
|
|
|
For advanced mode, you can remove `@EnableWebMvc` and extend directly from |
|
`DelegatingWebMvcConfiguration` instead of implementing `WebMvcConfigurer`, |
|
as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Configuration |
|
public class WebConfig extends DelegatingWebMvcConfiguration { |
|
|
|
// ... |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Configuration |
|
class WebConfig : 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` implementations 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 cannot change otherwise, you can use the `BeanPostProcessor` lifecycle |
|
hook of the Spring `ApplicationContext`, as the following example shows: |
|
|
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
|
.Java |
|
---- |
|
@Component |
|
public class MyPostProcessor implements BeanPostProcessor { |
|
|
|
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException { |
|
// ... |
|
} |
|
} |
|
---- |
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"] |
|
.Kotlin |
|
---- |
|
@Component |
|
class MyPostProcessor : BeanPostProcessor { |
|
|
|
override fun postProcessBeforeInitialization(bean: Any, name: String): Any { |
|
// ... |
|
} |
|
} |
|
---- |
|
|
|
|
|
Note that you need to declare `MyPostProcessor` as a bean, either explicitly in XML or |
|
by letting it be detected through a `<component-scan/>` declaration. |
|
|
|
|
|
|
|
|
|
[[mvc-http2]] |
|
== HTTP/2 |
|
[.small]#<<web-reactive.adoc#webflux-http2, 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, see 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. You can use the |
|
`jakarta.servlet.http.PushBuilder` to proactively push resources to clients, and it |
|
is supported as a <<mvc-ann-arguments, method argument>> to `@RequestMapping` methods.
|
|
|