6345 lines
230 KiB
Plaintext
6345 lines
230 KiB
Plaintext
[[mvc]]
|
|
:chapter: mvc
|
|
= Spring Web MVC
|
|
|
|
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 chapter covers Spring Web MVC. The <<web-reactive.adoc#spring-web-reactive, next chapter>>
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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<>();
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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`. 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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
{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`
|
|
| class name + 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, See equivalent in the Reactive stack>>#
|
|
|
|
`MultipartResolver` from the `org.springframework.web.multipart` package is a strategy
|
|
for parsing multipart requests including file uploads. There is a container-based
|
|
`StandardServletMultipartResolver` implementation for Servlet multipart request parsing.
|
|
Note that the outdated `CommonsMultipartResolver` based on Apache Commons FileUpload is
|
|
not available anymore, as of Spring Framework 6.0 with its new Servlet 5.0+ baseline.
|
|
|
|
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-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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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 the `-parameters`
|
|
compiler flag.
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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/{petId}", headers = "myHeader=myValue") // <1>
|
|
public void findPet(@PathVariable String petId) {
|
|
// ...
|
|
}
|
|
----
|
|
<1> Testing whether `myHeader` equals `myValue`.
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
.Kotlin
|
|
----
|
|
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
|
|
fun findPet(@PathVariable petId: String) {
|
|
// ...
|
|
}
|
|
----
|
|
<1> Testing whether `myHeader` equals `myValue`.
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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>>.
|
|
|
|
| Reactor and other reactive types registered via `ReactiveAdapterRegistry`
|
|
| A single value type, e.g. `Mono`, is comparable to returning `DeferredResult`.
|
|
A multi-value type, e.g. `Flux`, may be treated as a stream depending on the requested
|
|
media type, e.g. "text/event-stream", "application/json+stream", or otherwise is
|
|
collected to a List and rendered as a single value. See <<mvc-ann-async>> and
|
|
<<mvc-ann-async-reactive-types>>.
|
|
|
|
| Other return values
|
|
| If a return value remains unresolved in any other way, it is treated as a model
|
|
attribute, unless it is a simple type as determined by
|
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
|
|
in which case it remains unresolved.
|
|
|===
|
|
|
|
|
|
[[mvc-ann-typeconversion]]
|
|
==== Type Conversion
|
|
[.small]#<<web-reactive.adoc#webflux-ann-typeconversion, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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) { // <1>
|
|
// method logic...
|
|
}
|
|
----
|
|
<1> Bind an instance of `Pet`.
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
.Kotlin
|
|
----
|
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
|
fun processSubmit(@ModelAttribute pet: Pet): String { // <1>
|
|
// method logic...
|
|
}
|
|
----
|
|
<1> Bind an instance of `Pet`.
|
|
|
|
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) { // <1>
|
|
// ...
|
|
}
|
|
----
|
|
<1> Bind an instance of `Account` using an explicit attribute name.
|
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
|
.Kotlin
|
|
----
|
|
@PutMapping("/accounts/{account}")
|
|
fun save(@ModelAttribute("account") account: Account): String { // <1>
|
|
// ...
|
|
}
|
|
----
|
|
<1> Bind an instance of `Account` using an explicit attribute name.
|
|
|
|
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"
|
|
}
|
|
// ...
|
|
}
|
|
----
|
|
<1> Validate the `Pet` instance.
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
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>
|
|
// ...
|
|
}
|
|
----
|
|
<1> Using a `@SessionAttribute` annotation.
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`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, See equivalent in the Reactive stack>>#
|
|
|
|
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
|
|
----
|
|
@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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
include::web-data-binding-model-design.adoc[]
|
|
|
|
|
|
[[mvc-ann-exceptionhandler]]
|
|
=== Exceptions
|
|
[.small]#<<web-reactive.adoc#webflux-ann-controller-exceptions, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
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
|
|
|
|
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.
|
|
|
|
For an overview of how this differs from Spring WebFlux, see the <<mvc-ann-async-vs-webflux>> section below.
|
|
|
|
[[mvc-ann-async-deferredresult]]
|
|
=== `DeferredResult`
|
|
|
|
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<>();
|
|
// 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`
|
|
|
|
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 () -> "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
|
|
|
|
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]]
|
|
==== Async Spring MVC 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.
|
|
|
|
Finally, from a configuration perspective the asynchronous request processing feature must be
|
|
<<mvc-ann-async-configuration, enabled at the Servlet container level>>.
|
|
|
|
|
|
[[mvc-ann-async-http-streaming]]
|
|
=== HTTP Streaming
|
|
[.small]#<<web-reactive.adoc#webflux-codecs-streaming, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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"]
|
|
.Java
|
|
----
|
|
// Capture ThreadLocal values from the main thread ...
|
|
ContextSnapshot snapshot = ContextSnapshot.captureAll();
|
|
|
|
// On a different thread: restore ThreadLocal values
|
|
try (ContextSnapshot.Scope scope = 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, See equivalent in the Reactive stack>>#
|
|
|
|
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
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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:
|
|
|
|
* {docs-spring-security}/servlet/integrations/mvc.html[Spring MVC Security]
|
|
* {docs-spring-security}/servlet/test/mockmvc/setup.html[Spring MVC Test Support]
|
|
* {docs-spring-security}/features/exploits/csrf.html#csrf-protection[CSRF protection]
|
|
* {docs-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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
{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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, 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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
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, See equivalent in the Reactive stack>>#
|
|
|
|
`@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, See equivalent in the Reactive stack>>#
|
|
|
|
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.
|