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.
 |