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