diff --git a/src/docs/asciidoc/web-reactive.adoc b/src/docs/asciidoc/web-reactive.adoc index a7fa9bb5b4b..2a4594ccdfb 100644 --- a/src/docs/asciidoc/web-reactive.adoc +++ b/src/docs/asciidoc/web-reactive.adoc @@ -7,12 +7,12 @@ :tabsize: 4 :docinfo1: -This part of the documentation covers support for reactive stack, web applications built on a +This part of the documentation covers support for reactive-stack web applications built on a http://www.reactive-streams.org/[Reactive Streams] API to run on non-blocking -servers such as Netty, Undertow, and Servlet 3.1+ containers. Individual chapters cover +servers, such as Netty, Undertow, and Servlet 3.1+ containers. Individual chapters cover the <> framework, -the reactive <>, support for <>, -and <>. For Servlet stack, web applications, please see +the reactive <>, support for <>, +and <>. For Servlet-stack web applications, see <>. include::web/webflux.adoc[leveloffset=+1] @@ -29,19 +29,18 @@ include::web/webflux-websocket.adoc[leveloffset=+1] The `spring-test` module provides mock implementations of `ServerHttpRequest`, `ServerHttpResponse`, and `ServerWebExchange`. -See <> mock objects. +See <> for a discussion of mock objects. -The <> builds on these mock request and -response objects to provide support for testing WebFlux applications without and HTTP -server. The `WebTestClient` can be used for end-to-end integration tests too. +<> builds on these mock request and +response objects to provide support for testing WebFlux applications without an HTTP +server. You can use the `WebTestClient` for end-to-end integration tests, too. - -[[webflux-threading-model]] -=== Threading model - - +//[[webflux-threading-model]] +//=== Threading model +// TODO Once we have content for this heading, we should un-comment the heading and +// anchor. Until then, we should leave it commented. @@ -49,30 +48,27 @@ server. The `WebTestClient` can be used for end-to-end integration tests too. == Reactive Libraries `spring-webflux` depends on `reactor-core` and uses it internally to compose asynchronous -logic and to provide Reactive Streams support. Generally WebFlux APIs return `Flux` or -`Mono` -- since that's what's used internally, and leniently accept any Reactive Streams -`Publisher` implementation as input. The use of `Flux` vs `Mono` is important because it -helps to express cardinality -- e.g. whether a single or multiple async values are -expected, and that can be essential for making decisions, for example when encoding or -decoding HTTP messages. +logic and to provide Reactive Streams support. Generally, WebFlux APIs return `Flux` or +`Mono` (since those are used internally) and leniently accept any Reactive Streams +`Publisher` implementation as input. The use of `Flux` versus `Mono` is important, because it +helps to express cardinality -- for example, whether a single or multiple asynchronous values are +expected, and that can be essential for making decisions (for example, when encoding or +decoding HTTP messages). For annotated controllers, WebFlux transparently adapts to the reactive library chosen by the application. This is done with the help of the -{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] which +{api-spring-framework}/core/ReactiveAdapterRegistry.html[`ReactiveAdapterRegistry`], which provides pluggable support for reactive library and other asynchronous types. The registry -has built-in support for RxJava and `CompletableFuture`, but others can be registered too. +has built-in support for RxJava and `CompletableFuture`, but you can register others, too. -For functional APIs such as <>, the `WebClient`, and others, the general rules -for WebFlux APIs apply -- `Flux` and `Mono` as return values, and Reactive Streams +For functional APIs (such as <>, the `WebClient`, and others), the general rules +for WebFlux APIs apply -- `Flux` and `Mono` as return values and a Reactive Streams `Publisher` as input. When a `Publisher`, whether custom or from another reactive library, -is provided, it can only be treated as a stream with unknown semantics (0..N). If however +is provided, it can be treated only as a stream with unknown semantics (0..N). If, however, the semantics are known, you can wrap it with `Flux` or `Mono.from(Publisher)` instead of passing the raw `Publisher`. -[NOTE] -==== For example, given a `Publisher` that is not a `Mono`, the Jackson JSON message writer -expects multiple values. If the media type implies an infinite stream -- e.g. -`"application/json+stream"`, values are written and flushed individually; otherwise +expects multiple values. If the media type implies an infinite stream (for example, +`application/json+stream`), values are written and flushed individually. Otherwise, values are buffered into a list and rendered as a JSON array. -==== diff --git a/src/docs/asciidoc/web.adoc b/src/docs/asciidoc/web.adoc index d0c34bb8e12..e72482f4e97 100644 --- a/src/docs/asciidoc/web.adoc +++ b/src/docs/asciidoc/web.adoc @@ -7,10 +7,10 @@ :tabsize: 4 :docinfo1: -This part of the documentation covers support for Servlet stack, web applications built on the +This part of the documentation covers support for Servlet-stack web applications built on the Servlet API and deployed to Servlet containers. Individual chapters include <>, <>, <>, and <>. -For reactive stack, web applications, go to <>. +For reactive-stack web applications, see <>. include::web/webmvc.adoc[leveloffset=+1] @@ -21,4 +21,3 @@ include::web/webmvc-test.adoc[leveloffset=+1] include::web/websocket.adoc[leveloffset=+1] include::web/integration.adoc[leveloffset=+1] - diff --git a/src/docs/asciidoc/web/integration.adoc b/src/docs/asciidoc/web/integration.adoc index 42fc3505669..5a73bbcc9c6 100644 --- a/src/docs/asciidoc/web/integration.adoc +++ b/src/docs/asciidoc/web/integration.adoc @@ -1,53 +1,47 @@ [[web-integration]] = Other Web Frameworks - - - -[[intro]] -== Introduction - This chapter details Spring's integration with third party web frameworks. One of the core value propositions of the Spring Framework is that of enabling -__choice__. In a general sense, Spring does not force one to use or buy into any +_choice_. In a general sense, Spring does not force you to use or buy into any particular architecture, technology, or methodology (although it certainly recommends some over others). This freedom to pick and choose the architecture, technology, or methodology that is most relevant to a developer and their development team is arguably most evident in the web area, where Spring provides its own web framework -(<>), while at the same time providing integration with a number of +(<>) while, at the same time, providing integration with a number of popular third party web frameworks. - [[web-integration-common]] -== Common config +== Common Configuration Before diving into the integration specifics of each supported web framework, let us -first take a look at the Spring configuration that is __not__ specific to any one web +first take a look at the Spring configuration that is not specific to any one web framework. (This section is equally applicable to Spring's own web framework, Spring MVC.) -One of the concepts (for want of a better word) espoused by (Spring's) lightweight -application model is that of a layered architecture. Remember that in a 'classic' -layered architecture, the web layer is but one of many layers; it serves as one of the -entry points into a server side application and it delegates to service objects -(facades) defined in a service layer to satisfy business specific (and +One of the concepts (for want of a better word) espoused by Spring's lightweight +application model is that of a layered architecture. Remember that in a "`classic`" +layered architecture, the web layer is but one of many layers. It serves as one of the +entry points into a server-side application, and it delegates to service objects +(facades) that are defined in a service layer to satisfy business-specific (and presentation-technology agnostic) use cases. In Spring, these service objects, any other -business-specific objects, data access objects, etc. exist in a distinct 'business -context', which contains __no__ web or presentation layer objects (presentation objects -such as Spring MVC controllers are typically configured in a distinct 'presentation -context'). This section details how one configures a Spring container (a -`WebApplicationContext`) that contains all of the 'business beans' in one's application. +business-specific objects, data-access objects, and others exist in a distinct "`business +context`", which contains no web or presentation layer objects (presentation objects +,such as Spring MVC controllers, are typically configured in a distinct "`presentation +context`"). This section details how you can configure a Spring container (a +`WebApplicationContext`) that contains all of the 'business beans' in your application. -On to specifics: all that one need do is to declare a +Moving on to specifics, all you one need to do is declare a {api-spring-framework}/web/context/ContextLoaderListener.html[`ContextLoaderListener`] -in the standard Java EE servlet `web.xml` file of one's web application, and add a +in the standard Java EE servlet `web.xml` file of your web application and add a `contextConfigLocation` section (in the same file) that defines which set of Spring XML configuration files to load. -Find below the configuration: +Consider the following `` configuration: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -55,9 +49,11 @@ Find below the configuration: org.springframework.web.context.ContextLoaderListener ---- +==== -Find below the configuration: +Further consider the following `` configuration: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -66,30 +62,35 @@ Find below the configuration: /WEB-INF/applicationContext*.xml ---- +==== -If you don't specify the `contextConfigLocation` context parameter, the -`ContextLoaderListener` will look for a file called `/WEB-INF/applicationContext.xml` to +If you do not specify the `contextConfigLocation` context parameter, the +`ContextLoaderListener` looks for a file called `/WEB-INF/applicationContext.xml` to load. Once the context files are loaded, Spring creates a {api-spring-framework}/web/context/WebApplicationContext.html[`WebApplicationContext`] object based on the bean definitions and stores it in the `ServletContext` of the web application. -All Java web frameworks are built on top of the Servlet API, and so one can use the -following code snippet to get access to this 'business context' `ApplicationContext` +All Java web frameworks are built on top of the Servlet API, so you can use the +following code snippet to get access to this "`business context`" `ApplicationContext` created by the `ContextLoaderListener`. +The following example shows how to get the `WebApplicationContext`: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext); ---- +==== The {api-spring-framework}/web/context/support/WebApplicationContextUtils.html[`WebApplicationContextUtils`] -class is for convenience, so you don't have to remember the name of the `ServletContext` -attribute. Its __getWebApplicationContext()__ method will return `null` if an object -doesn't exist under the `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE` -key. Rather than risk getting `NullPointerExceptions` in your application, it's better +class is for convenience, so you need not remember the name of the `ServletContext` +attribute. Its `getWebApplicationContext()` method returns `null` if an object +does not exist under the `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE` +key. Rather than risk getting `NullPointerExceptions` in your application, it is better to use the `getRequiredWebApplicationContext()` method. This method throws an exception when the `ApplicationContext` is missing. @@ -99,29 +100,26 @@ their implemented interfaces. Fortunately, most of the frameworks in this section have simpler ways of looking up beans. Not only do they make it easy to get beans from a Spring container, but they also -allow you to use dependency injection on their controllers. Each web framework section +let you use dependency injection on their controllers. Each web framework section has more detail on its specific integration strategies. - [[jsf]] == JSF + JavaServer Faces (JSF) is the JCP's standard component-based, event-driven web user interface framework. As of Java EE 5, it is an official part of the Java EE umbrella. For a popular JSF runtime as well as for popular JSF component libraries, check out the http://myfaces.apache.org/[Apache MyFaces project]. The MyFaces project also provides -common JSF extensions such as http://myfaces.apache.org/orchestra/[MyFaces Orchestra]: -a Spring-based JSF extension that provides rich conversation scope support. +common JSF extensions, such as http://myfaces.apache.org/orchestra/[MyFaces Orchestra] +(a Spring-based JSF extension that provides rich conversation scope support). -[NOTE] -==== -Spring Web Flow 2.0 provides rich JSF support through its newly established Spring Faces +NOTE: Spring Web Flow 2.0 provides rich JSF support through its newly established Spring Faces module, both for JSF-centric usage (as described in this section) and for Spring-centric -usage (using JSF views within a Spring MVC dispatcher). Check out the -http://projects.spring.io/spring-webflow[Spring Web Flow website] for details! -==== +usage (using JSF views within a Spring MVC dispatcher). See the +http://projects.spring.io/spring-webflow[Spring Web Flow website] for details. The key element in Spring's JSF integration is the JSF `ELResolver` mechanism. @@ -129,15 +127,17 @@ The key element in Spring's JSF integration is the JSF `ELResolver` mechanism. [[jsf-springbeanfaceselresolver]] === Spring Bean Resolver + `SpringBeanFacesELResolver` is a JSF 1.2+ compliant `ELResolver` implementation, -integrating with the standard Unified EL as used by JSF 1.2 and JSP 2.1. Like -`SpringBeanVariableResolver`, it delegates to the Spring's 'business context' -`WebApplicationContext` __first__, then to the default resolver of the underlying JSF +integrating with the standard Unified EL as used by JSF 1.2 and JSP 2.1. As +`SpringBeanVariableResolver`, it delegates to Spring's "`business context`" +`WebApplicationContext` first and then to the default resolver of the underlying JSF implementation. -Configuration-wise, simply define `SpringBeanFacesELResolver` in your JSF -__faces-context.xml__ file: +Configuration-wise, you can define `SpringBeanFacesELResolver` in your JSF +`faces-context.xml` file, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -148,33 +148,39 @@ __faces-context.xml__ file: ---- +==== [[jsf-facescontextutils]] -=== FacesContextUtils -A custom `VariableResolver` works well when mapping one's properties to beans -in __faces-config.xml__, but at times one may need to grab a bean explicitly. The +=== Using `FacesContextUtils` + +A custom `VariableResolver` works well when mapping your properties to beans +in `faces-config.xml`, but, at times, you may need to explicitly grab a bean. The {api-spring-framework}/web/jsf/FacesContextUtils.html[`FacesContextUtils`] class makes this easy. It is similar to `WebApplicationContextUtils`, except that it takes a `FacesContext` parameter rather than a `ServletContext` parameter. +The following example shows how to use `FacesContextUtils`: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance()); ---- - +==== [[struts]] == Apache Struts 2.x -Invented by Craig McClanahan, http://struts.apache.org[Struts] is an open source project + +Invented by Craig McClanahan, http://struts.apache.org[Struts] is an open-source project hosted by the Apache Software Foundation. At the time, it greatly simplified the JSP/Servlet programming paradigm and won over many developers who were using proprietary -frameworks. It simplified the programming model, it was open source (and thus free as in -beer), and it had a large community, which allowed the project to grow and become popular +frameworks. It simplified the programming model, it was open source (and thus free, as in +beer), and it had a large community, which let the project grow and become popular among Java web developers. Check out the Struts @@ -183,28 +189,25 @@ built-in Spring integration shipped with Struts. - [[tapestry]] == Tapestry 5.x -From the http://tapestry.apache.org/[Tapestry homepage]: -Tapestry is a "__Component oriented framework for creating dynamic, robust, -highly scalable web applications in Java.__" +http://tapestry.apache.org/[Tapestry] is a ""Component oriented framework for creating dynamic, robust, +highly scalable web applications in Java."" While Spring has its own <>, there are a number of unique -advantages to building an enterprise Java application using a combination of Tapestry +advantages to building an enterprise Java application by using a combination of Tapestry for the web user interface and the Spring container for the lower layers. -For more information, check out Tapestry's dedicated +For more information, see Tapestry's dedicated https://tapestry.apache.org/integrating-with-spring-framework.html[integration module for Spring]. - [[web-integration-resources]] == Further Resources -Find below links to further resources about the various web frameworks described in this +The following links go to further resources about the various web frameworks described in this chapter. * The http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF] homepage diff --git a/src/docs/asciidoc/web/web-uris.adoc b/src/docs/asciidoc/web/web-uris.adoc index 539ab204e38..1eb04c5450b 100644 --- a/src/docs/asciidoc/web/web-uris.adoc +++ b/src/docs/asciidoc/web/web-uris.adoc @@ -3,8 +3,9 @@ = UriComponents [.small]#Spring MVC and Spring WebFlux# -`UriComponentsBuilder` helps to build URI's from URI templates with variables: +`UriComponentsBuilder` helps to build URI's from URI templates with variables, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -17,13 +18,16 @@ URI uri = uriComponents.expand("Westin", "123").toUri(); // <5> ---- <1> Static factory method with a URI template. -<2> Add and/or replace URI components. +<2> Add or replace URI components. <3> Request to have the URI template and URI variables encoded. <4> Build a `UriComponents`. -<5> Expand variables, and obtain the `URI`. +<5> Expand variables and obtain the `URI`. +==== -The above can be consolidated into one chain and shortened with `buildAndExpand`: +The preceding example can be consolidated into one chain and shortened with `buildAndExpand`, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -34,9 +38,12 @@ The above can be consolidated into one chain and shortened with `buildAndExpand` .buildAndExpand("Westin", "123") .toUri(); ---- +==== -It can be shortened further by going directly to URI (which implies encoding): +You can shorten it further by going directly to a URI (which implies encoding), +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -45,9 +52,11 @@ It can be shortened further by going directly to URI (which implies encoding): .queryParam("q", "{q}") .build("Westin", "123"); ---- +==== -Or shorter further yet, with a full URI template: +You shorter it further still with a full URI template, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -55,24 +64,27 @@ Or shorter further yet, with a full URI template: .fromUriString("http://example.com/hotels/{hotel}?q={q}") .build("Westin", "123"); ---- +==== + [[web-uribuilder]] = UriBuilder [.small]#Spring MVC and Spring WebFlux# -<> implements `UriBuilder`. A `UriBuilder` in turn -can be created with a `UriBuilderFactory`. Together `UriBuilderFactory` and `UriBuilder` +<> implements `UriBuilder`. You can create a `UriBuilder`, in turn, +with a `UriBuilderFactory`. Together, `UriBuilderFactory` and `UriBuilder` provide a pluggable mechanism to build URIs from URI templates, based on shared -configuration such as a base url, encoding preferences, and others. +configuration, such as a base URL, encoding preferences, and other details. -The `RestTemplate` and the `WebClient` can be configured with a `UriBuilderFactory` +You can configure `RestTemplate` and `WebClient` with a `UriBuilderFactory` to customize the preparation of URIs. `DefaultUriBuilderFactory` is a default implementation of `UriBuilderFactory` that uses `UriComponentsBuilder` internally and exposes shared configuration options. -`RestTemplate` example: +The following example shows how to configure a `RestTemplate`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -85,9 +97,11 @@ exposes shared configuration options. RestTemplate restTemplate = new RestTemplate(); restTemplate.setUriTemplateHandler(factory); ---- +==== -`WebClient` example: +The following example configures a `WebClient`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -99,11 +113,13 @@ exposes shared configuration options. WebClient client = WebClient.builder().uriBuilderFactory(factory).build(); ---- +==== -In addition `DefaultUriBuilderFactory` can also be used directly. It is similar to using -`UriComponentsBuilder` but instead of static factory methods, it is an actual instance -that holds configuration and preferences: +In addition, you can also use `DefaultUriBuilderFactory` directly. It is similar to using +`UriComponentsBuilder` but, instead of static factory methods, it is an actual instance +that holds configuration and preferences, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -114,35 +130,35 @@ that holds configuration and preferences: .queryParam("q", "{q}") .build("Westin", "123"); ---- +==== + [[web-uri-encoding]] = URI Encoding [.small]#Spring MVC and Spring WebFlux# -`UriComponentsBuilder` exposes encoding options at 2 levels: +`UriComponentsBuilder` exposes encoding options at two levels: -. {api-spring-framework}/web/util/UriComponentsBuilder.html#encode--[UriComponentsBuilder#encode()] - -pre-encodes the URI template first, then strictly encodes URI variables when expanded. -. {api-spring-framework}/web/util/UriComponents.html#encode--[UriComponents#encode()] - -encodes URI components _after_ URI variables are expanded. +* {api-spring-framework}/web/util/UriComponentsBuilder.html#encode--[UriComponentsBuilder#encode()]: +Pre-encodes the URI template first and then strictly encodes URI variables when expanded. +* {api-spring-framework}/web/util/UriComponents.html#encode--[UriComponents#encode()]: +Encodes URI components _after_ URI variables are expanded. -Both options replace non-ASCII and illegal characters with escaped octets, however option -1 also replaces characters with reserved meaning that appear in URI variables. +Both options replace non-ASCII and illegal characters with escaped octets. However, the first option +also replaces characters with reserved meaning that appear in URI variables. -[TIP] -==== -Consider ";" which is legal in a path but has reserved meaning. Option 1 replaces -";" with "%3B" in URI variables but not in the URI template. By contrast, option 2 never -replaces ";" since it is a legal character in a path. -==== +TIP: Consider ";", which is legal in a path but has reserved meaning. The first option replaces +";" with "%3B" in URI variables but not in the URI template. By contrast, the second option never +replaces ";", since it is a legal character in a path. -For most cases option 1 is likely to give the expected result because it treats URI +For most cases, the first option is likely to give the expected result, because it treats URI variables as opaque data to be fully encoded, while option 2 is useful only if URI variables intentionally contain reserved characters. -Example usage using option 1: +The following example uses the first option: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -154,9 +170,12 @@ URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}") // Result is "/hotel%20list/New%20York?q=foo%2Bbar" ---- +==== -The above can be shortened by going directly to URI (which implies encoding): +You can shorten the preceding example by going directly to the URI (which implies encoding), +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -164,19 +183,24 @@ URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}") .queryParam("q", "{q}") .build("New York", "foo+bar") ---- +==== -Or shorter further yet, with a full URI template: +You can shorten it further still with a full URI template, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}?q={q}") .build("New York", "foo+bar") ---- +==== The `WebClient` and the `RestTemplate` expand and encode URI templates internally through -the `UriBuilderFactory` strategy. Both can be configured with a custom strategy: +the `UriBuilderFactory` strategy. Both can be configured with a custom strategy. +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -191,22 +215,23 @@ the `UriBuilderFactory` strategy. Both can be configured with a custom strategy: // Customize the WebClient.. WebClient client = WebClient.builder().uriBuilderFactory(factory).build(); ---- +==== The `DefaultUriBuilderFactory` implementation uses `UriComponentsBuilder` internally to -expand and encode URI templates. As a factory it provides a single place to configure -the approach to encoding based on one of the below encoding modes: +expand and encode URI templates. As a factory, it provides a single place to configure +the approach to encoding, based on one of the below encoding modes: -* `TEMPLATE_AND_VALUES` -- uses `UriComponentsBuilder#encode()`, corresponding to -option 1 above, to pre-encode the URI template and strictly encode URI variables when +* `TEMPLATE_AND_VALUES`: Uses `UriComponentsBuilder#encode()`, corresponding to +the first option in the earlier list, to pre-encode the URI template and strictly encode URI variables when expanded. -* `VALUES_ONLY` -- does not encode the URI template and instead applies strict encoding -to URI variables via `UriUtils#encodeUriUriVariables` prior to expanding them into the +* `VALUES_ONLY`: Does not encode the URI template and, instead, applies strict encoding +to URI variables through `UriUtils#encodeUriUriVariables` prior to expanding them into the template. -* `URI_COMPONENTS` -- uses `UriComponents#encode()`, corresponding to option 2 above, to +* `URI_COMPONENTS`: Uses `UriComponents#encode()`, corresponding to the second option in the earlier list, to encode URI component value _after_ URI variables are expanded. -* `NONE` -- no encoding is applied. +* `NONE`: No encoding is applied. -Out of the box the `RestTemplate` is set to `EncodingMode.URI_COMPONENTS` for historic +The `RestTemplate` is set to `EncodingMode.URI_COMPONENTS` for historic reasons and for backwards compatibility. The `WebClient` relies on the default value -in `DefaultUriBuilderFactory` which was changed from `EncodingMode.URI_COMPONENTS` in -5.0.x to `EncodingMode.TEMPLATE_AND_VALUES` in 5.1. \ No newline at end of file +in `DefaultUriBuilderFactory`, which was changed from `EncodingMode.URI_COMPONENTS` in +5.0.x to `EncodingMode.TEMPLATE_AND_VALUES` in 5.1. diff --git a/src/docs/asciidoc/web/webflux-cors.adoc b/src/docs/asciidoc/web/webflux-cors.adoc index 2b5fc622cd4..8bba4da74e3 100644 --- a/src/docs/asciidoc/web/webflux-cors.adoc +++ b/src/docs/asciidoc/web/webflux-cors.adoc @@ -1,84 +1,86 @@ [[webflux-cors]] = CORS -[.small]#<># +[.small]#<># +Spring WebFlux lets you handle CORS (Cross-Origin Resource Sharing). This section +describes how to do so. [[webflux-cors-intro]] == Introduction -[.small]#<># +[.small]#<># -For security reasons browsers prohibit AJAX calls to resources outside the current origin. -For example you could have your bank account in one tab and evil.com in another. Scripts +For security reasons, browsers prohibit AJAX calls to resources outside the current origin. +For example, you could have your bank account in one tab and evil.com in another. Scripts from evil.com should not be able to make AJAX requests to your bank API with your -credentials, e.g. withdrawing money from your account! +credentials -- for example, withdrawing money from your account! Cross-Origin Resource Sharing (CORS) is a http://www.w3.org/TR/cors/[W3C specification] -implemented by http://caniuse.com/#feat=cors[most browsers] that allows you to specify -what kind of cross domain requests are authorized rather than using less secure and less +implemented by http://caniuse.com/#feat=cors[most browsers] that lets you specify +what kind of cross-domain requests are authorized, rather than using less secure and less powerful workarounds based on IFRAME or JSONP. - [[webflux-cors-processing]] == Processing -[.small]#<># +[.small]#<># The CORS specification distinguishes between preflight, simple, and actual requests. To learn how CORS works, you can read https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[this article], among -many others, or refer to the specification for more details. +many others, or see the specification for more details. -Spring WebFlux ``HandlerMapping``'s provide built-in support for CORS. After successfully -mapping a request to a handler, ``HandlerMapping``'s check the CORS configuration for the -given request and handler and take further actions. Preflight requests are handled -directly while simple and actual CORS requests are intercepted, validated, and have +Spring WebFlux `HandlerMapping` implementations provide built-in support for CORS. After successfully +mapping a request to a handler, a `HandlerMapping` checks the CORS configuration for the +given request and handler and takes further actions. Preflight requests are handled +directly, while simple and actual CORS requests are intercepted, validated, and have the required CORS response headers set. -In order to enable cross-origin requests (i.e. the `Origin` header is present and -differs from the host of the request) you need to have some explicitly declared CORS +In order to enable cross-origin requests (that is, the `Origin` header is present and +differs from the host of the request), you need to have some explicitly declared CORS configuration. If no matching CORS configuration is found, preflight requests are rejected. No CORS headers are added to the responses of simple and actual CORS requests -and consequently browsers reject them. +and, consequently, browsers reject them. Each `HandlerMapping` can be {api-spring-framework}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured] -individually with URL pattern based `CorsConfiguration` mappings. In most cases applications -will use the WebFlux Java config to declare such mappings, which results in a single, -global map passed to all ``HadlerMappping``'s. +individually with URL pattern-based `CorsConfiguration` mappings. In most cases, applications +use the WebFlux Java configuration to declare such mappings, which results in a single, +global map passed to all `HadlerMappping` implementations. -Global CORS configuration at the `HandlerMapping` level can be combined with more -fine-grained, handler-level CORS configuration. For example annotated controllers can use -class or method-level `@CrossOrigin` annotations (other handlers can implement +You can combine global CORS configuration at the `HandlerMapping` level with more +fine-grained, handler-level CORS configuration. For example, annotated controllers can use +class- or method-level `@CrossOrigin` annotations (other handlers can implement `CorsConfigurationSource`). -The rules for combining global and local configuration are generally additive -- e.g. +The rules for combining global and local configuration are generally additive -- for example, all global and all local origins. For those attributes where only a single value can be -accepted such as `allowCredentials` and `maxAge`, the local overrides the global value. See +accepted, such as `allowCredentials` and `maxAge`, the local overrides the global value. See {api-spring-framework}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`] for more details. [TIP] ==== -To learn more from the source or make advanced customizations, check: +To learn more from the source or to make advanced customizations, see: * `CorsConfiguration` -* `CorsProcessor`, `DefaultCorsProcessor` +* `CorsProcessor` and `DefaultCorsProcessor` * `AbstractHandlerMapping` ==== - [[webflux-cors-controller]] -== @CrossOrigin -[.small]#<># +== Using `@CrossOrigin` +[.small]#<># The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`] -annotation enables cross-origin requests on annotated controller methods: +annotation enables cross-origin requests on annotated controller methods, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -98,19 +100,25 @@ public class AccountController { } } ---- +==== -By default `@CrossOrigin` allows: +By default, `@CrossOrigin` allows: * All origins. * All headers. * All HTTP methods to which the controller method is mapped. -* `allowedCredentials` is not enabled by default since that establishes a trust level -that exposes sensitive user-specific information such as cookies and CSRF tokens, and -should only be used where appropriate. -* `maxAge` is set to 30 minutes. -`@CrossOrigin` is supported at the class level too and inherited by all methods: +`allowedCredentials` is not enabled by default, since that establishes a trust level +that exposes sensitive user-specific information (such as cookies and CSRF tokens) and +should be used only where appropriate. + +`maxAge` is set to 30 minutes. + +`@CrossOrigin` is supported at the class level, too, and inherited by all methods. +The following example specifies a certain domain and sets `maxAge` to an hour: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -130,18 +138,21 @@ public class AccountController { } } ---- +==== -`CrossOrigin` can be used at both class and method-level: +You can use `@CrossOrigin` at both the class and the method level, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- -@CrossOrigin(maxAge = 3600) +@CrossOrigin(maxAge = 3600) <1> @RestController @RequestMapping("/account") public class AccountController { - @CrossOrigin("http://domain2.com") + @CrossOrigin("http://domain2.com") <2> @GetMapping("/{id}") public Mono retrieve(@PathVariable Long id) { // ... @@ -153,31 +164,37 @@ public class AccountController { } } ---- - +<1> Using `@CrossOrigin` at the class level. +<2> Using `@CrossOrigin` at the method level. +==== [[webflux-cors-global]] -== Global Config -[.small]#<># +== Global Configuration +[.small]#<># -In addition to fine-grained, controller method level configuration you'll probably want to -define some global CORS configuration too. You can set URL-based `CorsConfiguration` -mappings individually on any `HandlerMapping`. Most applications however will use the -WebFlux Java config to do that. +In addition to fine-grained, controller method-level configuration, you probably want to +define some global CORS configuration, too. You can set URL-based `CorsConfiguration` +mappings individually on any `HandlerMapping`. Most applications, however, use the +WebFlux Java configuration to do that. By default global configuration enables the following: * All origins. * All headers. * `GET`, `HEAD`, and `POST` methods. -* `allowedCredentials` is not enabled by default since that establishes a trust level -that exposes sensitive user-specific information such as cookies and CSRF tokens, and -should only be used where appropriate. -* `maxAge` is set to 30 minutes. -To enable CORS in the WebFlux Java config, use the `CorsRegistry` callback: +`allowedCredentials` is not enabled by default, since that establishes a trust level +that exposes sensitive user-specific information( such as cookies and CSRF tokens) and +should be used only where appropriate. + +`maxAge` is set to 30 minutes. +To enable CORS in the WebFlux Java configuration, you can use the `CorsRegistry` callback, +as the following example shows: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -199,19 +216,22 @@ public class WebConfig implements WebFluxConfigurer { } } ---- +==== + [[webflux-cors-webfilter]] -== CORS WebFilter -[.small]#<># +== CORS `WebFilter` +[.small]#<># You can apply CORS support through the built-in {api-spring-framework}/web/cors/reactive/CorsWebFilter.html[`CorsWebFilter`], which is a good fit with <>. To configure the filter, you can declare a `CorsWebFilter` bean and pass a -`CorsConfigurationSource` to its constructor: +`CorsConfigurationSource` to its constructor, as the following example shows: +==== [source,java,indent=0] [subs="verbatim"] ---- @@ -234,4 +254,4 @@ CorsWebFilter corsFilter() { return new CorsWebFilter(source); } ---- - +==== diff --git a/src/docs/asciidoc/web/webflux-functional.adoc b/src/docs/asciidoc/web/webflux-functional.adoc index e04b099c412..1c679a1fe30 100644 --- a/src/docs/asciidoc/web/webflux-functional.adoc +++ b/src/docs/asciidoc/web/webflux-functional.adoc @@ -1,31 +1,31 @@ [[webflux-fn]] = Functional Endpoints -Spring WebFlux includes a lightweight, functional programming model in which functions +Spring WebFlux includes a lightweight functional programming model in which functions are used to route and handle requests and contracts are designed for immutability. -It is an alternative to the annotated-based programming model but otherwise running on -the same <> foundation - +It is an alternative to the annotation-based programming model but otherwise runs on +the same <> foundation. [[webflux-fn-overview]] == Overview -An HTTP request is handled with a **`HandlerFunction`** that takes `ServerRequest` and -returns `Mono`, both of which are immutable contracts that offer JDK-8 -friendly access to the HTTP request and response. `HandlerFunction` is the equivalent of -an `@RequestMapping` method in the annotation-based programming model. +An HTTP request is handled with a `HandlerFunction` that takes `ServerRequest` and +returns `Mono`, both of which are immutable contracts that offer +JDK 8-friendly access to the HTTP request and response. `HandlerFunction` is the equivalent of +a `@RequestMapping` method in the annotation-based programming model. -Requests are routed to a `HandlerFunction` with a **`RouterFunction`** that takes +Requests are routed to a `HandlerFunction` with a `RouterFunction` that takes `ServerRequest` and returns `Mono`. When a request is matched to a particular route, the `HandlerFunction` mapped to the route is used. `RouterFunction` is -the equivalent of an `@RequestMapping` annotation. +the equivalent of a `@RequestMapping` annotation. `RouterFunctions.route(RequestPredicate, HandlerFunction)` provides a router function -default implementation that can be used with a number of built-in request predicates. -For example: +default implementation that can be used with a number of built-in request predicates, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -59,6 +59,7 @@ public class PersonHandler { } } ---- +==== One way to run a `RouterFunction` is to turn it into an `HttpHandler` and install it through one of the built-in <>: @@ -66,96 +67,137 @@ through one of the built-in <>. - +Most applications can run through the WebFlux Java configuration, see <>. [[webflux-fn-handler-functions]] == HandlerFunction -`ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK-8 friendly +`ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK 8-friendly access to the HTTP request and response with http://www.reactive-streams.org[Reactive Streams] back pressure against the request and response body stream. The request body is represented with a Reactor `Flux` or `Mono`. The response body is represented with any Reactive Streams `Publisher`, including `Flux` -and `Mono`. For more on that see +and `Mono`. For more on that, see <>. [[webflux-fn-request]] -=== ServerRequest +=== Using `ServerRequest` -`ServerRequest` provides access to the HTTP method, URI, headers, and query parameters +`ServerRequest` provides access to the HTTP method, URI, headers, and query parameters, while access to the body is provided through the `body` methods. -To extract the request body to a `Mono`: +The following example extracts the request body to a `Mono`: - Mono string = request.bodyToMono(String.class); +==== +[source,java] +---- +Mono string = request.bodyToMono(String.class); +---- +==== -To extract the body to a `Flux`, where `Person` objects are decoded from some +The following example extracts the body to a `Flux`, where `Person` objects are decoded from some serialized form, such as JSON or XML: - Flux people = request.bodyToFlux(Person.class); +==== +[source,java] +---- +Flux people = request.bodyToFlux(Person.class); +---- +==== -The above are shortcuts that use the more general `ServerRequest.body(BodyExtractor)` -which accepts the `BodyExtractor` functional, strategy interface. The utility class -`BodyExtractors` provides access to a number of instances. For example, the above can +The preceding examples are shortcuts that use the more general `ServerRequest.body(BodyExtractor)`, +which accepts the `BodyExtractor` functional strategy interface. The utility class +`BodyExtractors` provides access to a number of instances. For example, the preceding examples can also be written as follows: - Mono string = request.body(BodyExtractors.toMono(String.class)); - Flux people = request.body(BodyExtractors.toFlux(Person.class)); +==== +[source,java] +---- +Mono string = request.body(BodyExtractors.toMono(String.class)); +Flux people = request.body(BodyExtractors.toFlux(Person.class)); +---- +==== -To access form data: +The following example shows how to access form data: - Mono map = request.body(BodyExtractors.toFormData()); +==== +[source,java] +---- +Mono map = request.body(BodyExtractors.toFormData()); +---- +==== -To access multipart data as a map: +The following example shows how to access multipart data as a map: - Mono map = request.body(BodyExtractors.toMultipartData()); +==== +[source,java] +---- +Mono map = request.body(BodyExtractors.toMultipartData()); +---- +==== -To access multiparts, one at a time, in streaming fashion: +The following example shows how to access multiparts, one at a time, in streaming fashion: - Flux parts = request.body(BodyExtractos.toParts()); +==== +[source,java] +---- +Flux parts = request.body(BodyExtractos.toParts()); +---- +==== [[webflux-fn-response]] -=== ServerResponse +=== Using `ServerResponse` -`ServerResponse` provides access to the HTTP response and since it is immutable, you use -a build to create it. The builder can be used to set the response status, to add response -headers, or to provide a body. Below is an example with a 200 (OK) response with JSON +`ServerResponse` provides access to the HTTP response and, since it is immutable, you can use +a `build` method to create it. You can use the builder to set the response status, to add response +headers, or to provide a body. The following example creates a 200 (OK) response with JSON content: - Mono person = ... - ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class); +==== +[source,java] +---- +Mono person = ... +ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class); +---- +==== -This is how to build a 201 (CREATED) response with `"Location"` header, and no body: +The following example shows how to build a 201 (CREATED) response with a `Location` header and no body: - URI location = ... - ServerResponse.created(location).build(); +==== +[source,java] +---- +URI location = ... +ServerResponse.created(location).build(); +---- +==== [[webflux-fn-handler-classes]] === Handler Classes -We can write a handler function as a lambda. For example: +We can write a handler function as a lambda, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- HandlerFunction helloWorld = request -> ServerResponse.ok().body(fromObject("Hello World")); ---- +==== -That is convenient but in an application we need multiple functions and useful to group -related handler functions together into a handler (like an `@Controller`). For example, -here is a class that exposes a reactive `Person` repository: +That is convenient, but, in an application, we need multiple functions, and it is useful to group +related handler functions together into a handler (like a `@Controller`). For example, +the following class exposes a reactive `Person` repository: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -192,35 +234,36 @@ public class PersonHandler { <1> `listPeople` is a handler function that returns all `Person` objects found in the repository as JSON. <2> `createPerson` is a handler function that stores a new `Person` contained in the request body. -Note that `PersonRepository.savePerson(Person)` returns `Mono`: an empty Mono that emits +Note that `PersonRepository.savePerson(Person)` returns `Mono`: an empty `Mono` that emits a completion signal when the person has been read from the request and stored. So we use the -`build(Publisher)` method to send a response when that completion signal is received, i.e. -when the `Person` has been saved. -<3> `getPerson` is a handler function that returns a single person, identified via the path -variable `id`. We retrieve that `Person` via the repository, and create a JSON response if it is +`build(Publisher)` method to send a response when that completion signal is received (that is, +when the `Person` has been saved). +<3> `getPerson` is a handler function that returns a single person, identified by the `id` path +variable. We retrieve that `Person` from the repository and create a JSON response, if it is found. If it is not found, we use `switchIfEmpty(Mono)` to return a 404 Not Found response. - +==== [[webflux-fn-router-functions]] -== RouterFunction +== Using `RouterFunction` `RouterFunction` is used to route requests to a `HandlerFunction`. Typically, you do not write router functions yourself, but rather use `RouterFunctions.route(RequestPredicate, HandlerFunction)`. If the predicate applies, the -request is routed to the given `HandlerFunction`, or otherwise no routing is performed, +request is routed to the given `HandlerFunction`. Otherwise, no routing is performed, and that would translate to a 404 (Not Found) response. [[webflux-fn-predicates]] -=== Predicates +=== Using Predicates You can write your own `RequestPredicate`, but the `RequestPredicates` utility class -offers commonly implementations, based on the request path, HTTP method, content-type, -and so on. For example: +offers commonly used implementations, based on the request path, HTTP method, content-type, +and so on. The following example creates a request predicate based on a path: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -228,18 +271,19 @@ RouterFunction route = RouterFunctions.route(RequestPredicates.path("/hello-world"), request -> Response.ok().body(fromObject("Hello World"))); ---- +==== -You can compose multiple request predicates together via: +You can compose multiple request predicates together by using: * `RequestPredicate.and(RequestPredicate)` -- both must match. -* `RequestPredicate.or(RequestPredicate)` -- either may match. +* `RequestPredicate.or(RequestPredicate)` -- either can match. -Many of the predicates from `RequestPredicates` are composed. For example +Many of the predicates from `RequestPredicates` are composed. For example, `RequestPredicates.GET(String)` is composed from `RequestPredicates.method(HttpMethod)` and `RequestPredicates.path(String)`. -You can compose multiple router functions into one, such that they're evaluated in order, -and if the first route doesn't match, the second is evaluated. You can declare more +You can compose multiple router functions into one, such that they are evaluated in order, +and, if the first route does not match, the second is evaluated. You can declare more specific routes before more general ones. @@ -247,16 +291,17 @@ specific routes before more general ones. [[webflux-fn-routes]] === Routes -You can compose multiple router functions together via: +You can compose multiple router functions together by using: * `RouterFunction.and(RouterFunction)` * `RouterFunction.andRoute(RequestPredicate, HandlerFunction)` -- shortcut for `RouterFunction.and()` with nested `RouterFunctions.route()`. Using composed routes and predicates, we can then declare the following routes, referring -to methods in the `PersonHandler`, shown in <>, through +to methods in the `PersonHandler` (shown in <>) through https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html[method-references]: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -271,45 +316,45 @@ RouterFunction personRoute = .andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople) .andRoute(POST("/person"), handler::createPerson); ---- - - +==== [[webflux-fn-running]] -== Running a server +== Running a Server How do you run a router function in an HTTP server? A simple option is to convert a router -function to an `HttpHandler` using one of the following: +function to an `HttpHandler` by using one of the following: * `RouterFunctions.toHttpHandler(RouterFunction)` * `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)` -The returned `HttpHandler` can then be used with a number of servers adapters by following +You can then use the returned `HttpHandler` with a number of server adapters by following <> for server-specific instructions. A more advanced option is to run with a -<>-based setup through the -<> which uses Spring configuration to declare the -components quired to process requests. The WebFlux Java config declares the following +<>-based setup through the +<>, which uses Spring configuration to declare the +components required to process requests. The WebFlux Java configuration declares the following infrastructure components to support functional endpoints: -* `RouterFunctionMapping` -- detects one or more `RouterFunction` beans in the Spring -configuration, combines them via `RouterFunction.andOther`, and routes requests to the +* `RouterFunctionMapping`: Detects one or more `RouterFunction` beans in the Spring +configuration, combines them through `RouterFunction.andOther`, and routes requests to the resulting composed `RouterFunction`. -* `HandlerFunctionAdapter` -- simple adapter that allows the `DispatcherHandler` to invoke +* `HandlerFunctionAdapter`: Simple adapter that lets `DispatcherHandler` invoke a `HandlerFunction` that was mapped to a request. -* `ServerResponseResultHandler` -- handles the result from the invocation of a +* `ServerResponseResultHandler`: Handles the result from the invocation of a `HandlerFunction` by invoking the `writeTo` method of the `ServerResponse`. -The above components allow functional endpoints to fit within the `DispatcherHandler` request -processing lifecycle, and also potentially run side by side with annotated controllers, if -any are declared. It is also how functional endpoints are enabled the Spring Boot WebFlux +The preceding components let functional endpoints fit within the `DispatcherHandler` request +processing lifecycle and also (potentially) run side by side with annotated controllers, if +any are declared. It is also how functional endpoints are enabled by the Spring Boot WebFlux starter. -Below is example WebFlux Java config (see -<> for how to run): +The following example shows a WebFlux Java configuration (see +<> for how to run it): +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -345,23 +390,24 @@ public class WebConfig implements WebFluxConfigurer { } } ---- - +==== [[webflux-fn-handler-filter-function]] -== HandlerFilterFunction +== Using `HandlerFilterFunction` -Routes mapped by a router function can be filtered by calling +You can filter routes mapped by a router function by calling `RouterFunction.filter(HandlerFilterFunction)`, where `HandlerFilterFunction` is essentially a -function that takes a `ServerRequest` and `HandlerFunction`, and returns a `ServerResponse`. -The handler function parameter represents the next element in the chain: this is typically the -`HandlerFunction` that is routed to, but can also be another `FilterFunction` if multiple filters +function that takes a `ServerRequest` and `HandlerFunction` and returns a `ServerResponse`. +The handler function parameter represents the next element in the chain. This is typically the +`HandlerFunction` that is routed to, but it can also be another `FilterFunction` if multiple filters are applied. -With annotations, similar functionality can be achieved using `@ControllerAdvice` and/or a `ServletFilter`. -Let's add a simple security filter to our route, assuming that we have a `SecurityManager` that -can determine whether a particular path is allowed: +With annotations, you can achieve similar functionality by using `@ControllerAdvice`, a `ServletFilter`, or both. +Now we can add a simple security filter to our route, assuming that we have a `SecurityManager` that +can determine whether a particular path is allowed. The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -380,11 +426,9 @@ RouterFunction filteredRoute = } }); ---- +==== -You can see in this example that invoking the `next.handle(ServerRequest)` is optional: we only -allow the handler function to be executed when access is allowed. +The preceding example demonstrates that invoking the `next.handle(ServerRequest)` is optional. We +allow only the handler function to be executed when access is allowed. -[NOTE] -==== -CORS support for functional endpoints is provided via a dedicated <>. -==== +NOTE: CORS support for functional endpoints is provided through a dedicated <>. diff --git a/src/docs/asciidoc/web/webflux-view.adoc b/src/docs/asciidoc/web/webflux-view.adoc index 050ee99773e..4844488c422 100644 --- a/src/docs/asciidoc/web/webflux-view.adoc +++ b/src/docs/asciidoc/web/webflux-view.adoc @@ -1,52 +1,51 @@ [[webflux-view]] = View Technologies -[.small]#<># +[.small]#<># -The use of view technologies in Spring WebFlux is pluggable, whether you decide to -use Thymeleaf, FreeMarker, or other, is primarily a matter of a configuration change. -This chapter covers view technologies integrated with Spring WebFlux. We assume you are +The use of view technologies in Spring WebFlux is pluggable. Whether you decide to +use Thymeleaf, FreeMarker, or some other view technology is primarily a matter of a configuration change. +This chapter covers the view technologies integrated with Spring WebFlux. We assume you are already familiar with <>. - [[webflux-view-thymeleaf]] == Thymeleaf -[.small]#<># +[.small]#<># -Thymeleaf is modern server-side Java template engine that emphasizes natural HTML +Thymeleaf is a modern server-side Java template engine that emphasizes natural HTML templates that can be previewed in a browser by double-clicking, which is very -helpful for independent work on UI templates, e.g. by designer, without the need for a -running server. Thymeleaf offers an extensive set of features and it is actively developed -and maintained. For a more complete introduction see the +helpful for independent work on UI templates (for example, by a designer) without the need for a +running server. Thymeleaf offers an extensive set of features, and it is actively developed +and maintained. For a more complete introduction, see the http://www.thymeleaf.org/[Thymeleaf] project home page. The Thymeleaf integration with Spring WebFlux is managed by the Thymeleaf project. The -configuration involves a few bean declarations such as +configuration involves a few bean declarations, such as `SpringResourceTemplateResolver`, `SpringWebFluxTemplateEngine`, and -`ThymeleafReactiveViewResolver`. For more details see +`ThymeleafReactiveViewResolver`. For more details, see http://www.thymeleaf.org/documentation.html[Thymeleaf+Spring] and the WebFlux integration http://forum.thymeleaf.org/Thymeleaf-3-0-8-JUST-PUBLISHED-td4030687.html[announcement]. - [[webflux-view-freemarker]] == FreeMarker -[.small]#<># +[.small]#<># http://www.freemarker.org[Apache FreeMarker] is a template engine for generating any -kind of text output from HTML to email, and others. The Spring Framework has a built-in +kind of text output from HTML to email and others. The Spring Framework has a built-in integration for using Spring WebFlux with FreeMarker templates. [[webflux-view-freemarker-contextconfig]] -=== View config -[.small]#<># +=== View Configuration +[.small]#<># -To configure FreeMarker as a view technology: +The following example shows how to configure FreeMarker as a view technology: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -69,24 +68,26 @@ To configure FreeMarker as a view technology: } } ---- +==== -Your templates need to be stored in the directory specified by the `FreeMarkerConfigurer` -shown above. Given the above configuration if your controller returns the view name -"welcome" then the resolver will look for the +Your templates need to be stored in the directory specified by the `FreeMarkerConfigurer`, +shown in the preceding example. Given the preceding configuration, if your controller returns the view name, +`welcome`, the resolver looks for the `classpath:/templates/freemarker/welcome.ftl` template. [[webflux-views-freemarker]] -=== FreeMarker config -[.small]#<># +=== FreeMarker Configuration +[.small]#<># -FreeMarker 'Settings' and 'SharedVariables' can be passed directly to the FreeMarker -`Configuration` object managed by Spring by setting the appropriate bean properties on +You can pass FreeMarker 'Settings' and 'SharedVariables' directly to the FreeMarker +`Configuration` object (managed by Spring) by setting the appropriate bean properties on the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a -`java.util.Properties` object and the `freemarkerVariables` property requires a -`java.util.Map`. +`java.util.Properties` object, and the `freemarkerVariables` property requires a +`java.util.Map`. The following example shows how to use a `FreeMarkerConfigurer`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -108,53 +109,53 @@ the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a } } ---- +==== See the FreeMarker documentation for details of settings and variables as they apply to the `Configuration` object. - [[webflux-view-script]] == Script Views -[.small]#<># +[.small]#<># The Spring Framework has a built-in integration for using Spring WebFlux with any templating library that can run on top of the -https://www.jcp.org/en/jsr/detail?id=223[JSR-223] Java scripting engine. Below is a list -of templating libraries we've tested on different script engines: - -[horizontal] -http://handlebarsjs.com/[Handlebars] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -https://mustache.github.io/[Mustache] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -http://facebook.github.io/react/[React] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -http://www.embeddedjs.com/[EJS] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -http://www.stuartellis.eu/articles/erb/[ERB] :: http://jruby.org[JRuby] -https://docs.python.org/2/library/string.html#template-strings[String templates] :: http://www.jython.org/[Jython] -https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] :: http://kotlinlang.org/[Kotlin] - -[TIP] -==== -The basic rule for integrating any other script engine is that it must implement the +https://www.jcp.org/en/jsr/detail?id=223[JSR-223] Java scripting engine. +The following table shows the templating libraries that we have tested on different script engines: + +[%header] +|=== +|Scripting Library |Scripting Engine +|http://handlebarsjs.com/[Handlebars] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|https://mustache.github.io/[Mustache] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|http://facebook.github.io/react/[React] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|http://www.embeddedjs.com/[EJS] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|http://www.stuartellis.eu/articles/erb/[ERB] |http://jruby.org[JRuby] +|https://docs.python.org/2/library/string.html#template-strings[String templates] |http://www.jython.org/[Jython] +|https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] |http://kotlinlang.org/[Kotlin] +|=== + +TIP: The basic rule for integrating any other script engine is that it must implement the `ScriptEngine` and `Invocable` interfaces. -==== [[webflux-view-script-dependencies]] === Requirements -[.small]#<># +[.small]#<># -You need to have the script engine on your classpath: +You need to have the script engine on your classpath, the details of which vary by script engine: -* http://openjdk.java.net/projects/nashorn/[Nashorn] JavaScript engine is provided with +* The http://openjdk.java.net/projects/nashorn/[Nashorn] JavaScript engine is provided with Java 8+. Using the latest update release available is highly recommended. * http://jruby.org[JRuby] should be added as a dependency for Ruby support. * http://www.jython.org[Jython] should be added as a dependency for Python support. * `org.jetbrains.kotlin:kotlin-script-util` dependency and a `META-INF/services/javax.script.ScriptEngineFactory` file containing a `org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory` - line should be added for Kotlin script support, see - https://github.com/sdeleuze/kotlin-script-templating[this example] for more details. + line should be added for Kotlin script support. See + https://github.com/sdeleuze/kotlin-script-templating[this example] for more detail. You need to have the script templating library. One way to do that for Javascript is through http://www.webjars.org/[WebJars]. @@ -162,13 +163,14 @@ through http://www.webjars.org/[WebJars]. [[webflux-view-script-integrate]] -=== Script templates -[.small]#<># +=== Script Templates +[.small]#<># -Declare a `ScriptTemplateConfigurer` bean in order to specify the script engine to use, +You can declare a `ScriptTemplateConfigurer` bean to specify the script engine to use, the script files to load, what function to call to render templates, and so on. -Below is an example with Mustache templates and the Nashorn JavaScript engine: +The following example uses Mustache templates and the Nashorn JavaScript engine: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -192,24 +194,27 @@ Below is an example with Mustache templates and the Nashorn JavaScript engine: } } ---- +==== -The render function is called with the following parameters: +The `render` function is called with the following parameters: -* `String template`: the template content -* `Map model`: the view model -* `RenderingContext renderingContext`: the - {api-spring-framework}/web/servlet/view/script/RenderingContext.html[RenderingContext] - that gives access to the application context, the locale, the template loader and the - url (since 5.0) +* `String template`: The template content +* `Map model`: The view model +* `RenderingContext renderingContext`: The + {api-spring-framework}/web/servlet/view/script/RenderingContext.html[`RenderingContext`] + that gives access to the application context, the locale, the template loader, and the + URL (since 5.0) `Mustache.render()` is natively compatible with this signature, so you can call it directly. -If your templating technology requires some customization, you may provide a script that +If your templating technology requires some customization, you can provide a script that implements a custom render function. For example, http://handlebarsjs.com[Handlerbars] -needs to compile templates before using them, and requires a +needs to compile templates before using them and requires a http://en.wikipedia.org/wiki/Polyfill[polyfill] in order to emulate some browser facilities not available in the server-side script engine. +The following example shows how to set a custom render function: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -233,28 +238,31 @@ browser facilities not available in the server-side script engine. } } ---- - -[NOTE] ==== -Setting the `sharedEngine` property to `false` is required when using non thread-safe -script engines with templating libraries not designed for concurrency, like Handlebars or -React running on Nashorn for example. In that case, Java 8u60 or greater is required due + +NOTE: Setting the `sharedEngine` property to `false` is required when using non-thread-safe +script engines with templating libraries not designed for concurrency, such as Handlebars or +React running on Nashorn. In that case, Java 8u60 or greater is required, due to https://bugs.openjdk.java.net/browse/JDK-8076099[this bug]. -==== -`polyfill.js` only defines the `window` object needed by Handlebars to run properly: +`polyfill.js` defines only the `window` object needed by Handlebars to run properly, +as the following snippet shows: +==== [source,javascript,indent=0] [subs="verbatim,quotes"] ---- var window = {}; ---- +==== This basic `render.js` implementation compiles the template before using it. A production -ready implementation should also store and reused cached templates / pre-compiled templates. +ready implementation should also store and reused cached templates or pre-compiled templates. This can be done on the script side, as well as any customization you need (managing template engine configuration for example). +The following example shows how compile a template: +==== [source,javascript,indent=0] [subs="verbatim,quotes"] ---- @@ -263,34 +271,31 @@ template engine configuration for example). return compiledTemplate(model); } ---- +==== Check out the Spring Framework unit tests, -https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/script[java], and +https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/script[Java], and https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/resources/org/springframework/web/reactive/result/view/script[resources], for more configuration examples. [[webflux-view-httpmessagewriter]] -== JSON, XML -[.small]#<># - -For <> purposes it is useful to be able to alternate -between rendering a model with an HTML template or as other formats such as JSON or XML, -depending on the content type requested by the client. To support this Spring WebFlux -provides the `HttpMessageWriterView` that can be used to plug in any of the available -<> from `spring-web` such as `Jackson2JsonEncoder`, +== JSON and XML +[.small]#<># + +For <> purposes, it is useful to be able to alternate +between rendering a model with an HTML template or as other formats (such as JSON or XML), +depending on the content type requested by the client. To support doing so, Spring WebFlux +provides the `HttpMessageWriterView`, which you can use to plug in any of the available +<> from `spring-web`, such as `Jackson2JsonEncoder`, `Jackson2SmileEncoder`, or `Jaxb2XmlEncoder`. -Unlike other view technologies, `HttpMessageWriterView` does not require a `ViewResolver`, -but instead is <> as a default view. You can -configure one more such default views, wrapping different ``HttpMessageWriter``'s or -``Encoder``'s. The one that matches the requested content type is used at runtime. - -In most cases a model will contain multiple attributes. In order to determine which one -to serialize, `HttpMessageWriterView` can be configured with the name of the model -attribute to use render, of if the model contains only one attribute, it will be used. - - - +Unlike other view technologies, `HttpMessageWriterView` does not require a `ViewResolver` +but is instead <> as a default view. You can +configure one or more such default views, wrapping different `HttpMessageWriter` instances or +`Encoder` instances. The one that matches the requested content type is used at runtime. +In most cases, a model contains multiple attributes. To determine which one +to serialize, you can configure `HttpMessageWriterView` with the name of the model +attribute to use for rendering. If the model contains only one attribute, that one is used. diff --git a/src/docs/asciidoc/web/webflux-webclient.adoc b/src/docs/asciidoc/web/webflux-webclient.adoc index d987982d39d..a0b225cef31 100644 --- a/src/docs/asciidoc/web/webflux-webclient.adoc +++ b/src/docs/asciidoc/web/webflux-webclient.adoc @@ -7,14 +7,13 @@ using a functional-style API that exposes Reactor `Flux` and `Mono` types, see <> that WebFlux server applications use to work with request and response content. -Internally `WebClient` delegates to an HTTP client library. By default it uses +Internally `WebClient` delegates to an HTTP client library. By default, it uses https://github.com/reactor/reactor-netty[Reactor Netty], there is built-in support for the Jetty https://github.com/jetty-project/jetty-reactive-httpclient[reactive HtpClient], and others can be plugged in through a `ClientHttpConnector`. - [[webflux-client-builder]] == Configuration @@ -23,22 +22,23 @@ The simplest way to create a `WebClient` is through one of the static factory me * `WebClient.create()` * `WebClient.create(String baseUrl)` -The above uses Reactor Netty `HttpClient` from "io.projectreactor.netty:reactor-netty" -with default settings and participates in global resources such for event loop threads and -a connection pool, see <>. +The preceding methods use Reactor Netty `HttpClient` from `io.projectreactor.netty:reactor-netty` +with default settings and participates in global resources for event loop threads and +a connection pool. See <>. -The `WebClient.Builder` can be used for access to further options: +You can use the `WebClient.Builder` for access to further options: -* `uriBuilderFactory` -- customized `UriBuilderFactory` to use as a base URL. -* `defaultHeader` -- headers for every request. -* `defaultCookie)` -- cookies for every request. -* `defaultRequest` -- `Consumer` to customize every request. -* `filter` -- client filter for every request. -* `exchangeStrategies` -- HTTP message reader/writer customizations. -* `clientConnector` -- HTTP client library settings. +* `uriBuilderFactory`: Customized `UriBuilderFactory` to use as a base URL. +* `defaultHeader`: Headers for every request. +* `defaultCookie)`: Cookies for every request. +* `defaultRequest`: `Consumer` to customize every request. +* `filter`: Client filter for every request. +* `exchangeStrategies`: HTTP message reader/writer customizations. +* `clientConnector`: HTTP client library settings. -For example, to configure <>: +The following example configures <>: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -52,10 +52,12 @@ For example, to configure <>: .exchangeStrategies(strategies) .build(); ---- +==== -Once built a `WebClient` instance is immutable. However, you can clone it, and build a -modified copy without affecting the original instance: +Once built, a `WebClient` instance is immutable. However, you can clone it and build a +modified copy without affecting the original instance, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -69,14 +71,16 @@ modified copy without affecting the original instance: // client2 has filterA, filterB, filterC, filterD ---- +==== [[webflux-client-builder-reactor]] === Reactor Netty -To customize Reactor Netty settings: +You can customize Reactor Netty settings: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -85,18 +89,21 @@ To customize Reactor Netty settings: WebClient webClient = WebClient.builder().clientConnector(connector).build(); ---- +==== -By default `HttpClient` participates in the global Reactor Netty resources held in +By default, `HttpClient` participates in the global Reactor Netty resources held in `reactor.netty.http.HttpResources`, including event loop threads and a connection pool. -This is the recommended mode since fixed, shared resources are preferred for event loop +This is the recommended mode, since fixed, shared resources are preferred for event loop concurrency. In this mode global resources remain active until the process exits. If the server is timed with the process, there is typically no need for an explicit -shutdown. However if the server can start or stop in-process, e.g. Spring MVC -application deployed as a WAR, you can declare a Spring-managed bean of type -`ReactorResourceFactory` with `useGlobalResources=true` (the default) to ensure the Reactor -Netty global resources are shut down when the Spring `ApplicationContext` is closed: +shutdown. However, if the server can start or stop in-process (for example, a Spring MVC +application deployed as a WAR), you can declare a Spring-managed bean of type +`ReactorResourceFactory` with `globalResources=true` (the default) to ensure that the Reactor +Netty global resources are shut down when the Spring `ApplicationContext` is closed, +as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -105,11 +112,13 @@ Netty global resources are shut down when the Spring `ApplicationContext` is clo return new ReactorResourceFactory(); } ---- +==== -You may also choose not to participate in the global Reactor Netty resources. However keep -in mind in this mode the burden is on you to ensure all Reactor Netty client and server -instances use shared resources: +You can also choose not to participate in the global Reactor Netty resources. However, +in this mode, the burden is on you to ensure that all Reactor Netty client and server +instances use shared resources, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -134,15 +143,18 @@ instances use shared resources: } ---- <1> Create resources independent of global ones. -<2> Use `ReactorClientHttpConnector` constructor with resource factory. +<2> Use the `ReactorClientHttpConnector` constructor with resource factory. <3> Plug the connector into the `WebClient.Builder`. +==== + [[webflux-client-builder-jetty]] === Jetty -To customize Jetty `HttpClient` settings: +The following example shows how to customize Jetty `HttpClient` settings: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -152,14 +164,16 @@ To customize Jetty `HttpClient` settings: WebClient webClient = WebClient.builder().clientConnector(connector).build(); ---- +==== -By default `HttpClient` creates its own resources (`Executor`, `ByteBufferPool`, `Scheduler`) +By default, `HttpClient` creates its own resources (`Executor`, `ByteBufferPool`, `Scheduler`), which remain active until the process exits or `stop()` is called. -You can share resources between multiple intances of Jetty client (and server) and ensure the +You can share resources between multiple instances of the Jetty client (and server) and ensure that the resources are shut down when the Spring `ApplicationContext` is closed by declaring a -Spring-managed bean of type `JettyResourceFactory`: +Spring-managed bean of type `JettyResourceFactory`, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -183,16 +197,19 @@ Spring-managed bean of type `JettyResourceFactory`: ---- <1> Create shared resources. -<2> Use `JettyClientHttpConnector` constructor with resource factory. +<2> Use the `JettyClientHttpConnector` constructor with resource factory. <3> Plug the connector into the `WebClient.Builder`. +==== [[webflux-client-retrieve]] -== Retrieve +== Using the `retrieve` Method -The `retrieve()` method is the easiest way to get a response body and decode it: +The `retrieve()` method is the easiest way to get a response body and decode it. +The following example shows how to do so: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -203,9 +220,11 @@ The `retrieve()` method is the easiest way to get a response body and decode it: .retrieve() .bodyToMono(Person.class); ---- +==== -You can also get a stream of objects decoded from the response: +You can also get a stream of objects decoded from the response, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -214,12 +233,15 @@ You can also get a stream of objects decoded from the response: .retrieve() .bodyToFlux(Quote.class); ---- +==== By default, responses with 4xx or 5xx status codes result in an -`WebClientResponseException` or one of its HTTP status specific sub-classes such as +`WebClientResponseException` or one of its HTTP status specific sub-classes, such as `WebClientResponseException.BadRequest`, `WebClientResponseException.NotFound`, and others. -You can also use the `onStatus` method to customize the resulting exception: +You can also use the `onStatus` method to customize the resulting exception, +as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -230,16 +252,17 @@ You can also use the `onStatus` method to customize the resulting exception: .onStatus(HttpStatus::is5xxServerError, response -> ...) .bodyToMono(Person.class); ---- - +==== [[webflux-client-exchange]] -== Exchange +== Using the `exchange` Method -The `exchange()` method provides more control. The below example is equivalent +The `exchange()` method provides more control than the `retrieve` method. The following example is equivalent to `retrieve()` but also provides access to the `ClientResponse`: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -248,9 +271,11 @@ to `retrieve()` but also provides access to the `ClientResponse`: .exchange() .flatMap(response -> response.bodyToMono(Person.class)); ---- +==== -At this level you can also create a full `ResponseEntity`: +At this level, you can also create a full `ResponseEntity`: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -259,27 +284,25 @@ At this level you can also create a full `ResponseEntity`: .exchange() .flatMap(response -> response.toEntity(Person.class)); ---- +==== -Note that unlike `retrieve()`, with `exchange()` there are no automatic error signals for +Note that (unlike `retrieve()`), with `exchange()`, there are no automatic error signals for 4xx and 5xx responses. You have to check the status code and decide how to proceed. -[CAUTION] -==== -When using `exchange()` you must always use any of the body or toEntity methods of +CAUTION: When you use `exchange()`, you must always use any of the `body` or `toEntity` methods of `ClientResponse` to ensure resources are released and to avoid potential issues with HTTP connection pooling. You can use `bodyToMono(Void.class)` if no response content is -expected. However keep in mind that if the response does have content, the connection -will be closed and will not be placed back in the pool. -==== - +expected. However, if the response does have content, the connection +is closed and is not placed back in the pool. [[webflux-client-body]] -== Request body +== Request Body -The request body can be encoded from an Object: +The request body can be encoded from an `Object`, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -292,9 +315,11 @@ The request body can be encoded from an Object: .retrieve() .bodyToMono(Void.class); ---- +==== -You can also have a stream of objects encoded: +You can also have a stream of objects be encoded, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -307,9 +332,12 @@ You can also have a stream of objects encoded: .retrieve() .bodyToMono(Void.class); ---- +==== -Or if you have the actual value, use the `syncBody` shortcut method: +Alternatively, if you have the actual value, you can use the `syncBody` shortcut method, +as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -322,16 +350,18 @@ Or if you have the actual value, use the `syncBody` shortcut method: .retrieve() .bodyToMono(Void.class); ---- +==== [[webflux-client-body-form]] -=== Form data +=== Form Data -To send form data, provide a `MultiValueMap` as the body. Note that the -content is automatically set to `"application/x-www-form-urlencoded"` by the -`FormHttpMessageWriter`: +To send form data, you can provide a `MultiValueMap` as the body. Note that the +content is automatically set to `application/x-www-form-urlencoded` by the +`FormHttpMessageWriter`. The following example shows how to use `MultiValueMap`: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -343,9 +373,11 @@ content is automatically set to `"application/x-www-form-urlencoded"` by the .retrieve() .bodyToMono(Void.class); ---- +==== -You can also supply form data in-line via `BodyInserters`: +You can also supply form data in-line by using `BodyInserters`, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -357,16 +389,17 @@ You can also supply form data in-line via `BodyInserters`: .retrieve() .bodyToMono(Void.class); ---- +==== [[webflux-client-body-multipart]] -=== Multipart data +=== Multipart Data To send multipart data, you need to provide a `MultiValueMap` whose values are -either Objects representing part content, or `HttpEntity` representing the content and +either `Object` instances that represent part content or `HttpEntity` instances that represent the content and headers for a part. `MultipartBodyBuilder` provides a convenient API to prepare a -multipart request: +multipart request. The following example shows how to create a `MultiValueMap`: [source,java,intent=0] [subs="verbatim,quotes"] @@ -379,15 +412,16 @@ multipart request: MultiValueMap> parts = builder.build(); ---- -In most cases you do not have to specify the `Content-Type` for each part. The content -type is determined automatically based on the `HttpMessageWriter` chosen to serialize it, -or in the case of a `Resource` based on the file extension. If necessary you can -explicitly provide the `MediaType` to use for each part through one fo the overloaded +In most cases, you do not have to specify the `Content-Type` for each part. The content +type is determined automatically based on the `HttpMessageWriter` chosen to serialize it +or, in the case of a `Resource`, based on the file extension. If necessary, you can +explicitly provide the `MediaType` to use for each part through one of the overloaded builder `part` methods. Once a `MultiValueMap` is prepared, the easiest way to pass it to the the `WebClient` is -through the `syncBody` method: +through the `syncBody` method, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -399,15 +433,17 @@ through the `syncBody` method: .retrieve() .bodyToMono(Void.class); ---- +==== -If the `MultiValueMap` contains at least one non-String value, which could also be -represent regular form data (i.e. "application/x-www-form-urlencoded"), you don't have to -set the `Content-Type` to "multipart/form-data". This is always the case when using -`MultipartBodyBuilder` which ensures an `HttpEntity` wrapper. +If the `MultiValueMap` contains at least one non-`String` value, which could also +represent regular form data (that is, `application/x-www-form-urlencoded`), you need not +set the `Content-Type` to `multipart/form-data`. This is always the case when using +`MultipartBodyBuilder`, which ensures an `HttpEntity` wrapper. As an alternative to `MultipartBodyBuilder`, you can also provide multipart content, -inline-style, through the built-in `BodyInserters`. For example: +inline-style, through the built-in `BodyInserters`, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -419,7 +455,7 @@ inline-style, through the built-in `BodyInserters`. For example: .retrieve() .bodyToMono(Void.class); ---- - +==== @@ -427,8 +463,9 @@ inline-style, through the built-in `BodyInserters`. For example: == Client Filters You can register a client filter (`ExchangeFilterFunction`) through the `WebClient.Builder` -in order to intercept and/or modify requests: +in order to intercept and modify requests, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -443,10 +480,12 @@ WebClient client = WebClient.builder() }) .build(); ---- +==== -This can be used for cross-cutting concerns such as authentication. The example below uses +This can be used for cross-cutting concerns, such as authentication. The following example uses a filter for basic authentication through a static factory method: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -457,11 +496,13 @@ WebClient client = WebClient.builder() .filter(basicAuthentication("user", "password")) .build(); ---- +==== -Filters apply globally to every request. To change how a filter's behavior for a specific +Filters apply globally to every request. To change a filter's behavior for a specific request, you can add request attributes to the `ClientRequest` that can then be accessed -by all filters in the chain: +by all filters in the chain, as the following example shows: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -479,11 +520,13 @@ client.get().uri("http://example.org/") } ---- +==== -You can also replicate an existing `WebClient`, and insert new filters or remove already -registered filters. In the example below, a basic authentication filter is inserted at +You can also replicate an existing `WebClient`, insert new filters, or remove already +registered filters. The following example, inserts a basic authentication filter at index 0: +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -496,17 +539,17 @@ WebClient client = webClient.mutate() }) .build(); ---- - +==== [[webflux-client-testing]] == Testing -To test code that uses the `WebClient`, you can use a mock web server such as the -https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer]. To see example -use, check -https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java[WebClientIntegrationTests] -in the Spring Framework tests, or the -https://github.com/square/okhttp/tree/master/samples/static-server[static-server] +To test code that uses the `WebClient`, you can use a mock web server, such as the +https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer]. To see an example +of its use, check +https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java[`WebClientIntegrationTests`] +in the Spring Framework tests or the +https://github.com/square/okhttp/tree/master/samples/static-server[`static-server`] sample in the OkHttp repository. diff --git a/src/docs/asciidoc/web/webflux-websocket.adoc b/src/docs/asciidoc/web/webflux-websocket.adoc index 6e1e917a51f..3ed7c04910d 100644 --- a/src/docs/asciidoc/web/webflux-websocket.adoc +++ b/src/docs/asciidoc/web/webflux-websocket.adoc @@ -1,30 +1,33 @@ [[webflux-websocket]] = WebSockets -[.small]#<># +[.small]#<># -This part of the reference documentation covers support for Reactive stack, WebSocket +This part of the reference documentation covers support for reactive-stack WebSocket messaging. + include::websocket-intro.adoc[leveloffset=+1] [[webflux-websocket-server]] == WebSocket API -[.small]#<># +[.small]#<># -The Spring Framework provides a WebSocket API that can be used to write client and -server side applications that handle WebSocket messages. +The Spring Framework provides a WebSocket API that you can use to write client- and +server-side applications that handle WebSocket messages. [[webflux-websocket-server-handler]] === Server -[.small]#<># +[.small]#<># -To create a WebSocket server, first create a `WebSocketHandler`: +To create a WebSocket server, you can first create a `WebSocketHandler`. +The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -39,9 +42,11 @@ To create a WebSocket server, first create a `WebSocketHandler`: } } ---- +==== -Then map it to a URL and add a `WebSocketHandlerAdapter`: +Then you can map it to a URL and add a `WebSocketHandlerAdapter`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -65,22 +70,24 @@ Then map it to a URL and add a `WebSocketHandlerAdapter`: } } ---- +==== [[webflux-websockethandler]] -=== WebSocketHandler +=== Using `WebSocketHandler` The `handle` method of `WebSocketHandler` takes `WebSocketSession` and returns `Mono` to indicate when application handling of the session is complete. The session is handled -through two streams, one for inbound and one for outbound messages: +through two streams, one for inbound and one for outbound messages. The following table +describes the two methods that handle the streams: [options="header"] |=== -| WebSocketSession method | Description +| `WebSocketSession` method | Description | `Flux receive()` -| Provides access to the inbound message stream, and completes when the connection is closed. +| Provides access to the inbound message stream and completes when the connection is closed. | `Mono send(Publisher)` | Takes a source for outgoing messages, writes the messages, and returns a `Mono` that @@ -88,21 +95,23 @@ through two streams, one for inbound and one for outbound messages: |=== -A `WebSocketHandler` must compose the inbound and outbound streams into a unified flow, and +A `WebSocketHandler` must compose the inbound and outbound streams into a unified flow and return a `Mono` that reflects the completion of that flow. Depending on application requirements, the unified flow completes when: -* Either inbound or outbound message streams complete. -* Inbound stream completes (i.e. connection closed), while outbound is infinite. -* At a chosen point through the `close` method of `WebSocketSession`. +* Either the inbound or the outbound message stream completes. +* The inbound stream completes (that is, the connection closed), while the outbound stream is infinite. +* At a chosen point, through the `close` method of `WebSocketSession`. When inbound and outbound message streams are composed together, there is no need to -check if the connection is open, since Reactive Streams signals will terminate activity. -The inbound stream receives a completion/error signal, and the outbound stream receives +check if the connection is open, since Reactive Streams signals terminate activity. +The inbound stream receives a completion or error signal, and the outbound stream receives a cancellation signal. -The most basic implementation of a handler is one that handles the inbound stream: +The most basic implementation of a handler is one that handles the inbound stream. The +following example shows such an implementation: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -121,21 +130,20 @@ class ExampleHandler implements WebSocketHandler { } } ---- -<1> Access stream of inbound messages. +<1> Access the stream of inbound messages. <2> Do something with each message. -<3> Perform nested async operation using message content. -<4> Return `Mono` that completes when receiving completes. - -[TIP] +<3> Perform nested asynchronous operations that use the message content. +<4> Return a `Mono` that completes when receiving completes. ==== -For nested, asynchronous operations, you may need to call `message.retain()` on underlying -servers that use pooled data buffers (e.g. Netty), or otherwise the data buffer may be -released before you've had a chance to read the data. For more background see + +TIP: For nested, asynchronous operations, you may need to call `message.retain()` on underlying +servers that use pooled data buffers (for example, Netty). Otherwise, the data buffer may be +released before you have had a chance to read the data. For more background, see <>. -==== -The below implementation combines the inbound with the outbound streams: +The following implementation combines the inbound and outbound streams: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -157,12 +165,15 @@ class ExampleHandler implements WebSocketHandler { } } ---- -<1> Handle inbound message stream. -<2> Create outbound message, producing a combined flow. -<3> Return `Mono` that doesn't complete while we continue to receive. +<1> Handle the inbound message stream. +<2> Create the outbound message, producing a combined flow. +<3> Return a `Mono` that does not complete while we continue to receive. +==== -Inbound and outbound streams can be independent, and joined only for completion: +Inbound and outbound streams can be independent and be joined only for completion, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -189,17 +200,18 @@ class ExampleHandler implements WebSocketHandler { ---- <1> Handle inbound message stream. <2> Send outgoing messages. -<3> Join the streams and return `Mono` that completes when _either_ stream ends. +<3> Join the streams and return a `Mono` that completes when either stream ends. +==== [[webflux-websocket-server-handshake]] === Handshake -[.small]#<># +[.small]#<># -`WebSocketHandlerAdapter` delegates to a `WebSocketService`. By default that's an instance +`WebSocketHandlerAdapter` delegates to a `WebSocketService`. By default, that is an instance of `HandshakeWebSocketService`, which performs basic checks on the WebSocket request and -then uses `RequestUpgradeStrategy` for the server in use. Currently there is built-in +then uses `RequestUpgradeStrategy` for the server in use. Currently, there is built-in support for Reactor Netty, Tomcat, Jetty, and Undertow. `HandshakeWebSocketService` exposes a `sessionAttributePredicate` property that allows @@ -208,15 +220,15 @@ into the attributes of the `WebSocketSession`. - [[webflux-websocket-server-config]] -=== Server config -[.small]#<># +=== Server Configation +[.small]#<># The `RequestUpgradeStrategy` for each server exposes WebSocket-related configuration -options available for the underlying WebSocket engine. Below is an example of setting +options available for the underlying WebSocket engine. The following example sets WebSocket options when running on Tomcat: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -236,21 +248,22 @@ WebSocket options when running on Tomcat: } } ---- +==== -Check the upgrade strategy for your server to see what options are available. Currently +Check the upgrade strategy for your server to see what options are available. Currently, only Tomcat and Jetty expose such options. [[webflux-websocket-server-cors]] === CORS -[.small]#<># +[.small]#<># The easiest way to configure CORS and restrict access to a WebSocket endpoint is to have your `WebSocketHandler` implement `CorsConfigurationSource` and return a -`CorsConfiguraiton` with allowed origins, headers, etc. If for any reason you can't do +`CorsConfiguraiton` with allowed origins, headers, and other details. If you cannot do that, you can also set the `corsConfigurations` property on the `SimpleUrlHandler` to -specify CORS settings by URL pattern. If both are specified they're combined via the +specify CORS settings by URL pattern. If both are specified, they are combined by using the `combine` method on `CorsConfiguration`. @@ -259,18 +272,16 @@ specify CORS settings by URL pattern. If both are specified they're combined via === Client Spring WebFlux provides a `WebSocketClient` abstraction with implementations for -Reactor Netty, Tomcat, Jetty, Undertow, and standard Java (i.e. JSR-356). +Reactor Netty, Tomcat, Jetty, Undertow, and standard Java (that is, JSR-356). -[NOTE] -==== -The Tomcat client is effectively an extension of the standard Java one with some extra -functionality in the `WebSocketSession` handling taking advantage of Tomcat specific +NOTE: The Tomcat client is effectively an extension of the standard Java one with some extra +functionality in the `WebSocketSession` handling to take advantage of the Tomcat-specific API to suspend receiving messages for back pressure. -==== -To start a WebSocket session, create an instance of the client and use its `execute` +To start a WebSocket session, you can create an instance of the client and use its `execute` methods: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -282,7 +293,8 @@ client.execute(url, session -> .doOnNext(System.out::println) .then()); ---- +==== -Some clients, e.g. Jetty, implement `Lifecycle` and need to be started in stopped +Some clients, such as Jetty, implement `Lifecycle` and need to be stopped and started before you can use them. All clients have constructor options related to configuration of the underlying WebSocket client. diff --git a/src/docs/asciidoc/web/webflux.adoc b/src/docs/asciidoc/web/webflux.adoc index 90f9d29ff10..72fd9b3aaef 100644 --- a/src/docs/asciidoc/web/webflux.adoc +++ b/src/docs/asciidoc/web/webflux.adoc @@ -2,23 +2,18 @@ = Spring WebFlux :doc-spring-security: {doc-root}/spring-security/site/docs/current/reference - - - -[[webflux-introduction]] -== Introduction -The original web framework included in the Spring Framework, Spring Web MVC, was purpose -built for the Servlet API and Servlet containers. The reactive stack, web framework, +The original web framework included in the Spring Framework, Spring Web MVC, was +purpose-built for the Servlet API and Servlet containers. The reactive-stack web framework, Spring WebFlux, was added later in version 5.0. It is fully non-blocking, supports -http://www.reactive-streams.org/[Reactive Streams] back pressure, and runs on servers such as +http://www.reactive-streams.org/[Reactive Streams] back pressure, and runs on such servers as Netty, Undertow, and Servlet 3.1+ containers. Both web frameworks mirror the names of their source modules -https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc[spring-webmvc] and -https://github.com/spring-projects/spring-framework/tree/master/spring-webflux[spring-webflux] +(https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc[spring-webmvc] and +https://github.com/spring-projects/spring-framework/tree/master/spring-webflux[spring-webflux]) and co-exist side by side in the Spring Framework. Each module is optional. -Applications may use one or the other module, or in some cases both -- -e.g. Spring MVC controllers with the reactive `WebClient`. +Applications can use one or the other module or, in some cases, both -- +for example, Spring MVC controllers with the reactive `WebClient`. @@ -28,56 +23,53 @@ e.g. Spring MVC controllers with the reactive `WebClient`. Why was Spring WebFlux created? Part of the answer is the need for a non-blocking web stack to handle concurrency with a -small number of threads and scale with less hardware resources. Servlet 3.1 did provide -an API for non-blocking I/O. However, using it leads away from the rest of the Servlet API +small number of threads and scale with fewer hardware resources. Servlet 3.1 did provide +an API for non-blocking I/O. However, using it leads away from the rest of the Servlet API, where contracts are synchronous (`Filter`, `Servlet`) or blocking (`getParameter`, `getPart`). This was the motivation for a new common API to serve as a foundation across -any non-blocking runtime. That is important because of servers such as Netty that are well -established in the async, non-blocking space. +any non-blocking runtime. That is important because of servers (such as Netty) that are +well-established in the async, non-blocking space. -The other part of the answer is functional programming. Much like the addition of annotations -in Java 5 created opportunities -- e.g. annotated REST controllers or unit tests, the addition +The other part of the answer is functional programming. Much as the addition of annotations +in Java 5 created opportunities (such as annotated REST controllers or unit tests), the addition of lambda expressions in Java 8 created opportunities for functional APIs in Java. -This is a boon for non-blocking applications and continuation style APIs -- as popularized -by `CompletableFuture` and http://reactivex.io/[ReactiveX], that allow declarative -composition of asynchronous logic. At the programming model level Java 8 enabled Spring -WebFlux to offer functional web endpoints alongside with annotated controllers. +This is a boon for non-blocking applications and continuation-style APIs (as popularized +by `CompletableFuture` and http://reactivex.io/[ReactiveX]) that allow declarative +composition of asynchronous logic. At the programming-model level, Java 8 enabled Spring +WebFlux to offer functional web endpoints alongside annotated controllers. [[webflux-why-reactive]] -=== Define "reactive" +=== Define "`Reactive`" -We touched on non-blocking and functional but what does reactive mean? +We touched on "`non-blocking`" and "`functional`" but what does reactive mean? -The term "reactive" refers to programming models that are built around reacting to change -- -network component reacting to I/O events, UI controller reacting to mouse events, etc. -In that sense non-blocking is reactive because instead of being blocked we are now in the mode +The term, "`reactive,`" refers to programming models that are built around reacting to change -- +network components reacting to I/O events, UI controllers reacting to mouse events, and others. +In that sense, non-blocking is reactive, because, instead of being blocked, we are now in the mode of reacting to notifications as operations complete or data becomes available. -There is also another important mechanism that we on the Spring team associate with "reactive" +There is also another important mechanism that we on the Spring team associate with "`reactive`" and that is non-blocking back pressure. In synchronous, imperative code, blocking calls serve as a natural form of back pressure that forces the caller to wait. In non-blocking -code it becomes important to control the rate of events so that a fast producer does not +code, it becomes important to control the rate of events so that a fast producer does not overwhelm its destination. Reactive Streams is a -https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.1/README.md#specification[small spec], -also https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html[adopted] in Java 9, +https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.1/README.md#specification[small spec] +(also https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html[adopted] in Java 9) that defines the interaction between asynchronous components with back pressure. -For example a data repository -- acting as -http://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/org/reactivestreams/Publisher.html[Publisher], -can produce data that an HTTP server -- acting as -http://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/org/reactivestreams/Subscriber.html[Subscriber], -can then write to the response. The main purpose of Reactive Streams is to allow the -subscriber to control how fast or how slow the publisher will produce data. - -[NOTE] -==== -*Common question: what if a publisher can't slow down?* + +For example a data repository (acting as +http://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/org/reactivestreams/Publisher.html[Publisher]) +can produce data that an HTTP server (acting as +http://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/org/reactivestreams/Subscriber.html[Subscriber]) +can then write to the response. The main purpose of Reactive Streams is to let the + subscriber to control how quickly or how slowly the publisher produces data. + +NOTE: *Common question: what if a publisher cannot slow down?* + The purpose of Reactive Streams is only to establish the mechanism and a boundary. -If a publisher can't slow down then it has to decide whether to buffer, drop, or fail. -==== +If a publisher cannot slow down, it has to decide whether to buffer, drop, or fail. @@ -85,49 +77,49 @@ If a publisher can't slow down then it has to decide whether to buffer, drop, or === Reactive API Reactive Streams plays an important role for interoperability. It is of interest to libraries -and infrastructure components but less useful as an application API because it is too -low level. What applications need is a higher level and richer, functional API to +and infrastructure components but less useful as an application API, because it is too +low-level. Applications need a higher-level and richer, functional API to compose async logic -- similar to the Java 8 `Stream` API but not only for collections. This is the role that reactive libraries play. https://github.com/reactor/reactor[Reactor] is the reactive library of choice for Spring WebFlux. It provides the -https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html[Mono] and -https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html[Flux] API types -to work on data sequences of 0..1 and 0..N through a rich set of operators aligned with the +https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html[`Mono`] and +https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html[`Flux`] API types +to work on data sequences of 0..1 (`Mono`) and 0..N (`Flux`) through a rich set of operators aligned with the ReactiveX http://reactivex.io/documentation/operators.html[vocabulary of operators]. -Reactor is a Reactive Streams library and therefore all of its operators support non-blocking back pressure. +Reactor is a Reactive Streams library and, therefore, all of its operators support non-blocking back pressure. Reactor has a strong focus on server-side Java. It is developed in close collaboration with Spring. WebFlux requires Reactor as a core dependency but it is interoperable with other reactive -libraries via Reactive Streams. As a general rule WebFlux APIs accept a plain `Publisher` -as input, adapt it to Reactor types internally, use those, and then return either -`Flux` or `Mono` as output. So you can pass any `Publisher` as input and you can apply -operations on the output, but you'll need to adapt the output for use with another reactive library. -Whenever feasible -- e.g. annotated controllers, WebFlux adapts transparently to the use -of RxJava or other reactive library. See <> for more details. +libraries via Reactive Streams. As a general rule, a WebFlux API accepts a plain `Publisher` +as input, adapts it to a Reactor type internally, uses that, and returns either a +`Flux` or a `Mono` as output. So, you can pass any `Publisher` as input and you can apply +operations on the output, but you need to adapt the output for use with another reactive library. +Whenever feasible (for example, annotated controllers), WebFlux adapts transparently to the use +of RxJava or another reactive library. See <> for more details. [[webflux-programming-models]] -=== Programming models +=== Programming Models -The `spring-web` module contains the reactive foundation that underlies Spring WebFlux +The `spring-web` module contains the reactive foundation that underlies Spring WebFlux, including HTTP abstractions, Reactive Streams <> for supported servers, <>, and a core <> comparable to the Servlet API but with non-blocking contracts. -On that foundation Spring WebFlux provides a choice of two programming models: +On that foundation, Spring WebFlux provides a choice of two programming models: -- <> -- consistent with Spring MVC, and based on the same annotations +* <>: Consistent with Spring MVC and based on the same annotations from the `spring-web` module. Both Spring MVC and WebFlux controllers support reactive -(Reactor, RxJava) return types and as a result it is not easy to tell them apart. One notable +(Reactor and RxJava) return types, and, as a result, it is not easy to tell them apart. One notable difference is that WebFlux also supports reactive `@RequestBody` arguments. -- <> -- lambda-based, lightweight, functional programming model. Think of +* <>: Lambda-based, lightweight, and functional programming model. You can think of this as a small library or a set of utilities that an application can use to route and handle requests. The big difference with annotated controllers is that the application -is in charge of request handling from start to finish vs declaring intent through +is in charge of request handling from start to finish versus declaring intent through annotations and being called back. @@ -137,54 +129,54 @@ annotations and being called back. Spring MVC or WebFlux? -A natural question to ask but one that sets up an unsound dichotomy. It's actually both -working together to expand the range of available options. The two are designed for +A natural question to ask but one that sets up an unsound dichotomy. Actually, both +work together to expand the range of available options. The two are designed for continuity and consistency with each other, they are available side by side, and feedback -from each side benefits both sides. The diagram below shows how the two relate, what they +from each side benefits both sides. The following diagram shows how the two relate, what they have in common, and what each supports uniquely: image::images/spring-mvc-and-webflux-venn.png[] -Below are some specific points to consider: +We suggest that you consider the following specific points: * If you have a Spring MVC application that works fine, there is no need to change. Imperative programming is the easiest way to write, understand, and debug code. -You have maximum choice of libraries since historically most are blocking. +You have maximum choice of libraries, since, historically, most are blocking. * If you are already shopping for a non-blocking web stack, Spring WebFlux offers the same -execution model benefits as others in this space and also provides a choice of servers -- -Netty, Tomcat, Jetty, Undertow, Servlet 3.1+ containers, a choice of programming models -- -annotated controllers and functional web endpoints, and a choice of reactive libraries -- -Reactor, RxJava, or other. +execution model benefits as others in this space and also provides a choice of servers +(Netty, Tomcat, Jetty, Undertow, and Servlet 3.1+ containers), a choice of programming models +(annotated controllers and functional web endpoints), and a choice of reactive libraries +(Reactor, RxJava, or other). * If you are interested in a lightweight, functional web framework for use with Java 8 lambdas -or Kotlin then use the Spring WebFlux functional web endpoints. That can also be a good choice +or Kotlin, you can use the Spring WebFlux functional web endpoints. That can also be a good choice for smaller applications or microservices with less complex requirements that can benefit from greater transparency and control. -* In a microservice architecture you can have a mix of applications with either Spring MVC -or Spring WebFlux controllers, or with Spring WebFlux functional endpoints. Having support +* In a microservice architecture, you can have a mix of applications with either Spring MVC +or Spring WebFlux controllers or with Spring WebFlux functional endpoints. Having support for the same annotation-based programming model in both frameworks makes it easier to re-use knowledge while also selecting the right tool for the right job. * A simple way to evaluate an application is to check its dependencies. If you have blocking -persistence APIs (JPA, JDBC), or networking APIs to use, then Spring MVC is the best choice -for common architectures at least. It is technically feasible with both Reactor and -RxJava to perform blocking calls on a separate thread but you wouldn't be making the +persistence APIs (JPA, JDBC) or networking APIs to use, Spring MVC is the best choice +for common architectures at, least. It is technically feasible with both Reactor and +RxJava to perform blocking calls on a separate thread but you would not be making the most of a non-blocking web stack. * If you have a Spring MVC application with calls to remote services, try the reactive `WebClient`. You can return reactive types (Reactor, RxJava, <>) -directly from Spring MVC controller methods. The greater the latency per call, or the +directly from Spring MVC controller methods. The greater the latency per call or the interdependency among calls, the more dramatic the benefits. Spring MVC controllers can call other reactive components too. * If you have a large team, keep in mind the steep learning curve in the shift to non-blocking, functional, and declarative programming. A practical way to start without a full switch -is to use the reactive `WebClient`. Beyond that start small and measure the benefits. -We expect that for a wide range of applications the shift is unnecessary. If you are +is to use the reactive `WebClient`. Beyond that, start small and measure the benefits. +We expect that, for a wide range of applications, the shift is unnecessary. If you are unsure what benefits to look for, start by learning about how non-blocking I/O works -(e.g. concurrency on single-threaded Node.js) and its effects. +(for example, concurrency on single-threaded Node.js) and its effects. @@ -193,22 +185,22 @@ unsure what benefits to look for, start by learning about how non-blocking I/O w Spring WebFlux is supported on Tomcat, Jetty, Servlet 3.1+ containers, as well as on non-Servlet runtimes such as Netty and Undertow. All servers are adapted to a low-level, -<> so that higher level +<> so that higher-level <> can be supported across servers. -Spring WebFlux does not have built-in support to start or stop a server. However it is -easy to <> an application from Spring configuration, and -<>, and <> with a few +Spring WebFlux does not have built-in support to start or stop a server. However, it is +easy to <> an application from Spring configuration and +<> and <> with a few lines of code. -Spring Boot has a WebFlux starter that automates these steps. By default the starter uses -Netty but it is easy to switch to Tomcat, Jetty, or Undertow simply by changing your -Maven or Gradle dependencies. Spring Boot defaults to Netty because it is more widely -used in the async, non-blocking space, and provides a client and a server share resources. +Spring Boot has a WebFlux starter that automates these steps. By default, the starter uses +Netty, but it is easy to switch to Tomcat, Jetty, or Undertow by changing your +Maven or Gradle dependencies. Spring Boot defaults to Netty, because it is more widely +used in the asynchronous, non-blocking space and lets a client and a server share resources. -Tomcat and Jetty can be used with both Spring MVC and WebFlux. Keep in mind however that -the way they're used is very different. Spring MVC relies on Servlet blocking I/O and -allows applications to use the Servlet API directly if they need to. Spring WebFlux +Tomcat and Jetty can be used with both Spring MVC and WebFlux. Keep in mind, however, that +the way they are used is very different. Spring MVC relies on Servlet blocking I/O and +lets applications use the Servlet API directly if they need to. Spring WebFlux relies on Servlet 3.1 non-blocking I/O and uses the Servlet API behind a low-level adapter and not exposed for direct use. @@ -217,18 +209,18 @@ For Undertow, Spring WebFlux uses Undertow APIs directly without the Servlet API [[webflux-performance]] -=== Performance vs scale +=== Performance versus Scale Performance has many characteristics and meanings. Reactive and non-blocking generally -do not make applications run faster. They can, in some cases, for example if using the -`WebClient` to execute remote calls in parallel. On the whole it requires more work to do +do not make applications run faster. They can, in some cases, (for example, if using the +`WebClient` to execute remote calls in parallel). On the whole, it requires more work to do things the non-blocking way and that can increase slightly the required processing time. The key expected benefit of reactive and non-blocking is the ability to scale with a small, -fixed number of threads and less memory. That makes applications more resilient under load -because they scale in a more predictable way. In order to observe those benefits however you -need to have some latency including a mix of slow and unpredictable network I/O. -That's where the reactive stack begins to show its strengths and the differences can be +fixed number of threads and less memory. That makes applications more resilient under load, +because they scale in a more predictable way. In order to observe those benefits, however, you +need to have some latency (including a mix of slow and unpredictable network I/O). +That is where the reactive stack begins to show its strengths, and the differences can be dramatic. @@ -237,71 +229,65 @@ dramatic. === Concurrency Model Both Spring MVC and Spring WebFlux support annotated controllers, but there is a key -difference in the concurrency model and default assumptions for blocking and threads. +difference in the concurrency model and the default assumptions for blocking and threads. -In Spring MVC, and servlet applications in general, it is assumed that applications _may -block_ the current thread, e.g. for remote calls, and for this reason servlet containers -use a large thread pool, to absorb potential blocking during request handling. +In Spring MVC (and servlet applications in general), it is assumed that applications can +block the current thread, (for example, for remote calls), and, for this reason, servlet containers +use a large thread pool to absorb potential blocking during request handling. -In Spring WebFlux, and non-blocking servers in general, it is assumed that applications -_will not block_, and therefore non-blocking servers use a small, fixed-size thread pool +In Spring WebFlux (and non-blocking servers in general), it is assumed that applications +do not block, and, therefore, non-blocking servers use a small, fixed-size thread pool (event loop workers) to handle requests. -[TIP] -==== -To "scale" and "small number of threads" may sound contradictory but to never block the -current thread, and rely on callbacks instead, means you don't need extra threads as +TIP: "`To scale`" and "`small number of threads`" may sound contradictory but to never block the +current thread (and rely on callbacks instead) means that you do not need extra threads, as there are no blocking calls to absorb. -==== -_Invoking a Blocking API_ + +.Invoking a Blocking API What if you do need to use a blocking library? Both Reactor and RxJava provide the `publishOn` operator to continue processing on a different thread. That means there is an -easy escape latch. Keep in mind however that blocking APIs are not a good fit for +easy escape hatch. Keep in mind, however, that blocking APIs are not a good fit for this concurrency model. -_Mutable State_ - -In Reactor and RxJava, logic is declared through operators, and at runtime, a reactive +.Mutable State +In Reactor and RxJava, you declare logic through operators, and, at runtime, a reactive pipeline is formed where data is processed sequentially, in distinct stages. A key benefit -of that is that it frees applications from having to protect mutable state because +of this is that it frees applications from having to protect mutable state because application code within that pipeline is never invoked concurrently. -_Threading Model_ - +.Threading Model What threads should you expect to see on a server running with Spring WebFlux? -* On a "vanilla" Spring WebFlux server (e.g. no data access, nor other optional -dependencies), you can expect one thread for the server, and several others for request +* On a "`vanilla`" Spring WebFlux server (for example, no data access nor other optional +dependencies), you can expect one thread for the server and several others for request processing (typically as many as the number of CPU cores). Servlet containers, however, -may start with more threads (e.g. 10 on Tomcat), in support of both servlet, blocking I/O -and servlet 3.1, non-blocking I/O usage. +may start with more threads (for example, 10 on Tomcat), in support of both servlet (blocking) I/O +and servlet 3.1 (non-blocking) I/O usage. -* The reactive `WebClient` operates in event loop style. So you'll see a small, fixed -number of processing threads related to that, e.g. "reactor-http-nio-" with the Reactor -Netty connector. However if Reactor Netty is used for both client and server, the two -will share event loop resources by default. +* The reactive `WebClient` operates in event loop style. So you can see a small, fixed +number of processing threads related to that (for example, `reactor-http-nio-` with the Reactor +Netty connector). However, if Reactor Netty is used for both client and server, the two +share event loop resources by default. * Reactor and RxJava provide thread pool abstractions, called Schedulers, to use with the `publishOn` operator that is used to switch processing to a different thread pool. -The schedulers have names that suggest a specific concurrency strategy, e.g. "parallel" -for CPU-bound work with a limited number of threads, or "elastic" for I/O-bound work with -a large number of threads. If you see such threads it means some code is using a +The schedulers have names that suggest a specific concurrency strategy -- for example, "`parallel`" +(for CPU-bound work with a limited number of threads) or "`elastic`" (for I/O-bound work with +a large number of threads). If you see such threads, it means some code is using a specific thread pool `Scheduler` strategy. -* Data access libraries and other 3rd party dependencies may also create and use threads +* Data access libraries and other third party dependencies can also create and use threads of their own. -_Configuring_ - +.Configuring The Spring Framework does not provide support for starting and stopping -<>. To configure the threading model for a server, you'll -need to use server-specific config APIs, or if using Spring Boot, check the Spring -Boot configuration options for each server. The WebClient -<> directly. For all other -libraries, refer to their respective documentation. - +<>. To configure the threading model for a server, you +need to use server-specific configuration APIs, or, if you use Spring Boot, check the Spring +Boot configuration options for each server. You can <> The `WebClient` +directly. For all other +libraries, see their respective documentation. @@ -309,13 +295,13 @@ libraries, refer to their respective documentation. == Reactive Core The `spring-web` module contains abstractions and infrastructure to build reactive web -applications. For server side processing this is organized in two distinct levels: +applications. For server-side processing, this is organized in two distinct levels: -* <> -- basic, common API for HTTP request handling with +* <>: Basic, common API for HTTP request handling with non-blocking I/O and (Reactive Streams) back pressure, along with adapters for each supported server. -* <> -- slightly higher level, but still general purpose API for -server request handling, which underlies higher level programming models such as annotated +* <>: Slightly higher level but still general-purpose API for +server request handling, which underlies higher-level programming models, such as annotated controllers and functional endpoints. The reactive core also includes <> for client and server side use. @@ -323,14 +309,14 @@ The reactive core also includes <> for client and server side us [[webflux-httphandler]] -=== HttpHandler +=== Using `HttpHandler` {api-spring-framework}/http/server/reactive/HttpHandler.html[HttpHandler] is a simple contract with a single method to handle a request and response. It is -intentionally minimal as its main purpose is to provide an abstraction over different +intentionally minimal, as its main purpose is to provide an abstraction over different server APIs for HTTP request handling. -Supported server APIs: +The following table describes the supported server APIs: [cols="1,2,2", options="header"] |=== @@ -357,7 +343,7 @@ Supported server APIs: | spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge |=== -Server dependencies (and +The following table describes server dependencies (and https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-the-Spring-Framework[supported versions]): |=== @@ -380,8 +366,9 @@ https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-the-Spr |jetty-server, jetty-servlet |=== -Code snippets to adapt `HttpHandler` to each server API: +The following code snippets adapt `HttpHandler` to each server API: +==== *Reactor Netty* [source,java,indent=0] [subs="verbatim,quotes"] @@ -439,26 +426,27 @@ server.start(); *Servlet 3.1+ Container* -To deploy as a WAR to any Servlet 3.1+ container, simply extend and include -{api-spring-framework}/web/server/adapter/AbstractReactiveWebInitializer.html[AbstractReactiveWebInitializer] -in the WAR, which wraps an `HttpHandler` with `ServletHttpHandlerAdapter` and registers +To deploy as a WAR to any Servlet 3.1+ container, you can extend and include +{api-spring-framework}/web/server/adapter/AbstractReactiveWebInitializer.html[`AbstractReactiveWebInitializer`] +in the WAR. That class wraps an `HttpHandler` with `ServletHttpHandlerAdapter` and registers that as a `Servlet`. +==== [[webflux-web-handler-api]] -=== WebHandler API +=== Using the `WebHandler` API -The WebHandler API is a general purpose, server, web API for processing requests through a -chain of {api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler], -{api-spring-framework}/web/server/WebFilter.html[WebFilter], and a target -{api-spring-framework}/web/server/WebHandler.html[WebHandler] components. The chain can be assembled +The WebHandler API is a general-purpose server web API for processing requests through a +chain of {api-spring-framework}/web/server/WebExceptionHandler.html[`WebExceptionHandler`] and +{api-spring-framework}/web/server/WebFilter.html[`WebFilter`] components and a target +{api-spring-framework}/web/server/WebHandler.html[`WebHandler`] component. You can assemble the chain with `WebHttpHandlerBuilder` either by adding components to the builder or by having them detected from a Spring `ApplicationContext`. The builder returns an -<> that can then be used to run on any of the supported servers. +<> that you can then use to run on any of the supported servers. While `HttpHandler` aims to be the most minimal contract across HTTP servers, the -WebHandler API provides essential features commonly used to build web applications. +`WebHandler` API provides essential features commonly used to build web applications. For example, the `ServerWebExchange` available to WebHandler API components provides access not only to the request and response, but also to request and session attributes, access to parsed form data, multipart data, and more. @@ -477,7 +465,7 @@ The table below lists the components that `WebHttpHandlerBuilder` detects: | | `WebExceptionHandler` | 0..N -| Provide handling for exceptions from the chain of ``WebFilter``'s and the target +| Provide handling for exceptions from the chain of `WebFilter` instances and the target `WebHandler`. For more details, see <>. | @@ -486,133 +474,138 @@ The table below lists the components that `WebHttpHandlerBuilder` detects: | Apply interception style logic to before and after the rest of the filter chain and the target `WebHandler`. For more details, see <>. -| "webHandler" +| `webHandler` | `WebHandler` | 1 | The handler for the request. -| "webSessionManager" +| `webSessionManager` | `WebSessionManager` | 0..1 -| The manager for ``WebSession``'s exposed through a method on `ServerWebExchange`. +| The manager for `WebSession` instances exposed through a method on `ServerWebExchange`. `DefaultWebSessionManager` by default. -| "serverCodecConfigurer" +| `serverCodecConfigurer` | `ServerCodecConfigurer` | 0..1 -| For access to ``HttpMessageReader``'s for parsing form data and multipart data that's then +| For access to `HttpMessageReader` instances for parsing form data and multipart data that is then exposed through methods on `ServerWebExchange`. `ServerCodecConfigurer.create()` by default. -| "localeContextResolver" +| `localeContextResolver` | `LocaleContextResolver` | 0..1 | The resolver for `LocaleContext` exposed through a method on `ServerWebExchange`. `AcceptHeaderLocaleContextResolver` by default. -| "forwardedHeaderTransformer" +| `forwardedHeaderTransformer` | `ForwardedHeaderTransformer` | 0..1 -| For processing Forwarded type headers, either extracting and removing, or removing them only. +| For processing forwarded type headers, either by extracting and removing them or by removing them only. Not used by default. |=== + [[webflux-form-data]] -==== Form data +==== Form Data `ServerWebExchange` exposes the following method for access to form data: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- Mono> getFormData(); ---- +==== The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse form data -("application/x-www-form-urlencoded") into a `MultiValueMap`. By default -`FormHttpMessageReader` is configured for use via the `ServerCodecConfigurer` bean -(see <>). +(`application/x-www-form-urlencoded`) into a `MultiValueMap`. By default, +`FormHttpMessageReader` is configured for use by the `ServerCodecConfigurer` bean +(see the <>). + [[webflux-multipart]] -==== Multipart data -[.small]#<># +==== Multipart Data +[.small]#<># `ServerWebExchange` exposes the following method for access to multipart data: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- Mono> getMultipartData(); ---- +==== The `DefaultServerWebExchange` uses the configured -`HttpMessageReader>` to parse "multipart/form-data" content -into a `MultiValueMap`. At present -https://github.com/synchronoss/nio-multipart[Synchronoss NIO Multipart] is the only 3rd -party library supported, and the only library we know for non-blocking parsing of +`HttpMessageReader>` to parse `multipart/form-data` content +into a `MultiValueMap`. At present, +https://github.com/synchronoss/nio-multipart[Synchronoss NIO Multipart] is the only +third-party library supported and the only library we know for non-blocking parsing of multipart requests. It is enabled through the `ServerCodecConfigurer` bean -(see <>). +(see the <>). -To parse multipart data in streaming fashion, use the `Flux` returned from an -`HttpMessageReader` instead. For example in an annotated controller use of -`@RequestPart` implies Map-like access to individual parts by name, and hence requires -parsing multipart data in full. By contrast `@RequestBody` can be used to decode the +To parse multipart data in streaming fashion, you can use the `Flux` returned from an +`HttpMessageReader` instead. For example, in an annotated controller, use of +`@RequestPart` implies `Map`-like access to individual parts by name and, hence, requires +parsing multipart data in full. By contrast, you can use `@RequestBody` to decode the content to `Flux` without collecting to a `MultiValueMap`. + [[webflux-forwarded-headers]] ==== Forwarded Headers -[.small]#<># +[.small]#<># -As a request goes through proxies such as load balancers the host, port, and -scheme may change and that makes it a challenge to create links that point to the correct -host, port, and scheme from a client perspective. +As a request goes through proxies (such as load balancers), the host, port, and +scheme may change, and that makes it a challenge, from a client perspective, to create links that point to the correct +host, port, and scheme. -https://tools.ietf.org/html/rfc7239[RFC 7239] defines the "Forwarded" HTTP header +https://tools.ietf.org/html/rfc7239[RFC 7239] defines the `Forwarded` HTTP header that proxies can use to provide information about the original request. There are other -non-standard headers too including "X-Forwarded-Host", "X-Forwarded-Port", -"X-Forwarded-Proto", "X-Forwarded-Ssl", and "X-Forwarded-Prefix". +non-standard headers, too, including `X-Forwarded-Host`, `X-Forwarded-Port`, +`X-Forwarded-Proto`, `X-Forwarded-Ssl`, and `X-Forwarded-Prefix`. `ForwardedHeaderTransformer` is a component that modifies the host, port, and scheme of -the request, based on Forwarded headers, and then removes those headers. Simply declare -it as a bean with the name "forwardedHeaderTransformer" and it will be +the request, based on forwarded headers, and then removes those headers. You can declare +it as a bean with a name of `forwardedHeaderTransformer`, and it is <> and used. -There are security considerations for forwarded headers since an application can't know -if the headers were added by a proxy as intended, or with a malicious client. This is why -a proxy at the boundary of trust should be configured to remove untrusted Forwarded coming +There are security considerations for forwarded headers, since an application cannot know +if the headers were added by a proxy, as intended, or by a malicious client. This is why +a proxy at the boundary of trust should be configured to remove untrusted forwarded traffic coming from the outside. You can also configure the `ForwardedHeaderTransformer` with -`removeOnly=true` in which case it will remove but not use the headers. +`removeOnly=true`, in which case it removes but does not use the headers. -[NOTE] -==== -In 5.1 `ForwardedHeaderFilter` was deprecated and superceded by +NOTE: In 5.1 `ForwardedHeaderFilter` was deprecated and superceded by `ForwardedHeaderTransformer` so forwarded headers can be processed earlier, before the exchange is created. If the filter is configured anyway, it is taken out of the list of filters, and `ForwardedHeaderTransformer` is used instead. -==== [[webflux-filters]] === Filters -[.small]#<># +[.small]#<># -In the <>, a `WebFilter` can be used to apply interception-style +In the <>, you can use a `WebFilter` to apply interception-style logic before and after the rest of the processing chain of filters and the target `WebHandler`. When using the <>, registering a `WebFilter` is as simple -as declaring it as a Spring bean, and optionally expressing precedence via `@Order` on +as declaring it as a Spring bean and (optionally) expressing precedence by using `@Order` on the bean declaration or by implementing `Ordered`. -The following describe the available `WebFilter` implementations: [[webflux-filters-cors]] ==== CORS -[.small]#<># +[.small]#<># Spring WebFlux 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. +controllers. However, when you use it with Spring Security, we advise relying on the built-in +`CorsFilter`, which must be ordered ahead of Spring Security's chain of filters. See the section on <> and the <> for more details. @@ -620,15 +613,15 @@ See the section on <> and the <> for more [[webflux-exception-handler]] === Exceptions -[.small]#<># +[.small]#<># -In the <>, a `WebExceptionHandler` can be used to to handle -exceptions from the chain of ``WebFilter``'s and the target `WebHandler`. When using the +In the <>, you can use a `WebExceptionHandler` to handle +exceptions from the chain of `WebFilter` instances and the target `WebHandler`. When using the <>, registering a `WebExceptionHandler` is as simple as declaring it as a -Spring bean, and optionally expressing precedence via `@Order` on the bean declaration or +Spring bean and (optionally) expressing precedence by using `@Order` on the bean declaration or by implementing `Ordered`. -Below are the available `WebExceptionHandler` implementations: +The following table describes the available `WebExceptionHandler` implementations: [cols="1,2", options="header"] |=== @@ -636,12 +629,12 @@ Below are the available `WebExceptionHandler` implementations: | `ResponseStatusExceptionHandler` | Provides handling for exceptions of type - {api-spring-framework}/web/server/ResponseStatusException.html[ResponseStatusException] + {api-spring-framework}/web/server/ResponseStatusException.html[`ResponseStatusException`] by setting the response to the HTTP status code of the exception. | `WebFluxResponseStatusExceptionHandler` | Extension of `ResponseStatusExceptionHandler` that can also determine the HTTP status - code an `@ResponseStatus` annotation on any exception. + code of a `@ResponseStatus` annotation on any exception. This handler is declared in the <>. @@ -651,81 +644,83 @@ Below are the available `WebExceptionHandler` implementations: [[webflux-codecs]] === Codecs -[.small]#<># +[.small]#<># -{api-spring-framework}/http/codec/HttpMessageReader.html[HttpMessageReader] and -{api-spring-framework}/http/codec/HttpMessageWriter.html[HttpMessageWriter] are contracts -for encoding and decoding HTTP request and response content via non-blocking I/O with +{api-spring-framework}/http/codec/HttpMessageReader.html[`HttpMessageReader`] and +{api-spring-framework}/http/codec/HttpMessageWriter.html[`HttpMessageWriter`] are contracts +for encoding and decoding HTTP request and response content through non-blocking I/O with (Rective Streams) back pressure. -{api-spring-framework}/core/codec/Encoder.html[Encoder] and -{api-spring-framework}/core/codec/Decoder.html[Decoder] are contracts for encoding and +{api-spring-framework}/core/codec/Encoder.html[`Encoder`] and +{api-spring-framework}/core/codec/Decoder.html[`Decoder`] are contracts for encoding and decoding content, independent of HTTP. They can be wrapped with `EncoderHttpMessageWriter` -or `DecoderHttpMessageReader` and used for web processing. +or `DecoderHttpMessageReader` and are used for web processing. -All codecs are for client or server side use. All build on -{api-spring-framework}/core/io/buffer/DataBuffer.html[DataBuffer] which abstracts byte -buffer representations such as the Netty `ByteBuf` or `java.nio.ByteBuffer` (see +All codecs are for client- or server-side use. All build on +{api-spring-framework}/core/io/buffer/DataBuffer.html[`DataBuffer`], which abstracts byte +buffer representations, such as the Netty `ByteBuf` or `java.nio.ByteBuffer` (see <> for more details). `ClientCodecConfigurer` and `ServerCodecConfigurer` are typically used to configure and customize the codecs to use in an application. The `spring-core` module has encoders and decoders for `byte[]`, `ByteBuffer`, `DataBuffer`, `Resource`, and `String`. The `spring-web` module adds encoders and decoders for Jackson -JSON, Jackson Smile, JAXB2, Protocol Buffers, along with other web-specific HTTP message +JSON, Jackson Smile, JAXB2, Protocol Buffers, and other web-specific HTTP message readers and writers for form data, multipart requests, and server-sent events. + [[webflux-codecs-jackson]] -==== Jackson +==== Using Jackson -The decoder relies on Jackson's non-blocking, byte array parser to parse a stream of byte -chunks into a `TokenBuffer` stream, which can then be turned into Objects with Jackson's -`ObjectMapper`. JSON and https://github.com/FasterXML/smile-format-specification[Smile] +The decoder relies on Jackson's non-blocking, byte-array parser to parse a stream of byte +chunks into a `TokenBuffer` stream, which can then be turned into objects with Jackson's +`ObjectMapper`. The JSON and https://github.com/FasterXML/smile-format-specification[Smile] (binary JSON) data formats are currently supported. -The encoder processes a `Publisher` as follows: +The encoder processes a `Publisher`, as follows: -* if the `Publisher` is a `Mono` (i.e. single value), the value is encoded when available. -* if media type is `application/stream+json` for JSON or `application/stream+x-jackson-smile` +* If the `Publisher` is a `Mono` (that is, a single value), the value is encoded when available. +* If media type is `application/stream+json` for JSON or `application/stream+x-jackson-smile` for Smile, each value produced by the `Publisher` is encoded individually (and followed by a new line in JSON). -* otherwise all items from the `Publisher` are gathered in with `Flux#collectToList()` +* Otherwise, all items from the `Publisher` are gathered in with `Flux#collectToList()`, and the resulting collection is encoded as an array. -As a special case to the above rules the `ServerSentEventHttpMessageWriter` feeds items +As a special case to the preceding rules, the `ServerSentEventHttpMessageWriter` feeds items emitted from its input `Publisher` individually into the `Jackson2JsonEncoder` as a `Mono`. Note that both the Jackson JSON encoder and decoder explicitly back out of rendering -elements of type `String`. Instead ``String``'s are treated as low level content, (i.e. +elements of type `String`. Instead `String` instances are treated as low level content (that is, serialized JSON) and are rendered as-is by the `CharSequenceEncoder`. If you want a -`Flux` rendered as a JSON array, you'll have to use `Flux#collectToList()` and +`Flux` rendered as a JSON array, you have to use `Flux#collectToList()` and provide a `Mono>` instead. + [[webflux-codecs-streaming]] ==== HTTP Streaming -[.small]#<># +[.small]#<># -When a multi-value, reactive type such as `Flux` is used for response rendering, it may -be collected to a `List` and rendered as a whole (e.g. JSON array), or it may be treated +When a multi-value reactive type such as a `Flux` is used for response rendering, it may +be collected to a `List` and rendered as a whole (for example, a JSON array), or it may be treated as an infinite stream with each item flushed immediately. The determination for which is -which is made based on content negotiation and the selected media type which may imply a -streaming format (e.g. "text/event-stream", "application/stream+json"), or not -(e.g. "application/json"). +which is made based on content negotiation and the selected media type, which may imply a +streaming format (for example, `text/event-stream`, `application/stream+json`) or not +(for example, `application/json`). -When streaming to the HTTP response, regardless of the media type (e.g. text/event-stream, -application/stream+json), it is important to send data periodically, since the write would +When streaming to the HTTP response, regardless of the media type (for example, `text/event-stream` and +`application/stream+json`), it is important to send data periodically, since the write would fail if the client has disconnected. The send could take the form of an empty -(comment-only) SSE event, or any other data that the other side would have to interpret as +(comment-only) SSE event or any other data that the other side would have to interpret as a heartbeat and ignore. [[webflux-logging]] === Logging -[.small]#<># +[.small]#<># DEBUG level logging in Spring WebFlux is designed to be compact, minimal, and human-friendly. It focuses on high value bits of information that are useful over and @@ -742,28 +737,30 @@ not meet the stated goals, please let us know. [[webflux-logging-id]] ==== Log Id -In WebFlux, a single request may be executed over multiple threads and the thread id +In WebFlux, a single request can be executed over multiple threads and the thread ID is not useful for correlating log messages that belong to a specific request. This is why -WebFlux log messages are prefixed with a request specific id by default. +WebFlux log messages are prefixed with a request-specific ID by default. -On the server side the log id is stored in the `ServerWebExchange` attribute -{api-spring-framework}/web/server/ServerWebExchange.html#LOG_ID_ATTRIBUTE[LOG_ID_ATTRIBUTE] -while a fully formatted prefix based on that id is available via -`ServerWebExchange#getLogPrefix()`. On the `WebClient` side, the log id is stored in the +On the server side, the log ID is stored in the `ServerWebExchange` attribute +({api-spring-framework}/web/server/ServerWebExchange.html#LOG_ID_ATTRIBUTE[`LOG_ID_ATTRIBUTE`]), +while a fully formatted prefix based on that ID is available from +`ServerWebExchange#getLogPrefix()`. On the `WebClient` side, the log ID is stored in the `ClientRequest` attribute -{api-spring-framework}/web/reactive/function/client/ClientRequest.html#LOG_ID_ATTRIBUTE[LOG_ID_ATTRIBUTE] -while a fully formatted prefix is available via `ClientRequest#logPrefix()`. +({api-spring-framework}/web/reactive/function/client/ClientRequest.html#LOG_ID_ATTRIBUTE[`LOG_ID_ATTRIBUTE`]) +,while a fully formatted prefix is available from `ClientRequest#logPrefix()`. + [[webflux-logging-sensitive-data]] -==== Sensitive Data -[.small]#<># +==== Logging Sensitive Data +[.small]#<># -DEBUG and TRACE logging may log sensitive information. This is why form parameters and -headers are masked by default and their logging in full must be enabled explicitly. +`DEBUG` and `TRACE` logging can log sensitive information. This is why form parameters and +headers are masked by default and you must explicitly enable their logging in full. -For server side requests: +The followig example shows how to do so for server-side requests: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -777,9 +774,11 @@ class MyConfig implements WebFluxConfigurer { } } ---- +==== -For client side requests: +The following example shows how to do so for client-side requests: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -790,41 +789,45 @@ WebClient webClient = WebClient.builder() .exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build()) .build(); ---- - +==== [[webflux-dispatcher-handler]] -== DispatcherHandler -[.small]#<># +== Using `DispatcherHandler` +[.small]#<># -Spring WebFlux, like Spring MVC, is designed around the front controller pattern where a +Spring WebFlux, similarly to Spring MVC, is designed around the front controller pattern, where a central `WebHandler`, the `DispatcherHandler`, provides a shared algorithm for request -processing while actual work is performed by configurable, delegate components. +processing, while actual work is performed by configurable, delegate components. This model is flexible and supports diverse workflows. `DispatcherHandler` discovers the delegate components it needs from Spring configuration. It is also designed to be a Spring bean itself and implements `ApplicationContextAware` -for access to the context it runs in. If `DispatcherHandler` is declared with the bean -name "webHandler" it is in turn discovered by -{api-spring-framework}/web/server/adapter/WebHttpHandlerBuilder.html[WebHttpHandlerBuilder] -which puts together a request processing chain as described in +for access to the context in which it runs. If `DispatcherHandler` is declared with a bean +name of `webHandler`, it is, in turn, discovered by +{api-spring-framework}/web/server/adapter/WebHttpHandlerBuilder.html[`WebHttpHandlerBuilder`], +which puts together a request-processing chain, as described in <>. Spring configuration in a WebFlux application typically contains: -* `DispatcherHandler` with the bean name "webHandler" +* `DispatcherHandler` with the bean name, `webHandler` * `WebFilter` and `WebExceptionHandler` beans -* <> +* <> * Others -The configuration is given to `WebHttpHandlerBuilder` to build the processing chain: +The configuration is given to `WebHttpHandlerBuilder` to build the processing chain, +as the following example shows: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- ApplicationContext context = ... HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context); ---- +==== The resulting `HttpHandler` is ready for use with a <>. @@ -832,40 +835,40 @@ The resulting `HttpHandler` is ready for use with a [[webflux-special-bean-types]] -=== Special bean types -[.small]#<># +=== Special Bean Types +[.small]#<># The `DispatcherHandler` 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. +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 them, or replace them. -The table below lists the special beans detected by the `DispatcherHandler`. Note that -there are also some other beans detected at a lower level, see -<> in the Web Handler API. +The following table lists the special beans detected by the `DispatcherHandler`. Note that +there are also some other beans detected at a lower level (see +<> in the Web Handler API). [[webflux-special-beans-table]] [cols="1,2", options="header"] |=== | Bean type | Explanation -| HandlerMapping -| Map a request to a handler. The mapping is based on some criteria the details of +| `HandlerMapping` +| Map a request to a handler. The mapping is based on some criteria, the details of which vary by `HandlerMapping` implementation -- annotated controllers, simple - URL pattern mappings, etc. + URL pattern mappings, and others. The main `HandlerMapping` implementations are `RequestMappingHandlerMapping` for `@RequestMapping` annotated methods, `RouterFunctionMapping` for functional endpoint routes, and `SimpleUrlHandlerMapping` for explicit registrations of URI path patterns - and ``WebHandler``'s. + and `WebHandler` instances. -| HandlerAdapter +| `HandlerAdapter` | Help the `DispatcherHandler` to invoke a handler mapped to a request regardless of - how the handler is actually invoked. For example invoking an annotated controller + 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 `DispatcherHandler` from such details. -| HandlerResultHandler +| `HandlerResultHandler` | Process the result from the handler invocation and finalize the response. See <>. @@ -875,33 +878,30 @@ there are also some other beans detected at a lower level, see [[webflux-framework-config]] === WebFlux Config -[.small]#<># +[.small]#<># -Applications can declare the infrastructure beans listed under +Applications can declare the infrastructure beans (listed under <> and -<> that are required to process requests. -However in most cases the <> is the best starting point. It declares the -required beans and provides a higher level configuration callback API to customize it. +<>) that are required to process requests. +However, in most cases, the <> is the best starting point. It declares the +required beans and provides a higher-level configuration callback API to customize it. -[NOTE] -==== -Spring Boot relies on the WebFlux config to configure Spring WebFlux and also provides +NOTE: Spring Boot relies on the WebFlux config to configure Spring WebFlux and also provides many extra convenient options. -==== [[webflux-dispatcher-handler-sequence]] === Processing -[.small]#<># +[.small]#<># -The `DispatcherHandler` processes requests as follows: +`DispatcherHandler` processes requests as follows: -* Each `HandlerMapping` is asked to find a matching handler and the first match is used. -* If a handler is found, it is executed through an appropriate `HandlerAdapter` which +* Each `HandlerMapping` is asked to find a matching handler, and the first match is used. +* If a handler is found, it is executed through an appropriate `HandlerAdapter`, which exposes the return value from the execution as `HandlerResult`. * The `HandlerResult` is given to an appropriate `HandlerResultHandler` to complete -processing by writing to the response directly or using a view to render. +processing by writing to the response directly or by using a view to render. @@ -909,16 +909,16 @@ processing by writing to the response directly or using a view to render. === Result Handling The return value from the invocation of a handler, through a `HandlerAdapter`, is wrapped -as `HandlerResult`, along with some additional context, and passed to the first -`HandlerResultHandler` that claims support for it. The table below shows the available -`HandlerResultHandler` implementations all of which are declared in the <>: +as a `HandlerResult`, along with some additional context, and passed to the first +`HandlerResultHandler` that claims support for it. The following table shows the available +`HandlerResultHandler` implementations, all of which are declared in the <>: [cols="1,2,1", options="header"] |=== | Result Handler Type | Return Values | Default Order | `ResponseEntityResultHandler` -| `ResponseEntity`, typically from ``@Controller``'s. +| `ResponseEntity`, typically from `@Controller` instances. | 0 | `ServerResponseResultHandler` @@ -930,12 +930,12 @@ as `HandlerResult`, along with some additional context, and passed to the first | 100 | `ViewResolutionResultHandler` -| `CharSequence` or {api-spring-framework}/web/reactive/result/view/View.html[View], - {api-spring-framework}/ui/Model.html[Model] or `Map`, +| `CharSequence`, {api-spring-framework}/web/reactive/result/view/View.html[`View`], + {api-spring-framework}/ui/Model.html[Model], `Map`, {api-spring-framework}/web/reactive/result/view/Rendering.html[Rendering], - or any other Object is treated as a model attribute. + or any other `Object` is treated as a model attribute. - Also see <>. + See also <>. | `Integer.MAX_VALUE` |=== @@ -944,65 +944,66 @@ as `HandlerResult`, along with some additional context, and passed to the first [[webflux-dispatcher-exceptions]] === Exceptions -[.small]#<># +[.small]#<># -The `HandlerResult` returned from a `HandlerAdapter` may expose a function for error +The `HandlerResult` returned from a `HandlerAdapter` can expose a function for error handling based on some handler-specific mechanism. This error function is called if: -* the handler (e.g. `@Controller`) invocation fails. -* handling of the handler return value through a `HandlerResultHandler` fails. +* The handler (for example, `@Controller`) invocation fails. +* The handling of the handler return value through a `HandlerResultHandler` fails. -The error function can change the response, e.g. to an error status, as long as an error +The error function can change the response (for example, to an error status), as long as an error signal occurs before the reactive type returned from the handler produces any data items. This is how `@ExceptionHandler` methods in `@Controller` classes are supported. By contrast, support for the same in Spring MVC is built on a `HandlerExceptionResolver`. -This generally shouldn't matter, however, keep in mind that in WebFlux you cannot use a +This generally should not matter. However, keep in mind that, in WebFlux, you cannot use a `@ControllerAdvice` to handle exceptions that occur before a handler is chosen. -See also <> in the Annotated Controller section, or +See also <> in the "`Annotated Controller`" section or <> in the WebHandler API section. [[webflux-viewresolution]] === View Resolution -[.small]#<># +[.small]#<># View resolution enables rendering to a browser with an HTML template and a model without tying you to a specific view technology. In Spring WebFlux, view resolution is supported through a dedicated <> that uses -``ViewResolver``'s to map a String, representing a logical view name, to a `View` + `ViewResolver` instances to map a String (representing a logical view name) to a `View` instance. The `View` is then used to render the response. + [[webflux-viewresolution-handling]] ==== Handling -[.small]#<># +[.small]#<># The `HandlerResult` passed into `ViewResolutionResultHandler` contains the return value -from the handler, and also the model that contains attributes added during request -handling. The return value is processed as one of the following: +from the handler and the model that contains attributes added during request +handling. The return value is processed as one of the following: -* `String`, `CharSequence` -- a logical view name to be resolved to a `View` through -the list of configured ``ViewResolver``'s. -* `void` -- select a default view name based on the request path minus the leading and +* `String`, `CharSequence`: A logical view name to be resolved to a `View` through +the list of configured `ViewResolver` implementations. +* `void`: Select a default view name based on the request path, minus the leading and trailing slash, and resolve it to a `View`. The same also happens when a view name -was not provided, e.g. model attribute was returned, or an async return value, e.g. -`Mono` completed empty. -* {api-spring-framework}/web/reactive/result/view/Rendering.html[Rendering] -- API for -view resolution scenarios; explore the options in your IDE with code completion. -* `Model`, `Map` -- extra model attributes to be added to the model for the request. -* Any other -- any other return value (except for simple types, as determined by +was not provided (for example, model attribute was returned) or an async return value +(for example, `Mono` completed empty). +* {api-spring-framework}/web/reactive/result/view/Rendering.html[Rendering]: API for +view resolution scenarios. Explore the options in your IDE with code completion. +* `Model`, `Map`: Extra model attributes to be added to the model for the request. +* Any other: Any other return value (except for simple types, as determined by {api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]) is treated as a model attribute to be added to the model. The attribute name is derived -from the Class name, using {api-spring-framework}/core/Conventions.html[Conventions], +from the class name by using {api-spring-framework}/core/Conventions.html[conventions], unless a handler method `@ModelAttribute` annotation is present. -The model can contain asynchronous, reactive types (e.g. from Reactor, RxJava). Prior +The model can contain asynchronous, reactive types (for example, from Reactor or RxJava). Prior to rendering, `AbstractView` resolves such model attributes into concrete values and updates the model. Single-value reactive types are resolved to a single -value, or no value (if empty) while multi-value reactive types, e.g. `Flux` are +value or no value (if empty), while multi-value reactive types (for example, `Flux`) are collected and resolved to `List`. To configure view resolution is as simple as adding a `ViewResolutionResultHandler` bean @@ -1012,49 +1013,50 @@ dedicated configuration API for view resolution. See <> for more on the view technologies integrated with Spring WebFlux. + [[webflux-redirecting-redirect-prefix]] ==== Redirecting -[.small]#<># +[.small]#<># -The special `redirect:` prefix in a view name allows you to perform a redirect. The +The special `redirect:` prefix in a view name lets you 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` or -`Rendering.redirectTo("abc").build()`, but now the controller itself can simply +`Rendering.redirectTo("abc").build()`, but now the controller itself can operate in terms of logical view names. A view name such as -`redirect:/some/resource` is relative to the current application, while the view name +`redirect:/some/resource` is relative to the current application, while a view name such as `redirect:http://example.com/arbitrary/path` redirects to an absolute URL. [[webflux-multiple-representations]] -==== Content negotiation -[.small]#<># +==== Content Negotiation +[.small]#<># `ViewResolutionResultHandler` supports content negotiation. It compares the request -media type(s) with the media type(s) supported by each selected `View`. The first `View` +media types with the media types supported by each selected `View`. The first `View` that supports the requested media type(s) is used. In order to support media types such as JSON and XML, Spring WebFlux provides -`HttpMessageWriterView` which is a special `View` that renders through an -<>. Typically you would configure these as default -views through the <>. Default views are +`HttpMessageWriterView`, which is a special `View` that renders through an +<>. Typically, you would configure these as default +views through the <>. Default views are always selected and used if they match the requested media type. - [[webflux-controller]] == Annotated Controllers -[.small]#<># +[.small]#<># -Spring WebFlux provides an annotation-based programming model where `@Controller` and +Spring WebFlux 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 +handle exceptions, and more. Annotated controllers have flexible method signatures and do not have to extend base classes nor implement specific interfaces. -Here is a basic example: +The following listing shows a basic example: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1067,66 +1069,71 @@ Here is a basic example: } } ---- +==== -In this example the methods returns a String to be written to the response body. +In the preceding example, the method returns a `String` to be written to the response body. [[webflux-ann-controller]] -=== @Controller -[.small]#<># +=== Using `@Controller` +[.small]#<># -You can define controller beans using a standard Spring bean definition. -The `@Controller` stereotype allows for auto-detection, aligned with Spring general support +You can define controller beans by using a standard Spring bean definition. +The `@Controller` stereotype allows for auto-detection and is 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: +your Java configuration, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Configuration - @ComponentScan("org.example.web") + @ComponentScan("org.example.web") <1> public class WebConfig { // ... } ---- +<1> Scan the `org.example.web` package. +==== `@RestController` is a <> 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. +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 versus view resolution and rendering with an HTML template. [[webflux-ann-requestmapping]] === Request Mapping -[.small]#<># +[.small]#<># 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 +types. You can use it at the class level to express shared mappings or at the method level to narrow down to a specific endpoint mapping. There are also HTTP method specific shortcut variants of `@RequestMapping`: -- `@GetMapping` -- `@PostMapping` -- `@PutMapping` -- `@DeleteMapping` -- `@PatchMapping` +* `@GetMapping` +* `@PostMapping` +* `@PutMapping` +* `@DeleteMapping` +* `@PatchMapping` -The above are <> 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 +The preceding annotations are <> that are provided +because, arguably, most controller methods should be mapped to a specific HTTP method versus +using `@RequestMapping`, which, by default, matches to all HTTP methods. At the same time, a `@RequestMapping` is still needed at the class level to express shared mappings. -Below is an example with type and method level mappings: +The following example uses type and method level mappings: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1146,20 +1153,24 @@ Below is an example with type and method level mappings: } } ---- +==== + [[webflux-ann-requestmapping-uri-templates]] ==== URI Patterns -[.small]#<># +[.small]#<># -You can map requests using glob patterns and wildcards: +You can map requests by 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`: +You can also declare URI variables and access their values with `@PathVariable`, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1168,39 +1179,46 @@ You can also declare URI variables and access their values with `@PathVariable`: // ... } ---- +==== -URI variables can be declared at the class and method level: +You can declare URI variables at the class and method levels, as the following example shows: + +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @Controller -@RequestMapping("/owners/{ownerId}") +@RequestMapping("/owners/{ownerId}") <1> public class OwnerController { - @GetMapping("/pets/{petId}") + @GetMapping("/pets/{petId}") <2> public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { // ... } } ---- +<1> Class-level URI mapping. +<2> Method-level URI mapping. +==== -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 +URI variables are automatically converted to the appropriate type or a `TypeMismatchException` +is raised. Simple types (`int`, `long`, `Date`, and so on) are supported by default and you can register support for any other data type. See <> and <>. -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 +URI variables can be named explicitly (for example, `@PathVariable("customId")`), but you can +leave that detail out if the names are the same and you compile your code with debugging information or with the `-parameters` compiler flag on Java 8. The syntax `{*varName}` declares a URI variable that matches zero or more remaining path segments. For example `/resources/{*path}` matches all files `/resources/` and the `"path"` variable captures the complete relative path. -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 +The syntax `{varName:regex}` declares a URI variable with a regular expression that has the +syntax: `{varName:regex}`. For example, given a URL of `/spring-web-3.0.5 .jar`, the following method extracts the name, version, and file extension: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1209,46 +1227,47 @@ extracts the name, version, and file extension: // ... } ---- +==== 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 +through `PropertyPlaceHolderConfigurer` against local, system, environment, and other property +sources. You ca use this to, for example, parameterize a base URL based on some external configuration. -[NOTE] -==== -Spring WebFlux uses `PathPattern` and the `PathPatternParser` for URI path matching support -both of which are located in `spring-web` and expressly designed for use with HTTP URL +NOTE: Spring WebFlux uses `PathPattern` and the `PathPatternParser` for URI path matching support. +Both classes are located in `spring-web` and are expressly designed for use with HTTP URL paths in web applications where a large number of URI path patterns are matched at runtime. -==== Spring WebFlux does not support suffix pattern matching -- unlike Spring MVC, where a -mapping such as `/person` also matches to `/person.{asterisk}`. For URL based content +mapping such as `/person` also matches to `/person.{asterisk}`. For URL-based content negotiation, if needed, we recommend using a query parameter, which is simpler, more explicit, and less vulnerable to URL path based exploits. [[webflux-ann-requestmapping-pattern-comparison]] ==== Pattern Comparison -[.small]#<># +[.small]#<># When multiple patterns match a URL, they must be compared to find the best match. This is done -with `PathPattern.SPECIFICITY_COMPARATOR` which looks for patterns that more specific. +with `PathPattern.SPECIFICITY_COMPARATOR`, which looks for patterns that are more specific. -For every pattern, a score is computed based the number of URI variables and wildcards +For every pattern, a score is computed, based on the number of URI variables and wildcards, where a URI variable scores lower than a wildcard. A pattern with a lower total score -wins. If two patterns have the same score, then the longer is chosen. +wins. If two patterns have the same score, the longer is chosen. -Catch-all patterns, e.g. `**`, `{*varName}`, are excluded from the scoring and are always +Catch-all patterns (for example, `**`, `{*varName}`) are excluded from the scoring and are always sorted last instead. If two patterns are both catch-all, the longer is chosen. + [[webflux-ann-requestmapping-consumes]] ==== Consumable Media Types -[.small]#<># +[.small]#<># -You can narrow the request mapping based on the `Content-Type` of the request: +You can narrow the request mapping based on the `Content-Type` of the request, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1257,28 +1276,28 @@ You can narrow the request mapping based on the `Content-Type` of the request: // ... } ---- +==== -The consumes attribute also supports negation expressions -- e.g. `!text/plain` means any -content type other than "text/plain". +The consumes attribute also supports negation expressions -- for example, `!text/plain` means any +content type other than `text/plain`. -You can declare a shared consumes attribute at the class level. Unlike most other request -mapping attributes however when used at the class level, a method-level consumes attribute -overrides rather than extend the class level declaration. +You can declare a shared `consumes` attribute at the class level. Unlike most other request +mapping attributes, however, when used at the class level, a method-level `consumes` attribute +overrides rather than extends the class-level declaration. + +TIP: `MediaType` provides constants for commonly used media types -- for example, +`APPLICATION_JSON_VALUE` and `APPLICATION_XML_VALUE`. -[TIP] -==== -`MediaType` provides constants for commonly used media types -- e.g. -`APPLICATION_JSON_VALUE`, `APPLICATION_XML_VALUE`. -==== [[webflux-ann-requestmapping-produces]] ==== Producible Media Types -[.small]#<># +[.small]#<># You can narrow the request mapping based on the `Accept` request header and the list of -content types that a controller method produces: +content types that a controller method produces, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1288,82 +1307,86 @@ content types that a controller method produces: // ... } ---- +==== -The media type can specify a character set. Negated expressions are supported -- e.g. -`!text/plain` means any content type other than "text/plain". +The media type can specify a character set. Negated expressions are supported -- for example, +`!text/plain` means any content type other than `text/plain`. -[NOTE] -==== -For JSON content type, the UTF-8 charset should be specified even if +NOTE: For JSON content type, you should specify the UTF-8 `charset` even if https://tools.ietf.org/html/rfc7159#section-11[RFC7159] -clearly states that "no charset parameter is defined for this registration" because some -browsers require it for interpreting correctly UTF-8 special characters. -==== +clearly states that "`no charset parameter is defined for this registration,`" because some +browsers require it to correctly interpret UTF-8 special characters. -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 +You can declare a shared `produces` attribute at the class level. Unlike most other request +mapping attributes, however, when used at the class level, a method-level `produces` attribute overrides rather than extend the class level declaration. -[TIP] -==== -`MediaType` provides constants for commonly used media types -- e.g. +TIP: `MediaType` provides constants for commonly used media types -- e.g. `APPLICATION_JSON_UTF8_VALUE`, `APPLICATION_XML_VALUE`. -==== + [[webflux-ann-requestmapping-params-and-headers]] ==== Parameters and Headers -[.small]#<># +[.small]#<># You can narrow request mappings based on query parameter conditions. You can test for the -presence of a query parameter (`"myParam"`), for the absence (`"!myParam"`), or for a -specific value (`"myParam=myValue"`): +presence of a query parameter (`myParam`), for its absence (`!myParam`), or for a +specific value (`myParam=myValue`). The following examples tests for a parameter with a value: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- - @GetMapping(path = "/pets/{petId}", **params = "myParam=myValue"**) + @GetMapping(path = "/pets/{petId}", params = "myParam=myValue") <1> public void findPet(@PathVariable String petId) { // ... } ---- +<1> Check that `myParam` equals `myValue`. +==== -You can also use the same with request header conditions: +You can also use the same with request header conditions, as the follwing example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- - @GetMapping(path = "/pets", **headers = "myHeader=myValue"**) + @GetMapping(path = "/pets", headers = "myHeader=myValue") <1> public void findPet(@PathVariable String petId) { // ... } ---- +<1> Check that `myHeader` equals `myValue`. +==== + [[webflux-ann-requestmapping-head-options]] ==== HTTP HEAD, OPTIONS -[.small]#<># +[.small]#<># -`@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 the `HttpHandler` server adapter, ensures a `"Content-Length"` -header is set to the number of bytes written and without actually writing to the response. +`@GetMapping` and `@RequestMapping(method=HttpMethod.GET)` support HTTP HEAD +transparently for request mapping purposes. Controller methods need not change. +A response wrapper, applied in the `HttpHandler` server adapter, ensures a `Content-Length` +header is set to the number of bytes written without actually writing to the response. -By default HTTP OPTIONS is handled by setting the "Allow" response header to the list of HTTP +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. +For a `@RequestMapping` without HTTP method declarations, the `Allow` header is set to +`GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS`. Controller methods should always declare the +supported HTTP methods (for example, by using the HTTP method specific variants -- +`@GetMapping`, `@PostMapping`, and others). -`@RequestMapping` method can be explicitly mapped to HTTP HEAD and HTTP OPTIONS, but that +You can explicitly map a `@RequestMapping` method to HTTP HEAD and HTTP OPTIONS, but that is not necessary in the common case. + [[webflux-ann-requestmapping-composed]] ==== Custom Annotations -[.small]#<># +[.small]#<># Spring WebFlux supports the use of <> for request mapping. Those are annotations that are themselves meta-annotated with @@ -1371,25 +1394,27 @@ for request mapping. Those are annotations that are themselves meta-annotated wi 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 +examples of composed annotations. They are provided, because, arguably, most +controller methods should be mapped to a specific HTTP method versus using `@RequestMapping`, +which, by default, matches to all HTTP methods. If you need an example of composed annotations, look at how those are declared. Spring WebFlux 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 +`RequestMappingHandlerMapping` and overriding the `getCustomMethodCondition` method, where you can check the custom attribute and return your own `RequestCondition`. + [[webflux-ann-requestmapping-registration]] ==== Explicit Registrations -[.small]#<># +[.small]#<># -Handler methods can be registered programmatically which can be used for dynamic -registrations, or for advanced cases such as different instances of the same handler -under different URLs. Below is an example: +You can programmatically register Handler methods, which can be used for dynamic +registrations or for advanced cases, such as different instances of the same handler +under different URLs. The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1410,36 +1435,37 @@ public class MyConfig { } ---- - -<1> Inject target handler(s) and the handler mapping for controllers. -<2> Prepare the request mapping meta data. +<1> Inject target handlers and the handler mapping for controllers. +<2> Prepare the request mapping metadata. <3> Get the handler method. <4> Add the registration. +==== [[webflux-ann-methods]] -=== Handler methods -[.small]#<># +=== Handler Methods +[.small]#<># `@RequestMapping` handler methods have a flexible signature and can choose from a range of supported controller method arguments and return values. + [[webflux-ann-arguments]] -==== Method arguments -[.small]#<># +==== Method Arguments +[.small]#<># -The table below shows supported controller method arguments. +The following table shows the supported controller method arguments. Reactive types (Reactor, RxJava, <>) are -supported on arguments that require blocking I/O, e.g. reading the request body, to -be resolved. This is marked in the description column. Reactive types are not expected -on arguments that don't require blocking. +supported on arguments that require blocking I/O (for example, reading the request body) to +be resolved. This is marked in the Description column. Reactive types are not expected +on arguments that do not require blocking. JDK 1.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`. +annotations that have a `required` attribute (for example, `@RequestParam`, `@RequestHeader`, +and others) and is equivalent to `required=false`. [cols="1,2", options="header"] |=== @@ -1453,18 +1479,18 @@ etc, and is equivalent to `required=false`. | Access to the HTTP request or response. | `WebSession` -| Access to the session; this does not force the start of a new session unless attributes +| Access to the session. This does not force the start of a new session unless attributes are added. Supports reactive types. | `java.security.Principal` -| Currently authenticated user; possibly a specific `Principal` implementation class if known. +| The currently authenticated user -- possibly a specific `Principal` implementation class if known. Supports reactive types. | `org.springframework.http.HttpMethod` | The HTTP method of the request. | `java.util.Locale` -| The current request locale, determined by the most specific `LocaleResolver` available, in +| The current request locale, determined by the most specific `LocaleResolver` available -- in effect, the configured `LocaleResolver`/`LocaleContextResolver`. | `java.util.TimeZone` + `java.time.ZoneId` @@ -1480,32 +1506,32 @@ etc, and is equivalent to `required=false`. | For access to Servlet request parameters. Parameter values are converted to the declared method argument type. See <>. - Note that use of `@RequestParam` is optional, e.g. to set its attributes. - See "Any other argument" further below in this table. + Note that use of `@RequestParam` is optional -- for example, to set its attributes. + See "`Any other argument`" later in this table. | `@RequestHeader` | For access to request headers. Header values are converted to the declared method argument type. See <>. | `@CookieValue` -| For access to cookies. Cookies values are converted to the declared method argument type. +| For access to cookies. Cookie values are converted to the declared method argument type. See <>. | `@RequestBody` | For access to the HTTP request body. Body content is converted to the declared method - argument type using ``HttpMessageReader``'s. Supports reactive types. + argument type byusing `HttpMessageReader` instances. Supports reactive types. See <>. | `HttpEntity` -| For access to request headers and body. The body is converted with ``HttpMessageReader``'s. +| For access to request headers and body. The body is converted with `HttpMessageReader` instances. Supports reactive types. See <>. | `@RequestPart` -| For access to a part in a "multipart/form-data" request. Supports reactive types. +| For access to a part in a `multipart/form-data` request. Supports reactive types. See <> and <>. -| `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 +| `java.util.Map`, `org.springframework.ui.Model`, and `org.springframework.ui.ModelMap`. +| For access to the model that is used in HTML controllers and is exposed to templates as part of view rendering. | `@ModelAttribute` @@ -1513,17 +1539,17 @@ etc, and is equivalent to `required=false`. data binding and validation applied. See <> as well as <> and <>. - Note that use of `@ModelAttribute` is optional, e.g. to set its attributes. - See "Any other argument" further below in this table. + Note that use of `@ModelAttribute` is optional -- for example, to set its attributes. + See "`Any other argument`" later 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 + (that is, a `@ModelAttribute` argument) or errors from the validation of a `@RequestBody` or + `@RequestPart` argument. 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 +| For marking form processing complete, which triggers cleanup of session attributes declared through a class-level `@SessionAttributes` annotation. See <> for more details. @@ -1532,7 +1558,7 @@ etc, and is equivalent to `required=false`. See <>. | `@SessionAttribute` -| For access to any session attribute; in contrast to model attributes stored in the session +| 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 <> for more details. @@ -1540,18 +1566,18 @@ etc, and is equivalent to `required=false`. | For access to request attributes. See <> 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 +| If a method argument is not matched to any of the above, it is, by default, resolved as + a `@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. + or as a `@ModelAttribute`, otherwise. |=== [[webflux-ann-return-types]] -==== Return values -[.small]#<># +==== Return Values +[.small]#<># -The table below shows supported controller method return values. Note that reactive types +The following table shows the supported controller method return values. Note that reactive types from libraries such as Reactor, RxJava, <> are generally supported for all return values. @@ -1560,101 +1586,103 @@ generally supported for all return values. | Controller method return value | Description | `@ResponseBody` -| The return value is encoded through ``HttpMessageWriter``'s and written to the response. +| The return value is encoded through `HttpMessageWriter` instances and written to the response. See <>. | `HttpEntity`, `ResponseEntity` -| The return value specifies the full response including HTTP headers and body be encoded - through ``HttpMessageWriter``'s and written to the response. +| The return value specifies the full response, including HTTP headers, and the body is encoded + through `HttpMessageWriter` instances and written to the response. See <>. | `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 +| A view name to be resolved with `ViewResolver` instances 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). + method can also programmatically enrich the model by declaring a `Model` argument + (described <>). | `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). + through command objects and `@ModelAttribute` methods. The handler method can also + programmatically enrich the model by declaring a `Model` argument (described <>). | `java.util.Map`, `org.springframework.ui.Model` -| Attributes to be added to the implicit model with the view name implicitly determined +| Attributes to be added to the implicit model, with the view name implicitly determined based on the request path. | `@ModelAttribute` -| An attribute to be added to the model with the view name implicitly determined based +| An attribute to be added to the model, with the view name implicitly determined based on the request path. - Note that `@ModelAttribute` is optional. See "Any other return value" further below in + Note that `@ModelAttribute` is optional. See "`Any other return value`" later in this table. | `Rendering` | An API for model and view rendering scenarios. | `void` -| A method with a `void`, possibly async (e.g. `Mono`), return type (or a `null` return +| A method with a `void`, possibly asynchronous (for example, `Mono`), return type (or a `null` return value) is considered to have fully handled the response if it also has a `ServerHttpResponse`, - or a `ServerWebExchange` argument, or an `@ResponseStatus` annotation. The same is true also - if the controller has made a positive ETag or lastModified timestamp check. + a `ServerWebExchange` argument, or an `@ResponseStatus` annotation. The same is also true + if the controller has made a positive ETag or `lastModified` timestamp check. // TODO: See <> 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. + If none of the above is true, a `void` return type can also indicate "`no response body`" for + REST controllers or default view name selection for HTML controllers. | `Flux`, `Observable`, or other reactive type -| Emit server-sent events; the `SeverSentEvent` wrapper can be omitted when only data needs - to be written (however `text/event-stream` must be requested or declared in the mapping - through the produces attribute). +| Emit server-sent events. The `ServerSentEvent` wrapper can be omitted when only data needs + to be written (however, `text/event-stream` must be requested or declared in the mapping + through the `produces` attribute). | 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 applies); or as a model +| If a return value is not matched to any of the above, it is, by default, treated as a view + name, if it is `String` or `void` (default view name selection 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] + {api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty], in which case it remains unresolved. |=== [[webflux-ann-typeconversion]] ==== Type Conversion -[.small]#<># +[.small]#<># -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`. +Some annotated controller method arguments that represent String-based request input (for example, +`@RequestParam`, `@RequestHeader`, `@PathVariable`, `@MatrixVariable`, and `@CookieValue`) +can require type conversion if the argument is declared as something other than `String`. + +For such cases, type conversion is automatically applied based on the configured converters. +By default, simple types (such as `int`, `long`, `Date`, and others) are supported. Type conversion +can be customized through a `WebDataBinder` (see <>) or by registering +`Formatters` with the `FormattingConversionService` (see +<>). -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 <>, or by registering -`Formatters` with the `FormattingConversionService`, see -<>. [[webflux-ann-matrix-variables]] -==== Matrix variables -[.small]#<># +==== Matrix Variables +[.small]#<># http://tools.ietf.org/html/rfc3986#section-3.3[RFC 3986] discusses name-value pairs in -path segments. In Spring WebFlux 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 +path segments. In Spring WebFlux, 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. +Matrix variables can appear in any path segment, with each variable separated by a semicolon and +multiple values separated by commas -- for example, `"/cars;color=red,green;year=2012"`. Multiple +values can also be specified through repeated variable names -- for example, `"color=red;color=green;color=blue"`. -Unlike Spring MVC, in WebFlux the presence or absence of matrix variables in a URL does -not affect request mappings. In other words you're not required to use a URI variable -to mask variable content. That said if you want to access matrix variables from a -controller method you need to add a URI variable to the path segment where matrix -variables are expected. Below is an example: +Unlike Spring MVC, in WebFlux, the presence or absence of matrix variables in a URL does +not affect request mappings. In other words, you are not required to use a URI variable +to mask variable content. That said, if you want to access matrix variables from a +controller method, you need to add a URI variable to the path segment where matrix +variables are expected. The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1667,11 +1695,13 @@ variables are expected. Below is an example: // 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: +Given that all path segments can contain matrix variables, you may sometimes need to +disambiguate which path variable the matrix variable is expected to be in, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1686,9 +1716,12 @@ For example: // q2 == 22 } ---- +==== -A matrix variable may be defined as optional and a default value specified: +You can define a matrix variable may be defined as optional and specify a default value +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1700,9 +1733,11 @@ A matrix variable may be defined as optional and a default value specified: // q == 1 } ---- +==== -To get all matrix variables, use a `MultiValueMap`: +To get all matrix variables, use a `MultiValueMap`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1717,15 +1752,18 @@ To get all matrix variables, use a `MultiValueMap`: // petMatrixVars: ["q" : 22, "s" : 23] } ---- +==== + [[webflux-ann-requestparam]] -==== @RequestParam -[.small]#<># +==== Using `@RequestParam` +[.small]#<># -Use the `@RequestParam` annotation to bind query parameters to a method argument in a +You can use the `@RequestParam` annotation to bind query parameters to a method argument in a controller. The following code snippet shows the usage: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1736,7 +1774,7 @@ controller. The following code snippet shows the usage: // ... @GetMapping - public String setupForm(**@RequestParam("petId") int petId**, Model model) { + public String setupForm(@RequestParam("petId") int petId, Model model) { <1> Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; @@ -1746,43 +1784,44 @@ controller. The following code snippet shows the usage: } ---- - -[TIP] +<1> Using `@RequestParam`. ==== -Unlike the Servlet API "request parameter" concept that conflates query parameters, form -data, and multiparts into one, in WebFlux each is accessed individually through the + +TIP: The Servlet API "`request parameter`" concept conflates query parameters, form +data, and multiparts into one. However, in WebFlux, each is accessed individually through `ServerWebExchange`. While `@RequestParam` binds to query parameters only, you can use data binding to apply query parameters, form data, and multiparts to a <>. -==== -Method parameters using using the `@RequestParam` 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 a `java.util.Optional` +Method parameters that use the `@RequestParam` annotation are required by default, but +you can specify that a method parameter is optional by setting the required flag of a `@RequestParam` +to `false` or by declaring the argument with a `java.util.Optional` wrapper. Type conversion is applied automatically if the target method parameter type is not `String`. See <>. -When an `@RequestParam` annotation is declared on a `Map` or +When a `@RequestParam` annotation is declared on a `Map` or `MultiValueMap` argument, the map is populated with all query parameters. Note that use of `@RequestParam` is optional -- for example, to set its attributes. By -default any argument that is a simple value type, as determined by -{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty], -and is not resolved by any other argument resolver, is treated as if it was annotated +default, any argument that is a simple value type (as determined by +{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]) +and is not resolved by any other argument resolver is treated as if it were annotated with `@RequestParam`. + [[webflux-ann-requestheader]] -==== @RequestHeader -[.small]#<># +==== Using `@RequestHeader` +[.small]#<># -Use the `@RequestHeader` annotation to bind a request header to a method argument in a +You can use the `@RequestHeader` annotation to bind a request header to a method argument in a controller. -Given request with headers: +The following example shows a request with headers: +==== [literal] [subs="verbatim,quotes"] ---- @@ -1793,140 +1832,159 @@ 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: +The following example 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) { + @RequestHeader("Accept-Encoding") String encoding, <1> + @RequestHeader("Keep-Alive") long keepAlive) { <2> //... } ---- +<1> Get the value of the `Accept-Encoging` header. +<2> Get the value of the `Keep-Alive` header. +==== Type conversion is applied automatically if the target method parameter type is not `String`. See <>. -When an `@RequestHeader` annotation is used on a `Map`, +When a `@RequestHeader` annotation is used on a `Map`, `MultiValueMap`, 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`. -==== +TIP: Built-in support is available for converting a comma-separated string into an +array or collection of strings or other types known to the type conversion system. For +example, a method parameter annotated with `@RequestHeader("Accept")` may be of type +`String` but also of `String[]` or `List`. + [[webflux-ann-cookievalue]] -==== @CookieValue -[.small]#<># +==== Using `@CookieValue` +[.small]#<># -Use the `@CookieValue` annotation to bind the value of an HTTP cookie to a method argument +You can 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: +The following example shows a request with a 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) { + public void handle(@CookieValue("JSESSIONID") String cookie) { <1> //... } ---- +<1> Get the cookie value. +==== Type conversion is applied automatically if the target method parameter type is not `String`. See <>. + [[webflux-ann-modelattrib-method-args]] -==== @ModelAttribute -[.small]#<># +==== Using `@ModelAttribute` +[.small]#<># -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 of query parameters and form fields 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: +You can 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 overlain with +the values of query parameters and form fields whose names match to field names. This is +referred to as data binding, and it saves you from having to deal with parsing and +converting individual query parameters and form fields. The following example binds an instance of `Pet`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/owners/{ownerId}/pets/{petId}/edit") - public String processSubmit(**@ModelAttribute Pet pet**) { } + public String processSubmit(@ModelAttribute Pet pet) { } <1> ---- +<1> Bind an instance of `Pet`. +==== -The `Pet` instance above is resolved as follows: +The `Pet` instance in the preceding example is resolved as follows: -* From the model if already added via <>. -* From the HTTP session via <>. +* From the model if already added through <>. +* From the HTTP session through <>. * From the invocation of a default constructor. -* From the invocation of a "primary constructor" with arguments matching to query -parameters or form fields; argument names are determined via JavaBeans -`@ConstructorProperties` or via runtime-retained parameter names in the bytecode. +* From the invocation of a "`primary constructor`" with arguments that match query +parameters or form fields. Argument names are determined through JavaBeans +`@ConstructorProperties` or through runtime-retained parameter names in the bytecode. After the model attribute instance is obtained, data binding is applied. The `WebExchangeDataBinder` class matches names of 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 -<>. For more on customizing data binding see +names on the target `Object`. Matching fields are populated after type conversion is applied +where necessary. For more on data binding (and validation), see +<>. For more on customizing data binding, see <>. -Data binding may result in errors. By default a `WebExchangeBindException` is raised but -to check for such errors in the controller method, add a `BindingResult` argument -immediately next to the `@ModelAttribute` as shown below: +Data binding can result in errors. By default, a `WebExchangeBindException` is raised, but, +to check for such errors in the controller method, you can add a `BindingResult` argument +immediately next to the `@ModelAttribute`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/owners/{ownerId}/pets/{petId}/edit") - public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { + public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { <1> if (result.hasErrors()) { return "petForm"; } // ... } ---- +<1> Adding a `BindingResult`. +==== -Validation can be applied automatically after data binding by adding the -`javax.validation.Valid` annotation or Spring's `@Validated` annotation (also see +You can automatically apply validation after data binding by adding the +`javax.validation.Valid` annotation or Spring's `@Validated` annotation (see also <> and -<>). For example: +<>). The following example uses the `@Valid` annotation: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/owners/{ownerId}/pets/{petId}/edit") - public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) { + public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) { <1> if (result.hasErrors()) { return "petForm"; } // ... } ---- +<1> +==== -Spring WebFlux, unlike Spring MVC, supports reactive types in the model, e.g. -`Mono` or `io.reactivex.Single`. An `@ModelAttribute` argument can be -declared with or without a reactive type wrapper, and it will be resolved accordingly, -to the actual value if necessary. Note however that in order to use a `BindingResult` +Spring WebFlux, unlike Spring MVC, supports reactive types in the model -- for example, +`Mono` or `io.reactivex.Single`. You can declare a `@ModelAttribute` argument +with or without a reactive type wrapper, and it will be resolved accordingly, +to the actual value if necessary. However, note that, to use a `BindingResult` argument, you must declare the `@ModelAttribute` argument before it without a reactive type wrapper, as shown earlier. Alternatively, you can handle any errors through the -reactive type: +reactive type, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1941,51 +1999,58 @@ reactive type: }); } ---- +==== -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 +Note that use of `@ModelAttribute` is optional -- for example, to set its attributes. +By default, any argument that is not a simple value type( as determined by +{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]) +and is not resolved by any other argument resolver is treated as if it were annotated with `@ModelAttribute`. + [[webflux-ann-sessionattributes]] -==== @SessionAttributes -[.small]#<># +==== Using `@SessionAttributes` +[.small]#<># `@SessionAttributes` is used to store model attributes in the `WebSession` 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 +specific controller. This typically lists the names of model attributes or types of +model attributes that should be transparently stored in the session for subsequent requests to access. -For example: +Consider the following example: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller - **@SessionAttributes("pet")** + @SessionAttributes("pet") <1> public class EditPetForm { // ... } ---- +<1> Using the `@SessionAttributes` annotation. +==== -On the first request when a model attribute with the name "pet" is added to the model, +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 `WebSession`. It remains there until -another controller method uses a `SessionStatus` method argument to clear the storage: +another controller method uses a `SessionStatus` method argument to clear the storage, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller - **@SessionAttributes("pet")** + @SessionAttributes("pet") <1> public class EditPetForm { // ... @PostMapping("/pets/{id}") - public String handle(Pet pet, BindingResult errors, SessionStatus status) { + public String handle(Pet pet, BindingResult errors, SessionStatus status) { <2> if (errors.hasErrors) { // ... } @@ -1995,58 +2060,72 @@ another controller method uses a `SessionStatus` method argument to clear the st } } ---- +<1> Using the `@SessionAttributes` annotation. +<2> Using a `SessionStatus` variable. +==== + [[webflux-ann-sessionattribute]] -==== @SessionAttribute -[.small]#<># +==== Using `@SessionAttribute` +[.small]#<># -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: +If you need access to pre-existing session attributes that are managed globally +(that is, outside the controller -- for example, by a filter) and may or may not be present, +you can use the `@SessionAttribute` annotation on a method parameter, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @GetMapping("/") - public String handle(**@SessionAttribute** User user) { + public String handle(@SessionAttribute User user) { <1> // ... } ---- +<1> Using `@SessionAttribute`. +==== -For use cases that require adding or removing session attributes consider injecting +For use cases that require adding or removing session attributes, consider injecting `WebSession` 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 +workflow, consider using `SessionAttributes`, as described in <>. [[webflux-ann-requestattrib]] -==== @RequestAttribute -[.small]#<># +==== Using `@RequestAttribute` +[.small]#<># -Similar to `@SessionAttribute` the `@RequestAttribute` annotation can be used to -access pre-existing request attributes created earlier, e.g. by a `WebFilter`: +Similarly to `@SessionAttribute`, you can use the `@RequestAttribute` annotation to +access pre-existing request attributes created earlier (for example, by a `WebFilter`), +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @GetMapping("/") - public String handle(**@RequestAttribute** Client client) { + public String handle(@RequestAttribute Client client) { <1> // ... } ---- +<1> Using `@RequestAttribute`. +==== + [[webflux-multipart-forms]] -==== Multipart -[.small]#<># +==== Multipart Content +[.small]#<># As explained in <>, `ServerWebExchange` provides access to multipart -content. The best way to handle a file upload form (e.g. from a browser) in a controller -is through data binding to a <>: +content. The best way to handle a file upload form (for example, from a browser) in a controller +is through data binding to a <>, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2070,10 +2149,12 @@ public class FileUploadController { } ---- +==== -Multipart requests can also be submitted from non-browser clients in a RESTful service -scenario. For example a file along with JSON: +You can also submit multipart requests from non-browser clients in a RESTful service +scenario. The following example uses a file along with JSON: +==== [literal] [subs="verbatim,quotes"] ---- @@ -2094,79 +2175,100 @@ Content-Type: text/xml Content-Transfer-Encoding: 8bit ... File Data ... ---- +==== -You can access individual parts with `@RequestPart`: +You can access individual parts with `@RequestPart`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/") - public String handle(**@RequestPart("meta-data") Part metadata, - @RequestPart("file-data") FilePart file**) { + public String handle(@RequestPart("meta-data") Part metadata, <1> + @RequestPart("file-data") FilePart file) { <2> // ... } ---- +<1> Using `@RequestPart` to get the metadata. +<2> Using `@RequestPart` to get the file. +==== -To deserialize the raw part content, for example to JSON (similar to `@RequestBody`), -simply declare a concrete target Object, instead of `Part`: +To deserialize the raw part content (for example, to JSON -- similar to `@RequestBody`), +you can declare a concrete target `Object`, instead of `Part`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/") - public String handle(**@RequestPart("meta-data") MetaData metadata**) { + public String handle(@RequestPart("meta-data") MetaData metadata) { <1> // ... } ---- +<1> Using `@RequestPart` to get the metadata. +==== -`@RequestPart` can be used in combination with `javax.validation.Valid`, or Spring's +You can use `@RequestPart` combination with `javax.validation.Valid` or Spring's `@Validated` annotation, which causes Standard Bean Validation to be applied. -By default validation errors cause a `WebExchangeBindException` 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: +By default, validation errors cause a `WebExchangeBindException`, which is turned +into a 400 (`BAD_REQUEST`) response. Alternatively, you can handle validation errors locally +within the controller through an `Errors` or `BindingResult` argument, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/") -public String handle(**@Valid** @RequestPart("meta-data") MetaData metadata, - **BindingResult result**) { +public String handle(@Valid @RequestPart("meta-data") MetaData metadata, <1> + BindingResult result) { <2> // ... } ---- +<1> Using a `@Valid` annotation. +<2> Using a `BindingResult` argument. +==== -To access all multipart data in as a `MultiValueMap` use `@RequestBody`: +To access all multipart data as a `MultiValueMap`, you can use `@RequestBody`, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/") - public String handle(**@RequestBody Mono> parts**) { + public String handle(@RequestBody Mono> parts) { <1> // ... } ---- +<1> Using `@RequestBody`. +==== -To access multipart data sequentially, in streaming fashion, use `@RequestBody` with -`Flux` instead. For example: +To access multipart data sequentially, in streaming fashion, you can use `@RequestBody` with +`Flux` instead, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/") - public String handle(**@RequestBody Flux parts**) { + public String handle(@RequestBody Flux parts) { <1> // ... } ---- +<1> Using `@RequestBody`. +==== + [[webflux-ann-requestbody]] -==== @RequestBody -[.small]#<># +==== Using `@RequestBody` +[.small]#<># -Use the `@RequestBody` annotation to have the request body read and deserialized into an -Object through an <>. -Below is an example with an `@RequestBody` argument: +You can use the `@RequestBody` annotation to have the request body read and deserialized into an +`Object` through an <>. +The following example uses a `@RequestBody` argument: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2175,10 +2277,13 @@ Below is an example with an `@RequestBody` argument: // ... } ---- +==== -Unlike Spring MVC, in WebFlux the `@RequestBody` method argument supports reactive types -and fully non-blocking reading and (client-to-server) streaming: +Unlike Spring MVC, in WebFlux, the `@RequestBody` method argument supports reactive types +and fully non-blocking reading and (client-to-server) streaming. The following example +uses a `Mono`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2187,16 +2292,19 @@ and fully non-blocking reading and (client-to-server) streaming: // ... } ---- +==== You can use the <> option of the <> to configure or customize message readers. -`@RequestBody` can be used in combination with `javax.validation.Valid`, or Spring's +You can use `@RequestBody` 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 `WebExchangeBindException` 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: +By default, validation errors cause a `WebExchangeBindException`, which is turned +into a 400 (`BAD_REQUEST`) response. Alternatively, you can handle validation errors locally +within the controller through an `Errors` or a `BindingResult` argument. The following +example uses a `BindingResult` argument`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2205,15 +2313,19 @@ within the controller through an `Errors` or `BindingResult` argument: // ... } ---- +==== + [[webflux-ann-httpentity]] -==== HttpEntity -[.small]#<># +==== Using `HttpEntity` +[.small]#<># -`HttpEntity` is more or less identical to using <> but based on a -container object that exposes request headers and body. Below is an example: +`HttpEntity` is more or less identical to using <> but is based on a +container object that exposes request headers and the body. The following example uses an +`HttpEntity`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2222,15 +2334,18 @@ container object that exposes request headers and body. Below is an example: // ... } ---- +==== + [[webflux-ann-responsebody]] -==== @ResponseBody -[.small]#<># +==== Using `@ResponseBody` +[.small]#<># -Use the `@ResponseBody` annotation on a method to have the return serialized to the -response body through an <>. For example: +You can use the `@ResponseBody` annotation on a method to have the return serialized to the +response body through an <>. The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2240,30 +2355,33 @@ response body through an <>. For example: // ... } ---- +==== -`@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 +`@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` supports reactive types which means you can return Reactor or RxJava +`@ResponseBody` supports reactive types, which means you can return Reactor or RxJava types and have the asynchronous values they produce rendered to the response. For additional details, see <> and <>. -`@ResponseBody` methods can be combined with JSON serialization views. +You can combine `@ResponseBody` methods with JSON serialization views. See <> for details. You can use the <> option of the <> to configure or customize message writing. + [[webflux-ann-responseentity]] -==== ResponseEntity -[.small]#<># +==== Using `ResponseEntity` +[.small]#<># -`ResponseEntity` is more or less identical to using <> but based -on a container object that specifies request headers and body. Below is an example: +Using `ResponseEntity` is more or less identical to using <> but is based +on a container object that specifies request headers and body. The following example uses `ResponseEntity`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2274,21 +2392,26 @@ on a container object that specifies request headers and body. Below is an examp return new ResponseEntity.created(location).build(); } ---- +==== + [[webflux-ann-jackson]] ==== Jackson JSON +Spring offers support for the Jackson JSON library. + [[webflux-ann-jsonview]] -===== Jackson serialization views -[.small]#<># +===== Jackson Serialization Views +[.small]#<># Spring WebFlux 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: +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, you can use Jackson's +`@JsonView` annotation to activate a serialization view class, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2329,40 +2452,39 @@ which allows rendering only a subset of all fields in an Object. To use it with } } ---- - -[NOTE] ==== -`@JsonView` allows an array of view classes but you can only specify only one per + +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. -==== [[webflux-ann-modelattrib-methods]] -=== Model -[.small]#<># +=== Using a Model +[.small]#<># -The `@ModelAttribute` annotation can be used: +You can use the `@ModelAttribute` annotation: * On a <> in `@RequestMapping` methods -to create or access an Object from the model, and to bind it to the request through a +to create or access an Object from the model and to bind it to the request through a `WebDataBinder`. -* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes helping +* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes, helping to initialize the model prior to any `@RequestMapping` method invocation. -* On a `@RequestMapping` method to mark its return value is a model attribute. +* On a `@RequestMapping` method to mark its return value as a model attribute. -This section discusses `@ModelAttribute` methods, or the 2nd from the list above. +This section discusses `@ModelAttribute` methods, or the second item from the preceding list. A controller can have any number of `@ModelAttribute` methods. All such methods are invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute` -method can also be shared across controllers via `@ControllerAdvice`. See the section on +method can also be shared across controllers through `@ControllerAdvice`. See the section on <> 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. +arguments as `@RequestMapping` methods (except for `@ModelAttribute` itself and anything +related to the request body). -An example `@ModelAttribute` method: +The following example uses a `@ModelAttribute` method: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2372,9 +2494,11 @@ An example `@ModelAttribute` method: // add more ... } ---- +==== -To add one attribute only: +The following example adds one attribute only: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2383,22 +2507,21 @@ To add one attribute only: 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]. + +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). -==== -Spring WebFlux, unlike Spring MVC, explicitly supports reactive types in the model, -e.g. `Mono` or `io.reactivex.Single`. Such asynchronous model -attributes may be transparently resolved (and the model updated) to their actual values -at the time of `@RequestMapping` invocation, providing a `@ModelAttribute` argument is -declared without a wrapper, for example: +Spring WebFlux, unlike Spring MVC, explicitly supports reactive types in the model +(for example, `Mono` or `io.reactivex.Single`). Such asynchronous model +attributes can be transparently resolved (and the model updated) to their actual values +at the time of `@RequestMapping` invocation, provided a `@ModelAttribute` argument is +declared without a wrapper, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2413,16 +2536,19 @@ declared without a wrapper, for example: // ... } ---- +==== -In addition any model attributes that have a reactive type wrapper are resolved to their +In addition, any model attributes that have a reactive type wrapper are resolved to their actual values (and the model updated) just prior to view rendering. -`@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 +You can also use `@ModelAttribute` as a method-level annotation on `@RequestMapping` +methods, in which case the return value of the `@RequestMapping` method is interpreted as a model attribute. This is typically not required, as it is the default behavior in HTML -controllers, unless the return value is a `String` which would otherwise be interpreted -as a view name. `@ModelAttribute` can also help to customize the model attribute name: +controllers, unless the return value is a `String` that would otherwise be interpreted +as a view name. `@ModelAttribute` can also help to customize the model attribute name, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2433,38 +2559,40 @@ as a view name. `@ModelAttribute` can also help to customize the model attribute return account; } ---- +==== [[webflux-ann-initbinder]] -=== DataBinder -[.small]#<># +=== Using `DataBinder` +[.small]#<># -`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods in order to -initialize instances of `WebDataBinder`, and those in turn are used to: +`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods, to +initialize instances of `WebDataBinder`. Those, in turn, are used to: -* Bind request parameters (i.e. form data or query) to a model object. -* Convert String-based request values such as request parameters, path variables, -headers, cookies, and others, to the target type of controller method arguments. -* Format model object values as String values when rendering HTML forms. +* Bind request parameters (that is, form data or query) to a model object. +* Convert `String`-based request values (such as request parameters, path variables, +headers, cookies, and others) to the target type of controller method arguments. +* Format model object values as `String` values when rendering HTML forms. -`@InitBinder` methods can register controller-specific `java.bean.PropertyEditor`, or -Spring `Converter` and `Formatter` components. In addition, the -<> can be used to register `Converter` and +`@InitBinder` methods can register controller-specific `java.bean.PropertyEditor` or +Spring `Converter` and `Formatter` components. In addition, you can use the +<> 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 +`@InitBinder` methods support many of the same arguments that `@RequestMapping` methods +do, except for `@ModelAttribute` (command object) arguments. Typically, they are declared with a `WebDataBinder` argument, for registrations, and a `void` return value. -Below is an example: +The following example uses the `@InitBinder` annotation: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller public class FormController { - **@InitBinder** + @InitBinder <1> public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); @@ -2474,35 +2602,42 @@ Below is an example: // ... } ---- +<1> Using the `@InitBinder` annotation. +==== -Alternatively when using a `Formatter`-based setup through a shared +Alternatively, when using a `Formatter`-based setup through a shared `FormattingConversionService`, you could re-use the same approach and register -controller-specific ``Formatter``'s: +controller-specific `Formatter` instances, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller public class FormController { - **@InitBinder** + @InitBinder protected void initBinder(WebDataBinder binder) { - binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); + binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); <1> } // ... } ---- +<1> Adding a custom formatter (a `DateFormatter`, in this case). +==== [[webflux-ann-controller-exceptions]] -=== Exceptions -[.small]#<># +=== Managing Exceptions +[.small]#<># `@Controller` and <> classes can have -`@ExceptionHandler` methods to handle exceptions from controller methods. For example: +`@ExceptionHandler` methods to handle exceptions from controller methods. The following +example includes such a handler method: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2511,80 +2646,79 @@ controller-specific ``Formatter``'s: // ... - @ExceptionHandler + @ExceptionHandler <1> public ResponseEntity handle(IOException ex) { // ... } } ---- +<1> Declaring an `@ExceptionHandler`: +==== -The exception may match against a top-level exception being propagated (i.e. a direct -`IOException` thrown), or against the immediate cause within a top-level wrapper exception -(e.g. an `IOException` wrapped inside an `IllegalStateException`). +The exception can match against a top-level exception being propagated (that is, a direct +`IOException` being thrown) or against the immediate cause within a top-level wrapper exception +(for example, an `IOException` wrapped inside an `IllegalStateException`). -For matching exception types, preferably declare the target exception as a method argument -as shown above. Alternatively, the annotation declaration may narrow the exception types to -match. We generally recommend to be as specific as possible in the argument signature and to +For matching exception types, preferably declare the target exception as a method argument, +as shown in the preceding example. Alternatively, the annotation declaration can narrow the exception types to +match. We generally recommend being as specific as possible in the argument signature and to declare your primary root exception mappings on a `@ControllerAdvice` prioritized with a corresponding order. See <> for details. -[NOTE] -==== -An `@ExceptionHandler` method in WebFlux supports the same method arguments and -return values as an `@RequestMapping` method, with the exception of request body -and `@ModelAttribute` related method arguments. -==== +NOTE: An `@ExceptionHandler` method in WebFlux supports the same method arguments and +return values as a `@RequestMapping` method, with the exception of request body- +and `@ModelAttribute`-related method arguments. Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the -`HandlerAdapter` for `@RequestMapping` methods. See <> -under the `DispatcherHandler` section for more details. +`HandlerAdapter` for `@RequestMapping` methods. See <> +for more detail. + [[webflux-ann-rest-exceptions]] ==== REST API exceptions -[.small]#<># +[.small]#<># 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 +response. The Spring Framework does not automatically do so, because the representation +of error details in the response body is application-specific. However, a +`@RestController` can use `@ExceptionHandler` methods with a `ResponseEntity` return +value to set the status and the body of the response. Such methods can also be declared in `@ControllerAdvice` classes to apply them globally. -[NOTE] -==== -Note that Spring WebFlux does not have an equivalent for the Spring MVC -`ResponseEntityExceptionHandler` because WebFlux only raises `ResponseStatusException` -(or subclasses thereof), which and those do not need to be translated translation to +NOTE: Note that Spring WebFlux does not have an equivalent for the Spring MVC +`ResponseEntityExceptionHandler`, because WebFlux raises only `ResponseStatusException` +(or subclasses thereof), and those do not need to be translated to an HTTP status code. -==== [[webflux-ann-controller-advice]] === Controller Advice -[.small]#<># +[.small]#<># -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 +Typically, the `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply within +the `@Controller` class (or class hierarchy) in which they are declared. 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 <>. +`@ControllerAdvice` is marked with `@Component`, which means that such classes can be registered +as Spring beans through <>. `@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). +`@ResponseBody`, which essentially means `@ExceptionHandler` methods are rendered to the +response body through message conversion (versus view resolution or 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` +detect Spring beans of type `@ControllerAdvice` and apply their methods at runtime. +Global `@ExceptionHandler` methods (from a `@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: +By default `@ControllerAdvice` methods apply to every request (that is, all controllers), but +you can narrow that down to a subset of controllers through attributes on the annotation, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2600,10 +2734,11 @@ you can narrow that down to a subset of controllers via attributes on the annota @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] +The preceding selectors are evaluated at runtime and may negatively impact +performance if you use them extensively. See the +{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[`@ControllerAdvice`] Javadoc for more details. @@ -2612,16 +2747,15 @@ include::webflux-functional.adoc[leveloffset=+1] - [[webflux-uri-building]] == URI Links -[.small]#<># +[.small]#<># This section describes various options available in the Spring Framework to prepare URIs. -include::web-uris.adoc[leveloffset=+2] +include::web-uris.adoc[leveloffset=+2] @@ -2629,59 +2763,57 @@ include::webflux-cors.adoc[leveloffset=+1] - [[webflux-web-security]] == Web Security -[.small]#<># +[.small]#<># 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: +for protecting web applications from malicious exploits. See the Spring Security +reference documentation, including: * {doc-spring-security}/html5/#jc-webflux[WebFlux Security] -* {doc-spring-security}/html5/#test-webflux["WebFlux Testing Support"] +* {doc-spring-security}/html5/#test-webflux[WebFlux Testing Support] * {doc-spring-security}/html5/#csrf[CSRF Protection] * {doc-spring-security}/html5/#headers[Security Response Headers] - include::webflux-view.adoc[leveloffset=+1] - [[webflux-caching]] == HTTP Caching -[.small]#<># +[.small]#<># HTTP caching can significantly improve the performance of a web application. HTTP caching -revolves around the "Cache-Control" response header and subsequently conditional request -headers such as "Last-Modified" and "ETag". "Cache-Control" advises private (e.g. browser) -and public (e.g. proxy) caches how to cache and re-use responses. An "ETag" header is used +revolves around the `Cache-Control` response header and subsequent conditional request +headers, such as `Last-Modified` and `ETag`. `Cache-Control` advises private (for example, browser) +and public (for example, proxy) caches how to cache and re-use responses. An `ETag` header is used to make a conditional request that may result in a 304 (NOT_MODIFIED) without a body, -if the content has not changed. "ETag" can be seen as a more sophisticated successor to +if the content has not changed. `ETag` can be seen as a more sophisticated successor to the `Last-Modified` header. -This section describes HTTP caching related options available in Spring Web MVC. +This section describes the HTTP caching related options available in Spring WebFlux. [[webflux-caching-cachecontrol]] === `CacheControl` -[.small]#<># +[.small]#<># {api-spring-framework}/http/CacheControl.html[`CacheControl`] provides support for -configuring settings related to the "Cache-Control" header and is accepted as an argument +configuring settings related to the `Cache-Control` header and is accepted as an argument in a number of places: * <> * <> While https://tools.ietf.org/html/rfc7234#section-5.2.2[RFC 7234] describes all possible -directives for the "Cache-Control" response header, the `CacheControl` type takes a -use case oriented approach focusing on the common scenarios: +directives for the `Cache-Control` response header, the `CacheControl` type takes a +use case-oriented approach that focuses on the common scenarios, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2696,18 +2828,20 @@ use case oriented approach focusing on the common scenarios: // "Cache-Control: max-age=864000, public, no-transform" CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic(); ---- +==== [[webflux-caching-etag-lastmodified]] === Controllers -[.small]#<># +[.small]#<># -Controllers can add explicit support for HTTP caching. This is recommended since the -lastModified or ETag value for a resource needs to be calculated before it can be compared -against conditional request headers. A controller can add an ETag and "Cache-Control" -settings to a `ResponseEntity`: +Controllers can add explicit support for HTTP caching. We recommend doing so, since the +`lastModified` or `ETag` value for a resource needs to be calculated before it can be compared +against conditional request headers. A controller can add an `ETag` and `Cache-Control` +settings to a `ResponseEntity`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2724,13 +2858,16 @@ settings to a `ResponseEntity`: .body(book); } ---- +==== -This will send an 304 (NOT_MODIFIED) response with an empty body, if the comparison -to the conditional request headers indicates the content has not changed. Otherwise the -"ETag" and "Cache-Control" headers will be added to the response. +The preceding example sends a 304 (NOT_MODIFIED) response with an empty body if the comparison +to the conditional request headers indicates the content has not changed. Otherwise, the +`ETag` and `Cache-Control` headers are added to the response. -The check against conditional request headers can also be made in the controller: +You can also make the check against conditional request headers in the controller, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2749,49 +2886,49 @@ The check against conditional request headers can also be made in the controller ---- <1> Application-specific calculation. -<2> Response has been set to 304 (NOT_MODIFIED), no further processing. +<2> Response has been set to 304 (NOT_MODIFIED). No further processing. <3> Continue with request processing. +==== -There are 3 variants for checking conditional requests against eTag values, lastModified -values, or both. For conditional "GET" and "HEAD" requests, the response may be set to -304 (NOT_MODIFIED). For conditional "POST", "PUT", and "DELETE", the response would be set -to 409 (PRECONDITION_FAILED) instead to prevent concurrent modification. +There are three variants for checking conditional requests against `eTag` values, `lastModified` +values, or both. For conditional `GET` and `HEAD` requests, you can set the response to +304 (NOT_MODIFIED). For conditional `POST`, `PUT`, and `DELETE`, you can instead set the response +to 409 (PRECONDITION_FAILED) to prevent concurrent modification. [[webflux-caching-static-resources]] -=== Static resources -[.small]#<># - -Static resources should be served with a "Cache-Control" and conditional response headers -for optimal performance. See section on configuring <>. +=== Static Resources +[.small]#<># +You should serve static resources with a `Cache-Control` and conditional response headers +for optimal performance. See the section on configuring <>. [[webflux-config]] == WebFlux Config -[.small]#<># +[.small]#<># -The WebFlux Java config declares components required to process requests with annotated +The WebFlux Java configuration declares the components that are required to process requests with annotated controllers or functional endpoints, and it offers an API to customize the configuration. -That means you do not need to understand the underlying beans created by the Java config -but, if you want to, it's very easy to see them in `WebFluxConfigurationSupport` or read more -what they are in <>. +That means you do not need to understand the underlying beans created by the Java configuration. +However, if you want to understand them, you can see them in `WebFluxConfigurationSupport` or read more +about what they are in <>. -For more advanced customizations, not available in the configuration API, it is also -possible to gain full control over the configuration through the +For more advanced customizations, not available in the configuration API, you can +gain full control over the configuration through the <>. - [[webflux-config-enable]] -=== Enable WebFlux config -[.small]#<># +=== Enabling WebFlux Configuration +[.small]#<># -Use the `@EnableWebFlux` annotation in your Java config: +You can use the `@EnableWebFlux` annotation in your Java config, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2800,19 +2937,22 @@ Use the `@EnableWebFlux` annotation in your Java config: public class WebConfig { } ---- +==== -The above registers a number of Spring WebFlux -<> also adapting to dependencies -available on the classpath -- for JSON, XML, etc. +The preceding example registers a number of Spring WebFlux +<> and adapts to dependencies +available on the classpath -- for JSON, XML, and others. [[webflux-config-customize]] === WebFlux config API -[.small]#<># +[.small]#<># -In your Java config implement the `WebFluxConfigurer` interface: +In your Java configuration, you can implement the `WebFluxConfigurer` interface, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2824,19 +2964,21 @@ In your Java config implement the `WebFluxConfigurer` interface: } ---- +==== [[webflux-config-conversion]] === Conversion, formatting -[.small]#<># +[.small]#<># -By default formatters for `Number` and `Date` types are installed, including support for +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. -To register custom formatters and converters: +The following example shows how to register custom formatters and converters: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2851,26 +2993,26 @@ To register custom formatters and converters: } ---- - -[NOTE] -==== -See <> -and the `FormattingConversionServiceFactoryBean` for more information on when to use FormatterRegistrars. ==== +NOTE: See <> +and the `FormattingConversionServiceFactoryBean` for more information on when to use `FormatterRegistrar` implementations. + [[webflux-config-validation]] === Validation -[.small]#<># +[.small]#<># -By default if <> is present -on the classpath -- e.g. Hibernate Validator, the `LocalValidatorFactoryBean` is registered -as a global <> for use with `@Valid` and `Validated` on +By default, if <> is present +on the classpath (for example, the Hibernate Validator), the `LocalValidatorFactoryBean` is registered +as a global <> for use with `@Valid` and `Validated` on `@Controller` method arguments. -In your Java config, you can customize the global `Validator` instance: +In your Java configuration, you can customize the global `Validator` instance, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2885,9 +3027,12 @@ In your Java config, you can customize the global `Validator` instance: } ---- +==== -Note that you can also register ``Validator``'s locally: +Note that you can also register `Validator` implementations locally, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2901,25 +3046,24 @@ Note that you can also register ``Validator``'s locally: } ---- - -[TIP] ==== -If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and + +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. -==== [[webflux-config-content-negotiation]] -=== Content type resolvers -[.small]#<># +=== Content Type Resolvers +[.small]#<># You can configure how Spring WebFlux determines the requested media types for -``@Controller``'s from the request. By default only the "Accept" header is checked but you -can also enable a query parameter based strategy. +`@Controller` instances from the request. By default, only the `Accept` header is checked, but you +can also enable a query parameter-based strategy. -To customize the requested content type resolution: +The following example shows how to customize the requested content type resolution: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2933,15 +3077,17 @@ To customize the requested content type resolution: } } ---- +==== [[webflux-config-message-codecs]] === HTTP message codecs -[.small]#<># +[.small]#<># -To customize how the request and response body are read and written: +The following example shows how to customize how the request and response body are read and written: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2955,32 +3101,34 @@ To customize how the request and response body are read and written: } } ---- +==== `ServerCodecConfigurer` provides a set of default readers and writers. You can use it to add more readers and writers, customize the default ones, or replace the default ones completely. -For Jackson JSON and XML, consider using the -{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[Jackson2ObjectMapperBuilder] +For Jackson JSON and XML, consider using +{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`], which 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. +* 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`. +* 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 and Time API types. +* https://github.com/FasterXML/jackson-datatype-jdk8[`jackson-datatype-jdk8`]: Support for other Java 8 types, such as `Optional`. [[webflux-config-view-resolvers]] -=== View resolvers -[.small]#<># +=== View Resolvers +[.small]#<># -To configure view resolution: +The following example shows how to configure view resolution: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2994,11 +3142,13 @@ To configure view resolution: } } ---- +==== -The `ViewResolverRegistry` has shortcuts for view technologies that the Spring Framework -integrates with. Here is an example with FreeMarker which also requires configuring the -underlying FreeMarker view technology: +The `ViewResolverRegistry` has shortcuts for view technologies with which the Spring Framework +integrates. The following example uses FreeMarker (which also requires configuring the +underlying FreeMarker view technology): +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3022,9 +3172,11 @@ underlying FreeMarker view technology: } } ---- +==== -You can also plug in any `ViewResolver` implementation: +You can also plug in any `ViewResolver` implementation, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3040,12 +3192,14 @@ You can also plug in any `ViewResolver` implementation: } } ---- +==== To support <> and rendering other formats -through view resolution, besides HTML, you can configure one or more default views based -on the `HttpMessageWriterView` implementation which accepts any of the available -<> from `spring-web`: +through view resolution (besides HTML), you can configure one or more default views based +on the `HttpMessageWriterView` implementation, which accepts any of the available +<> from `spring-web`. The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3065,25 +3219,27 @@ on the `HttpMessageWriterView` implementation which accepts any of the available // ... } ---- +==== -See <> for more on the view technologies integrated with Spring WebFlux. - +See <> for more on the view technologies that are integrated with Spring WebFlux. [[webflux-config-static-resources]] -=== Static resources -[.small]#<># +=== Static Resources +[.small]#<># This option provides a convenient way to serve static resources from a list of -{api-spring-framework}/core/io/Resource.html[Resource]-based locations. +{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 `"/static"` on the classpath. Resources -will be served with a 1-year future expiration to ensure maximum use of the browser cache +In the next example, given a request that starts with `/resources`, the relative path is +used to find and serve static resources relative to `/static` on the classpath. Resources +are served with a one-year future expiration to ensure maximum use of the browser cache and a reduction in HTTP requests made by the browser. The `Last-Modified` header is also -evaluated and if present a `304` status code is returned. +evaluated and, if present, a `304` status code is returned. The following list shows +the example: +==== [source,java,indent=0] [subs="verbatim"] ---- @@ -3100,21 +3256,23 @@ evaluated and if present a `304` status code is returned. } ---- +==== // TODO: See also <>. The resource handler also supports a chain of -{api-spring-framework}/web/reactive/resource/ResourceResolver.html[ResourceResolver]'s and -{api-spring-framework}/web/reactive/resource/ResourceTransformer.html[ResourceTransformer]'s. +{api-spring-framework}/web/reactive/resource/ResourceResolver.html[`ResourceResolver`] implementations and +{api-spring-framework}/web/reactive/resource/ResourceTransformer.html[`ResourceTransformer`] implementations, 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. +You can use the `VersionResourceResolver` for versioned resource URLs based on an MD5 hash +computed from the content, a fixed application version, or other information. A +`ContentVersionStrategy` (MD5 hash) is a good choice with some notable exceptions (such as +JavaScript resources used with a module loader). -For example in your Java config; +The following example shows how to use `VersionResourceResolver` in your Java configuration: +==== [source,java,indent=0] [subs="verbatim"] ---- @@ -3132,35 +3290,38 @@ For example in your Java config; } ---- +==== You can use `ResourceUrlProvider` to rewrite URLs and apply the full chain of resolvers and -transformers -- e.g. to insert versions. The WebFlux config provides a `ResourceUrlProvider` -so it can be injected into others. +transformers (for example, to insert versions). The WebFlux configuration provides a `ResourceUrlProvider` +so that it can be injected into others. -Unlike Spring MVC at present in WebFlux there is no way to transparently rewrite static -resource URLs since there are no view technologies that can make use of a non-blocking chain -of resolvers and transformers. When serving only local resources the workaround is to use -`ResourceUrlProvider` directly (e.g. through a custom tag) and block. +Unlike Spring MVC, at present, in WebFlux, there is no way to transparently rewrite static +resource URLs, since there are no view technologies that can make use of a non-blocking chain +of resolvers and transformers. When serving only local resources, the workaround is to use +`ResourceUrlProvider` directly (for example, through a custom element) and block. -Note that when using both `EncodedResourceResolver` (e.g. Gzip, Brotli encoded) and -`VersionedResourceResolver`, they must be registered in that order to ensure content -based versions are always computed reliably based on the unencoded file. +Note that, when using both `EncodedResourceResolver` (for example, Gzip, Brotli encoded) and +`VersionedResourceResolver`, they must be registered in that order, to ensure content-based +versions are always computed reliably based on the unencoded file. -http://www.webjars.org/documentation[WebJars] is also supported via `WebJarsResourceResolver` -and automatically registered when `"org.webjars:webjars-locator"` is present on the +http://www.webjars.org/documentation[WebJars] is also supported through `WebJarsResourceResolver` +and is 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"`. +match to incoming URLs without versions (for example, `/jquery/jquery.min.js` to +`/jquery/1.2.0/jquery.min.js`). [[webflux-config-path-matching]] === Path Matching -[.small]#<># +[.small]#<># -Customize options related to path matching. For details on the individual options, see the -{api-spring-framework}/web/reactive/config/PathMatchConfigurer.html[PathMatchConfigurer] Javadoc. +You can customize options related to path matching. For details on the individual options, see the +{api-spring-framework}/web/reactive/config/PathMatchConfigurer.html[`PathMatchConfigurer`] Javadoc. +The following example shows how to use `PathMatchConfigurer`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3179,33 +3340,38 @@ Customize options related to path matching. For details on the individual option } ---- +==== [TIP] ==== Spring WebFlux relies on a parsed representation of the request path called `RequestPath` for access to decoded path segment values, with semicolon content removed -(i.e. path/matrix variables). That means, unlike Spring MVC, there is no need to indicate -neither whether to decode the request path, nor whether to remove semicolon content for +(that is, path or matrix variables). That means, unlike in Spring MVC, you need not indicate +whether to decode the request path nor whether to remove semicolon content for path matching purposes. -Spring WebFlux also does not support suffix pattern matching, unlike Spring MVC, where we -also <> moving away from +Spring WebFlux also does not support suffix pattern matching, unlike in Spring MVC, where we +are also <> moving away from reliance on it. ==== [[webflux-config-advanced-java]] -=== Advanced config mode -[.small]#<># +=== Advanced Configuration Mode +[.small]#<># + +`@EnableWebFlux` imports `DelegatingWebFluxConfiguration` that: + +* Provides default Spring configuration for WebFlux applications -`@EnableWebFlux` imports `DelegatingWebFluxConfiguration` that (1) provides default -Spring configuration for WebFlux applications and (2) detects and delegates to -``WebFluxConfigurer``'s to customize that configuration. +* detects and delegates to `WebFluxConfigurer` implementations to customize that configuration. -For advanced mode, remove `@EnableWebFlux` and extend directly from -`DelegatingWebFluxConfiguration` instead of implementing `WebFluxConfigurer`: +For advanced mode, you can remove `@EnableWebFlux` and extend directly from +`DelegatingWebFluxConfiguration` instead of implementing `WebFluxConfigurer`, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3216,23 +3382,23 @@ For advanced mode, remove `@EnableWebFlux` and extend directly from } ---- +==== -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 +You can keep existing methods in `WebConfig`, but you can now also override bean declarations +from the base class and still have any number of other `WebMvcConfigurer` implementations on the classpath. - [[webflux-http2]] == HTTP/2 -[.small]#<># +[.small]#<># -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 +Servlet 4 containers are required to support HTTP/2, and Spring Framework 5 is compatible +with Servlet API 4. From a programming model perspective, there is nothing specific that +applications need to do. However, there are considerations related to server configuration. +For more details, see the https://github.com/spring-projects/spring-framework/wiki/HTTP-2-support[HTTP/2 wiki page]. -Currently Spring WebFlux does not support HTTP/2 with Netty. There is also no support for +Currently, Spring WebFlux does not support HTTP/2 with Netty. There is also no support for pushing resources programmatically to the client. diff --git a/src/docs/asciidoc/web/webmvc-client.adoc b/src/docs/asciidoc/web/webmvc-client.adoc index 020fa17f4e2..ab96885b31f 100644 --- a/src/docs/asciidoc/web/webmvc-client.adoc +++ b/src/docs/asciidoc/web/webmvc-client.adoc @@ -5,40 +5,35 @@ This section describes options for client-side access to REST endpoints. - [[webmvc-resttemplate]] -== RestTemplate +== Using `RestTemplate` `RestTemplate` is a synchronous client to perform HTTP requests. It is the original -Spring REST client, exposing a simple, template method API over underlying HTTP client +Spring REST client and exposes a simple, template-method API over underlying HTTP client libraries. -[NOTE] -==== -As of 5.0, the non-blocking, reactive `WebClient` offers a modern alternative to the -`RestTemplate` with efficient support for both sync and async, as well as streaming +NOTE: As of 5.0, the non-blocking, reactive `WebClient` offers a modern alternative to the +`RestTemplate`, with efficient support for both synchronous and asynchronous, as well as streaming scenarios. The `RestTemplate` will be deprecated in a future version and will not have major new features added going forward. -==== - -See <> for details. +See <> for details. [[webmvc-webclient]] -== WebClient +== Using `WebClient` `WebClient` is a non-blocking, reactive client to perform HTTP requests. It was -introduced in 5.0 and offers a modern alternative to the `RestTemplate` with efficient +introduced in 5.0 and offers a modern alternative to the `RestTemplate`, with efficient support for both synchronous and asynchronous, as well as streaming scenarios. -In contrast to the `RestTemplate`, the `WebClient` supports the following: +In contrast to `RestTemplate`, `WebClient` supports the following: * Non-blocking I/O. * Reactive Streams back pressure. -* High concurrency with less hardware resources. -* Functional-style, fluent API taking advantage of Java 8 lambdas. +* High concurrency with fewer hardware resources. +* Functional-style, fluent API that takes advantage of Java 8 lambdas. * Synchronous and asynchronous interactions. * Streaming up to or streaming down from a server. diff --git a/src/docs/asciidoc/web/webmvc-cors.adoc b/src/docs/asciidoc/web/webmvc-cors.adoc index 5fa44a03ecf..3757624b8a9 100644 --- a/src/docs/asciidoc/web/webmvc-cors.adoc +++ b/src/docs/asciidoc/web/webmvc-cors.adoc @@ -1,68 +1,69 @@ [[mvc-cors]] = CORS -[.small]#<># +[.small]#<># +Spring MVC lets you handle CORS (Cross-Origin Resource Sharing). This section +describes how to do so. [[mvc-cors-intro]] == Introduction -[.small]#<># +[.small]#<># -For security reasons browsers prohibit AJAX calls to resources outside the current origin. -For example you could have your bank account in one tab and evil.com in another. Scripts +For security reasons, browsers prohibit AJAX calls to resources outside the current origin. +For example, you could have your bank account in one tab and evil.com in another. Scripts from evil.com should not be able to make AJAX requests to your bank API with your -credentials, e.g. withdrawing money from your account! +credentials -- for example withdrawing money from your account! Cross-Origin Resource Sharing (CORS) is a http://www.w3.org/TR/cors/[W3C specification] -implemented by http://caniuse.com/#feat=cors[most browsers] that allows you to specify -what kind of cross domain requests are authorized rather than using less secure and less +implemented by http://caniuse.com/#feat=cors[most browsers] that lets you specify +what kind of cross-domain requests are authorized, rather than using less secure and less powerful workarounds based on IFRAME or JSONP. - [[mvc-cors-processing]] == Processing -[.small]#<># +[.small]#<># The CORS specification distinguishes between preflight, simple, and actual requests. To learn how CORS works, you can read https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[this article], among -many others, or refer to the specification for more details. +many others, or see the specification for more details. -Spring MVC ``HandlerMapping``'s provide built-in support for CORS. After successfully -mapping a request to a handler, ``HandlerMapping``'s check the CORS configuration for the +Spring MVC `HandlerMapping` implementations provide built-in support for CORS. After successfully +mapping a request to a handler, `HandlerMapping` implementations check the CORS configuration for the given request and handler and take further actions. Preflight requests are handled -directly while simple and actual CORS requests are intercepted, validated, and have +directly, while simple and actual CORS requests are intercepted, validated, and have required CORS response headers set. -In order to enable cross-origin requests (i.e. the `Origin` header is present and -differs from the host of the request) you need to have some explicitly declared CORS +In order to enable cross-origin requests (that is, the `Origin` header is present and +differs from the host of the request), you need to have some explicitly declared CORS configuration. If no matching CORS configuration is found, preflight requests are rejected. No CORS headers are added to the responses of simple and actual CORS requests -and consequently browsers reject them. +and, consequently, browsers reject them. Each `HandlerMapping` can be {api-spring-framework}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured] -individually with URL pattern based `CorsConfiguration` mappings. In most cases applications -will use the MVC Java config or the XML namespace to declare such mappings, which results -in a single, global map passed to all ``HadlerMappping``'s. +individually with URL pattern-based `CorsConfiguration` mappings. In most cases, applications +use the MVC Java configuration or the XML namespace to declare such mappings, which results +in a single global map being passed to all `HandlerMappping` instances. -Global CORS configuration at the `HandlerMapping` level can be combined with more -fine-grained, handler-level CORS configuration. For example annotated controllers can use -class or method-level `@CrossOrigin` annotations (other handlers can implement +You can combine global CORS configuration at the `HandlerMapping` level with more +fine-grained, handler-level CORS configuration. For example, annotated controllers can use +class- or method-level `@CrossOrigin` annotations (other handlers can implement `CorsConfigurationSource`). -The rules for combining global and local configuration are generally additive -- e.g. +The rules for combining global and local configuration are generally additive -- for example, all global and all local origins. For those attributes where only a single value can be -accepted such as `allowCredentials` and `maxAge`, the local overrides the global value. See +accepted (such as `allowCredentials` and `maxAge`), the local overrides the global value. See {api-spring-framework}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`] for more details. [TIP] ==== -To learn more from the source or make advanced customizations, check: +To learn more from the source or make advanced customizations, check the code behind: * `CorsConfiguration` * `CorsProcessor`, `DefaultCorsProcessor` @@ -71,14 +72,15 @@ To learn more from the source or make advanced customizations, check: - [[mvc-cors-controller]] -== @CrossOrigin -[.small]#<># +== Using `@CrossOrigin` +[.small]#<># The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`] -annotation enables cross-origin requests on annotated controller methods: +annotation enables cross-origin requests on annotated controller methods, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -98,19 +100,24 @@ public class AccountController { } } ---- +==== -By default `@CrossOrigin` allows: +By default, `@CrossOrigin` allows: * All origins. * All headers. * All HTTP methods to which the controller method is mapped. -* `allowedCredentials` is not enabled by default since that establishes a trust level -that exposes sensitive user-specific information such as cookies and CSRF tokens, and + +`allowedCredentials` is not enabled by default, since that establishes a trust level +that exposes sensitive user-specific information (such as cookies and CSRF tokens) and should only be used where appropriate. -* `maxAge` is set to 30 minutes. -`@CrossOrigin` is supported at the class level too and inherited by all methods: +`maxAge` is set to 30 minutes. + +`@CrossOrigin` is supported at the class level, too, and is inherited by all methods, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -130,9 +137,12 @@ public class AccountController { } } ---- +==== -`CrossOrigin` can be used at both class and method-level: +You can use `@CrossOrigin` at both the class level and the method level, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -153,38 +163,43 @@ public class AccountController { } } ---- - +==== [[mvc-cors-global]] -== Global Config -[.small]#<># +== Global Configuration +[.small]#<># -In addition to fine-grained, controller method level configuration you'll probably want to -define some global CORS configuration too. You can set URL-based `CorsConfiguration` -mappings individually on any `HandlerMapping`. Most applications however will use the -MVC Java config or the MVC XNM namespace to do that. +In addition to fine-grained, controller method level configuration, you probably want to +define some global CORS configuration, too. You can set URL-based `CorsConfiguration` +mappings individually on any `HandlerMapping`. Most applications, however, use the +MVC Java configuration or the MVC XNM namespace to do that. -By default global configuration enables the following: +By default, global configuration enables the following: * All origins. * All headers. * `GET`, `HEAD`, and `POST` methods. -* `allowedCredentials` is not enabled by default since that establishes a trust level -that exposes sensitive user-specific information such as cookies and CSRF tokens, and + + +`allowedCredentials` is not enabled by default, since that establishes a trust level +that exposes sensitive user-specific information (such as cookies and CSRF tokens) and should only be used where appropriate. -* `maxAge` is set to 30 minutes. + +`maxAge` is set to 30 minutes. [[mvc-cors-global-java]] -=== Java Config -[.small]#<># +=== Java Configuration +[.small]#<># -To enable CORS in the MVC Java config, use the `CorsRegistry` callback: +To enable CORS in the MVC Java config, you can use the `CorsRegistry` callback, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -206,14 +221,17 @@ public class WebConfig implements WebMvcConfigurer { } } ---- +==== [[mvc-cors-global-xml]] -=== XML Config +=== XML Configuration -To enable CORS in the XML namespace, use the `` element: +To enable CORS in the XML namespace, you can use the `` element, +as the following example shows: +==== [source,xml,indent=0] [subs="verbatim"] ---- @@ -231,28 +249,26 @@ To enable CORS in the XML namespace, use the `` element: ---- - +==== [[mvc-cors-filter]] == CORS Filter -[.small]#<># +[.small]#<># You can apply CORS support through the built-in {api-spring-framework}/web/filter/CorsFilter.html[`CorsFilter`]. -[NOTE] -==== -If you're trying to use the `CorsFilter` with Spring Security, keep in mind that Spring +NOTE: If you try to use the `CorsFilter` with Spring Security, keep in mind that Spring Security has https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors[built-in support] for CORS. -==== -To configure the filter pass a -`CorsConfigurationSource` to its constructor: +To configure the filter, pass a +`CorsConfigurationSource` to its constructor, as the following example shows: +==== [source,java,indent=0] [subs="verbatim"] ---- @@ -271,4 +287,4 @@ source.registerCorsConfiguration("/**", config); CorsFilter filter = new CorsFilter(source); ---- - +==== diff --git a/src/docs/asciidoc/web/webmvc-test.adoc b/src/docs/asciidoc/web/webmvc-test.adoc index d17f3fdd42e..87a167f2314 100644 --- a/src/docs/asciidoc/web/webmvc-test.adoc +++ b/src/docs/asciidoc/web/webmvc-test.adoc @@ -4,35 +4,25 @@ This section summarizes the options available in `spring-test` for Spring MVC applications. -**Servlet API Mocks** - -Mock implementations of Servlet API contracts for unit testing controllers, filters, and +* Servlet API Mocks: Mock implementations of Servlet API contracts for unit testing controllers, filters, and other web components. See <> mock objects for more details. -**TestContext Framework** - -Support for loading Spring configuration in JUnit and TestNG tests including efficient +* TestContext Framework: Support for loading Spring configuration in JUnit and TestNG tests, including efficient caching of the loaded configuration across test methods and support for loading a `WebApplicationContext` with a `MockServletContext`. See <> for more details. -**Spring MVC Test** - -A framework, also known as `MockMvc`, for testing annotated controllers through the -`DispatcherServlet`, i.e. supporting annotations and complete with Spring MVC -infrastructure, but without an HTTP server. See +* Spring MVC Test: A framework, also known as `MockMvc`, for testing annotated controllers through the +`DispatcherServlet` (that is, supporting annotations), complete with the Spring MVC +infrastructure but without an HTTP server. See <> for more details. -**Client-side REST** - -`spring-test` provides a `MockRestServiceServer` that can be used as a mock server for +* Client-side REST: `spring-test` provides a `MockRestServiceServer` that you can use as a mock server for testing client-side code that internally uses the `RestTemplate`. See <> for more details. -**WebTestClient** - -`WebTestClient` was built for testing WebFlux applications but it can also be used for +* `WebTestClient`: Built for testing WebFlux applications, but it can also be used for end-to-end integration testing, to any server, over an HTTP connection. It is a -non-blocking, reactive client and well suited for testing asynchronous and streaming +non-blocking, reactive client and is well suited for testing asynchronous and streaming scenarios. diff --git a/src/docs/asciidoc/web/webmvc-view.adoc b/src/docs/asciidoc/web/webmvc-view.adoc index fea8bc5d7a3..b06402cc25c 100644 --- a/src/docs/asciidoc/web/webmvc-view.adoc +++ b/src/docs/asciidoc/web/webmvc-view.adoc @@ -1,51 +1,50 @@ [[mvc-view]] = View Technologies -[.small]#<># +[.small]#<># The use of view technologies in Spring MVC is pluggable, whether you decide to -use Thymeleaf, Groovy Markup Templates, JSPs, or other, is primarily a matter of a +use Thymeleaf, Groovy Markup Templates, JSPs, or other technologies, is primarily a matter of a configuration change. This chapter covers view technologies integrated with Spring MVC. We assume you are already familiar with <>. - [[mvc-view-thymeleaf]] == Thymeleaf -[.small]#<># +[.small]#<># -Thymeleaf is modern server-side Java template engine that emphasizes natural HTML +Thymeleaf is a modern server-side Java template engine that emphasizes natural HTML templates that can be previewed in a browser by double-clicking, which is very -helpful for independent work on UI templates, e.g. by designer, without the need for a -running server. If you're looking to replace JSPs, Thymeleaf offers one of the most -extensive set of features that will make such a transition easier. Thymeleaf is actively -developed and maintained. For a more complete introduction see the +helpful for independent work on UI templates (for example, by a designer) without the need for a +running server. If you want to replace JSPs, Thymeleaf offers one of the most +extensive set of features to make such a transition easier. Thymeleaf is actively +developed and maintained. For a more complete introduction, see the http://www.thymeleaf.org/[Thymeleaf] project home page. The Thymeleaf integration with Spring MVC is managed by the Thymeleaf project. -The configuration involves a few bean declarations such as +The configuration involves a few bean declarations, such as `ServletContextTemplateResolver`, `SpringTemplateEngine`, and `ThymeleafViewResolver`. See http://www.thymeleaf.org/documentation.html[Thymeleaf+Spring] for more details. - [[mvc-view-freemarker]] == FreeMarker -[.small]#<># +[.small]#<># http://www.freemarker.org[Apache FreeMarker] is a template engine for generating any -kind of text output from HTML to email, and others. The Spring Framework has a built-in +kind of text output from HTML to email and others. The Spring Framework has a built-in integration for using Spring MVC with FreeMarker templates. [[mvc-view-freemarker-contextconfig]] -=== View config -[.small]#<># +=== View Configuration +[.small]#<># -To configure FreeMarker as a view technology: +The following example shows how to configure FreeMarker as a view technology: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -68,9 +67,11 @@ To configure FreeMarker as a view technology: } } ---- +==== -To configure the same in XML: +The following example shows how to configure the same in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -85,10 +86,12 @@ To configure the same in XML: ---- +==== -Or you can also declare the `FreeMarkerConfigurer` bean for full control over all -properties: +Alternatively, you can also declare the `FreeMarkerConfigurer` bean for full control over all +properties, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -96,22 +99,25 @@ properties: ---- +==== Your templates need to be stored in the directory specified by the `FreeMarkerConfigurer` -shown above. Given the above configuration if your controller returns the view name -"welcome" then the resolver will look for the `/WEB-INF/freemarker/welcome.ftl` template. +shown in the preceding example. Given the preceding configuration, if your controller returns a view name +of `welcome`, the resolver looks for the `/WEB-INF/freemarker/welcome.ftl` template. + [[mvc-views-freemarker]] -=== FreeMarker config -[.small]#<># +=== FreeMarker Configuration +[.small]#<># -FreeMarker 'Settings' and 'SharedVariables' can be passed directly to the FreeMarker -`Configuration` object managed by Spring by setting the appropriate bean properties on +You can pass FreeMarker 'Settings' and 'SharedVariables' directly to the FreeMarker +`Configuration` object (which is managed by Spring) by setting the appropriate bean properties on the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a -`java.util.Properties` object and the `freemarkerVariables` property requires a -`java.util.Map`. +`java.util.Properties` object, and the `freemarkerVariables` property requires a +`java.util.Map`. The following example shows how to do so: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -126,6 +132,7 @@ the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a ---- +==== See the FreeMarker documentation for details of settings and variables as they apply to the `Configuration` object. @@ -133,37 +140,40 @@ the `Configuration` object. [[mvc-view-freemarker-forms]] -=== Form handling +=== Form Handling -Spring provides a tag library for use in JSP's that contains, amongst others, a -`` tag. This tag primarily enables forms to display values from form -backing objects and to show the results of failed validations from a `Validator` in the +Spring provides a tag library for use in JSPs that contains, among others, a +`` element. This element primarily lets forms display values from +form-backing objects and show the results of failed validations from a `Validator` in the web or business tier. Spring also has support for the same functionality in FreeMarker, with additional convenience macros for generating form input elements themselves. + [[mvc-view-bind-macros]] -==== The bind macros +==== The Bind Macros A standard set of macros are maintained within the `spring-webmvc.jar` file for both languages, so they are always available to a suitably configured application. -Some of the macros defined in the Spring libraries are considered internal (private) but -no such scoping exists in the macro definitions making all macros visible to calling +Some of the macros defined in the Spring libraries are considered internal (private), but +no such scoping exists in the macro definitions, making all macros visible to calling code and user templates. The following sections concentrate only on the macros you need -to be directly calling from within your templates. If you wish to view the macro code -directly, the file is called `spring.ftl` in the package -`org.springframework.web.servlet.view.freemarker`. +to directly call from within your templates. If you wish to view the macro code +directly, the file is called `spring.ftl` and is in the +`org.springframework.web.servlet.view.freemarker` package. + [[mvc-view-simple-binding]] -==== Simple binding +==== Simple Binding -In your HTML forms (vm / ftl templates) which act as a form view for a Spring MVC -controller, you can use code similar to the following to bind to field values and +In your HTML forms (vm or ftl templates) that act as a form view for a Spring MVC +controller, you can use code similar to the next example to bind to field values and display error messages for each input field in similar fashion to the JSP equivalent. -Example code is shown below for the `personForm` view configured earlier: +The following example shows the `personForm` view that was configured earlier: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -186,31 +196,33 @@ Example code is shown below for the `personForm` view configured earlier: ... ---- +==== -`<@spring.bind>` requires a 'path' argument which consists of the name of your command -object (it will be 'command' unless you changed it in your FormController properties) -followed by a period and the name of the field on the command object you wish to bind to. -Nested fields can be used too such as "command.address.street". The `bind` macro assumes +`<@spring.bind>` requires a 'path' argument, which consists of the name of your command +object (it is 'command', unless you changed it in your `FormController` properties) +followed by a period and the name of the field on the command object to which you wish to bind. +You can also use nested fields, such as `command.address.street`. The `bind` macro assumes the default HTML escaping behavior specified by the ServletContext parameter `defaultHtmlEscape` in `web.xml`. The optional form of the macro called `<@spring.bindEscaped>` takes a second argument and explicitly specifies whether HTML escaping should be used in the status error -messages or values. Set to true or false as required. Additional form handling macros -simplify the use of HTML escaping and these macros should be used wherever possible. +messages or values. You can set it to `true` or `false` as required. Additional form handling macros +simplify the use of HTML escaping, and you should use these macros wherever possible. They are explained in the next section. + [[mvc-views-form-macros]] ==== Input macros Additional convenience macros for both languages simplify both binding and form generation (including validation error display). It is never necessary to use these -macros to generate form input fields, and they can be mixed and matched with simple HTML -or calls direct to the spring bind macros highlighted previously. +macros to generate form input fields, and you can mix and match them with simple HTML +or direct calls to the spring bind macros that we highlighted previously. -The following table of available macros show the FTL definitions and the -parameter list that each takes. +The following table of available macros shows the FTL definitions and the +parameter list that each takes: [[views-macros-defs-tbl]] .Table of macro definitions @@ -218,107 +230,113 @@ parameter list that each takes. |=== | macro | FTL definition -| **message** (output a string from a resource bundle based on the code parameter) +| `message` (output a string from a resource bundle based on the code parameter) | <@spring.message code/> -| **messageText** (output a string from a resource bundle based on the code parameter, +| `messageText` (output a string from a resource bundle based on the code parameter, falling back to the value of the default parameter) | <@spring.messageText code, text/> -| **url** (prefix a relative URL with the application's context root) +| `url` (prefix a relative URL with the application's context root) | <@spring.url relativeUrl/> -| **formInput** (standard input field for gathering user input) +| `formInput` (standard input field for gathering user input) | <@spring.formInput path, attributes, fieldType/> -| **formHiddenInput *** (hidden input field for submitting non-user input) +| `formHiddenInput` (hidden input field for submitting non-user input) | <@spring.formHiddenInput path, attributes/> -| **formPasswordInput** * (standard input field for gathering passwords. Note that no - value will ever be populated in fields of this type) +| `formPasswordInput` (standard input field for gathering passwords. Note that no + value is ever populated in fields of this type.) | <@spring.formPasswordInput path, attributes/> -| **formTextarea** (large text field for gathering long, freeform text input) +| `formTextarea` (large text field for gathering long, freeform text input) | <@spring.formTextarea path, attributes/> -| **formSingleSelect** (drop down box of options allowing a single required value to be +| `formSingleSelect` (drop down box of options that let a single required value be selected) | <@spring.formSingleSelect path, options, attributes/> -| **formMultiSelect** (a list box of options allowing the user to select 0 or more values) +| `formMultiSelect` (a list box of options that let the user select 0 or more values) | <@spring.formMultiSelect path, options, attributes/> -| **formRadioButtons** (a set of radio buttons allowing a single selection to be made +| `formRadioButtons` (a set of radio buttons that let a single selection be made from the available choices) | <@spring.formRadioButtons path, options separator, attributes/> -| **formCheckboxes** (a set of checkboxes allowing 0 or more values to be selected) +| `formCheckboxes` (a set of checkboxes that let 0 or more values be selected) | <@spring.formCheckboxes path, options, separator, attributes/> -| **formCheckbox** (a single checkbox) +| `formCheckbox` (a single checkbox) | <@spring.formCheckbox path, attributes/> -| **showErrors** (simplify display of validation errors for the bound field) +| `showErrors` (simplify display of validation errors for the bound field) | <@spring.showErrors separator, classOrStyle/> |=== -* In FTL (FreeMarker), `formHiddenInput` and `formPasswordInput` are not actually required +* In FTL (FreeMarker), `formHiddenInput` and `formPasswordInput` are not actually required, as you can use the normal `formInput` macro, specifying `hidden` or `password` as the value for the `fieldType` parameter. The parameters to any of the above macros have consistent meanings: -* path: the name of the field to bind to (ie "command.name") -* options: a Map of all the available values that can be selected from in the input - field. The keys to the map represent the values that will be POSTed back from the form +* `path`: The name of the field to bind to (ie "command.name") +* `options`: A `Map` of all the available values that can be selected from in the input + field. The keys to the map represent the values that are POSTed back from the form and bound to the command object. Map objects stored against the keys are the labels displayed on the form to the user and may be different from the corresponding values - posted back by the form. Usually such a map is supplied as reference data by the - controller. Any Map implementation can be used depending on required behavior. For - strictly sorted maps, a `SortedMap` such as a `TreeMap` with a suitable Comparator may - be used and for arbitrary Maps that should return values in insertion order, use a - `LinkedHashMap` or a `LinkedMap` from commons-collections. -* separator: where multiple options are available as discreet elements (radio buttons or - checkboxes), the sequence of characters used to separate each one in the list (ie - "
"). -* attributes: an additional string of arbitrary tags or text to be included within the + posted back by the form. Usually, such a map is supplied as reference data by the + controller. You can use any `Map` implementation, depending on required behavior. For + strictly sorted maps, you can use a `SortedMap` (such as a `TreeMap`) with a suitable `Comparator` + and, for arbitrary Maps that should return values in insertion order, use a + `LinkedHashMap` or a `LinkedMap` from `commons-collections`. +* `separator`: Where multiple options are available as discreet elements (radio buttons or + checkboxes), the sequence of characters used to separate each one in the list (such as + `
`). +* `attributes`: An additional string of arbitrary tags or text to be included within the HTML tag itself. This string is echoed literally by the macro. For example, in a - textarea field you may supply attributes as 'rows="5" cols="60"' or you could pass + `textarea` field, you may supply attributes (such as 'rows="5" cols="60"'), or you could pass style information such as 'style="border:1px solid silver"'. -* classOrStyle: for the showErrors macro, the name of the CSS class that the span tag - wrapping each error will use. If no information is supplied (or the value is empty) - then the errors will be wrapped in tags. +* `classOrStyle`: For the `showErrors` macro, the name of the CSS class that the `span` element + that wraps each error uses. If no information is supplied (or the value is empty), + the errors are wrapped in `` tags. -Examples of the macros are outlined below some in FTL and some in VTL. Where usage +The following sections outline examples of the macros (some in FTL and some in VTL). Where usage differences exist between the two languages, they are explained in the notes. + + [[mvc-views-form-macros-input]] ===== Input Fields -The formInput macro takes the path parameter (command.name) and an additional attributes -parameter which is empty in the example above. The macro, along with all other form -generation macros, performs an implicit spring bind on the path parameter. The binding -remains valid until a new bind occurs so the showErrors macro doesn't need to pass the -path parameter again - it simply operates on whichever field a bind was last created for. +The `formInput` macro takes the `path` parameter (`command.name`) and an additional `attributes` +parameter (which is empty in the upcoming example). The macro, along with all other form +generation macros, performs an implicit Spring bind on the path parameter. The binding +remains valid until a new bind occurs, so the `showErrors` macro does not need to pass the +path parameter again -- it operates on the field for which a bind was last created. -The showErrors macro takes a separator parameter (the characters that will be used to -separate multiple errors on a given field) and also accepts a second parameter, this -time a class name or style attribute. Note that FreeMarker is able to specify default -values for the attributes parameter. +The `showErrors` macro takes a separator parameter (the characters that are used to +separate multiple errors on a given field) and also accepts a second parameter -- this +time, a class name or style attribute. Note that FreeMarker can specify default +values for the attributes parameter. The following example shows how to use the `formInput` +and `showWErrors` macros: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- <@spring.formInput "command.name"/> <@spring.showErrors "
"/> ---- +==== -Output is shown below of the form fragment generating the name field, and displaying a +The next example shows the output of the form fragment, generating the name field and displaying a validation error after the form was submitted with no value in the field. Validation occurs through Spring's Validation framework. -The generated HTML looks like this: +The generated HTML resembles the following example: +==== [source,jsp,indent=0] [subs="verbatim,quotes"] ---- @@ -329,31 +347,35 @@ The generated HTML looks like this:

---- +==== + +The `formTextarea` macro works the same way as the `formInput` macro and accepts the same +parameter list. Commonly, the second parameter (attributes) is used to pass style +information or `rows` and `cols` attributes for the `textarea`. + -The formTextarea macro works the same way as the formInput macro and accepts the same -parameter list. Commonly, the second parameter (attributes) will be used to pass style -information or rows and cols attributes for the textarea. [[mvc-views-form-macros-select]] ===== Selection Fields -Four selection field macros can be used to generate common UI value selection inputs in -your HTML forms. +You can use four selection field macros to generate common UI value selection inputs in +your HTML forms: -* formSingleSelect -* formMultiSelect -* formRadioButtons -* formCheckboxes +* `formSingleSelect` +* `formMultiSelect` +* `formRadioButtons` +* `formCheckboxes` -Each of the four macros accepts a Map of options containing the value for the form -field, and the label corresponding to that value. The value and the label can be the +Each of the four macros accepts a `Map` of options that contains the value for the form +field and the label that corresponds to that value. The value and the label can be the same. -An example of radio buttons in FTL is below. The form backing object specifies a default -value of 'London' for this field and so no validation is necessary. When the form is +The next example is for radio buttons in FTL. The form-backing object specifies a default +value of 'London' for this field, so no validation is necessary. When the form is rendered, the entire list of cities to choose from is supplied as reference data in the -model under the name 'cityMap'. +model under the name 'cityMap'. The following listing shows the example: +==== [source,jsp,indent=0] [subs="verbatim,quotes"] ---- @@ -361,14 +383,16 @@ model under the name 'cityMap'. Town: <@spring.formRadioButtons "command.address.town", cityMap, ""/>

---- +==== -This renders a line of radio buttons, one for each value in `cityMap` using the -separator "". No additional attributes are supplied (the last parameter to the macro is -missing). The cityMap uses the same String for each key-value pair in the map. The map's -keys are what the form actually submits as POSTed request parameters, map values are the -labels that the user sees. In the example above, given a list of three well known cities -and a default value in the form backing object, the HTML would be +The preceding listing renders a line of radio buttons, one for each value in `cityMap`, and uses a +separator of `""`. No additional attributes are supplied (the last parameter to the macro is +missing). The `cityMap` uses the same `String` for each key-value pair in the map. The map's +keys are what the form actually submits as POSTed request parameters. The map values are the +labels that the user sees. In the preceding example, given a list of three well known cities +and a default value in the form backing object, the HTML resembles the following: +==== [source,jsp,indent=0] [subs="verbatim,quotes"] ---- @@ -377,10 +401,12 @@ and a default value in the form backing object, the HTML would be Paris New York ---- +==== -If your application expects to handle cities by internal codes for example, the map of -codes would be created with suitable keys like the example below. +If your application expects to handle cities by internal codes (for example), you can create the map of +codes with suitable keys, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -395,10 +421,12 @@ codes would be created with suitable keys like the example below. return model; } ---- +==== -The code would now produce output where the radio values are the relevant codes but the -user still sees the more user friendly city names. +The code now produces output where the radio values are the relevant codes, but the +user still sees the more user-friendly city names, as follows: +==== [source,jsp,indent=0] [subs="verbatim,quotes"] ---- @@ -407,34 +435,39 @@ user still sees the more user friendly city names. Paris New York ---- +==== + [[mvc-views-form-macros-html-escaping]] -==== HTML escaping +==== HTML Escaping -Default usage of the form macros above will result in HTML tags that are HTML 4.01 -compliant and that use the default value for HTML escaping defined in your web.xml as -used by Spring's bind support. In order to make the tags XHTML compliant or to override +Default usage of the form macros described earlier results in HTML elemets that are HTML 4.01 +compliant and that use the default value for HTML escaping defined in your `web.xml` file, as +used by Spring's bind support. To make the elements be XHTML compliant or to override the default HTML escaping value, you can specify two variables in your template (or in -your model where they will be visible to your templates). The advantage of specifying +your model, where they are visible to your templates). The advantage of specifying them in the templates is that they can be changed to different values later in the template processing to provide different behavior for different fields in your form. To switch to XHTML compliance for your tags, specify a value of `true` for a -model/context variable named xhtmlCompliant: +model or context variable named `xhtmlCompliant`, as the following example shows: +==== [source,jsp,indent=0] [subs="verbatim,quotes"] ---- <#-- for FreeMarker --> <#assign xhtmlCompliant = true> ---- +==== -Any tags generated by the Spring macros will now be XHTML compliant after processing -this directive. +After processing +this directive, any elements generated by the Spring macros are now XHTML compliant. -In similar fashion, HTML escaping can be specified per field: +In similar fashion, you can specify HTML escaping per field, as the following example shows: +==== [source,jsp,indent=0] [subs="verbatim,quotes"] ---- @@ -447,30 +480,28 @@ In similar fashion, HTML escaping can be specified per field: <#assign htmlEscape = false in spring> <#-- all future fields will be bound with HTML escaping off --> ---- - +==== [[mvc-view-groovymarkup]] == Groovy Markup -http://groovy-lang.org/templating.html#_the_markuptemplateengine[Groovy Markup Template Engine] -is primarily aimed at generating XML-like markup (XML, XHTML, HTML5, etc) but that can -be used to generate any text based content. The Spring Framework has a built-in +The http://groovy-lang.org/templating.html#_the_markuptemplateengine[Groovy Markup Template Engine] +is primarily aimed at generating XML-like markup (XML, XHTML, HTML5, and others), but you can +use it to generate any text-based content. The Spring Framework has a built-in integration for using Spring MVC with Groovy Markup. -[TIP] -==== -The Groovy Markup Template engine requires Groovy 2.3.1+. -==== +NOTE: The Groovy Markup Template engine requires Groovy 2.3.1+. [[mvc-view-groovymarkup-configuration]] === Configuration -To configure the Groovy Markup Template Engine: +The following example shows how to configure the Groovy Markup Template Engine: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -493,9 +524,11 @@ To configure the Groovy Markup Template Engine: } } ---- +==== -To configure the same in XML: +The following example shows how to configure the same in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -508,6 +541,7 @@ To configure the same in XML: ---- +==== @@ -515,8 +549,9 @@ To configure the same in XML: === Example Unlike traditional template engines, Groovy Markup relies on a DSL that uses a builder -syntax. Here is a sample template for an HTML page: +syntax. The following example shows a sample template for an HTML page: +==== [source,groovy,indent=0] [subs="verbatim,quotes"] ---- @@ -531,49 +566,49 @@ syntax. Here is a sample template for an HTML page: } } ---- - +==== [[mvc-view-script]] == Script Views -[.small]#<># +[.small]#<># The Spring Framework has a built-in integration for using Spring MVC with any templating library that can run on top of the -https://www.jcp.org/en/jsr/detail?id=223[JSR-223] Java scripting engine. Below is a list -of templating libraries we've tested on different script engines: +https://www.jcp.org/en/jsr/detail?id=223[JSR-223] Java scripting engine. We have tested the following +templating libraries on different script engines: -[horizontal] -http://handlebarsjs.com/[Handlebars] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -https://mustache.github.io/[Mustache] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -http://facebook.github.io/react/[React] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -http://www.embeddedjs.com/[EJS] :: http://openjdk.java.net/projects/nashorn/[Nashorn] -http://www.stuartellis.eu/articles/erb/[ERB] :: http://jruby.org[JRuby] -https://docs.python.org/2/library/string.html#template-strings[String templates] :: http://www.jython.org/[Jython] -https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] :: http://kotlinlang.org/[Kotlin] +[%header] +|=== +|Scripting Library |Scripting Engine +|http://handlebarsjs.com/[Handlebars] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|https://mustache.github.io/[Mustache] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|http://facebook.github.io/react/[React] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|http://www.embeddedjs.com/[EJS] |http://openjdk.java.net/projects/nashorn/[Nashorn] +|http://www.stuartellis.eu/articles/erb/[ERB] |http://jruby.org[JRuby] +|https://docs.python.org/2/library/string.html#template-strings[String templates] |http://www.jython.org/[Jython] +|https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] |http://kotlinlang.org/[Kotlin] +|=== -[TIP] -==== -The basic rule for integrating any other script engine is that it must implement the +TIP: The basic rule for integrating any other script engine is that it must implement the `ScriptEngine` and `Invocable` interfaces. -==== [[mvc-view-script-dependencies]] === Requirements -[.small]#<># +[.small]#<># -You need to have the script engine on your classpath: +You need to have the script engine on your classpath, the details of which vary by script engine: -* http://openjdk.java.net/projects/nashorn/[Nashorn] JavaScript engine is provided with +* The http://openjdk.java.net/projects/nashorn/[Nashorn] JavaScript engine is provided with Java 8+. Using the latest update release available is highly recommended. * http://jruby.org[JRuby] should be added as a dependency for Ruby support. * http://www.jython.org[Jython] should be added as a dependency for Python support. * `org.jetbrains.kotlin:kotlin-script-util` dependency and a `META-INF/services/javax.script.ScriptEngineFactory` file containing a `org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory` - line should be added for Kotlin script support, see + line should be added for Kotlin script support. See https://github.com/sdeleuze/kotlin-script-templating[this example] for more details. You need to have the script templating library. One way to do that for Javascript is @@ -582,13 +617,14 @@ through http://www.webjars.org/[WebJars]. [[mvc-view-script-integrate]] -=== Script templates -[.small]#<># +=== Script Templates +[.small]#<># -Declare a `ScriptTemplateConfigurer` bean in order to specify the script engine to use, +You can declare a `ScriptTemplateConfigurer` bean to specify the script engine to use, the script files to load, what function to call to render templates, and so on. -Below is an example with Mustache templates and the Nashorn JavaScript engine: +The following example uses Mustache templates and the Nashorn JavaScript engine: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -612,9 +648,11 @@ Below is an example with Mustache templates and the Nashorn JavaScript engine: } } ---- +==== -The same in XML: +The following example shows the same arrangement in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -628,9 +666,11 @@ The same in XML: ---- +==== -The controller would look no different: +The controller would look no different for the Java and XML configurations, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -645,9 +685,11 @@ The controller would look no different: } } ---- +==== -And the Mustache template is: +The following example shows the Mustache template: +==== [source,html,indent=0] [subs="verbatim,quotes"] ---- @@ -660,24 +702,28 @@ And the Mustache template is: ---- +==== The render function is called with the following parameters: -* `String template`: the template content -* `Map model`: the view model -* `RenderingContext renderingContext`: the - {api-spring-framework}/web/servlet/view/script/RenderingContext.html[RenderingContext] - that gives access to the application context, the locale, the template loader and the - url (since 5.0) +* `String template`: The template content +* `Map model`: The view model +* `RenderingContext renderingContext`: The + {api-spring-framework}/web/servlet/view/script/RenderingContext.html[`RenderingContext`] + that gives access to the application context, the locale, the template loader, and the + URL (since 5.0) `Mustache.render()` is natively compatible with this signature, so you can call it directly. -If your templating technology requires some customization, you may provide a script that +If your templating technology requires some customization, you can provide a script that implements a custom render function. For example, http://handlebarsjs.com[Handlerbars] -needs to compile templates before using them, and requires a -http://en.wikipedia.org/wiki/Polyfill[polyfill] in order to emulate some -browser facilities not available in the server-side script engine. +needs to compile templates before using them and requires a +http://en.wikipedia.org/wiki/Polyfill[polyfill] to emulate some +browser facilities that are not available in the server-side script engine. + +The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -701,28 +747,29 @@ browser facilities not available in the server-side script engine. } } ---- - -[NOTE] ==== -Setting the `sharedEngine` property to `false` is required when using non thread-safe -script engines with templating libraries not designed for concurrency, like Handlebars or -React running on Nashorn for example. In that case, Java 8u60 or greater is required due + +NOTE: Setting the `sharedEngine` property to `false` is required when you use non-thread-safe +script engines with templating libraries not designed for concurrency, such as Handlebars or +React running on Nashorn. In that case, Java 8u60 or greater is required, due to https://bugs.openjdk.java.net/browse/JDK-8076099[this bug]. -==== -`polyfill.js` only defines the `window` object needed by Handlebars to run properly: +`polyfill.js` defines only the `window` object needed by Handlebars to run properly, as follows: +==== [source,javascript,indent=0] [subs="verbatim,quotes"] ---- var window = {}; ---- +==== -This basic `render.js` implementation compiles the template before using it. A production -ready implementation should also store and reused cached templates / pre-compiled templates. -This can be done on the script side, as well as any customization you need (managing -template engine configuration for example). +This basic `render.js` implementation compiles the template before using it. A production-ready +implementation should also store any reused cached templates or pre-compiled templates. +You can do so on the script side (and handle any customization you need -- managing +template engine configuration, for example). The following example shows how to do so: +==== [source,javascript,indent=0] [subs="verbatim,quotes"] ---- @@ -731,32 +778,33 @@ template engine configuration for example). return compiledTemplate(model); } ---- +==== Check out the Spring Framework unit tests, -https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script[java], and +https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script[Java], and https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script[resources], for more configuration examples. - [[mvc-view-jsp]] -== JSP & JSTL +== JSP and JSTL The Spring Framework has a built-in integration for using Spring MVC with JSP and JSTL. [[mvc-view-jsp-resolver]] -=== View resolvers +=== View Resolvers -When developing with JSPs you can declare a `InternalResourceViewResolver` or a +When developing with JSPs, you can declare a `InternalResourceViewResolver` or a `ResourceBundleViewResolver` bean. `ResourceBundleViewResolver` relies on a properties file to define the view names -mapped to a class and a URL. With a `ResourceBundleViewResolver` you -can mix different types of views using only one resolver. Here is an example: +mapped to a class and a URL. With a `ResourceBundleViewResolver`, you +can mix different types of views byusing only one resolver, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -786,6 +834,7 @@ directory so there can be no direct access by clients.
---- +==== @@ -793,18 +842,18 @@ directory so there can be no direct access by clients. === JSPs versus JSTL When using the Java Standard Tag Library you must use a special view class, the -`JstlView`, as JSTL needs some preparation before things such as the I18N features will +`JstlView`, as JSTL needs some preparation before things such as the I18N features can work. [[mvc-view-jsp-tags]] -=== Spring's JSP tag library +=== Spring's JSP Tag Library -Spring provides data binding of request parameters to command objects as described in +Spring provides data binding of request parameters to command objects, as described in earlier chapters. To facilitate the development of JSP pages in combination with those data binding features, Spring provides a few tags that make things even easier. All -Spring tags have__HTML escaping__ features to enable or disable escaping of characters. +Spring tags have HTML escaping features to enable or disable escaping of characters. The `spring.tld` tag library descriptor (TLD) is included in the `spring-webmvc.jar`. For a comprehensive reference on individual tags, browse the @@ -822,13 +871,14 @@ familiar and intuitive to use. The tag-generated HTML is HTML 4.01/XHTML 1.0 com Unlike other form/input tag libraries, Spring's form tag library is integrated with Spring Web MVC, giving the tags access to the command object and reference data your -controller deals with. As you will see in the following examples, the form tags make -JSPs easier to develop, read and maintain. +controller deals with. As we show in the following examples, the form tags make +JSPs easier to develop, read, and maintain. -Let's go through the form tags and look at an example of how each tag is used. We have +We go through the form tags and look at an example of how each tag is used. We have included generated HTML snippets where certain tags require further commentary. + [[mvc-view-jsp-formtaglib-configuration]] ==== Configuration @@ -838,28 +888,31 @@ called `spring-form.tld`. To use the tags from this library, add the following directive to the top of your JSP page: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> ---- - where `form` is the tag name prefix you want to use for the tags from this library. +==== + [[mvc-view-jsp-formtaglib-formtag]] -==== The form tag +==== The Form Tag -This tag renders an HTML 'form' tag and exposes a binding path to inner tags for +This tag renders an HTML 'form' element and exposes a binding path to inner tags for binding. It puts the command object in the `PageContext` so that the command object can -be accessed by inner tags. __All the other tags in this library are nested tags of the -`form` tag__. +be accessed by inner tags. All the other tags in this library are nested tags of the +`form` tag. -Let's assume we have a domain object called `User`. It is a JavaBean with properties -such as `firstName` and `lastName`. We will use it as the form backing object of our -form controller which returns `form.jsp`. Below is an example of what `form.jsp` would +Assume that we have a domain object called `User`. It is a JavaBean with properties +such as `firstName` and `lastName`. We can use it as the form-backing object of our +form controller, which returns `form.jsp`. The following example shows what `form.jsp` could look like: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -881,13 +934,15 @@ look like: ---- +==== The `firstName` and `lastName` values are retrieved from the command object placed in the `PageContext` by the page controller. Keep reading to see more complex examples of how inner tags are used with the `form` tag. -The generated HTML looks like a standard form: +The following listing shows the generated HTML, which looks like a standard form: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -909,11 +964,14 @@ The generated HTML looks like a standard form: ---- +==== -The preceding JSP assumes that the variable name of the form backing object is -`'command'`. If you have put the form backing object into the model under another name -(definitely a best practice), then you can bind the form to the named variable like so: +The preceding JSP assumes that the variable name of the form-backing object is +`command`. If you have put the form-backing object into the model under another name +(definitely a best practice), you can bind the form to the named variable, as the +following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -935,24 +993,28 @@ The preceding JSP assumes that the variable name of the form backing object is ---- +==== + [[mvc-view-jsp-formtaglib-inputtag]] -==== The input tag +==== The `input` Tag + +This tag renders an HTML `input` element with the bound value and `type='text'` by default. +For an example of this tag, see <>. You can also use +HTML5-specific types, such as `email`, `tel`, `date`, and others. -This tag renders an HTML 'input' tag using the bound value and type='text' by default. -For an example of this tag, see <>. You may also use -HTML5-specific types like 'email', 'tel', 'date', and others. [[mvc-view-jsp-formtaglib-checkboxtag]] -==== The checkbox tag +==== The `checkbox` Tag -This tag renders an HTML 'input' tag with type 'checkbox'. +This tag renders an HTML `input` tag with the `type` set to `checkbox`. -Let's assume our `User` has preferences such as newsletter subscription and a list of -hobbies. Below is an example of the `Preferences` class: +Assume that our `User` has preferences such as newsletter subscription and a list of +hobbies. The following example shows the `Preferences` class: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -987,9 +1049,11 @@ hobbies. Below is an example of the `Preferences` class: } } ---- +==== -The `form.jsp` would look like: +The corresponding `form.jsp` could then resemble the following: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1021,21 +1085,23 @@ The `form.jsp` would look like: ---- +==== -There are 3 approaches to the `checkbox` tag which should meet all your checkbox needs. +There are three approaches to the `checkbox` tag, which should meet all your checkbox needs. -* Approach One - When the bound value is of type `java.lang.Boolean`, the - `input(checkbox)` is marked as 'checked' if the bound value is `true`. The `value` +* Approach One: When the bound value is of type `java.lang.Boolean`, the + `input(checkbox)` is marked as `checked` if the bound value is `true`. The `value` attribute corresponds to the resolved value of the `setValue(Object)` value property. -* Approach Two - When the bound value is of type `array` or `java.util.Collection`, the - `input(checkbox)` is marked as 'checked' if the configured `setValue(Object)` value is +* Approach Two: When the bound value is of type `array` or `java.util.Collection`, the + `input(checkbox)` is marked as `checked` if the configured `setValue(Object)` value is present in the bound `Collection`. -* Approach Three - For any other bound value type, the `input(checkbox)` is marked as - 'checked' if the configured `setValue(Object)` is equal to the bound value. +* Approach Three: For any other bound value type, the `input(checkbox)` is marked as + `checked` if the configured `setValue(Object)` is equal to the bound value. -Note that regardless of the approach, the same HTML structure is generated. Below is an -HTML snippet of some checkboxes: +Note that, regardless of the approach, the same HTML structure is generated. The following +HTML snippet defines some checkboxes: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1051,30 +1117,33 @@ HTML snippet of some checkboxes: ---- +==== -What you might not expect to see is the additional hidden field after each checkbox. -When a checkbox in an HTML page is __not__ checked, its value will not be sent to the +You might not expect to see the additional hidden field after each checkbox. +When a checkbox in an HTML page is not checked, its value is not sent to the server as part of the HTTP request parameters once the form is submitted, so we need a -workaround for this quirk in HTML in order for Spring form data binding to work. The +workaround for this quirk in HTML for Spring form data binding to work. The `checkbox` tag follows the existing Spring convention of including a hidden parameter -prefixed by an underscore ("_") for each checkbox. By doing this, you are effectively -telling Spring that "__the checkbox was visible in the form and I want my object to -which the form data will be bound to reflect the state of the checkbox no matter what__". +prefixed by an underscore (`_`) for each checkbox. By doing this, you are effectively +telling Spring that "`the checkbox was visible in the form, and I want my object to +which the form data binds to reflect the state of the checkbox, no matter what.`" + [[mvc-view-jsp-formtaglib-checkboxestag]] -==== The checkboxes tag +==== The `checkboxes` Tag -This tag renders multiple HTML 'input' tags with type 'checkbox'. +This tag renders multiple HTML `input` tags with the `type` set to `checkbox`. -Building on the example from the previous `checkbox` tag section. Sometimes you prefer +This section build on the example from the previous `checkbox` tag section. Sometimes, you prefer not to have to list all the possible hobbies in your JSP page. You would rather provide a list at runtime of the available options and pass that in to the tag. That is the -purpose of the `checkboxes` tag. You pass in an `Array`, a `List` or a `Map` containing -the available options in the "items" property. Typically the bound property is a -collection so it can hold multiple values selected by the user. Below is an example of -the JSP using this tag: +purpose of the `checkboxes` tag. You can pass in an `Array`, a `List`, or a `Map` that contains +the available options in the `items` property. Typically, the bound property is a +collection so that it can hold multiple values selected by the user. The following example +shows a JSP that uses this tag: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1090,22 +1159,25 @@ the JSP using this tag: ---- +==== -This example assumes that the "interestList" is a `List` available as a model attribute -containing strings of the values to be selected from. In the case where you use a Map, -the map entry key will be used as the value and the map entry's value will be used as +This example assumes that the `interestList` is a `List` available as a model attribute +that contains strings of the values to be selected from. If you use a `Map`, +the map entry key is used as the value, and the map entry's value is used as the label to be displayed. You can also use a custom object where you can provide the -property names for the value using "itemValue" and the label using "itemLabel". +property names for the value by using `itemValue` and the label by using `itemLabel`. + [[mvc-view-jsp-formtaglib-radiobuttontag]] -==== The radiobutton tag +==== The `radiobutton` Tag -This tag renders an HTML 'input' tag with type 'radio'. +This tag renders an HTML `input` element with the `type` set to `radio`. -A typical usage pattern will involve multiple tag instances bound to the same property -but with different values. +A typical usage pattern involves multiple tag instances bound to the same property +but with different values, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1117,21 +1189,24 @@ but with different values. ---- +==== + [[mvc-view-jsp-formtaglib-radiobuttonstag]] -==== The radiobuttons tag +==== The `radiobuttons` Tag -This tag renders multiple HTML 'input' tags with type 'radio'. +This tag renders multiple HTML `input` elements with the `type` set to `radio`. -Just like the `checkboxes` tag above, you might want to pass in the available options as -a runtime variable. For this usage you would use the `radiobuttons` tag. You pass in an -`Array`, a `List` or a `Map` containing the available options in the "items" property. -In the case where you use a Map, the map entry key will be used as the value and the map -entry's value will be used as the label to be displayed. You can also use a custom -object where you can provide the property names for the value using "itemValue" and the -label using "itemLabel". +As with the <>, you might want to pass in the available options as +a runtime variable. For this usage, you can use the `radiobuttons` tag. You pass in an +`Array`, a `List`, or a `Map` that contains the available options in the `items` property. +If you use a `Map`, the map entry key is used as the value and the map +entry's value are used as the label to be displayed. You can also use a custom +object where you can provide the property names for the value by using `itemValue` and the +label by using `itemLabel`, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1140,13 +1215,15 @@ label using "itemLabel". ---- +==== [[mvc-view-jsp-formtaglib-passwordtag]] -==== The password tag +==== The `password` Tag -This tag renders an HTML 'input' tag with type 'password' using the bound value. +This tag renders an HTML `input` tag with the type set to `password` with the bound value. +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1157,11 +1234,13 @@ This tag renders an HTML 'input' tag with type 'password' using the bound value. ---- +==== -Please note that by default, the password value is __not__ shown. If you do want the -password value to be shown, then set the value of the `'showPassword'` attribute to -true, like so. +Note that, by default, the password value is not shown. If you do want the +password value to be shown, you can set the value of the `showPassword` attribute to +`true`, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1172,16 +1251,19 @@ true, like so. ---- +==== + [[mvc-view-jsp-formtaglib-selecttag]] -==== The select tag +==== The `select` Tag This tag renders an HTML 'select' element. It supports data binding to the selected option as well as the use of nested `option` and `options` tags. -Let's assume a `User` has a list of skills. +Assume that a `User` has a list of skills. The corresponding HTML could be as follows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1190,10 +1272,12 @@ Let's assume a `User` has a list of skills. ---- +==== -If the `User's` skill were in Herbology, the HTML source of the 'Skills' row would look -like: +If the `User's` skill are in Herbology, the HTML source of the 'Skills' row could be +as follows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1208,14 +1292,17 @@ like: ---- +==== + [[mvc-view-jsp-formtaglib-optiontag]] -==== The option tag +==== The `option` Tag -This tag renders an HTML 'option'. It sets 'selected' as appropriate based on the bound -value. +This tag renders an HTML `option` element. It sets `selected`, based on the bound +value. The following HTML shows typical output for it: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1231,10 +1318,12 @@ value. ---- +==== -If the `User's` house was in Gryffindor, the HTML source of the 'House' row would look -like: +If the `User's` house was in Gryffindor, the HTML source of the 'House' row would be +as follows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1242,7 +1331,7 @@ like: House: - + <1> ---- +<1> Note the addition of a `selected` attribute. +==== -As the example shows, the combined usage of an `option` tag with the `options` tag -generates the same standard HTML, but allows you to explicitly specify a value in the -JSP that is for display only (where it belongs) such as the default string in the +As the preceding example shows, the combined usage of an `option` tag with the `options` tag +generates the same standard HTML but lets you explicitly specify a value in the +JSP that is for display only (where it belongs), such as the default string in the example: "-- Please Select". The `items` attribute is typically populated with a collection or array of item objects. -`itemValue` and `itemLabel` simply refer to bean properties of those item objects, if -specified; otherwise, the item objects themselves will be stringified. Alternatively, -you may specify a `Map` of items, in which case the map keys are interpreted as option -values and the map values correspond to option labels. If `itemValue` and/or `itemLabel` -happen to be specified as well, the item value property will apply to the map key and -the item label property will apply to the map value. +`itemValue` and `itemLabel` refer to bean properties of those item objects, if +specified. Otherwise, the item objects themselves are turned into strings. Alternatively, +you can specify a `Map` of items, in which case the map keys are interpreted as option +values and the map values correspond to option labels. If `itemValue` or `itemLabel` (or both) +happen to be specified as well, the item value property applies to the map key, and +the item label property applies to the map value. + [[mvc-view-jsp-formtaglib-textareatag]] -==== The textarea tag +==== The `textarea` Tag -This tag renders an HTML 'textarea'. +This tag renders an HTML `textarea` element. The following HTML shows typical output for it: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1318,42 +1417,50 @@ This tag renders an HTML 'textarea'. ---- +==== + [[mvc-view-jsp-formtaglib-hiddeninputtag]] -==== The hidden tag +==== The `hidden` Tag -This tag renders an HTML 'input' tag with type 'hidden' using the bound value. To submit -an unbound hidden value, use the HTML `input` tag with type 'hidden'. +This tag renders an HTML `input` tag with the `type` set to `hidden` with the bound value. To submit +an unbound hidden value, use the HTML `input` tag with the `type` set to `hidden`. +The following HTML shows typical output for it: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- - ---- +==== -If we choose to submit the 'house' value as a hidden one, the HTML would look like: +If we choose to submit the `house` value as a hidden one, the HTML would be as follows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- ---- +==== + [[mvc-view-jsp-formtaglib-errorstag]] -==== The errors tag +==== The `errors` Tag -This tag renders field errors in an HTML 'span' tag. It provides access to the errors +This tag renders field errors in an HTML `span` element. It provides access to the errors created in your controller or those that were created by any validators associated with your controller. -Let's assume we want to display all error messages for the `firstName` and `lastName` +Assume that we want to display all error messages for the `firstName` and `lastName` fields once we submit the form. We have a validator for instances of the `User` class -called `UserValidator`. +called `UserValidator`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1369,9 +1476,11 @@ called `UserValidator`. } } ---- +==== -The `form.jsp` would look like: +The `form.jsp` could be as follows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1398,10 +1507,12 @@ The `form.jsp` would look like: ---- +==== -If we submit a form with empty values in the `firstName` and `lastName` fields, this is -what the HTML would look like: +If we submit a form with empty values in the `firstName` and `lastName` fields, +the HTML would be as follows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1428,17 +1539,19 @@ what the HTML would look like: ---- +==== -What if we want to display the entire list of errors for a given page? The example below +What if we want to display the entire list of errors for a given page? The next example shows that the `errors` tag also supports some basic wildcarding functionality. -* `path="{asterisk}"` - displays all errors -* `path="lastName"` - displays all errors associated with the `lastName` field -* if `path` is omitted - object errors only are displayed +* `path="{asterisk}"`: Displays all errors. +* `path="lastName"`: Displays all errors associated with the `lastName` field. +* If `path` is omitted, only object errors are displayed. -The example below will display a list of errors at the top of the page, followed by +The following example displays a list of errors at the top of the page, followed by field-specific errors next to the fields: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1463,9 +1576,11 @@ field-specific errors next to the fields: ---- +==== -The HTML would look like: +The HTML would be as follows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1491,6 +1606,7 @@ The HTML would look like: ---- +==== The `spring-form.tld` tag library descriptor (TLD) is included in the `spring-webmvc.jar`. For a comprehensive reference on individual tags, browse the @@ -1498,28 +1614,30 @@ For a comprehensive reference on individual tags, browse the or see the tag library description. + [[mvc-rest-method-conversion]] -==== HTTP method conversion +==== HTTP Method Conversion -A key principle of REST is the use of the Uniform Interface. This means that all -resources (URLs) can be manipulated using the same four HTTP methods: GET, PUT, POST, +A key principle of REST is the use of the "`Uniform Interface`". This means that all +resources (URLs) can be manipulated by using the same four HTTP methods: GET, PUT, POST, and DELETE. For each method, the HTTP specification defines the exact semantics. For instance, a GET should always be a safe operation, meaning that is has no side effects, and a PUT or DELETE should be idempotent, meaning that you can repeat these operations over and over again, but the end result should be the same. While HTTP defines these four methods, HTML only supports two: GET and POST. Fortunately, there are two possible -workarounds: you can either use JavaScript to do your PUT or DELETE, or simply do a POST -with the 'real' method as an additional parameter (modeled as a hidden input field in an -HTML form). This latter trick is what Spring's `HiddenHttpMethodFilter` does. This -filter is a plain Servlet Filter and therefore it can be used in combination with any -web framework (not just Spring MVC). Simply add this filter to your web.xml, and a POST -with a hidden _method parameter will be converted into the corresponding HTTP method +workarounds: you can either use JavaScript to do your PUT or DELETE, or you can do a POST +with the "`real`" method as an additional parameter (modeled as a hidden input field in an +HTML form). Spring's `HiddenHttpMethodFilter` uses this latter trick. This +filter is a plain Servlet filter and, therefore, it can be used in combination with any +web framework (not just Spring MVC). Add this filter to your web.xml, and a POST +with a hidden `method` parameter is converted into the corresponding HTTP method request. -To support HTTP method conversion the Spring MVC form tag was updated to support setting -the HTTP method. For example, the following snippet taken from the updated Petclinic -sample +To support HTTP method conversion, the Spring MVC form tag was updated to support setting +the HTTP method. For example, the following snippet comes from the Pet Clinic +sample: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1527,11 +1645,13 @@ sample

---- +==== -This will actually perform an HTTP POST, with the 'real' DELETE method hidden behind a -request parameter, to be picked up by the `HiddenHttpMethodFilter`, as defined in -web.xml: +The preceding example perform an HTTP POST, with the "`real`" DELETE method hidden behind a +request parameter. It is picked up by the `HiddenHttpMethodFilter`, which is defined in +web.xml, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1545,9 +1665,11 @@ web.xml: petclinic ---- +==== -The corresponding `@Controller` method is shown below: +The following example shows the corresponding `@Controller` method: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1557,33 +1679,31 @@ The corresponding `@Controller` method is shown below: return "redirect:/owners/" + ownerId; } ---- +==== + [[mvc-view-jsp-formtaglib-html5]] -==== HTML5 tags +==== HTML5 Tags The Spring form tag library allows entering dynamic attributes, which means you can enter any HTML5 specific attributes. -The form input tag supports entering a type attribute other than 'text'. This is -intended to allow rendering new HTML5 specific input types such as 'email', 'date', -'range', and others. Note that entering type='text' is not required since 'text' +The form `input` tag supports entering a type attribute other than `text`. This is +intended to allow rendering new HTML5 specific input types, such as `email`, `date`, +`range`, and others. Note that entering `type='text'` is not required, since `text` is the default type. - [[mvc-view-tiles]] == Tiles -It is possible to integrate Tiles - just as any other view technology - in web -applications using Spring. The following describes in a broad way how to do this. +You can integrate Tiles - just as any other view technology - in web +applications that use Spring. This section describes, in a broad way, how to do so. -[NOTE] -==== -This section focuses on Spring's support for Tiles v3 in the +NOTE: This section focuses on Spring's support for Tiles version 3 in the `org.springframework.web.servlet.view.tiles3` package. -==== @@ -1599,11 +1719,12 @@ to your project. [[mvc-view-tiles-integrate]] === Configuration -To be able to use Tiles, you have to configure it using files containing definitions -(for basic information on definitions and other Tiles concepts, please have a look at -http://tiles.apache.org[]). In Spring this is done using the `TilesConfigurer`. Have a -look at the following piece of example ApplicationContext configuration: +To be able to use Tiles, you have to configure it by using files that contain definitions +(for basic information on definitions and other Tiles concepts, see +http://tiles.apache.org[]). In Spring, this is done by using the `TilesConfigurer`. +The following example `ApplicationContext` configuration shows how to do so: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1619,18 +1740,20 @@ look at the following piece of example ApplicationContext configuration:
---- +==== -As you can see, there are five files containing definitions, which are all located in -the `'WEB-INF/defs'` directory. At initialization of the `WebApplicationContext`, the -files will be loaded and the definitions factory will be initialized. After that has -been done, the Tiles includes in the definition files can be used as views within your -Spring web application. To be able to use the views you have to have a `ViewResolver` -just as with any other view technology used with Spring. Below you can find two -possibilities, the `UrlBasedViewResolver` and the `ResourceBundleViewResolver`. +The preceding example defines five files that contain definitions. The files are all located in +the `WEB-INF/defs` directory. At initialization of the `WebApplicationContext`, the +files are loaded, and the definitions factory are initialized. After that has +been done, the Tiles included in the definition files can be used as views within your +Spring web application. To be able to use the views, you have to have a `ViewResolver` +as with any other view technology used with Spring. You can can use either of two +implementations, the `UrlBasedViewResolver` and the `ResourceBundleViewResolver`. -You can specify locale specific Tiles definitions by adding an underscore and then -the locale. For example: +You can specify locale-specific Tiles definitions by adding an underscore and then +the locale, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1643,23 +1766,23 @@ the locale. For example: ---- +==== -With this configuration, `tiles_fr_FR.xml` will be used for requests with the `fr_FR` locale, -and `tiles.xml` will be used by default. +With the preceding configuration, `tiles_fr_FR.xml` is used for requests with the `fr_FR` locale, +and `tiles.xml` is used by default. -[NOTE] -==== -Since underscores are used to indicate locales, it is recommended to avoid using +NOTE: Since underscores are used to indicate locales, we recommended not using them otherwise in the file names for Tiles definitions. -==== + [[mvc-view-tiles-url]] -==== UrlBasedViewResolver +==== Using `UrlBasedViewResolver` The `UrlBasedViewResolver` instantiates the given `viewClass` for each view it has to -resolve. +resolve. The following bean defines a `UrlBasedViewResolver`: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1667,14 +1790,19 @@ resolve. ---- +==== + [[mvc-view-tiles-resource]] -==== ResourceBundleViewResolver +==== Using `ResourceBundleViewResolver` -The `ResourceBundleViewResolver` has to be provided with a property file containing -view names and view classes the resolver can use: +The `ResourceBundleViewResolver` has to be provided with a property file that contains +view names and view classes that the resolver can use. The following example shows a bean +definition for a `ResourceBundleViewResolver` and the corresponding view names and view +classes (taken from the Pet Clinic sample): +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1683,6 +1811,7 @@ view names and view classes the resolver can use: ---- + [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1697,35 +1826,38 @@ view names and view classes the resolver can use: findOwnersForm.url=/WEB-INF/jsp/findOwners.jsp ... ---- +==== -As you can see, when using the `ResourceBundleViewResolver`, you can easily mix +When you use the `ResourceBundleViewResolver`, you can easily mix different view technologies. -Note that the `TilesView` class supports JSTL (the JSP Standard Tag Library) out of the -box. +Note that the `TilesView` class supports JSTL (the JSP Standard Tag Library). + [[mvc-view-tiles-preparer]] -==== SimpleSpringPreparerFactory and SpringBeanPreparerFactory +==== Using `SimpleSpringPreparerFactory` and `SpringBeanPreparerFactory` As an advanced feature, Spring also supports two special Tiles `PreparerFactory` -implementations. Check out the Tiles documentation for details on how to use +implementations. See the Tiles documentation for details on how to use `ViewPreparer` references in your Tiles definition files. -Specify `SimpleSpringPreparerFactory` to autowire ViewPreparer instances based on +You can specify `SimpleSpringPreparerFactory` to autowire `ViewPreparer` instances based on specified preparer classes, applying Spring's container callbacks as well as applying -configured Spring BeanPostProcessors. If Spring's context-wide annotation-config has -been activated, annotations in ViewPreparer classes will be automatically detected and -applied. Note that this expects preparer __classes__ in the Tiles definition files, just -like the default `PreparerFactory` does. - -Specify `SpringBeanPreparerFactory` to operate on specified preparer __names__ instead -of classes, obtaining the corresponding Spring bean from the DispatcherServlet's -application context. The full bean creation process will be in the control of the Spring +configured Spring BeanPostProcessors. If Spring's context-wide annotation configuration has +been activated, annotations in `ViewPreparer` classes aree automatically detected and +applied. Note that this expects preparer classes in the Tiles definition files, as +the default `PreparerFactory` does. + +You can specify `SpringBeanPreparerFactory` to operate on specified preparer names (instead +of classes), obtaining the corresponding Spring bean from the DispatcherServlet's +application context. The full bean creation process is in the control of the Spring application context in this case, allowing for the use of explicit dependency injection -configuration, scoped beans etc. Note that you need to define one Spring bean definition -per preparer name (as used in your Tiles definitions). +configuration, scoped beans, and so on. Note that you need to define one Spring bean definition +for each preparer name (as used in your Tiles definitions). The following example shows +how to define a set a `SpringBeanPreparerFactory` property on a `TilesConfigurer` bean: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1746,22 +1878,23 @@ per preparer name (as used in your Tiles definitions). ---- - +==== [[mvc-view-feeds]] -== RSS, Atom +== RSS and Atom -Both `AbstractAtomFeedView` and `AbstractRssFeedView` inherit from the base class -`AbstractFeedView` and are used to provide Atom and RSS Feed views respectfully. They +Both `AbstractAtomFeedView` and `AbstractRssFeedView` inherit from the +`AbstractFeedView` base class and are used to provide Atom and RSS Feed views, respectively. They are based on java.net's https://rome.dev.java.net[ROME] project and are located in the package `org.springframework.web.servlet.view.feed`. `AbstractAtomFeedView` requires you to implement the `buildFeedEntries()` method and optionally override the `buildFeedMetadata()` method (the default implementation is -empty), as shown below. +empty). The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1781,9 +1914,11 @@ empty), as shown below. } ---- +==== -Similar requirements apply for implementing `AbstractRssFeedView`, as shown below. +Similar requirements apply for implementing `AbstractRssFeedView`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1802,52 +1937,53 @@ Similar requirements apply for implementing `AbstractRssFeedView`, as shown belo } } ---- +==== -The `buildFeedItems()` and `buildFeedEntires()` methods pass in the HTTP request in case +The `buildFeedItems()` and `buildFeedEntries()` methods pass in the HTTP request, in case you need to access the Locale. The HTTP response is passed in only for the setting of -cookies or other HTTP headers. The feed will automatically be written to the response +cookies or other HTTP headers. The feed is automatically written to the response object after the method returns. -For an example of creating an Atom view please refer to Alef Arendsen's Spring Team Blog +For an example of creating an Atom view, see Alef Arendsen's Spring Team Blog https://spring.io/blog/2009/03/16/adding-an-atom-view-to-an-application-using-spring-s-rest-support[entry]. - [[mvc-view-document]] -== PDF, Excel +== PDF and Excel + +Spring offers ways to return output other than HTML, including PDF and Excel spreadsheets. +This section describes how to use those features. [[mvc-view-document-intro]] -=== Introduction to document views +=== Introduction to Document Views -Returning an HTML page isn't always the best way for the user to view the model output, +An HTML page is not always the best way for the user to view the model output, and Spring makes it simple to generate a PDF document or an Excel spreadsheet -dynamically from the model data. The document is the view and will be streamed from the -server with the correct content type to (hopefully) enable the client PC to run their +dynamically from the model data. The document is the view and is streamed from the +server with the correct content type, to (hopefully) enable the client PC to run their spreadsheet or PDF viewer application in response. In order to use Excel views, you need to add the Apache POI library to your classpath, -and for PDF generation preferably the OpenPDF library. +For PDF generation, you need to add (preferably) the OpenPDF library. -[NOTE] -==== -Use the latest versions of the underlying document generation libraries if possible. -In particular, we strongly recommend OpenPDF (e.g. OpenPDF 1.0.5) instead of the -outdated original iText 2.1.7 since it is actively maintained and fixes an important +NOTE: You should use the latest versions of the underlying document-generation libraries, if possible. +In particular, we strongly recommend OpenPDF (for example, OpenPDF 1.0.5) instead of the +outdated original iText 2.1.7, since OpenPDF is actively maintained and fixes an important vulnerability for untrusted PDF content. -==== [[mvc-view-document-pdf]] -=== PDF views +=== PDF Views A simple PDF view for a word list could extend `org.springframework.web.servlet.view.document.AbstractPdfView` and implement the -`buildPdfDocument()` method as follows: +`buildPdfDocument()` method, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1863,19 +1999,20 @@ A simple PDF view for a word list could extend } } ---- +==== -A controller may return such a view either from an external view definition +A controller can return such a view either from an external view definition (referencing it by name) or as a `View` instance from the handler method. [[mvc-view-document-pdf]] -=== Excel views +=== Excel Views Since Spring Framework 4.2, `org.springframework.web.servlet.view.document.AbstractXlsView` is provided as a base -class for Excel views based on POI, with specialized subclasses `AbstractXlsxView` -and `AbstractXlsxStreamingView`, superseding the outdated `AbstractExcelView` class. +class for Excel views. It is based on Apache POI, with specialized subclasses (`AbstractXlsxView` +and `AbstractXlsxStreamingView`) that supersede the outdated `AbstractExcelView` class. The programming model is similar to `AbstractPdfView`, with `buildExcelDocument()` as the central template method and controllers being able to return such a view from @@ -1883,77 +2020,77 @@ an external definition (by name) or as a `View` instance from the handler method - [[mvc-view-jackson]] == Jackson -[.small]#<># +[.small]#<># + +Spring offers support for the Jackson JSON library. [[mvc-view-json-mapping]] -=== Jackson-based JSON views -[.small]#<># +=== Jackson-based JSON Views +[.small]#<># The `MappingJackson2JsonView` uses the Jackson library's `ObjectMapper` to render the response content as JSON. By default, the entire contents of the model map (with the exception of -framework-specific classes) will be encoded as JSON. For cases where the contents of the -map need to be filtered, users may specify a specific set of model attributes to encode -via the `modelKeys` property. The `extractValueFromSingleKeyModel` property may also be -used to have the value in single-key models extracted and serialized directly rather than +framework-specific classes) are encoded as JSON. For cases where the contents of the +map need to be filtered, you can specify a specific set of model attributes to encode +by using the `modelKeys` property. You can also use the `extractValueFromSingleKeyModel` property +to have the value in single-key models extracted and serialized directly rather than as a map of model attributes. -JSON mapping can be customized as needed through the use of Jackson's provided -annotations. When further control is needed, a custom `ObjectMapper` can be injected -through the `ObjectMapper` property for cases where custom JSON -serializers/deserializers need to be provided for specific types. +You can customize JSON mapping as needed by using Jackson's provided +annotations. When you need further control, you can inject a custom `ObjectMapper` +through the `ObjectMapper` property, for cases where you need to provide custom JSON +serializers and deserializers for specific types. [[mvc-view-xml-mapping]] -=== Jackson-based XML views -[.small]#<># +=== Jackson-based XML Views +[.small]#<># -The `MappingJackson2XmlView` uses the -https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML extension]'s `XmlMapper` -to render the response content as XML. If the model contains multiples entries, the -object to be serialized should be set explicitly using the `modelKey` bean property. -If the model contains a single entry, it will be serialized automatically. +`MappingJackson2XmlView` uses the +https://github.com/FasterXML/jackson-dataformat-xml[Jackson XML extension's] `XmlMapper` +to render the response content as XML. If the model contains multiples entries, you should explicitly set the +object to be serialized by using the `modelKey` bean property. +If the model contains a single entry, it is serialized automatically. -XML mapping can be customized as needed through the use of JAXB or Jackson's provided -annotations. When further control is needed, a custom `XmlMapper` can be injected -through the `ObjectMapper` property for cases where custom XML -serializers/deserializers need to be provided for specific types. +You can customized XML mapping as needed by using JAXB or Jackson's provided +annotations. When you need further control, you can inject a custom `XmlMapper` +through the `ObjectMapper` property, for cases where custom XML +you need to provide serializers and deserializers for specific types. [[mvc-view-xml-marshalling]] -== XML marshalling +== XML Marshalling -The `MarshallingView` uses an XML `Marshaller` defined in the `org.springframework.oxm` -package to render the response content as XML. The object to be marshalled can be set -explicitly using ``MarshallingView``'s `modelKey` bean property. Alternatively, the view -will iterate over all model properties and marshal the first type that is supported +The `MarshallingView` uses an XML `Marshaller` (defined in the `org.springframework.oxm` +package) to render the response content as XML. You can explicitly set the object to be marshalled +by using a `MarshallingView` instance's `modelKey` bean property. Alternatively, the view +iterates over all model properties and marshals the first type that is supported by the `Marshaller`. For more information on the functionality in the -`org.springframework.oxm` package refer to the chapter +`org.springframework.oxm` package, see <>. - [[mvc-view-xslt]] -== XSLT views +== XSLT Views XSLT is a transformation language for XML and is popular as a view technology within web applications. XSLT can be a good choice as a view technology if your application -naturally deals with XML, or if your model can easily be converted to XML. The following +naturally deals with XML or if your model can easily be converted to XML. The following section shows how to produce an XML document as model data and have it transformed with XSLT in a Spring Web MVC application. This example is a trivial Spring application that creates a list of words in the -`Controller` and adds them to the model map. The map is returned along with the view +`Controller` and adds them to the model map. The map is returned, along with the view name of our XSLT view. See <> for details of Spring Web MVC's -`Controller` interface. The XSLT Controller will turn the list of words into a simple XML +`Controller` interface. The XSLT controller turns the list of words into a simple XML document ready for transformation. @@ -1963,7 +2100,9 @@ document ready for transformation. Configuration is standard for a simple Spring web application: The MVC configuration has to define an `XsltViewResolver` bean and regular MVC annotation configuration. +The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1981,17 +2120,19 @@ public class WebConfig implements WebMvcConfigurer { } } ---- - -And we need a Controller that encapsulates our word generation logic. +==== [[mvc-view-xslt-controllercode]] === Controller +We also need a Controller that encapsulates our word-generation logic. + The controller logic is encapsulated in a `@Controller` class, with the handler method being defined as follows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2016,29 +2157,30 @@ handler method being defined as follows: } } ---- +==== -So far we've only created a DOM document and added it to the Model map. Note that you +So far, we have only created a DOM document and added it to the Model map. Note that you can also load an XML file as a `Resource` and use it instead of a custom DOM document. -Of course, there are software packages available that will automatically 'domify' -an object graph, but within Spring, you have complete flexibility to create the DOM +There are software packages available that automatically 'domify' +an object graph, but, within Spring, you have complete flexibility to create the DOM from your model in any way you choose. This prevents the transformation of XML playing -too great a part in the structure of your model data which is a danger when using tools -to manage the domification process. - -Next, `XsltViewResolver` will resolve the "home" XSLT template file and merge the -DOM document into it to generate our view. +too great a part in the structure of your model data, which is a danger when using tools +to manage the DOMification process. [[mvc-view-xslt-transforming]] === Transformation -Finally, the `XsltViewResolver` will resolve the "home" XSLT template file and merge the +Finally, the `XsltViewResolver` resolves the "`home`" XSLT template file and merges the DOM document into it to generate our view. As shown in the `XsltViewResolver` -configuration, XSLT templates live in the war file in the `'WEB-INF/xsl'` directory -and end with a `"xslt"` file extension. +configuration, XSLT templates live in the `war` file in the `WEB-INF/xsl` directory +and end with an `xslt` file extension. +The following example shows an XSLT transform: + +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -2065,9 +2207,11 @@ and end with a `"xslt"` file extension. ---- +==== -This is rendered as: +The preceding transform is rendered as the following HTML: +==== [source,html,indent=0] [subs="verbatim,quotes"] ---- @@ -2086,3 +2230,4 @@ This is rendered as: ---- +==== diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index 3b0ea7e2bf9..ab1d3fbbfc2 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -2,48 +2,43 @@ = 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" +Spring Web MVC is the original web framework built on the Servlet API and has been included +in the Spring Framework from the very beginning. The formal name, "`Spring Web MVC,`" comes from the name of its source module -https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc[spring-webmvc] -but it is more commonly known as "Spring MVC". +(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]. +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 <> covers Spring WebFlux. For baseline information and compatibility with Servlet container and Java EE version -ranges please visit the Spring Framework +ranges, see the Spring Framework https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions[Wiki]. [[mvc-servlet]] == DispatcherServlet -[.small]#<># +[.small]#<># -Spring MVC, like many other web frameworks, is designed around the front controller +Spring MVC, as many other web frameworks, is designed around the front controller pattern where a central `Servlet`, the `DispatcherServlet`, provides a shared algorithm -for request processing while actual work is performed by configurable, delegate components. +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 +to the Servlet specification by using Java configuration or in `web.xml`. +In turn, the `DispatcherServlet` uses Spring configuration to discover the delegate components it needs for request mapping, view resolution, exception handling, <>. -Below is an example of the Java configuration that registers and initializes -the `DispatcherServlet`. This class is auto-detected by the Servlet container +The following example of the Java configuration registers and initializes +the `DispatcherServlet`, which is auto-detected by the Servlet container (see <>): +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -65,16 +60,15 @@ the `DispatcherServlet`. This class is auto-detected by the Servlet container } } ---- - -[NOTE] ==== -In addition to using the ServletContext API directly, you can also extend + +NOTE: In addition to using the ServletContext API directly, you can also extend `AbstractAnnotationConfigDispatcherServletInitializer` and override specific methods -(see example under <>). -==== +(see the example under <>). -Below is an example of `web.xml` configuration to register and initialize the `DispatcherServlet`: +The following example of `web.xml` configuration registers and initializes the `DispatcherServlet`: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -106,44 +100,44 @@ Below is an example of `web.xml` configuration to register and initialize the `D ---- - -[NOTE] ==== -Spring Boot follows a different initialization sequence. Rather than hooking into + +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]. -==== +For more details, see the +https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container[Spring Boot documentation]. [[mvc-servlet-context-hierarchy]] === Context Hierarchy -`DispatcherServlet` expects a `WebApplicationContext`, an extension of a plain -`ApplicationContext`, for its own configuration. `WebApplicationContext` has a link to the -`ServletContext` and `Servlet` it is associated with. It is also bound to the `ServletContext` +`DispatcherServlet` expects a `WebApplicationContext` (an extension of a plain +`ApplicationContext`) for its own configuration. `WebApplicationContext` has a link to the +`ServletContext` and the `Servlet` with which it is associated. It is also bound to the `ServletContext` such that applications can use static methods on `RequestContextUtils` to look up the `WebApplicationContext` if they need access to it. -For many applications having a single `WebApplicationContext` is simple and sufficient. +For many applications, having a single `WebApplicationContext` is simple and suffices. It is also possible to have a context hierarchy where one root `WebApplicationContext` is shared across multiple `DispatcherServlet` (or other `Servlet`) instances, each with its own child `WebApplicationContext` configuration. -See <> +See <> for more on the context hierarchy feature. -The root `WebApplicationContext` typically contains infrastructure beans such as data repositories and +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`: +are effectively inherited and can be overridden (that is, re-declared) in the Servlet-specific +child `WebApplicationContext`, which typically contains beans local to the given `Servlet`. +The following image shows this relationship: image::images/mvc-context-hierarchy.png[] -Below is example configuration with a `WebApplicationContext` hierarchy: +The following example configures a `WebApplicationContext` hierarchy: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -165,15 +159,14 @@ Below is example configuration with a `WebApplicationContext` hierarchy: } } ---- - -[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: +TIP: If an application context hierarchy is not required, applications can return all +configuration through `getRootConfigClasses()` and `null` from `getServletConfigClasses()`. + +The following example shows the `web.xml` equivalent: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -205,69 +198,67 @@ And the `web.xml` equivalent: ---- - -[TIP] -==== -If an application context hierarchy is not required, applications may configure a -"root" context only and leave the `contextConfigLocation` Servlet parameter empty. ==== +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]#<># +[.small]#<># The `DispatcherServlet` delegates to special beans to process requests and render the -appropriate responses. By "special beans" we mean Spring-managed, Object instances that -implement WebFlux framework contracts. Those usually come with built-in contracts but -you can customize their properties, extend or replace them. +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 and extend or replace them. -The table below lists the special beans detected by the `DispatcherHandler`: +The following table lists the special beans detected by the `DispatcherHandler`: [[mvc-webappctx-special-beans-tbl]] [cols="1,2", options="header"] |=== | Bean type| Explanation -| <> +| <> | Map a request to a handler along with a list of <> for pre- and post-processing. - The mapping is based on some criteria the details of which vary by `HandlerMapping` + 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. + 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 +| `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. -| <> -| Strategy to resolve exceptions possibly mapping them to handlers, or to HTML error - views, or other. See <>. +| <> +| Strategy to resolve exceptions, possibly mapping them to handlers, to HTML error + views, or other targets. See <>. -| <> -| Resolve logical String-based view names returned from a handler to an actual `View` - to render to the response with. See <> and <>. +| <> +| Resolve logical `String`-based view names returned from a handler to an actual `View` + with which to render to the response. See <> and <>. -| <>, <> +| <>, <> | Resolve the `Locale` a client is using and possibly their time zone, in order to be able to offer internationalized views. See <>. -| <> -| Resolve themes your web application can use, for example, to offer personalized layouts. +| <> +| Resolve themes your web application can use -- for example, to offer personalized layouts. See <>. -| <> -| Abstraction for parsing a multi-part request (e.g. browser form file upload) with +| <> +| Abstraction for parsing a multi-part request (for example, browser form file upload) with the help of some multipart parsing library. See <>. -| <> -| Store and retrieve the "input" and the "output" `FlashMap` that can be used to pass +| <> +| 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 <>. |=== @@ -275,33 +266,31 @@ The table below lists the special beans detected by the `DispatcherHandler`: [[mvc-servlet-config]] === Web MVC Config -[.small]#<># +[.small]#<># Applications can declare the infrastructure beans listed in <> 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]. +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 <> is the best starting point. It declares the required -beans in either Java or XML, and provides a higher level configuration callback API to +In most cases, the <> 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 +NOTE: Spring Boot relies on the MVC Java configuration to configure Spring MVC and provides many extra convenient options. -==== [[mvc-container-config]] -=== Servlet Config +=== Servlet Configuration 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`: +programmatically as an alternative or in combination with a `web.xml` file. The following +example registers a `DispatcherServlet`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -320,16 +309,19 @@ example of registering a `DispatcherServlet`: } } ---- +==== `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 +`DispatcherServlet` by overriding methods to specify the servlet mapping and the location of the `DispatcherServlet` configuration. -This is recommended for applications that use Java-based Spring configuration: +This is recommended for applications that use Java-based Spring configuration, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -351,10 +343,12 @@ This is recommended for applications that use Java-based Spring configuration: } } ---- +==== -If using XML-based Spring configuration, you should extend directly from -`AbstractDispatcherServletInitializer`: +If you use XML-based Spring configuration, you should extend directly from +`AbstractDispatcherServletInitializer`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -378,10 +372,13 @@ If using XML-based Spring configuration, you should extend directly from } } ---- +==== `AbstractDispatcherServletInitializer` also provides a convenient way to add `Filter` -instances and have them automatically mapped to the `DispatcherServlet`: +instances and have them be automatically mapped to the `DispatcherServlet`, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -396,42 +393,44 @@ instances and have them automatically mapped to the `DispatcherServlet`: } } ---- +==== 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`. +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]#<># +[.small]#<># 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 + under the `DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE` key. +* The locale resolver is bound to the request to let elements in the process resolve the locale to use when processing the request (rendering the view, preparing - data, and so on). If you do not need locale resolving, you do not need it. + data, and so on). If you do not need locale resolving, you do not need the locale resolver. * The theme resolver is bound to the request to let elements such as views determine which theme to use. If you do not use themes, you can ignore it. -* If you specify a multipart file resolver, the request is inspected for multiparts; if +* 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 <> 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 + executed in order to prepare a model or rendering. Alternatively, for annotated + controllers, the response can be rendered (within the `HandlerAdapter`) instead of returning a view. -* If a model is returned, the view is rendered. If no model is returned, (may be due to +* If a model is returned, the view is rendered. If no model is returned (maybe due to a preprocessor or postprocessor intercepting the request, perhaps for security reasons), no view is rendered, because the request could already have been fulfilled. @@ -440,16 +439,16 @@ resolve exceptions thrown during request processing. Those exception resolvers a customizing the logic to address exceptions. See <> 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 +`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 +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. +initialization parameters (`init-param` elements) to the Servlet declaration in the +`web.xml` file. The following table lists the supported parameters: [[mvc-disp-servlet-init-params-tbl]] .DispatcherServlet initialization parameters @@ -458,12 +457,12 @@ initialization parameters ( `init-param` elements) to the Servlet declaration in | `contextClass` | Class that implements `WebApplicationContext`, which instantiates the context used by - this Servlet. By default, the `XmlWebApplicationContext` is used. + this Servlet. By default, `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 + indicate where contexts can be found. The string consists potentially of multiple + strings (using a comma as a delimiter) to support multiple contexts. In the case of multiple context locations with beans that are defined twice, the latest location takes precedence. @@ -472,15 +471,15 @@ initialization parameters ( `init-param` elements) to the Servlet declaration in | `throwExceptionIfNoHandlerFound` | Whether to throw a `NoHandlerFoundException` when no handler was found for a request. - The exception can then be caught with a `HandlerExceptionResolver`, e.g. via an - `@ExceptionHandler` controller method, and handled as any others. + The exception can then be caught with a `HandlerExceptionResolver` (for example, by using an + `@ExceptionHandler` controller method) and handled as any others. - By default this is set to "false", in which case the `DispatcherServlet` sets the + By default, this is set to `false`, in which case the `DispatcherServlet` sets the response status to 404 (NOT_FOUND) without raising an exception. - Note that if <> is - also configured, then unresolved requests are always forwarded to the default servlet - and a 404 would never be raised. + Note that, if <> is + also configured, unresolved requests are always forwarded to the default servlet + and a 404 is never raised. |=== @@ -488,31 +487,31 @@ initialization parameters ( `init-param` elements) to the Servlet declaration in [[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 +All `HandlerMapping` implementations support handler interceptors that are useful when +you want to apply specific functionality to certain requests -- for example, checking for a principal. Interceptors must implement `HandlerInterceptor` from the `org.springframework.web.servlet` package with three methods that should provide enough flexibility to do all kinds of pre-processing and post-processing: -* `preHandle(..)` -- __before__ the actual handler is executed -* `postHandle(..)` -- __after__ the handler is executed -* `afterCompletion(..)` -- __after the complete request has finished__ +* `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` +handler execution chain continues. When it returns false, the `DispatcherServlet` assumes the interceptor itself has taken care of requests (and, for example, rendered an appropriate view) and does not continue executing the other interceptors and the actual handler in the execution chain. See <> in the section on MVC configuration for examples of how to -configure interceptors. You can also register them directly via setters on individual +configure interceptors. You can also register them directly by using setters on individual `HandlerMapping` implementations. Note that `postHandle` is less useful with `@ResponseBody` and `ResponseEntity` methods for which 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 +`postHandle`. That means it is too late to make any changes to the response, such as adding +an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either declare it as an <> bean or configure it directly on `RequestMappingHandlerAdapter`. @@ -520,50 +519,50 @@ declare it as an <> bean or configure it directly on [[mvc-exceptionhandlers]] === Exceptions -[.small]#<># +[.small]#<># -If an exception occurs during request mapping or is thrown from a request handler such as -an `@Controller`, the `DispatcherServlet` delegates to a chain of `HandlerExceptionResolver` -beans to resolve the exception and provide alternative handling, which typically is an +If an exception occurs during request mapping or is thrown from a request handler (such as +a `@Controller`), the `DispatcherServlet` delegates to a chain of `HandlerExceptionResolver` +beans to resolve the exception and provide alternative handling, which is typically an error response. -The table below lists the available `HandlerExceptionResolver` implementations: +The following table lists the available `HandlerExceptionResolver` implementations: [cols="1,2", options="header"] .HandlerExceptionResolver implementations |=== -| HandlerExceptionResolver | Description +| `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] +| {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 <>. + See also alternative `ResponseEntityExceptionHandler` and <>. | `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 +| Resolves exceptions by invoking an `@ExceptionHandler` method in a `@Controller` or a `@ControllerAdvice` class. See <>. |=== [[mvc-excetionhandlers-handling]] -==== Chain of resolvers +==== Chain of Resolvers -You can form an exception resolver chain simply by declaring multiple `HandlerExceptionResolver` +You can form an exception resolver chain by declaring multiple `HandlerExceptionResolver` beans in your Spring configuration and setting their `order` properties as needed. The higher the order property, the later the exception resolver is positioned. The contract of `HandlerExceptionResolver` specifies that it can return: -* `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; and if the +* a `ModelAndView` that points to an error view. +* An empty `ModelAndView` if the exception was handled within the resolver. +* `null` if the exception remains unresolved, for subsequent resolvers to try, and, if the exception remains at the end, it is allowed to bubble up to the Servlet container. The <> automatically declares built-in resolvers for default Spring MVC @@ -571,14 +570,17 @@ 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 +==== 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`: +If an exception remains unresolved by any `HandlerExceptionResolver` and is, therefore, +left to propagate or if the response status is set to an error status (that is, 4xx, 5xx), +Servlet containers ca render a default error page in HTML. To customize the default +error page of the container, you can declare an error page mapping in `web.xml`. +The following example shows how to do so: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -586,13 +588,15 @@ error page of the container, you can declare an error page mapping in `web.xml`: /error ---- +==== -Given the above, when an exception bubbles up, or the response has an error status, the +Given the preceding example, when an exception bubbles up or the response has an error status, the Servlet container makes an ERROR dispatch within the container to the configured URL -(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: +(for example, `/error`). This is then processed by the `DispatcherServlet`, possibly mapping it +to a `@Controller`, which could be implemented to return an error view name with a model +or to render a JSON response, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -608,26 +612,23 @@ or to render a JSON response as shown below: } } ---- - -[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`. ==== +TIP: The Servlet API does not provide a way to create error page mappings in Java. You can, +however, use both a `WebApplicationInitializer` and a minimal `web.xml`. [[mvc-viewresolver]] === View Resolution -[.small]#<># +[.small]#<># -Spring MVC defines the `ViewResolver` and `View` interfaces that enable you to render +Spring MVC defines the `ViewResolver` and `View` interfaces that let you render models in a browser without tying you to a specific view technology. `ViewResolver` provides a mapping between view names and actual views. `View` addresses the preparation of data before handing over to a specific view technology. -The table below provides more details on the `ViewResolver` hierarchy: +The following table provides more details on the `ViewResolver` hierarchy: [[mvc-view-resolvers-tbl]] .ViewResolver implementations @@ -636,9 +637,9 @@ The table below provides more details on the `ViewResolver` hierarchy: | `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 + Caching improves performance of certain view technologies. You can turn off the cache by setting the `cache` property to `false`. Furthermore, if you must refresh a - certain view at runtime (for example when a FreeMarker template is modified), you can use + certain view at runtime (for example, when a FreeMarker template is modified), you can use the `removeFromCache(String viewName, Locale loc)` method. | `XmlViewResolver` @@ -648,14 +649,14 @@ The table below provides more details on the `ViewResolver` hierarchy: | `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 + specified by the bundle base name. 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 + property `[viewname].url` as the view URL. You can find examples in the chapter on <>. | `UrlBasedViewResolver` -| Simple implementation of the `ViewResolver` interface that effects the direct - resolution of logical view names to URLs, without an explicit mapping definition. This +| Simple implementation of the `ViewResolver` interface that affects 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. @@ -663,7 +664,7 @@ The table below provides more details on the `ViewResolver` hierarchy: | 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. + `setViewClass(..)`. See the {api-spring-framework}/web/reactive/result/view/UrlBasedViewResolver.html[`UrlBasedViewResolver` Javadoc] for details. | `FreeMarkerViewResolver` | Convenient subclass of `UrlBasedViewResolver` that supports `FreeMarkerView` and @@ -677,75 +678,78 @@ The table below provides more details on the `ViewResolver` hierarchy: [[mvc-viewresolver-handling]] ==== Handling -[.small]#<># +[.small]#<># -You chain view resolvers by declaring more than one resolver beans and, if necessary, by +You can chain view resolvers by declaring more than one resolver bean and, if necessary, by setting the `order` property to specify ordering. Remember, the higher the order property, the later the view resolver is positioned in the chain. -The contract of a `ViewResolver` specifies that it __can__ return null to indicate the -view could not be found. However in the case of JSPs, and `InternalResourceViewResolver`, +The contract of a `ViewResolver` specifies that it can return null to indicate that the +view could not be found. However, in the case of JSPs and `InternalResourceViewResolver`, the only way to figure out if a JSP exists is to perform a dispatch through -`RequestDispatcher`. Therefore an `InternalResourceViewResolver` must always be configured +`RequestDispatcher`. Therefore, you must always configure an `InternalResourceViewResolver` 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 <> provides provides a dedicated configuration API for -<> and also for adding logic-less +Configuring view resolution is as simple as adding `ViewResolver` beans to your Spring +configuration. The <> provides a dedicated configuration API for +<> and for adding logic-less <> which are useful for HTML template rendering without controller logic. + [[mvc-redirecting-redirect-prefix]] ==== Redirecting -[.small]#<># +[.small]#<># -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 +The special `redirect:` prefix in a view name lets you perform a redirect. The +`UrlBasedViewResolver` (and its subclasses) recognize this as an instruction that a redirect is needed. The rest of the view name is the redirect URL. The net effect is the same as if the controller had returned a `RedirectView`, but now -the controller itself can simply operate in terms of logical view names. A logical view -name such as `redirect:/myapp/some/resource` will redirect relative to the current +the controller itself can operate in terms of logical view names. A logical view +name (such as `redirect:/myapp/some/resource`) redirects relative to the current Servlet context, while a name such as `redirect:http://myhost.com/some/arbitrary/path` -will redirect to an absolute URL. +redirects to an absolute URL. -Note that if a controller method is annotated with the `@ResponseStatus`, the annotation +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 +You can also use a special `forward:` prefix for view names that are ultimately resolved by `UrlBasedViewResolver` and subclasses. This creates an -`InternalResourceView` which does a `RequestDispatcher.forward()`. +`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 +`InternalResourceView` (for JSPs), but it can be helpful if you use another view +technology but still want to force a forward of a resource to be handled by the Servlet/JSP engine. Note that you may also chain multiple view resolvers, instead. + [[mvc-multiple-representations]] -==== Content negotiation -[.small]#<># +==== Content Negotiation +[.small]#<># -{api-spring-framework}/web/servlet/view/ContentNegotiatingViewResolver.html[ContentNegotiatingViewResolver] +{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 +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"`. +query parameter (for example, `"/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 +by comparing the request media types with the media type (also known as `Content-Type`) supported by the `View` associated with each of its `ViewResolvers`. The first `View` in the list that has a compatible `Content-Type` returns the representation -to the client. If a compatible view cannot be supplied by the `ViewResolver` chain, then -the list of views specified through the `DefaultViews` property will be consulted. This +to the client. If a compatible view cannot be supplied by the `ViewResolver` chain, +the list of views specified through the `DefaultViews` property is consulted. This latter option is appropriate for singleton `Views` that can render an appropriate representation of the current resource regardless of the logical view name. The `Accept` -header may include wild cards, for example `text/{asterisk}`, in which case a `View` whose -Content-Type was `text/xml` is a compatible match. +header can include wildcards (for example `text/{asterisk}`), in which case a `View` whose +`Content-Type` is `text/xml` is a compatible match. See <> under <> for configuration details. @@ -754,55 +758,65 @@ See <> under <> 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. +Most parts of Spring's architecture support internationalization, as the Spring web +MVC framework does. `DispatcherServlet` lets you automatically resolve messages +by using the client's locale. This is done with `LocaleResolver` objects. -When a request comes in, the `DispatcherServlet` looks for a locale resolver, and if it -finds one it tries to use it to set the locale. Using the `RequestContext.getLocale()` +When a request comes in, the `DispatcherServlet` looks for a locale resolver and, if it +finds one, it tries to use it to set the locale. By using the `RequestContext.getLocale()` method, you can always retrieve the locale that was resolved by the locale resolver. In addition to automatic locale resolution, you can also attach an interceptor to the handler mapping (see <> for more information on handler -mapping interceptors) to change the locale under specific circumstances, for example, -based on a parameter in the request. +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 +context in the normal way. The following selection of locale resolvers is included in Spring. +* <> +* <> +* <> +* <> +* <> + + [[mvc-timezone]] -==== TimeZone +==== Time Zone -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. +In addition to obtaining the client's locale, it is often useful to know its time zone. +The `LocaleContextResolver` interface offers an extension to `LocaleResolver` that lets +resolvers provide a richer `LocaleContext`, which may include time zone information. -When available, the user's `TimeZone` can be obtained using the -`RequestContext.getTimeZone()` method. Time zone information will automatically be used -by Date/Time `Converter` and `Formatter` objects registered with Spring's +When available, the user's `TimeZone` can be obtained by using the +`RequestContext.getTimeZone()` method. Time zone information is automatically used +by any Date/Time `Converter` and `Formatter` objects that are registered with Spring's `ConversionService`. + [[mvc-localeresolver-acceptheader]] -==== Header resolver +==== 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.__ +by the client (for example, a web browser). Usually, this header field contains the locale of +the client's operating system. Note that this resolver does not support time zone +information. + [[mvc-localeresolver-cookie]] -==== Cookie resolver +==== 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 +`Locale` or `TimeZone` is specified. If so, it uses the specified details. By using the properties of this locale resolver, you can specify the name of the cookie as well as the -maximum age. Find below an example of defining a `CookieLocaleResolver`. +maximum age. The following example defines a `CookieLocaleResolver`: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -815,6 +829,9 @@ maximum age. Find below an example of defining a `CookieLocaleResolver`. ---- +==== + +The following table describes the properties `CookieLocaleResolver`: [[mvc-cookie-locale-resolver-props-tbl]] .CookieLocaleResolver properties @@ -822,48 +839,51 @@ maximum age. Find below an example of defining a `CookieLocaleResolver`. |=== | Property | Default | Description -| cookieName +| `cookieName` | classname + LOCALE | The name of the cookie -| cookieMaxAge +| `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. +| The maximum time a cookie persists on the client. If `-1` is specified, the + cookie will not be persisted. It is available only until the client shuts down + the browser. -| cookiePath +| `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. +| Limits the visibility of the cookie to a certain part of your site. When `cookiePath` is + specified, the cookie is visible only to that path and the paths below it. |=== + [[mvc-localeresolver-session]] -==== Session resolver +==== Session Resolver -The `SessionLocaleResolver` allows you to retrieve `Locale` and `TimeZone` from the +The `SessionLocaleResolver` lets you retrieve `Locale` and `TimeZone` from the session that might be associated with the user's request. In contrast to `CookieLocaleResolver`, this strategy stores locally chosen locale settings in the -Servlet container's `HttpSession`. As a consequence, those settings are just temporary -for each session and therefore lost when each session terminates. +Servlet container's `HttpSession`. As a consequence, those settings are temporary +for each session and are, 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` evaluates and +modifies the corresponding `HttpSession` attributes against the current `HttpServletRequest`. -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 +==== Locale Interceptor You can enable changing of locales by adding the `LocaleChangeInterceptor` to one of the -handler mappings (see <>). 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. +handler mappings (see <>). It detects a parameter in the request +and changes the locale. It calls `setLocale()` on the `LocaleResolver` that also exists +in the context. The next example shows that calls to all `{asterisk}.view` resources +that contain a parameter named `siteLanguage` now changes the locale. So, for example, +a request for the URL, `http://www.sf.net/home.view?siteLanguage=nl`, +changes the site language to Dutch. The following example shows how to intercept the locale: +==== [source,xml,indent=0] [subs="verbatim"] ---- @@ -887,6 +907,7 @@ change the site language to Dutch. ---- +==== @@ -899,34 +920,38 @@ resources, typically style sheets and images, that affect the visual style of th application. + [[mvc-themeresolver-defining]] -==== Define a theme +==== Defining a theme To use themes in your web application, you must set up an implementation of the `org.springframework.ui.context.ThemeSource` interface. The `WebApplicationContext` interface extends `ThemeSource` but delegates its responsibilities to a dedicated -implementation. By default the delegate will be an +implementation. By default, the delegate is an `org.springframework.ui.context.support.ResourceBundleThemeSource` implementation that loads properties files from the root of the classpath. To use a custom `ThemeSource` implementation or to configure the base name prefix of the `ResourceBundleThemeSource`, -you can register a bean in the application context with the reserved name `themeSource`. +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: +When you use the `ResourceBundleThemeSource`, a theme is defined in a simple properties +file. The properties file lists the resources that make up the theme, as the following example shows: +==== [literal] [subs="verbatim,quotes"] ---- styleSheet=/themes/cool/style.css background=/themes/cool/img/coolBg.jpg ---- +==== The keys of the properties are the names that refer to the themed elements from view code. For a JSP, you typically do this using the `spring:theme` custom tag, which is very similar to the `spring:message` tag. The following JSP fragment uses the theme defined in the previous example to customize the look and feel: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -940,24 +965,26 @@ defined in the previous example to customize the look and feel: ---- +==== 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 +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 +==== Resolving 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 +After you define themes, as described in the <>, you decide which theme to use. The +`DispatcherServlet` looks for a bean named `themeResolver` to find out which `ThemeResolver` implementation to use. A theme resolver works in much the same way as a `LocaleResolver`. It detects the theme to use for a particular request and can also -alter the request's theme. The following theme resolvers are provided by Spring: +alter the request's theme. The following table describes the theme resolvers provided by Spring: [[mvc-theme-resolver-impls-tbl]] .ThemeResolver implementations @@ -966,54 +993,59 @@ alter the request's theme. The following theme resolvers are provided by Spring: | Class | Description | `FixedThemeResolver` -| Selects a fixed theme, set using the `defaultThemeName` property. +| Selects a fixed theme, set by 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. +| The theme is maintained in the user's HTTP session. It needs to be set only once for + each session but is not persisted between sessions. | `CookieThemeResolver` | The selected theme is stored in a cookie on the client. |=== -Spring also provides a `ThemeChangeInterceptor` that allows theme changes on every +Spring also provides a `ThemeChangeInterceptor` that lets theme changes on every request with a simple request parameter. [[mvc-multipart]] -=== Multipart resolver -[.small]#<># +=== Multipart Resolver +[.small]#<># `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 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 +To enable multipart handling, you need to declare a `MultipartResolver` bean in your +`DispatcherServlet` Spring configuration with a name of `multipartResolver`. +The `DispatcherServlet` detects it and applies it to the incoming request. When a POST with +content-type of `multipart/form-data` is received, the resolver parses the content and +wraps the current `HttpServletRequest` as `MultipartHttpServletRequest` to provide access to resolved parts in addition to exposing them as request parameters. + [[mvc-multipart-resolver-commons]] -==== Apache FileUpload +==== Apache `FileUpload` -To use Apache Commons FileUpload, simply configure a bean of type -`CommonsMultipartResolver` with the name `multipartResolver`. Of course you also need to +To use Apache Commons `FileUpload`, you can configure a bean of type +`CommonsMultipartResolver` with a name of `multipartResolver`. You also need to have `commons-fileupload` as a dependency on your classpath. [[mvc-multipart-resolver-standard]] ==== Servlet 3.0 -Servlet 3.0 multipart parsing needs to be enabled through Servlet container configuration: +Servlet 3.0 multipart parsing needs to be enabled through Servlet container configuration. +To do so: + +* In Java, set a `MultipartConfigElement` on the Servlet registration. +* In `web.xml`, add a `""` section to the servlet declaration. -* in Java, set a `MultipartConfigElement` on the Servlet registration. -* in `web.xml`, add a `""` section to the servlet declaration. +The following example shows how to set a `MultipartConfigElement` on the Servlet registration: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1030,38 +1062,41 @@ Servlet 3.0 multipart parsing needs to be enabled through Servlet container conf } ---- +==== -Once the Servlet 3.0 configuration is in place, simply add a bean of type -`StandardServletMultipartResolver` with the name `multipartResolver`. +Once the Servlet 3.0 configuration is in place, you can add a bean of type +`StandardServletMultipartResolver` with a name of `multipartResolver`. [[mvc-logging]] === Logging -[.small]#<># +[.small]#<># -DEBUG level logging in Spring MVC is designed to be compact, minimal, and -human-friendly. It focuses on high value bits of information that are useful over and -over again vs others that are useful only when debugging a specific issue. +DEBUG-level logging in Spring MVC is designed to be compact, minimal, and +human-friendly. It focuses on high-value bits of information that are useful over and +over again versus others that are useful only when debugging a specific issue. -TRACE level logging generally follows the same principles as DEBUG (and for example also -should not be a firehose) but can be used for debugging any issue. In addition some log -messages may show a different level of detail at TRACE vs DEBUG. +TRACE-level logging generally follows the same principles as DEBUG (and, for example, also +should not be a fire hose) but can be used for debugging any issue. In addition, some log +messages may show a different level of detail at TRACE versus DEBUG. Good logging comes from the experience of using the logs. If you spot anything that does not meet the stated goals, please let us know. + [[mvc-logging-sensitive-data]] ==== Sensitive Data -[.small]#<># +[.small]#<># DEBUG and TRACE logging may log sensitive information. This is why request parameters and headers are masked by default and their logging in full must be enabled explicitly through the `enableLoggingRequestDetails` property on `DispatcherServlet`. -For example if using Java config: +The following example shows how to do so by using Java configuration: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1090,96 +1125,102 @@ public class MyInitializer } ---- - +==== [[filters]] == Filters -[.small]#<># +[.small]#<># + +The `spring-web` module provides some useful filters: -The `spring-web` module provides some useful filters. +* <> +* <> +* <> +* <> [[filters-http-put]] === Form Data -Browsers can only submit form data via HTTP GET or HTTP POST but non-browser clients can also +Browsers can submit form data only through HTTP GET or HTTP POST but non-browser clients can also use HTTP PUT, PATCH, and DELETE. The Servlet API requires `ServletRequest.getParameter{asterisk}()` methods to support form field access only for HTTP POST. -The `spring-web` module provides `FormContentFilter` that intercepts HTTP PUT, PATCH, and DELETE -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 +The `spring-web` module provides `FormContentFilter` to intercept HTTP PUT, PATCH, and DELETE +requests with a content type of `application/x-www-form-urlencoded`, read the form data from +the body of the request, and wrap the `ServletRequest` to make the form data available through the `ServletRequest.getParameter{asterisk}()` family of methods. [[filters-forwarded-headers]] === Forwarded Headers -[.small]#<># +[.small]#<># -As a request goes through proxies such as load balancers the host, port, and -scheme may change and that makes it a challenge to create links that point to the correct +As a request goes through proxies (such as load balancers) the host, port, and +scheme may change, and that makes it a challenge to create links that point to the correct host, port, and scheme from a client perspective. -https://tools.ietf.org/html/rfc7239[RFC 7239] defines the "Forwarded" HTTP header +https://tools.ietf.org/html/rfc7239[RFC 7239] defines the `Forwarded` HTTP header that proxies can use to provide information about the original request. There are other -non-standard headers too including "X-Forwarded-Host", "X-Forwarded-Port", -"X-Forwarded-Proto", "X-Forwarded-Ssl", and "X-Forwarded-Prefix". +non-standard headers, too, including `X-Forwarded-Host`, `X-Forwarded-Port`, +`X-Forwarded-Proto`, `X-Forwarded-Ssl`, and `X-Forwarded-Prefix`. `ForwardedHeaderFilter` is a Servlet filter that modifies the host, port, and scheme of -the request, based on Forwarded headers, and then removes those headers. +the request, based on `Forwarded` headers, and then removes those headers. -There are security considerations for forwarded headers since an application can't know -if the headers were added by a proxy as intended, or with a malicious client. This is why -a proxy at the boundary of trust should be configured to remove untrusted Forwarded coming +There are security considerations for forwarded headers since an application cannot know +if the headers were added by a proxy, as intended, or by a malicious client. This is why +a proxy at the boundary of trust should be configured to remove untrusted `Forwarded` headers that come from the outside. You can also configure the `ForwardedHeaderFilter` with -`removeOnly=true` in which case it will remove but not use the headers. +`removeOnly=true`, in which case it removes but does not use the headers. [[filters-shallow-etag]] === Shallow ETag -The `ShallowEtagHeaderFilter` filter creates a "shallow" ETag by caching the content -written to the response, and computing an MD5 hash from it. The next time a client sends, -it does the same, but also compares the computed value against the `If-None-Match` request -header and if the two are equal, it returns a 304 (NOT_MODIFIED). +The `ShallowEtagHeaderFilter` filter creates a "`shallow`" ETag by caching the content +written to the response and computing an MD5 hash from it. The next time a client sends, +it does the same, but it also compares the computed value against the `If-None-Match` request +header and, if the two are equal, returns a 304 (NOT_MODIFIED). This strategy saves network bandwidth but not CPU, as the full response must be computed -for each request. Other strategies at the controller level, described above, can avoid the +for each request. Other strategies at the controller level, described earlier, can avoid the computation. See <>. -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]. +This filter has a `writeWeakETag` parameter that configures the filter to write weak ETags +similar to the following: `W/"02a2d595e6ed9a0b24f027f2b63b134d6"` (as defined in +https://tools.ietf.org/html/rfc7232#section-2.3[RFC 7232 Section 2.3]). [[filters-cors]] === CORS -[.small]#<># +[.small]#<># 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 +controllers. However, when used with Spring Security, we advise relying on the built-in `CorsFilter` that must be ordered ahead of Spring Security's chain of filters. -See the section on <> and the <> for more details. - +See the sections on <> and the <> for more details. [[mvc-controller]] == Annotated Controllers -[.small]#<># +[.small]#<># Spring MVC provides an annotation-based programming model where `@Controller` and `@RestController` components use annotations to express request mappings, request input, exception handling, and more. Annotated controllers have flexible method signatures and do not have to extend base classes nor implement specific interfaces. +The following example shows a controller defined by annotations: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1193,31 +1234,30 @@ do not have to extend base classes nor implement specific interfaces. } } ---- +==== -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. +In the preceding example, the method accepts a `Model` and returns a view name as a `String`, +but many other options exist and are explained later in this chapter. -[TIP] -==== -Guides and tutorials on https://spring.io/guides[spring.io] use the annotation-based +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]#<># +[.small]#<># -You can define controller beans using a standard Spring bean definition in the +You can define controller beans by using a standard Spring bean definition in the Servlet's `WebApplicationContext`. The `@Controller` stereotype allows for auto-detection, aligned with Spring general support for detecting `@Component` classes in the classpath and auto-registering bean definitions for them. It also acts as a stereotype for the annotated class, indicating its role as a web component. To enable auto-detection of such `@Controller` beans, you can add component scanning to -your Java configuration: +your Java configuration, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1228,9 +1268,11 @@ your Java configuration: // ... } ---- +==== -The XML configuration equivalent: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -1251,51 +1293,54 @@ The XML configuration equivalent: ---- +==== `@RestController` is a <> 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. +itself meta-annotated with `@Controller` and `@ResponseBody` to indicate a controller whose +every method inherits the type-level `@ResponseBody` annotation and, therefore, writes +directly to the response body versus view resolution and rendering with an HTML template. + [[mvc-ann-requestmapping-proxying]] -==== AOP proxies +==== AOP Proxies -In some cases a controller may need to be decorated with an AOP proxy at runtime. +In some cases, you many need to decorate a controller with an AOP proxy at runtime. One example is if you choose to have `@Transactional` annotations directly on the controller. When this is the case, for controllers specifically, we recommend using class-based proxying. This is 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 ``, +However, if a controller must implement an interface that is not a Spring Context +callback (such as `InitializingBean`, `*Aware`, and others), you may need to explicitly +configure class-based proxying. For example, with ``, you can change to ``. [[mvc-ann-requestmapping]] === Request Mapping -[.small]#<># +[.small]#<># -The `@RequestMapping` annotation is used to map requests to controllers methods. It has +You can use the `@RequestMapping` annotation to map requests to controllers methods. It has various attributes to match by URL, HTTP method, request parameters, headers, and media -types. It can be used at the class-level to express shared mappings or at the method level +types. You can use it at the class level to express shared mappings or at the method level to narrow down to a specific endpoint mapping. There are also HTTP method specific shortcut variants of `@RequestMapping`: -- `@GetMapping` -- `@PostMapping` -- `@PutMapping` -- `@DeleteMapping` -- `@PatchMapping` +* `@GetMapping` +* `@PostMapping` +* `@PutMapping` +* `@DeleteMapping` +* `@PatchMapping` -The above are <> 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 +The shortcuts are <> that are provided +because, arguably, most controller methods should be mapped to a specific HTTP method versus +using `@RequestMapping`, which, by default, matches to all HTTP methods. At the same, a `@RequestMapping` is still needed at the class level to express shared mappings. -Below is an example with type and method level mappings: +The following example has type and method level mappings: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1315,21 +1360,25 @@ Below is an example with type and method level mappings: } } ---- +==== + [[mvc-ann-requestmapping-uri-templates]] ==== URI patterns -[.small]#<># -You can map requests using glob patterns and wildcards: +You can map requests by using the following 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`: +You can also declare URI variables and access their values with `@PathVariable`, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1338,8 +1387,11 @@ You can also declare URI variables and access their values with `@PathVariable`: // ... } ---- +==== -URI variables can be declared at the class and method level: +You can declare URI variables at the class and method levels, as the following example shows: + +==== [source,java,intent=0] [subs="verbatim,quotes"] ---- @@ -1353,20 +1405,22 @@ public class OwnerController { } } ---- +==== -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 +URI variables are automatically converted to the appropriate type, or `TypeMismatchException` +is raised. Simple types (`int`, `long`, `Date`, and so on) are supported by default and you can register support for any other data type. See <> and <>. -URI variables can be named explicitly -- e.g. `@PathVariable("customId")`, but you can +You can explicitly name URI variables (for example, `@PathVariable("customId")`), but you can leave that detail out if the names are the same and your code is compiled with debugging information or with the `-parameters` compiler flag on Java 8. -The syntax `{varName:regex}` declares a URI variable with a regular expressions with the -syntax `{varName:regex}` -- e.g. given URL `"/spring-web-3.0.5 .jar"`, the below method +The syntax `{varName:regex}` declares a URI variable with a regular expression that has +syntax of `{varName:regex}`. For example, given URL `"/spring-web-3.0.5 .jar"`, the following method extracts the name, version, and file extension: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1375,26 +1429,25 @@ extracts the name, version, and file extension: // ... } ---- +==== 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 +by using `PropertyPlaceHolderConfigurer` against local, system, environment, and other property +sources. You can use this, for example, to parameterize a base URL based on some external configuration. -[NOTE] -==== -Spring MVC uses the `PathMatcher` contract and the `AntPathMatcher` implementation from +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]#<># -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 +When multiple patterns match a URL, they must be compared to find the best match. This is done +by using `AntPathMatcher.getPatternComparator(String path)`, which looks for patterns that are more specific. A pattern is less specific if it has a lower count of URI variables and single wildcards @@ -1402,62 +1455,64 @@ counted as 1 and double wildcards counted as 2. Given an equal score, the longer 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. +The default mapping pattern (`/{asterisk}{asterisk}`) is excluded from scoring and always +sorted last. Also, prefix patterns (such as `/public/{asterisk}{asterisk}`) are considered less +specific than other pattern that do not have double wildcards. -For the full details see `AntPatternComparator` in `AntPathMatcher` and also keep mind that -the `PathMatcher` implementation used can be customized. See <> +For the full details, see {api-spring-framework}/util/AntPathMatcher.AntPatternComparator.html[`AntPatternComparator`] in {api-spring-framework}/util/AntPathMatcher.html[`AntPathMatcher`] and also keep mind that +you can customize the {api-spring-framework}/util/PathMatcher.html[`PathMatcher`] implementation. See <> in the configuration section. + [[mvc-ann-requestmapping-suffix-pattern-match]] -==== Suffix match +==== Suffix Match -By default Spring MVC performs `".{asterisk}"` suffix pattern matching so that a +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. +the response (that is, instead of the `Accept` header) -- for example, `/person.pdf`, +`/person.xml`, and others. -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. +Using file extensions in this way was necessary when browsers used to send `Accept` headers +that were hard to interpret consistently. At present, that is no longer a necessity and +using the `Accept` header should be the preferred choice. -Over time the use of file name extensions has proven problematic in a variety of ways. -It can cause ambiguity when 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). +Over time, the use of file name extensions has proven problematic in a variety of ways. +It can cause ambiguity when overlain with the use of URI variables, path parameters, and +URI encoding. Reasoning about URL-based authorization +and security (see next section for more details) also become more difficult. -To completely disable the use of file extensions, you must set both of these: +To completely disable the use of file extensions, you must set both of the following: * `useSuffixPatternMatching(false)`, see <> * `favorPathExtension(false)`, see <> -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 +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. Alternatively, if you must use file extensions, consider restricting them to a list of explicitly registered extensions through the `mediaTypes` property of <>. + [[mvc-ann-requestmapping-rfd]] -==== Suffix match and 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 +A reflected file download (RFD) attack is similar to XSS in that it relies on request input +(for example, a query parameter and a URI variable) being reflected in the response. However, instead of inserting JavaScript into HTML, an RFD attack relies on the browser switching to perform a download and treating the response as an executable script when double-clicked later. -In Spring MVC `@ResponseBody` and `ResponseEntity` methods are at risk because -they can render different content types which clients can request via URL path extensions. -Disabling suffix pattern matching and the use of path extensions for content negotiation +In Spring MVC, `@ResponseBody` and `ResponseEntity` methods are at risk, because +they can render different content types, which clients can request through URL path extensions. +Disabling suffix pattern matching and using path extensions for content negotiation lower the risk but are not sufficient to prevent RFD attacks. -To prevent RFD attacks, prior to rendering the response body Spring MVC adds a +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 +nor explicitly registered for content negotiation. However, it can potentially have side effects when URLs are typed directly into a browser. Many common path extensions are whitelisted by default. Applications with custom @@ -1465,146 +1520,150 @@ Many common path extensions are whitelisted by default. Applications with custom negotiation to avoid having a `Content-Disposition` header added for those extensions. See <>. -Check http://pivotal.io/security/cve-2015-5211[CVE-2015-5211] for additional +See http://pivotal.io/security/cve-2015-5211[CVE-2015-5211] for additional recommendations related to RFD. - [[mvc-ann-requestmapping-consumes]] -==== Consumable media types -[.small]#<># +==== Consumable Media Types +[.small]#<># -You can narrow the request mapping based on the `Content-Type` of the request: +You can narrow the request mapping based on the `Content-Type` of the request, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- - @PostMapping(path = "/pets", **consumes = "application/json"**) + @PostMapping(path = "/pets", consumes = "application/json") <1> public void addPet(@RequestBody Pet pet) { // ... } ---- +<1> Using a `consumes` attribute to narrow the mapping by the content type. +==== -The consumes attribute also supports negation expressions -- e.g. `!text/plain` means any -content type other than "text/plain". +The `consumes` attribute also supports negation expressions -- for example, `!text/plain` means any +content type other than `text/plain`. -You can declare a shared consumes attribute at the class level. Unlike most other request -mapping attributes however when used at the class level, a method-level consumes attribute -will overrides rather than extend the class level declaration. +You can declare a shared `consumes` attribute at the class level. Unlike most other +request-mapping attributes, however, when used at the class level, a method-level `consumes` attribute +overrides rather than extends the class-level declaration. + +TIP: `MediaType` provides constants for commonly used media types, such as +`APPLICATION_JSON_VALUE` and `APPLICATION_XML_VALUE`. -[TIP] -==== -`MediaType` provides constants for commonly used media types -- e.g. -`APPLICATION_JSON_VALUE`, `APPLICATION_XML_VALUE`. -==== [[mvc-ann-requestmapping-produces]] -==== Producible media types -[.small]#<># +==== Producible Media Types +[.small]#<># You can narrow the request mapping based on the `Accept` request header and the list of -content types that a controller method produces: +content types that a controller method produces, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- - @GetMapping(path = "/pets/{petId}", **produces = "application/json;charset=UTF-8"**) + @GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8") <1> @ResponseBody public Pet getPet(@PathVariable String petId) { // ... } ---- +<1> Using a `produces` attribute to narrow the mapping by the content type. +==== -The media type can specify a character set. Negated expressions are supported -- e.g. +The media type can specify a character set. Negated expressions are supported -- for example, `!text/plain` means any content type other than "text/plain". -[NOTE] -==== -For JSON content type, the UTF-8 charset should be specified even if +NOTE: For the JSON content type, the UTF-8 charset should be specified even if https://tools.ietf.org/html/rfc7159#section-11[RFC7159] -clearly states that "no charset parameter is defined for this registration" because some -browsers require it for interpreting correctly UTF-8 special characters. -==== +clearly states that "`no charset parameter is defined for this registration`", because some +browsers require it to correctly interpret UTF-8 special characters. -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. +You can declare a shared `produces` attribute at the class level. Unlike most other +request-mapping attributes, however, when used at the class level, a method-level `produces` attribute +overrides rather than extends the class-level declaration. + +TIP: `MediaType` provides constants for commonly used media types, such as +`APPLICATION_JSON_UTF8_VALUE` and `APPLICATION_XML_VALUE`. -[TIP] -==== -`MediaType` provides constants for commonly used media types -- e.g. -`APPLICATION_JSON_UTF8_VALUE`, `APPLICATION_XML_VALUE`. -==== [[mvc-ann-requestmapping-params-and-headers]] ==== Parameters, headers -[.small]#<># 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"`): +presence of a request parameter (`myParam`), for the absence of one (`!myParam`), or for a +specific value (`myParam=myValue`). The following example shows how to test for a specific value: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- - @GetMapping(path = "/pets/{petId}", **params = "myParam=myValue"**) + @GetMapping(path = "/pets/{petId}", params = "myParam=myValue") <1> public void findPet(@PathVariable String petId) { // ... } ---- +<1> Testing whether `myParam` equals `myValue`. +==== -You can also use the same with request header conditions: +You can also use the same with request header conditions, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- - @GetMapping(path = "/pets", **headers = "myHeader=myValue"**) + @GetMapping(path = "/pets", headers = "myHeader=myValue") <1> public void findPet(@PathVariable String petId) { // ... } ---- - -[TIP] +<1> Testing whether `myHeader` equals `myValue`. ==== -You can match `Content-Type` and `Accept` with the headers condition but it is better to use + +TIP: You can match `Content-Type` and `Accept` with the headers condition, but it is better to use <> and <> instead. -==== + [[mvc-ann-requestmapping-head-options]] ==== HTTP HEAD, OPTIONS -[.small]#<># +[.small]#<># -`@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 `@RequestMapping(method=HttpMethod.GET)`) support HTTP HEAD +transparently for request mapping. Controller methods do not 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 (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. +`@GetMapping` (and `@RequestMapping(method=HttpMethod.GET)`) are implicitly mapped to +and support HTTP HEAD. An HTTP HEAD request is processed as if it were HTTP GET except +that, instead of writing the body, the number of bytes are counted and the `Content-Length` +header is set. -By default HTTP OPTIONS is handled by setting the "Allow" response header to the list of HTTP -methods listed in all `@RequestMapping` methods with matching URL patterns. +By default, HTTP OPTIONS is handled by setting the `Allow` response header to the list of HTTP +methods listed in all `@RequestMapping` methods that have matching URL patterns. -For a `@RequestMapping` without HTTP method declarations, the "Allow" header is set to -`"GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"`. Controller methods should always declare the -supported HTTP methods for example by using the HTTP method specific variants -- -`@GetMapping`, `@PostMapping`, etc. +For a `@RequestMapping` without HTTP method declarations, the `Allow` header is set to +`GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS`. Controller methods should always declare the +supported HTTP methods (for example, by using the HTTP method specific variants: +`@GetMapping`, `@PostMapping`, and others). -`@RequestMapping` method can be explicitly mapped to HTTP HEAD and HTTP OPTIONS, but that +You can explicitly map the `@RequestMapping` method to HTTP HEAD and HTTP OPTIONS, but that is not necessary in the common case. + [[mvc-ann-requestmapping-composed]] ==== Custom Annotations -[.small]#<># +[.small]#<># Spring MVC supports the use of <> for request mapping. Those are annotations that are themselves meta-annotated with @@ -1612,25 +1671,27 @@ for request mapping. Those are annotations that are themselves meta-annotated wi 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 +examples of composed annotations. They are provided because, arguably, most +controller methods should be mapped to a specific HTTP method versus using `@RequestMapping`, +which, by default, matches to all HTTP methods. If you need an example of composed annotations, look at how those are declared. -Spring MVC also supports custom request mapping attributes with custom request matching -logic. This is a more advanced option that requires sub-classing -`RequestMappingHandlerMapping` and overriding the `getCustomMethodCondition` method where +Spring MVC also supports custom request-mapping attributes with custom request-matching +logic. This is a more advanced option that requires subclassing +`RequestMappingHandlerMapping` and overriding the `getCustomMethodCondition` method, where you can check the custom attribute and return your own `RequestCondition`. + [[mvc-ann-requestmapping-registration]] ==== Explicit Registrations -[.small]#<># +[.small]#<># -Handler methods can be registered programmatically which can be used for dynamic -registrations, or for advanced cases such as different instances of the same handler -under different URLs. Below is an example: +You can programmatically register handler methods, which you can use for dynamic +registrations or for advanced cases, such as different instances of the same handler +under different URLs. The following example registers a handler method: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1652,64 +1713,66 @@ public class MyConfig { } ---- -<1> Inject target handler(s) and the handler mapping for controllers. +<1> Inject the target handler and the handler mapping for controllers. <2> Prepare the request mapping meta data. <3> Get the handler method. <4> Add the registration. +==== [[mvc-ann-methods]] === Handler Methods -[.small]#<># +[.small]#<># `@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]#<># +[.small]#<># -The table below shows supported controller method arguments. Reactive types are not supported +The next table describes the supported controller method arguments. Reactive types are not supported for any arguments. JDK 8's `java.util.Optional` is supported as a method argument in combination with -annotations that have a `required` attribute -- e.g. `@RequestParam`, `@RequestHeader`, -etc, and is equivalent to `required=false`. +annotations that have a `required` attribute (for example, `@RequestParam`, `@RequestHeader`, +and others) and is equivalent to `required=false`. [cols="1,2", options="header"] |=== | Controller method argument | Description | `WebRequest`, `NativeWebRequest` -| Generic access to request parameters, request & session attributes, without direct +| Generic access to request parameters and request and 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`, +| Choose any specific request or response type -- for example, `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. +| Enforces the presence of a session. As a consequence, such an argument is never `null`. + Note that session access is not thread-safe. Consider setting the + `RequestMappingHandlerAdapter` instance's `synchronizeOnSession` flag to `true` if multiple + requests are allowed to concurrently access a session. | `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 + Note that, per the Servlet specification, the injected `PushBuilder` instance can be null if the client does not support that HTTP/2 feature. | `java.security.Principal` -| Currently authenticated user; possibly a specific `Principal` implementation class if known. +| 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`. +| The current request locale, determined by the most specific `LocaleResolver` available (in + effect, the configured `LocaleResolver` or `LocaleContextResolver`). | `java.util.TimeZone` + `java.time.ZoneId` | The time zone associated with the current request, as determined by a `LocaleContextResolver`. @@ -1727,11 +1790,11 @@ etc, and is equivalent to `required=false`. | For access to name-value pairs in URI path segments. See <>. | `@RequestParam` -| For access to Servlet request parameters. Parameter values are converted to the declared +| For access to the Servlet request parameters. Parameter values are converted to the declared method argument type. See <>. - Note that use of `@RequestParam` is optional, e.g. to set its attributes. - See "Any other argument" further below in this table. + Note that use of `@RequestParam` is optional (for example, to set its attributes). + See "`Any other argument`", at the end of this table. | `@RequestHeader` | For access to request headers. Header values are converted to the declared method argument @@ -1743,14 +1806,14 @@ etc, and is equivalent to `required=false`. | `@RequestBody` | For access to the HTTP request body. Body content is converted to the declared method - argument type using ``HttpMessageConverter``s. See <>. + argument type by using `HttpMessageConverter` implementations. See <>. | `HttpEntity` -| For access to request headers and body. The body is converted with ``HttpMessageConverter``s. +| For access to request headers and body. The body is converted with `HttpMessageConverter` implementations. See <>. | `@RequestPart` -| For access to a part in a "multipart/form-data" request. +| For access to a part in a `multipart/form-data` request. See <>. | `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap` @@ -1758,8 +1821,8 @@ etc, and is equivalent to `required=false`. 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. +| Specify attributes to use in case of a redirect (that is, to be appended to the query + string) and flash attributes to be stored temporarily until the request after redirect. See <> and <>. | `@ModelAttribute` @@ -1767,17 +1830,17 @@ etc, and is equivalent to `required=false`. data binding and validation applied. See <> as well as <> and <>. - Note that use of `@ModelAttribute` is optional, e.g. to set its attributes. - See "Any other argument" further below in this table. + Note that use of `@ModelAttribute` is optional (for example, to set its attributes). + See "`Any other argument`" at the end of this table. | `Errors`, `BindingResult` | For access to errors from validation and data binding for a command object - (i.e. `@ModelAttribute` argument), or errors from the validation of an `@RequestBody` or - `@RequestPart` arguments; an `Errors`, or `BindingResult` argument must be declared + (that is, a `@ModelAttribute` argument) or errors from the validation of a `@RequestBody` or + `@RequestPart` arguments. You must declare an `Errors`, or `BindingResult` argument immediately after the validated method argument. | `SessionStatus` + class-level `@SessionAttributes` -| For marking form processing complete which triggers cleanup of session attributes +| For marking form processing complete, which triggers cleanup of session attributes declared through a class-level `@SessionAttributes` annotation. See <> for more details. @@ -1786,7 +1849,7 @@ etc, and is equivalent to `required=false`. the literal part of the servlet mapping. See <>. | `@SessionAttribute` -| For access to any session attribute; in contrast to model attributes stored in the session +| 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 <> for more details. @@ -1794,149 +1857,151 @@ etc, and is equivalent to `required=false`. | For access to request attributes. See <> 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. +| If a method argument is not matched to any of the earlier values in this table and it is + a simple type (as determined by + {api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty], + it is a resolved as a `@RequestParam`. Otherwise, it is resolved as a `@ModelAttribute`. |=== + [[mvc-ann-return-types]] ==== Return Values -[.small]#<># +[.small]#<># -The table below shows supported controller method return values. Reactive types are -supported for all return values, see below for more details. +The next table describes the supported controller method return values. Reactive types are +supported for all return values. [cols="1,2", options="header"] |=== | Controller method return value | Description | `@ResponseBody` -| The return value is converted through ``HttpMessageConverter``s and written to the +| The return value is converted through `HttpMessageConverter` implementations and written to the response. See <>. | `HttpEntity`, `ResponseEntity` -| The return value specifies the full response including HTTP headers and body be converted - through ``HttpMessageConverter``s and written to the response. +| The return value that specifies the full response (including HTTP headers and body) is to be converted + through `HttpMessageConverter` implementations and written to the response. See <>. | `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 +| A view name to be resolved with `ViewResolver` implementations and used together with the implicit model -- determined through command objects and `@ModelAttribute` methods. The handler - method may also programmatically enrich the model by declaring a `Model` argument - (see above). + method can also programmatically enrich the model by declaring a `Model` argument + (see <>). | `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). + through command objects and `@ModelAttribute` methods. The handler method can also + programmatically enrich the model by declaring a `Model` argument (see <>). | `java.util.Map`, `org.springframework.ui.Model` -| Attributes to be added to the implicit model with the view name implicitly determined +| 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 +| 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 + Note that `@ModelAttribute` is optional. See "Any other return value" at the end of this table. | `ModelAndView` object -| The view and model attributes to use, and optionally a response status. +| 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 <> for details). + handled the response if it also has a `ServletResponse`, an `OutputStream` argument, or + an `@ResponseStatus` annotation. The same is also true if the controller has made a positive + `ETag` or `lastModified` timestamp check (see <> 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. + If none of the above is true, a `void` return type can also indicate "`no response body`" for + REST controllers or a default view name selection for HTML controllers. | `DeferredResult` -| Produce any of the above return values asynchronously from any thread -- e.g. possibly as a +| Produce any of the preceding return values asynchronously from any thread -- for example, as a result of some event or callback. See <> and <>. | `Callable` -| Produce any of the above return values asynchronously in a Spring MVC managed thread. +| Produce any of the above return values asynchronously in a Spring MVC-managed thread. See <> and <>. | `ListenableFuture`, `java.util.concurrent.CompletionStage`, `java.util.concurrent.CompletableFuture` -| Alternative to `DeferredResult` as a convenience for example when an underlying service - returns one of those. +| 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`. + `HttpMessageConverter` implementations. Also supported as the body of a `ResponseEntity`. See <> and <>. | `StreamingResponseBody` -| Write to the response `OutputStream` asynchronously; also supported as the body of a +| Write to the response `OutputStream` asynchronously. Also supported as the body of a `ResponseEntity`. See <> and <>. -| Reactive types -- Reactor, RxJava, or others via `ReactiveAdapterRegistry` -| Alternative to `DeferredResult` with multi-value streams (e.g. `Flux`, `Observable`) +| Reactive types -- Reactor, RxJava, or others through `ReactiveAdapterRegistry` +| Alternative to `DeferredResult` with multi-value streams (for example, `Flux`, `Observable`) collected to a `List`. - For streaming scenarios -- e.g. `text/event-stream`, `application/json+stream` -- + For streaming scenarios (for example, `text/event-stream`, `application/json+stream`), `SseEmitter` and `ResponseBodyEmitter` are used instead, where `ServletOutputStream` - blocking I/O is performed on a Spring MVC managed thread and back pressure applied + blocking I/O is performed on a Spring MVC-managed thread and back pressure is applied against the completion of each write. See <> and <>. | 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. +| Any return value that does not match any of the earlier values in this table and that + is a `String` or `void` is treated as a view name (default view name selection through + `RequestToViewNameTranslator` applies), provided it is not a simple type, as determined by + {api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]. + Values that are simple types remain unresolved. |=== [[mvc-ann-typeconversion]] ==== Type Conversion -[.small]#<># +[.small]#<># -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`. +Some annotated controller method arguments that represent `String`-based request input (such as +`@RequestParam`, `@RequestHeader`, `@PathVariable`, `@MatrixVariable`, and `@CookieValue`) +can require type conversion if the argument is declared as something other than `String`. -For such cases type conversion is automatically applied based on the configured converters. -By default simple types such as `int`, `long`, `Date`, etc. are supported. Type conversion -can be customized through a `WebDataBinder`, see <>, or by registering -`Formatters` with the `FormattingConversionService`, see +For such cases, type conversion is automatically applied based on the configured converters. +By default, simple types (`int`, `long`, `Date`, and others) are supported. You can customize type conversion +through a `WebDataBinder` (see <>) or by registering +`Formatters` with the `FormattingConversionService`. See <>. + [[mvc-ann-matrix-variables]] -==== Matrix variables -[.small]#<># +==== Matrix Variables +[.small]#<># 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 +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"`. +Matrix variables can appear in any path segment, with each variable separated by a semicolon and +multiple values separated by comma (for example, `/cars;color=red,green;year=2012`). Multiple +values can also be specified through repeated variable names (for example, +`color=red;color=green;color=blue`). If a URL is expected to contain matrix variables, the request mapping for a controller method must use a URI variable to mask that variable content and ensure the request can be matched successfully independent of matrix variable order and presence. -Below is an example: +The following example uses a matrix variable: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1949,11 +2014,13 @@ Below is an example: // q == 11 } ---- +==== -Given that all path segments may contain matrix variables, sometimes you may need to +Given that all path segments may contain matrix variables, you may sometimes need to disambiguate which path variable the matrix variable is expected to be in. -For example: +The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1968,9 +2035,12 @@ For example: // q2 == 22 } ---- +==== -A matrix variable may be defined as optional and a default value specified: +A matrix variable may be defined as optional and a default value specified, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1982,9 +2052,11 @@ A matrix variable may be defined as optional and a default value specified: // q == 1 } ---- +==== -To get all matrix variables, use a `MultiValueMap`: +To get all matrix variables, you can use a `MultiValueMap`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1999,22 +2071,25 @@ To get all matrix variables, use a `MultiValueMap`: // 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 -<>. In the MVC XML namespace, use +Note that you need to enable the use of matrix variables. In the MVC Java configuration, you need +to set a `UrlPathHelper` with `removeSemicolonContent=false` through +<>. In the MVC XML namespace, you can set ``. + [[mvc-ann-requestparam]] -==== @RequestParam -[.small]#<># +==== Using `@RequestParam` +[.small]#<># -Use the `@RequestParam` annotation to bind Servlet request parameters (i.e. query +You can use the `@RequestParam` annotation to bind Servlet request parameters (that is, query parameters or form data) to a method argument in a controller. -The following code snippet shows the usage: +The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2025,7 +2100,7 @@ The following code snippet shows the usage: // ... @GetMapping - public String setupForm(**@RequestParam("petId") int petId**, Model model) { + public String setupForm(**@RequestParam("petId") int petId**, Model model) { <1> Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; @@ -2035,34 +2110,38 @@ The following code snippet shows the usage: } ---- +<1> Using `@RequestParam` to bind `petId`. +==== -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` +By default, method parameters that use this annotation are required, but you can specify that +a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to `false` or by declaring the argument with an `java.util.Optional` wrapper. -Type conversion is applied automatically if the target method parameter type is not +Type conversion is automatically applied if the target method parameter type is not `String`. See <>. -When an `@RequestParam` annotation is declared as `Map` or +When an `@RequestParam` annotation is declared as a `Map` or `MultiValueMap` 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 +Note that use of `@RequestParam` is optional, (for example, to set its attributes). +By default, any argument that is a simple value type (as determined by +{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]) +and is not resolved by any other argument resolver, is treated as if it were annotated with `@RequestParam`. + [[mvc-ann-requestheader]] -==== @RequestHeader -[.small]#<># +==== Using `@RequestHeader` +[.small]#<># -Use the `@RequestHeader` annotation to bind a request header to a method argument in a +You can use the `@RequestHeader` annotation to bind a request header to a method argument in a controller. -Given request with headers: +Consider the following request, with headers: +==== [literal] [subs="verbatim,quotes"] ---- @@ -2073,99 +2152,112 @@ 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: +The following example 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) { + @RequestHeader("Accept-Encoding") String encoding, <1> + @RequestHeader("Keep-Alive") long keepAlive) { <2> //... } ---- +<1> Get the value of the `Accept-Encoding` header. +<2> Get the value of the `Keep-Alive` header. +==== -Type conversion is applied automatically if the target method parameter type is not -`String`. See <>. +If the target method parameter type is not +`String`, type conversion is automatically applied. See <>. When an `@RequestHeader` annotation is used on a `Map`, `MultiValueMap`, 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 +TIP: Built-in support is available for converting a comma-separated string into an +array or collection of strings or other types known to the type conversion system. For +example, a method parameter annotated with `@RequestHeader("Accept")` can be of type `String` but also `String[]` or `List`. -==== + [[mvc-ann-cookievalue]] -==== @CookieValue -[.small]#<># +==== Using `@CookieValue` +[.small]#<># -Use the `@CookieValue` annotation to bind the value of an HTTP cookie to a method argument +You can 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: +Consider a request with the following cookie: +==== [literal] [subs="verbatim,quotes"] ---- JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 ---- +==== -The following code sample demonstrates how to get the cookie value: +The following example shows how to get the cookie value: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @GetMapping("/demo") - public void handle(**@CookieValue("JSESSIONID")** String cookie) { + public void handle(@CookieValue("JSESSIONID") String cookie) { <1> //... } ---- +<1> Get the value of the `JSESSIONID` cookie. +==== + +if the target method parameter type is not +`String`, type conversion is applied automatically. See <>. -Type conversion is applied automatically if the target method parameter type is not -`String`. See <>. [[mvc-ann-modelattrib-method-args]] -==== @ModelAttribute -[.small]#<># +==== Using `@ModelAttribute` +[.small]#<># -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 +You can use the `@ModelAttribute` annotation on a method argument to access an attribute from the +model or have it be instantiated if not present. The model attribute is also overlain with values from HTTP Servlet request parameters whose names match to field names. This is -referred to as data binding and it saves you from having to deal with parsing and -converting individual query parameters and form fields. For example: +referred to as data binding, and it saves you from having to deal with parsing and +converting individual query parameters and form fields. The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/owners/{ownerId}/pets/{petId}/edit") - public String processSubmit(**@ModelAttribute Pet pet**) { } + public String processSubmit(@ModelAttribute Pet pet) { } <1> ---- +<1> Bind an instance of `Pet`. +==== The `Pet` instance above is resolved as follows: -* From the model if already added via <>. -* From the HTTP session via <>. -* From a URI path variable passed through a `Converter` (example below). +* From the model if already added by using <>. +* From the HTTP session by using <>. +* From a URI path variable passed through a `Converter` (see the next example). * 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. +* From the invocation of a "`primary constructor`" with arguments that match to Servlet +request parameters. Argument names are determined through JavaBeans +`@ConstructorProperties` or through runtime-retained parameter names in the bytecode. While it is common to use a <> to populate the model with attributes, one other alternative is to rely on a `Converter` 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 +with a URI path variable convention. In the following example, 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`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2174,34 +2266,39 @@ the `String` account number through a registered `Converter`: // ... } ---- +==== 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 -<>. For more on customizing data binding see +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 +<>. For more on customizing data binding, see <>. -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: +Data binding can result in errors. By default, a `BindException` is raised. However, to check +for such errors in the controller method, you can add a `BindingResult` argument immediately next +to the `@ModelAttribute`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/owners/{ownerId}/pets/{petId}/edit") - public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) { + public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { <1> if (result.hasErrors()) { return "petForm"; } // ... } ---- +<1> Adding a `BindingResult` next to the `@ModelAttribute`. +==== -In some cases you may want access to a model attribute without data binding. For such -cases you can inject the `Model` into the controller and access it directly or -alternatively set `@ModelAttribute(binding=false)` as shown below: +In some cases, you may want access to a model attribute without data binding. For such +cases, you can inject the `Model` into the controller and access it directly or, +alternatively, set `@ModelAttribute(binding=false)`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2217,68 +2314,77 @@ alternatively set `@ModelAttribute(binding=false)` as shown below: @PostMapping("update") public String update(@Valid AccountUpdateForm form, BindingResult result, - **@ModelAttribute(binding=false)** Account account) { + @ModelAttribute(binding=false) Account account) { <1> // ... } ---- +<1> Setting `@ModelAttribute(binding=false)`. +==== -Validation can be applied automatically after data binding by adding the -`javax.validation.Valid` annotation or Spring's `@Validated` annotation (also see +You can automatically apply validation after data binding by adding the +`javax.validation.Valid` annotation or Spring's `@Validated` annotation (ee <> and -<>). For example: +<>). The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @PostMapping("/owners/{ownerId}/pets/{petId}/edit") - public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) { + public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { <1> if (result.hasErrors()) { return "petForm"; } // ... } ---- +<1> Validate the `Pet` instance. +==== -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 +Note that using `@ModelAttribute` is optional (for example, to set its attributes). +By default, any argument that is not a simple value type (as determined by +{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]) +and is not resolved by any other argument resolver is treated as if it were annotated with `@ModelAttribute`. [[mvc-ann-sessionattributes]] -==== @SessionAttributes -[.small]#<># +==== Using `@SessionAttributes` +[.small]#<># `@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. It is a type-level annotation that declares the session attributes used by a +specific controller. This typically lists the names of model attributes or types of +model attributes that should be transparently stored in the session for subsequent requests to access. -For example: +The following example uses a `@SessionAttributes` annotation: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller - **@SessionAttributes("pet")** + @SessionAttributes("pet") public class EditPetForm { // ... } ---- +<1> Using a `@SessionAttributes` annotation. +==== -On the first request when a model attribute with the name "pet" is added to the model, +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: +storage, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller - **@SessionAttributes("pet")** + @SessionAttributes("pet") <1> public class EditPetForm { // ... @@ -2294,80 +2400,93 @@ storage: } } ---- +<1> Clearing the `Pet` value from storage. +==== + [[mvc-ann-sessionattribute]] -==== @SessionAttribute -[.small]#<># +==== Using `@SessionAttribute` +[.small]#<># -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: +If you need access to pre-existing session attributes that are managed globally +(that is, outside the controller -- for example, by a filter) and may or may not be present, +you can use the `@SessionAttribute` annotation on a method parameter, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @RequestMapping("/") - public String handle(**@SessionAttribute** User user) { + public String handle(@SessionAttribute User user) { <1> // ... } ---- +<1> Using a `@SessionAttribute` annotation. +==== -For use cases that require adding or removing session attributes consider injecting +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 +workflow, consider using `@SessionAttributes` as described in <>. + [[mvc-ann-requestattrib]] -==== @RequestAttribute -[.small]#<># +==== Using `@RequestAttribute` +[.small]#<># -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`: +Similar to `@SessionAttribute`, you can use the `@RequestAttribute` annotations to +access pre-existing request attributes created earlier (for example, by a Servlet `Filter` +or `HandlerInterceptor`): +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @GetMapping("/") - public String handle(**@RequestAttribute** Client client) { + public String handle(@RequestAttribute Client client) { // ... } ---- +<1> Using a `@RequestAttribute` annotation. +==== + [[mvc-redirecting-passing-data]] -==== Redirect attributes +==== 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. +By default, all model attributes are considered to be exposed as URI template variables in +the redirect URL. Of the remaining attributes, those that are primitive types or +collections or arrays of primitive types are automatically appended as query parameters. -Appending primitive type attributes as query parameters may be the desired result if a +Appending primitive type attributes as query parameters can be the desired result if a model instance was prepared specifically for the redirect. However, in annotated -controllers the model may contain additional attributes added for rendering purposes (e.g. +controllers, the model can contain additional attributes added for rendering purposes (for example, drop-down field values). To avoid the possibility of having such attributes appear in the -URL, an `@RequestMapping` method can declare an argument of type `RedirectAttributes` and +URL, a `@RequestMapping` method can declare an argument of type `RedirectAttributes` and use it to specify the exact attributes to make available to `RedirectView`. If the method -does redirect, the content of `RedirectAttributes` is used. Otherwise the content of the +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 +`ignoreDefaultModelOnRedirect`, which you can use to indicate that the content of the default +`Model` should never be used if a controller method redirects. Instead, the controller +method should declare an attribute of type `RedirectAttributes` or, if it does not do so, no attributes should be passed on to `RedirectView`. Both the MVC namespace and the MVC -Java config keep this flag set to `false` in order to maintain backwards compatibility. -However, for new applications we recommend setting it to `true` +Java configuration keep this flag set to `false`, to maintain backwards compatibility. +However, for new applications, we recommend setting it to `true`. Note that URI template variables from the present request are automatically made -available when expanding a redirect URL and do not need to be added explicitly neither -through `Model` nor `RedirectAttributes`. For example: +available when expanding a redirect URL, and you need explicitly add them +through `Model` or `RedirectAttributes`. The following example shows how to define a redirect: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2377,67 +2496,71 @@ through `Model` nor `RedirectAttributes`. For example: 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 +Another way of passing data to the redirect target is by using flash attributes. Unlike +other redirect attributes, flash attributes are saved in the HTTP session (and, hence, do not appear in the URL). See <> for more information. + [[mvc-flash-attributes]] -==== Flash attributes +==== Flash Attributes -Flash attributes provide a way for one request to store attributes intended for use in +Flash attributes provide a way for one request to store attributes that are intended for use in another. This is most commonly needed when redirecting -- for example, the -__Post/Redirect/Get__ pattern. Flash attributes are saved temporarily before the +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. +redirect and are removed immediately. Spring MVC has two main abstractions in support of flash attributes. `FlashMap` is used -to hold flash attributes while `FlashMapManager` is used to store, retrieve, and manage +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" +Flash attribute support is always "`on`" and does not need to enabled explicitly. However, +if not used, it never causes HTTP session creation. On each request, there is an "`input`" +`FlashMap` with attributes passed from a previous request (if any) and an "`output`" `FlashMap` with attributes to save for a subsequent request. Both `FlashMap` instances are accessible from anywhere in Spring MVC through static methods in `RequestContextUtils`. -Annotated controllers typically do not need to work with `FlashMap` directly. Instead an +Annotated controllers typically do not need to work with `FlashMap` directly. Instead, a `@RequestMapping` method can accept an argument of type `RedirectAttributes` and use it -to add flash attributes for a redirect scenario. Flash attributes added 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. +to add flash attributes for a redirect scenario. Flash attributes added through +`RedirectAttributes` are automatically propagated to the "`output`" FlashMap. Similarly, +after the redirect, attributes from the "`input`" `FlashMap` are automatically added to the +`Model` of the controller that serves the target URL. .Matching requests to flash attributes **** -The concept of flash attributes exists in many other Web frameworks and has proven to 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) +The concept of flash attributes exists in many other web frameworks and has proven to sometimes be +exposed to concurrency issues. This is because, by definition, flash attributes +are to be stored until the next request. However the very "`next`" request may not be the +intended recipient but another asynchronous request (for example, polling or resource requests), in which case the flash attributes are removed too early. -To reduce the possibility of such issues, `RedirectView` automatically "stamps" +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`. +turn, the default `FlashMapManager` matches that information to incoming requests when +it looks up the "`input`" `FlashMap`. -This does not eliminate the possibility of a concurrency issue entirely but nevertheless +This does not entirely eliminate the possibility of a concurrency issue but reduces it greatly with information that is already available in the redirect URL. -Therefore the use of flash attributes is recommended mainly for redirect scenarios . +Therefore, we recommend that you use flash attributes mainly for redirect scenarios. **** + [[mvc-multipart-forms]] ==== Multipart -[.small]#<># +[.small]#<># After a `MultipartResolver` has been <>, 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 +requests with `multipart/form-data` is parsed and accessible as regular request +parameters. The following example accesses one regular form field and one uploaded file: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2459,17 +2582,17 @@ public class FileUploadController { } ---- - -[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 -<>. For example the above form field -and file could have been fields on a form object: +NOTE: When you use Servlet 3.0 multipart parsing, you can also use `javax.servlet.http.Part`, +instead of Spring's `MultipartFile`, as a method argument +You can also use multipart content as part of data binding to a +<>. For example, the form field +and file from the preceding example could be fields on a form object, +as the following example shows: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2500,10 +2623,12 @@ public class FileUploadController { } ---- +==== Multipart requests can also be submitted from non-browser clients in a RESTful service -scenario. For example a file along with JSON: +scenario. The following example shows a file with JSON: +==== [literal] [subs="verbatim,quotes"] ---- @@ -2539,13 +2664,16 @@ public String handle(**@RequestPart("meta-data") MetaData metadata, // ... } ---- +==== -`@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: +You can use `@RequestPart` in combination with `javax.validation.Valid` or use Spring's +`@Validated` annotation, both of which cause Standard Bean Validation to be applied. +By default, validation errors cause a `MethodArgumentNotValidException`, which is turned +into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation errors locally +within the controller through an `Errors` or `BindingResult` argument, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2555,16 +2683,19 @@ public String handle(**@Valid** @RequestPart("meta-data") MetaData metadata, // ... } ---- +==== + [[mvc-ann-requestbody]] -==== @RequestBody -[.small]#<># +==== Using `@RequestBody` +[.small]#<># -Use the `@RequestBody` annotation to have the request body read and deserialized into an -Object through an <>. -Below is an example with an `@RequestBody` argument: +You can use the `@RequestBody` annotation to have the request body read and deserialized into an +`Object` through an <>. +The following example uses a `@RequestBody` argument: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2573,16 +2704,19 @@ Below is an example with an `@RequestBody` argument: // ... } ---- +==== You can use the <> option of the <> 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: +You can use `@RequestBody` in combination with `javax.validation.Valid` or Spring's +`@Validated` annotation, both of which cause Standard Bean Validation to be applied. +By default, validation errors cause a `MethodArgumentNotValidException`, which is turned +into a 400 (BAD_REQUEST) response. Alternatively, you can handle validation errors locally +within the controller through an `Errors` or `BindingResult` argument, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2591,15 +2725,18 @@ within the controller through an `Errors` or `BindingResult` argument: // ... } ---- +==== + [[mvc-ann-httpentity]] ==== HttpEntity -[.small]#<># +[.small]#<># -`HttpEntity` is more or less identical to using <> but based on a -container object that exposes request headers and body. Below is an example: +`HttpEntity` is more or less identical to using <> but is based on a +container object that exposes request headers and body. The following listing shows an example: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2608,16 +2745,19 @@ container object that exposes request headers and body. Below is an example: // ... } ---- +==== + [[mvc-ann-responsebody]] -==== @ResponseBody -[.small]#<># +==== Using `@ResponseBody` +[.small]#<># -Use the `@ResponseBody` annotation on a method to have the return serialized to the +You can use the `@ResponseBody` annotation on a method to have the return serialized to the response body through an -<>. For example: +<>. The following listing shows an example: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2627,28 +2767,31 @@ response body through an // ... } ---- +==== -`@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 +`@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. +You can use `@ResponseBody` with reactive types. See <> and <> for more details. You can use the <> option of the <> to configure or customize message conversion. -`@ResponseBody` methods can be combined with JSON serialization views. +You can combine `@ResponseBody` methods with JSON serialization views. See <> for details. + [[mvc-ann-responseentity]] ==== ResponseEntity -[.small]#<># +[.small]#<># -`ResponseEntity` is more or less identical to using <> but based -on a container object that specifies request headers and body. Below is an example: +`ResponseEntity` is more or less identical to using <> but is based +on a container object that specifies request headers and body. The following listing shows an example: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2659,21 +2802,28 @@ on a container object that specifies request headers and body. Below is an examp return ResponseEntity.created(location).build(); } ---- +==== + [[mvc-ann-jackson]] ==== Jackson JSON +Spring offers support for the Jackson JSON library. + + + [[mvc-ann-jsonview]] -===== Jackson serialization views -[.small]#<># +===== Jackson Serialization Views +[.small]#<># 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: +http://wiki.fasterxml.com/JacksonJsonViews[Jackson's Serialization Views], +which allow rendering only a subset of all fields in an `Object`. To use it with +`@ResponseBody` or `ResponseEntity` controller methods, you can use Jackson's +`@JsonView` annotation to activate a serialization view class, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2714,16 +2864,15 @@ which allows rendering only a subset of all fields in an Object. To use it with } } ---- - -[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: +NOTE: `@JsonView` allows an array of view classes, but you can specify only one per +controller method. If you need to activate multiple views, you can use a composite interface. +For controllers that rely on view resolution, you can add the serialization view class +to the model, as the following example shows: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2738,34 +2887,36 @@ to the model: } } ---- +==== [[mvc-ann-modelattrib-methods]] === Model -[.small]#<># +[.small]#<># -The `@ModelAttribute` annotation can be used: +You can use the `@ModelAttribute` annotation: * On a <> in `@RequestMapping` methods -to create or access an Object from the model, and to bind it to the request through a +to create or access an `Object` from the model and to bind it to the request through a `WebDataBinder`. -* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes helping +* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes that help to initialize the model prior to any `@RequestMapping` method invocation. * On a `@RequestMapping` method to mark its return value is a model attribute. -This section discusses `@ModelAttribute` methods, or the 2nd from the list above. +This section discusses `@ModelAttribute` methods -- the second item in the preceding list. A controller can have any number of `@ModelAttribute` methods. All such methods are invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute` -method can also be shared across controllers via `@ControllerAdvice`. See the section on +method can also be shared across controllers through `@ControllerAdvice`. See the section on <> 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 +arguments as `@RequestMapping` methods, except for `@ModelAttribute` itself or anything related to the request body. -An example `@ModelAttribute` method: +The following example shows a `@ModelAttribute` method: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2775,9 +2926,11 @@ An example `@ModelAttribute` method: // add more ... } ---- +==== -To add one attribute only: +The following example adds only one attribute: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2786,23 +2939,22 @@ To add one attribute only: 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]. + +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). -==== +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 +You can also use `@ModelAttribute` as a method-level annotation on `@RequestMapping` +methods, in which case the return value of the `@RequestMapping` method is interpreted as a model attribute. This is typically not required, as it is the default behavior in HTML -controllers, unless the return value is a `String` which would otherwise be interpreted -as a view name (also see <>). `@ModelAttribute` can also help to customize -the model attribute name: +controllers, unless the return value is a `String` that would otherwise be interpreted +as a view name (see also <>). `@ModelAttribute` can also help to customize +the model attribute name, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2813,41 +2965,40 @@ the model attribute name: return account; } ---- - - +==== [[mvc-ann-initbinder]] -=== DataBinder -[.small]#<># +=== Using `DataBinder` +[.small]#<># -`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods in order to -initialize instances of `WebDataBinder`, and those in turn are used to: +`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods that +initialize instances of `WebDataBinder`, and those, in turn, can: -* Bind request parameters (i.e. form data or query) to a model object. -* Convert String-based request values such as request parameters, path variables, -headers, cookies, and others, to the target type of controller method arguments. -* Format model object values as String values when rendering HTML forms. +* Bind request parameters (that is, form or query data) to a model object. +* Convert String-based request values (such as request parameters, path variables, +headers, cookies, and others) to the target type of controller method arguments. +* Format model object values as `String` values when rendering HTML forms. -`@InitBinder` methods can register controller-specific `java.bean.PropertyEditor`, or -Spring `Converter` and `Formatter` components. In addition, the -<> can be used to register `Converter` and `Formatter` +`@InitBinder` methods can register controller-specific `java.bean.PropertyEditor` or +Spring `Converter` and `Formatter` components. In addition, you can use the +<> to register `Converter` and `Formatter` types in a globally shared `FormattingConversionService`. +`@InitBinder` methods support many of the same arguments that `@RequestMapping` methods +do, except for `@ModelAttribute` (command object) arguments. Typically, they are declared +with a `WebDataBinder` argument (for registrations) and a `void` return value. +The following listing shows an example: -`@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** + @InitBinder <1> public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); @@ -2857,18 +3008,21 @@ Below is an example: // ... } ---- +<1> Defining an `@InitBinder` method. +==== -Alternatively when using a `Formatter`-based setup through a shared -`FormattingConversionService`, you could re-use the same approach and register -controller-specific ``Formatter``'s: +Alternatively, when you use a `Formatter`-based setup through a shared +`FormattingConversionService`, you can re-use the same approach and register +controller-specific `Formatter` implementations, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller public class FormController { - **@InitBinder** + @InitBinder <1> protected void initBinder(WebDataBinder binder) { binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); } @@ -2876,16 +3030,19 @@ controller-specific ``Formatter``'s: // ... } ---- +<1> Defining an `@InitBinder` method on a custom formatter. +==== [[mvc-ann-exceptionhandler]] === Exceptions -[.small]#<># +[.small]#<># `@Controller` and <> classes can have -`@ExceptionHandler` methods to handle exceptions from controller methods. For example: +`@ExceptionHandler` methods to handle exceptions from controller methods, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2900,18 +3057,21 @@ controller-specific ``Formatter``'s: } } ---- +==== -The exception may match against a top-level exception being propagated (i.e. a direct -`IOException` thrown), or against the immediate cause within a top-level wrapper exception -(e.g. an `IOException` wrapped inside an `IllegalStateException`). +The exception may match against a top-level exception being propagated (that is, a direct +`IOException` being thrown) or against the immediate cause within a top-level wrapper exception +(for example, an `IOException` wrapped inside an `IllegalStateException`). -For matching exception types, preferably declare the target exception as a method argument -as shown above. When multiple exception methods match, a root exception match is generally +For matching exception types, preferably declare the target exception as a method argument, +as the preceding example shows. When multiple exception methods match, a root exception match is generally preferred to a cause exception match. More specifically, the `ExceptionDepthComparator` is used to sort exceptions based on their depth from the thrown exception type. -Alternatively, the annotation declaration may narrow the exception types to match: +Alternatively, the annotation declaration may narrow the exception types to match, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2920,9 +3080,12 @@ Alternatively, the annotation declaration may narrow the exception types to matc // ... } ---- +==== -Or even a list of specific exception types with a very generic argument signature: +You can even use a list of specific exception types with a very generic argument signature, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2931,49 +3094,51 @@ Or even a list of specific exception types with a very generic argument signatur // ... } ---- +==== [NOTE] ==== -The distinction between root and cause exception matching can be surprising: +The distinction between root and cause exception matching can be surprising. -In the `IOException` variant above, the method will typically be called with -the actual `FileSystemException` or `RemoteException` instance as the argument +In the `IOException` variant shown earlier, the method is typically called with +the actual `FileSystemException` or `RemoteException` instance as the argument, since both of them extend from `IOException`. However, if any such matching -exception is propagated within a wrapper exception which is an `IOException` -itself, the passed-in exception instance will be that wrapper exception. +exception is propagated within a wrapper exception which is itself an `IOException`, +the passed-in exception instance is that wrapper exception. -The behavior is even simpler in the `handle(Exception)` variant: This will -always be invoked with the wrapper exception in a wrapping scenario, with the +The behavior is even simpler in the `handle(Exception)` variant. This is +always invoked with the wrapper exception in a wrapping scenario, with the actually matching exception to be found through `ex.getCause()` in that case. -The passed-in exception will only be the actual `FileSystemException` or -`RemoteException` instance when these are thrown as top-level exceptions. +The passed-in exception is the actual `FileSystemException` or +`RemoteException` instance only when these are thrown as top-level exceptions. ==== -We generally recommend to be as specific as possible in the argument signature, +We generally recommend that you be as specific as possible in the argument signature, reducing the potential for mismatches between root and cause exception types. Consider breaking a multi-matching method into individual `@ExceptionHandler` methods, each matching a single specific exception type through its signature. -In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception +In a multi-`@ControllerAdvice` arrangement, we recommend declaring your primary root exception mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root exception match is preferred to a cause, this is defined among the methods of a given controller or `@ControllerAdvice` class. This means a cause match on a higher-priority -`@ControllerAdvice` bean is preferred to any match (e.g. root) on a lower-priority +`@ControllerAdvice` bean is preferred to any match (for example, root) on a lower-priority `@ControllerAdvice` bean. -Last but not least, an `@ExceptionHandler` method implementation may choose to back +Last but not least, an `@ExceptionHandler` method implementation can choose to back out of dealing with a given exception instance by rethrowing it in its original form. -This is useful in scenarios where you are only interested in root-level matches or in +This is useful in scenarios where you are interested only in root-level matches or in matches within a specific context that cannot be statically determined. A rethrown -exception will be propagated through the remaining resolution chain, just like if +exception is propagated through the remaining resolution chain, as though the given `@ExceptionHandler` method would not have matched in the first place. Support for `@ExceptionHandler` methods in Spring MVC is built on the `DispatcherServlet` level, <> mechanism. + [[mvc-ann-exceptionhandler-args]] -==== Method arguments +==== Method Arguments `@ExceptionHandler` methods support the following arguments: @@ -2988,45 +3153,45 @@ level, <> mechanism. | For access to the controller method that raised the exception. | `WebRequest`, `NativeWebRequest` -| Generic access to request parameters, request & session attributes, without direct +| Generic access to request parameters and request and 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`. +| Choose any specific request or response type (for example, `ServletRequest` or `HttpServletRequest` or + or Spring's `MultipartRequest` or `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 + Note that session access is not thread-safe. Consider setting the + `RequestMappingHandlerAdapter` instance's `synchronizeOnSession` flag to `true` if multiple requests are allowed to access a session concurrently. | `java.security.Principal` -| Currently authenticated user; possibly a specific `Principal` implementation class if known. +| 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`. +| The current request locale, determined by the most specific `LocaleResolver` available -- in + effect, the configured `LocaleResolver` or `LocaleContextResolver`. -| `java.util.TimeZone` + `java.time.ZoneId` +| `java.util.TimeZone`, `java.time.ZoneId` | The time zone associated with the current request, as determined by a `LocaleContextResolver`. | `java.io.OutputStream`, `java.io.Writer` -| For access to the raw response body as exposed by the Servlet API. +| For access to the raw response body, as exposed by the Servlet API. | `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap` -| For access to the model for an error response, always empty. +| For access to the model for an error response. Always empty. | `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. +| Specify attributes to use in case of a redirect -- (that is to be appended to the query + string) and flash attributes to be stored temporarily until the request after the redirect. See <> and <>. | `@SessionAttribute` -| For access to any session attribute; in contrast to model attributes stored in the session +| 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 <> for more details. @@ -3046,24 +3211,24 @@ level, <> mechanism. | Return value | Description | `@ResponseBody` -| The return value is converted through ``HttpMessageConverter``s and written to the +| The return value is converted through `HttpMessageConverter` instances and written to the response. See <>. | `HttpEntity`, `ResponseEntity` -| The return value specifies the full response including HTTP headers and body be converted - through ``HttpMessageConverter``s and written to the response. +| The return value specifies that the full response (including the HTTP headers and the body) be converted + through `HttpMessageConverter` instances and written to the response. See <>. | `String` -| A view name to be resolved with ``ViewResolver``'s and used together with the implicit +| A view name to be resolved with `ViewResolver` implementations and used together with the implicit model -- determined through command objects and `@ModelAttribute` methods. The handler - method may also programmatically enrich the model by declaring a `Model` argument - (see above). + method can also programmatically enrich the model by declaring a `Model` argument + (described earlier). | `View` | A `View` instance to use for rendering together with the implicit model -- determined through command objects and `@ModelAttribute` methods. The handler method may also - programmatically enrich the model by declaring a `Model` argument (see above). + programmatically enrich the model by declaring a `Model` argument (descried earlier). | `java.util.Map`, `org.springframework.ui.Model` | Attributes to be added to the implicit model with the view name implicitly determined @@ -3073,74 +3238,76 @@ level, <> mechanism. | 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 + Note that `@ModelAttribute` is optional. See "`Any other return value`" at the end of this table. | `ModelAndView` object -| The view and model attributes to use, and optionally a response status. +| 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 <> for details). + handled the response if it also has a `ServletResponse` an `OutputStream` argument, or + a `@ResponseStatus` annotation. The same is also true if the controller has made a positive + `ETag` or `lastModified` timestamp check (see <> 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. + If none of the above is true, a `void` return type can also indicate "`no response body`" for + REST controllers or default view name selection for HTML controllers. | Any other return value -| If a return value is not matched to any of the above, by default it is treated 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. +| If a return value is not matched to any of the above and is not a simple type (as determined by + {api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]), + by default, it is treated as a model attribute to be added to the model. If it is a simple type, + it remains unresolved. |=== [[mvc-ann-rest-exceptions]] ==== REST API exceptions -[.small]#<># +[.small]#<># 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 +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 +value to set the status and the body of the response. Such methods can 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 +{api-spring-framework}/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.html[`ResponseEntityExceptionHandler`], +which provides handling for exceptions that Spring MVC raises and provides hooks to customize the response body. To make use of this, create a subclass of -`ResponseEntityExceptionHandler`, annotate with `@ControllerAdvice`, override the +`ResponseEntityExceptionHandler`, annotate it with `@ControllerAdvice`, override the necessary methods, and declare it as a Spring bean. [[mvc-ann-controller-advice]] === Controller Advice -[.small]#<># +[.small]#<># 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 +the `@Controller` class (or class hierarchy) in which they are declared. 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 <>. +`@ControllerAdvice` is marked with `@Component`, which means such classes can be registered +as Spring beans through <>. `@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). +`@ResponseBody`, which essentially means `@ExceptionHandler` methods are rendered to the +response body through message conversion (versus view resolution or 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. +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: +By default, `@ControllerAdvice` methods apply to every request (that is, all controllers), but +you can narrow that down to a subset of controllers by using attributes on the annotation, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3156,18 +3323,18 @@ you can narrow that down to a subset of controllers via attributes on the annota @ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) public class ExampleAdvice3 {} ---- +==== -Keep in mind the above selectors are evaluated at runtime and may negatively impact +The selectors in the preceding example are evaluated at runtime and may negatively impact performance if used extensively. See the -{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[@ControllerAdvice] +{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[`@ControllerAdvice`] Javadoc for more details. - [[mvc-uri-building]] == URI Links -[.small]#<># +[.small]#<># This section describes various options available in the Spring Framework to work with URI's. @@ -3178,10 +3345,12 @@ include::web-uris.adoc[leveloffset=+2] [[mvc-servleturicomponentsbuilder]] -=== Servlet request relative +=== Relative Servlet Requests -You can use `ServletUriComponentsBuilder` to create URIs relative to the current request: +You can use `ServletUriComponentsBuilder` to create URIs relative to the current request, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3194,9 +3363,11 @@ You can use `ServletUriComponentsBuilder` to create URIs relative to the current .expand("123") .encode(); ---- +==== -You can create URIs relative to the context path: +You can create URIs relative to the context path, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3205,9 +3376,12 @@ You can create URIs relative to the context path: ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromContextPath(request) .path("/accounts").build() ---- +==== -You can create URIs relative to a Servlet (e.g. `/main/{asterisk}`): +You can create URIs relative to a Servlet (for example, `/main/{asterisk}`), +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3216,23 +3390,22 @@ You can create URIs relative to a Servlet (e.g. `/main/{asterisk}`): ServletUriComponentsBuilder ucb = ServletUriComponentsBuilder.fromServletMapping(request) .path("/accounts").build() ---- - -[NOTE] ==== -As of 5.1 `ServletUriComponentsBuilder` ignores information from the "Forwarded", -"X-Forwarded-*" headers, that specify the client-originated address. Consider using the -<> to extract and use, or to discard + +NOTE: As of 5.1, `ServletUriComponentsBuilder` ignores information from the `Forwarded` and +`X-Forwarded-*` headers, which specify the client-originated address. Consider using the +<> to extract and use or to discard such headers. -==== [[mvc-links-to-controllers]] -=== Links to controllers +=== Links to Controllers Spring MVC provides a mechanism to prepare links to controller methods. For example, -the following MVC controller easily allows for link creation: +the following MVC controller allows for link creation: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3246,9 +3419,11 @@ the following MVC controller easily allows for link creation: } } ---- +==== -You can prepare a link by referring to the method by name: +You can prepare a link by referring to the method by name, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3257,18 +3432,20 @@ You can prepare a link by referring to the method by name: 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 +In the preceding example, we provide actual method argument values (in this case, the long value: `21`) +to be used as a path variable and inserted into the URL. Furthermore, we provide the +value, `42`, to fill in any remaining URI variables, such as the `hotel` variable inherited +from the type-level request mapping. If the method had more arguments, we could supply null for +arguments not needed for the URL. In general, only `@PathVariable` and `@RequestParam` arguments are relevant for constructing the URL. -There are additional ways to use `MvcUriComponentsBuilder`. For example you can use a technique -akin to mock testing through proxies to avoid referring to the controller method by name +There are additional ways to use `MvcUriComponentsBuilder`. For example, you can use a technique +akin to mock testing through proxies to avoid referring to the controller method by name, as the following example shows (the example assumes static import of `MvcUriComponentsBuilder.on`): +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3277,29 +3454,29 @@ akin to mock testing through proxies to avoid referring to the controller method URI uri = uriComponents.encode().toUri(); ---- - -[NOTE] ==== -Controller method signatures are limited in their design when supposed to be usable for + +NOTE: Controller method signatures are limited in their design when they are supposed to be usable for link creation with `fromMethodCall`. Aside from needing a proper parameter signature, -there is a technical limitation on the return type: namely generating a runtime proxy -for link builder invocations, so the return type must not be `final`. In particular, -the common `String` return type for view names does not work here; use `ModelAndView` +there is a technical limitation on the return type (namely, generating a runtime proxy +for link builder invocations), so the return type must not be `final`. In particular, +the common `String` return type for view names does not work here. You should use `ModelAndView` or even plain `Object` (with a `String` return value) instead. -==== -The above examples use static methods in `MvcUriComponentsBuilder`. Internally they rely +The earlier examples use static methods in `MvcUriComponentsBuilder`. Internally, they rely on `ServletUriComponentsBuilder` to prepare a base URL from the scheme, host, port, -context path and servlet path of the current request. This works well in most cases, -however sometimes it 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 +context path, and servlet path of the current request. This works well in most cases. +However, sometimes, it can be insufficient. For example, you may be outside the context of +a request (such as a batch process that prepares links) or perhaps you need to insert a path +prefix (such as a locale prefix that was removed from the request path and needs to be re-inserted into links). -For such cases you can use the static "fromXxx" overloaded methods that accept a -`UriComponentsBuilder` to use base URL. Or you can create an instance of `MvcUriComponentsBuilder` -with a base URL and then use the instance-based "withXxx" methods. For example: +For such cases, you can use the static `fromXxx` overloaded methods that accept a +`UriComponentsBuilder` to use a base URL. Alternatively, you can create an instance of `MvcUriComponentsBuilder` +with a base URL and then use the instance-based `withXxx` methods. For example, the +following listing uses `withMethodCall`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3309,26 +3486,24 @@ with a base URL and then use the instance-based "withXxx" methods. For example: URI uri = uriComponents.encode().toUri(); ---- - -[NOTE] -==== -As of 5.1 `MvcUriComponentsBuilder` ignores information from the "Forwarded", -"X-Forwarded-*" headers, that specify the client-originated address. Consider using the -<> to extract and use, or to discard -such headers. ==== +NOTE: As of 5.1, `MvcUriComponentsBuilder` ignores information from the `Forwarded` and +`X-Forwarded-*` headers, which specify the client-originated address. Consider using the +<> to extract and use or to discard +such headers. [[mvc-links-to-controllers-from-views]] -=== Links in views +=== Links in Views -In views such as Thymeleaf, FreeMarker, JSP you can build links to annotated controllers +In views such as Thymeleaf, FreeMarker, or JSP, you can build links to annotated controllers by referring to the implicitly or explicitly assigned name for each request mapping. -For example given: +Consider the following example: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3339,9 +3514,11 @@ For example given: public HttpEntity getAddress(@PathVariable String country) { ... } } ---- +==== -You can prepare a link from a JSP as follows: +Given the preceding controller, you can prepare a link from a JSP, as follows: +==== [source,jsp,indent=0] [subs="verbatim,quotes"] ---- @@ -3349,31 +3526,31 @@ You can prepare a link from a JSP as follows: ... Get Address ---- +==== -The above example relies on the `mvcUrl` function declared in the Spring tag library -(i.e. META-INF/spring.tld), but it is easy to define your own function, or prepare a +The preceding example relies on the `mvcUrl` function declared in the Spring tag library +(that is, META-INF/spring.tld), but it is easy to define your own function or prepare a similar one for other templating technologies. -Here is how this works. On startup every `@RequestMapping` is assigned a default name -through a `HandlerMethodMappingNamingStrategy` whose default implementation uses the -capital letters of the class and the method name, e.g. the `getFoo` method in -`FooController` becomes "FC#getFoo". If there is a name clash you can use -`@RequestMapping(name="..")` to assign an explicit name, or implement your own +Here is how this works. On startup, every `@RequestMapping` is assigned a default name +through `HandlerMethodMappingNamingStrategy`, whose default implementation uses the +capital letters of the class and the method name (for example, the `getThing` method in +`ThingController` becomes "TC#getThing"). If there is a name clash, you can use +`@RequestMapping(name="..")` to assign an explicit name or implement your own `HandlerMethodMappingNamingStrategy`. - [[mvc-ann-async]] -== Async Requests +== Asynchronous Requests [.small]#<># Spring MVC has an extensive integration with Servlet 3.0 asynchronous request <>: -* <> and <> return values in -controller method provide basic support for a single asynchronous return value. -* Controllers can <> multiple values including +* <> and <> return values in +controller methods and provide basic support for a single asynchronous return value. +* Controllers can <> multiple values, including <> and <>. * Controllers can use reactive clients and return <> for response handling. @@ -3381,13 +3558,15 @@ controller method provide basic support for a single asynchronous return value. [[mvc-ann-async-deferredresult]] -=== `DeferredResult` +=== Using `DeferredResult` [.small]#<># Once the asynchronous request processing feature is <> in the Servlet container, controller methods can -wrap any supported controller method return value with `DeferredResult`: +wrap any supported controller method return value with `DeferredResult`, as the following +example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3402,18 +3581,21 @@ wrap any supported controller method return value with `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. +The controller can produce the return value asynchronously, from a different thread -- for +example, in response to an external event (JMS message), a scheduled task, or other event. [[mvc-ann-async-callable]] -=== `Callable` +=== Using `Callable` [.small]#<># -A controller may also wrap any supported return value with `java.util.concurrent.Callable`: +A controller can wrap any supported return value with `java.util.concurrent.Callable`, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3429,8 +3611,9 @@ A controller may also wrap any supported return value with `java.util.concurrent } ---- +==== -The return value will then be obtained by executing the the given task through the +The return value can then be obtained by running the the given task through the <> `TaskExecutor`. @@ -3442,124 +3625,129 @@ The return value will then be obtained by executing the the given task through t 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 + The main effect of doing so is that the Servlet (as well as any filters) can exit, but + the response remains open to let processing complete later. +* The call to `request.startAsync()` returns `AsyncContext`, which you can use for + further control over asynchronous processing. For example, it provides the `dispatch` method, + which is similar to a forward from the Servlet API, except that it lets an + application resume request processing on a Servlet container thread. +* The `ServletRequest` provides access to the current `DispatcherType`, which you can + use to distinguish between processing the initial request, an asynchronous dispatch, a forward, and other dispatcher types. -`DeferredResult` processing: +`DeferredResult` processing works as follows: -* Controller returns a `DeferredResult` and saves it in some in-memory +* The controller returns a `DeferredResult` and saves it in some in-memory queue or list where it can be accessed. * Spring MVC calls `request.startAsync()`. -* Meanwhile the `DispatcherServlet` and all configured Filter's exit the request - processing thread but the response remains open. -* The application sets the `DeferredResult` from some thread and Spring MVC +* Meanwhile, the `DispatcherServlet` and all configured filters exit the request + processing thread, but the response remains open. +* The application sets the `DeferredResult` from some thread, and Spring MVC dispatches the request back to the Servlet container. -* The `DispatcherServlet` is invoked again and processing resumes with the +* The `DispatcherServlet` is invoked again, and processing resumes with the asynchronously produced return value. -`Callable` processing: +`Callable` processing works as follows: -* Controller returns a `Callable`. +* The controller returns a `Callable`. * Spring MVC calls `request.startAsync()` and submits the `Callable` to a `TaskExecutor` for processing in a separate thread. -* Meanwhile the `DispatcherServlet` and all Filter's exit the Servlet container thread +* Meanwhile, the `DispatcherServlet` and all filters exit the Servlet container thread, but the response remains open. -* Eventually the `Callable` produces a result and Spring MVC dispatches the request back +* 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 +* 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 +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 +==== 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 +When you use a `DeferredResult`, you can choose whether to call `setResult` or +`setErrorResult` with an exception. In both cases, Spring MVC dispatches the request back to the Servlet container to complete processing. It is then treated either as if the -controller method returned the given value, or as if it produced the given exception. -The exception then goes through the regular exception handling mechanism, e.g. invoking -`@ExceptionHandler` methods. +controller method returned the given value or as if it produced the given exception. +The exception then goes through the regular exception handling mechanism (for example, invoking +`@ExceptionHandler` methods). -When using `Callable`, similar processing logic follows. The main difference being that +When you use `Callable`, similar processing logic occurs, the main difference being that the result is returned from the `Callable` or an exception is raised by it. + [[mvc-ann-async-interception]] ==== Interception -``HandlerInterceptor``'s can also be `AsyncHandlerInterceptor` in order to receive the +`HandlerInterceptor` instances can be of type `AsyncHandlerInterceptor`, to receive the `afterConcurrentHandlingStarted` callback on the initial request that starts asynchronous -processing instead of `postHandle` and `afterCompletion`. +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] +`HandlerInterceptor` implementations can also register a `CallableProcessingInterceptor` +or a `DeferredResultProcessingInterceptor`, to integrate more deeply with the +lifecycle of an asynchronous request (for example, to handle a timeout event). See +{api-spring-framework}/web/servlet/AsyncHandlerInterceptor.html[`AsyncHandlerInterceptor`] for more details. `DeferredResult` provides `onTimeout(Runnable)` and `onCompletion(Runnable)` callbacks. -See the Javadoc of `DeferredResult` for more details. `Callable` can be substituted for +See the {api-spring-framework}/web/context/request/async/DeferredResult.html[Javadoc of `DeferredResult`] for more details. `Callable` can be substituted for `WebAsyncTask` that exposes additional methods for timeout and completion callbacks. + [[mvc-ann-async-vs-webflux]] ==== Compared to WebFlux The Servlet API was originally built for making a single pass through the Filter-Servlet -chain. Asynchronous request processing, added in Servlet 3.0, allows applications to exit +chain. Asynchronous request processing, added in Servlet 3.0, lets applications exit the Filter-Servlet chain but leave the response open for further processing. The Spring MVC -async support is built around that mechanism. When a controller returns a `DeferredResult`, -the Filter-Servlet chain is exited and the Servlet container thread is released. Later when -the `DeferredResult` is set, an ASYNC dispatch (to the same URL) is made during which the -controller is mapped again but rather than invoking it, the `DeferredResult` value is used +asynchronous support is built around that mechanism. When a controller returns a `DeferredResult`, +the Filter-Servlet chain is exited, and the Servlet container thread is released. Later, when +the `DeferredResult` is set, an `ASYNC` dispatch (to the same URL) is made, during which the +controller is mapped again but, rather than invoking it, the `DeferredResult` value is used (as if the controller returned it) to resume processing. -By contrast Spring WebFlux is neither built on the Servlet API, nor does it need such an -asynchronous request processing feature because it is asynchronous by design. Asynchronous -handling is built into all framework contracts and is intrinsically supported through :: +By contrast, Spring WebFlux is neither built on the Servlet API, nor does it need such an +asynchronous request processing feature, because it is asynchronous by design. Asynchronous +handling is built into all framework contracts and is intrinsically supported through all stages of request processing. From a programming model perspective, both Spring MVC and Spring WebFlux support asynchronous and <> as return values in controller methods. -Spring MVC even supports streaming, including reactive back pressure. However individual -writes to the response remain blocking (and performed on a separate thread) unlike WebFlux -that relies on non-blocking I/O and does not need an extra thread for each write. +Spring MVC even supports streaming, including reactive back pressure. However, individual +writes to the response remain blocking (and are performed on a separate thread), unlike WebFlux, +which relies on non-blocking I/O and does not need an extra thread for each write. Another fundamental difference is that Spring MVC does not support asynchronous or -reactive types in controller method arguments, e.g. `@RequestBody`, `@RequestPart`, and -others, nor does it have any explicit support for asynchronous and reactive types as +reactive types in controller method arguments (for example, `@RequestBody`, `@RequestPart`, and +others), nor does it have any explicit support for asynchronous and reactive types as model attributes. Spring WebFlux does support all that. [[mvc-ann-async-http-streaming]] === HTTP Streaming -[.small]#<># +[.small]#<># -`DeferredResult` and `Callable` can be used for a single asynchronous return value. +You can use `DeferredResult` and `Callable` for a single asynchronous return value. What if you want to produce multiple asynchronous values and have those written to the -response? +response? This section describes how to do so. + [[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 -<> and written to the -response. For example: +You can use the `ResponseBodyEmitter` return value to produce a stream of objects, where +each object is serialized with an +<> and written to the +response, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3579,26 +3767,29 @@ response. For example: // and done at some point emitter.complete(); ---- +==== -`ResponseBodyEmitter` can also be used as the body in a `ResponseEntity` allowing you to +You can also use `ResponseBodyEmitter` as the body in a `ResponseEntity`, letting you customize the status and headers of the response. -When an `emitter` throws an `IOException` (e.g. if the remote client went away) applications -are not responsible for cleaning up the connection, and should not invoke `emitter.complete` -or `emitter.completeWithError`. Instead the servlet container automatically initiates an -`AsyncListener` error notification in which Spring MVC makes a `completeWithError` call, -which in turn performs one a final ASYNC dispatch to the application during which Spring MVC +When an `emitter` throws an `IOException` (for example, if the remote client went away), applications +are not responsible for cleaning up the connection and should not invoke `emitter.complete` +or `emitter.completeWithError`. Instead, the servlet container automatically initiates an +`AsyncListener` error notification, in which Spring MVC makes a `completeWithError` call. +This call, in turn, performs one final `ASYNC` dispatch to the application, during which Spring MVC invokes the configured exception resolvers and completes the request. + [[mvc-ann-async-sse]] ==== SSE -`SseEmitter` 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`: +`SseEmitter` (a subclass of `ResponseBodyEmitter`) 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. To produce an SSE +stream from a controller, return `SseEmitter`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3618,6 +3809,7 @@ stream from a controller simply return `SseEmitter`: // 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 @@ -3625,16 +3817,18 @@ does not support Server-Sent Events. Consider using Spring's <> transports (including SSE) that target a wide range of browsers. -Also see <> for notes on exception handling. +See also <> for notes on exception handling. + [[mvc-ann-async-output-stream]] -==== Raw data +==== 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: +Sometimes, it is useful to bypass message conversion and stream directly to the response +`OutputStream` (for example, for a file download). You can use the `StreamingResponseBody` +return value type to do so, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3648,62 +3842,60 @@ return value type to do that: }; } ---- +==== -`StreamingResponseBody` can be used as the body in a `ResponseEntity` allowing you to +You can use `StreamingResponseBody` as the body in a `ResponseEntity` to customize the status and headers of the response. [[mvc-ann-async-reactive-types]] -=== Reactive types -[.small]#<># +=== Reactive Types +[.small]#<># 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 +`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 +* A single-value promise is adapted to, similar to using `DeferredResult`. Examples include `Mono` (Reactor) or `Single` (RxJava). -* A multi-value stream, with a streaming media type such as `"application/stream+json"` -or `"text/event-stream"`, is adapted to, and similar to using `ResponseBodyEmitter` or +* A multi-value stream with a streaming media type (such as `application/stream+json` +or `text/event-stream`) is adapted to, similar to using `ResponseBodyEmitter` or `SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava). Applications can also return `Flux` or `Observable`. -* A multi-value stream, with any other media type (e.g. "application/json"), is adapted -to, and similar to using `DeferredResult>`. +* A multi-value stream with any other media type (such as `application/json`) is adapted +to, similar to using `DeferredResult>`. -[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. -==== +TIP: Spring MVC supports Reactor and RxJava through the +{api-spring-framework}/core/ReactiveAdapterRegistry.html[`ReactiveAdapterRegistry`] from +`spring-core`, which lets it adapt from multiple reactive libraries. For streaming to the response, reactive back pressure is supported, but writes to the -response are still blocking, and are executed on a separate thread through the -<> `TaskExecutor` in order to avoid -blocking the upstream source (e.g. a `Flux` returned from the `WebClient`). -By default `SimpleAsyncTaskExecutor` is used for the blocking writes but that is not -suitable under load. If you plan to stream with a reactive type, please use the -<> to configure a task executor. +response are still blocking and are executed on a separate thread through the +<> `TaskExecutor`, to avoid +blocking the upstream source (such as a `Flux` returned from `WebClient`). +By default, `SimpleAsyncTaskExecutor` is used for the blocking writes, but that is not +suitable under load. If you plan to stream with a reactive type, you should use the +<> to configure a task executor. [[mvc-ann-async-disconnects]] === Disconnects -[.small]#<># +[.small]#<># The Servlet API does not provide any notification when a remote client goes away. -Therefore while streaming to the response, whether via <> or +Therefore, while streaming to the response, whether through <> or <, it is important to send data periodically, -since the write would fail if the client has disconnected. The send could take the form -of an empty (comment-only) SSE event, or any other data that the other side would have to +since the write fails if the client has disconnected. The send could take the form +of an empty (comment-only) SSE event or any other data that the other side would have to to interpret as a heartbeat and ignore. -Alternatively consider using web messaging solutions such as -<> or WebSocket with <> +Alternatively, consider using web messaging solutions (such as +<> or WebSocket with <>) that have a built-in heartbeat mechanism. @@ -3712,63 +3904,63 @@ that have a built-in heartbeat mechanism. === Configuration [.small]#<># -The async request processing feature must be enabled at the Servlet container level. -The MVC config also exposes several options for asynchronous requests. +The asynchronous request processing feature must be enabled at the Servlet container level. +The MVC configuration also exposes several options for asynchronous requests. + [[mvc-ann-async-configuration-servlet3]] -==== Servlet container +==== 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`. +Filter and Servlet declarations have an `asyncSupported` flag that needs to be set to `true` +to enable asynchronous request processing. In addition, Filter mappings should be +declared to handle the `ASYNC` `javax.servlet.DispatchType`. In Java configuration, when you use `AbstractAnnotationConfigDispatcherServletInitializer` to initialize the Servlet container, this is done automatically. -In `web.xml` configuration, add `true` to the -`DispatcherServlet` and to `Filter` declarations, and also add +In `web.xml` configuration, you can add `true` to the +`DispatcherServlet` and to `Filter` declarations and add `ASYNC` to filter mappings. + [[mvc-ann-async-configuration-spring-mvc]] ==== Spring MVC -The MVC config exposes options related to async request processing: +The MVC configuration exposes the following options related to asynchronous request processing: -* Java config -- use the `configureAsyncSupport` callback on `WebMvcConfigurer`. -* XML namespace -- use the `` element under ``. +* Java configuration: Use the `configureAsyncSupport` callback on `WebMvcConfigurer`. +* XML namespace: Use the `` element under ``. 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). +* Default timeout value for async requests, whichm if not set, depends +on the underlying Servlet container (for example, 10 seconds on Tomcat). * `AsyncTaskExecutor` to use for blocking writes when streaming with -<>, 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 +<> and for executing `Callable` instances returned from +controller methods. We highly recommended configuring this property if you +stream with reactive types or have controller methods that return `Callable`, since +by default, it is a `SimpleAsyncTaskExecutor`. +* `DeferredResultProcessingInterceptor` implementations and `CallableProcessingInterceptor` implementations. + +Note that you can also set the default timeout value on a `DeferredResult`, +a `ResponseBodyEmitter`, and an `SseEmitter`. For a `Callable`, you can use `WebAsyncTask` to provide a timeout value. - include::webmvc-cors.adoc[leveloffset=+1] - [[mvc-web-security]] == Web Security -[.small]#<># +[.small]#<># 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: +for protecting web applications from malicious exploits. See the Spring Security +reference documentation, including: * {doc-spring-security}/html5/#mvc[Spring MVC Security] * {doc-spring-security}/html5/#test-mockmvc[Spring MVC Test Support] @@ -3779,29 +3971,28 @@ http://hdiv.org/[HDIV] is another web security framework that integrates with Sp - [[mvc-caching]] == HTTP Caching -[.small]#<># +[.small]#<># HTTP caching can significantly improve the performance of a web application. HTTP caching -revolves around the "Cache-Control" response header and subsequently conditional request -headers such as "Last-Modified" and "ETag". "Cache-Control" advises private (e.g. browser) -and public (e.g. proxy) caches how to cache and re-use responses. An "ETag" header is used +revolves around the `Cache-Control` response header and, subsequently, conditional request +headers (such as `Last-Modified` and `ETag`). `Cache-Control` advises private (for example, browser) +and public (for example, proxy) caches on how to cache and re-use responses. An `ETag` header is used to make a conditional request that may result in a 304 (NOT_MODIFIED) without a body, -if the content has not changed. "ETag" can be seen as a more sophisticated successor to +if the content has not changed. `ETag` can be seen as a more sophisticated successor to the `Last-Modified` header. -This section describes HTTP caching related options available in Spring Web MVC. +This section describes the HTTP caching-related options that are available in Spring Web MVC. [[mvc-caching-cachecontrol]] -=== `CacheControl` -[.small]#<># +=== Using `CacheControl` +[.small]#<># {api-spring-framework}/http/CacheControl.html[`CacheControl`] provides support for -configuring settings related to the "Cache-Control" header and is accepted as an argument +configuring settings related to the `Cache-Control` header and is accepted as an argument in a number of places: * {api-spring-framework}/web/servlet/mvc/WebContentInterceptor.html[`WebContentInterceptor`] @@ -3810,9 +4001,10 @@ in a number of places: * <> While https://tools.ietf.org/html/rfc7234#section-5.2.2[RFC 7234] describes all possible -directives for the "Cache-Control" response header, the `CacheControl` type takes a -use case oriented approach focusing on the common scenarios: +directives for the `Cache-Control` response header, the `CacheControl` type takes a +use case-oriented approach that focuses on the common scenarios: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3827,26 +4019,28 @@ use case oriented approach focusing on the common scenarios: // "Cache-Control: max-age=864000, public, no-transform" CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic(); ---- +==== -`WebContentGenerator` also accept a simpler `cachePeriod` property, in seconds, that +`WebContentGenerator` also accept a simpler `cachePeriod` property (defined in seconds) that works as follows: -* 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 +* A `-1` value does not generate a `Cache-Control` response header. +* A `0` value prevents caching by using the `'Cache-Control: no-store'` directive. +* An `n > 0` value caches the given response for `n` seconds by using the `'Cache-Control: max-age=n'` directive. [[mvc-caching-etag-lastmodified]] === Controllers -[.small]#<># +[.small]#<># -Controllers can add explicit support for HTTP caching. This is recommended since the -lastModified or ETag value for a resource needs to be calculated before it can be compared -against conditional request headers. A controller can add an ETag and "Cache-Control" -settings to a `ResponseEntity`: +Controllers can add explicit support for HTTP caching. We recommended doing so, since the +`lastModified` or `ETag` value for a resource needs to be calculated before it can be compared +against conditional request headers. A controller can add an `ETag` header and `Cache-Control` +settings to a `ResponseEntity`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3863,13 +4057,16 @@ settings to a `ResponseEntity`: .body(book); } ---- +==== -This will send an 304 (NOT_MODIFIED) response with an empty body, if the comparison -to the conditional request headers indicates the content has not changed. Otherwise the -"ETag" and "Cache-Control" headers will be added to the response. +The preceding example sends an 304 (NOT_MODIFIED) response with an empty body if the comparison +to the conditional request headers indicates that the content has not changed. Otherwise, the +`ETag` and `Cache-Control` headers are added to the response. -The check against conditional request headers can also be made in the controller: +You can also make the check against conditional request headers in the controller, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3888,31 +4085,31 @@ The check against conditional request headers can also be made in the controller ---- <1> Application-specific calculation. -<2> Response has been set to 304 (NOT_MODIFIED), no further processing. -<3> Continue with request processing. +<2> The esponse has been set to 304 (NOT_MODIFIED) -- no further processing. +<3> Continue with the request processing. +==== -There are 3 variants for checking conditional requests against eTag values, lastModified -values, or both. For conditional "GET" and "HEAD" requests, the response may be set to -304 (NOT_MODIFIED). For conditional "POST", "PUT", and "DELETE", the response would be set -to 409 (PRECONDITION_FAILED) instead to prevent concurrent modification. +There are three variants for checking conditional requests against `eTag` values, `lastModified` +values, or both. For conditional `GET` and `HEAD` requests, you can set the response to +304 (NOT_MODIFIED). For conditional `POST`, `PUT`, and `DELETE`, you can instead set the response +to 409 (PRECONDITION_FAILED), to prevent concurrent modification. [[mvc-caching-static-resources]] -=== Static resources -[.small]#<># +=== Static Resources +[.small]#<># -Static resources should be served with a "Cache-Control" and conditional response headers -for optimal performance. See section on configuring <>. +You should serve static resources with a `Cache-Control` and conditional response headers +for optimal performance. See the section on configuring <>. [[mvc-httpcaching-shallowetag]] -=== ETag Filter - -The `ShallowEtagHeaderFilter` can be used to add "shallow" eTag values, computed from the -response content and thus saving bandwidth but not CPU time. See <>. +=== `ETag` Filter +You can use the `ShallowEtagHeaderFilter` to add "`shallow`" `eTag` values that are computed from the +response content and, thus, save bandwidth but not CPU time. See <>. @@ -3920,28 +4117,30 @@ include::webmvc-view.adoc[leveloffset=+1] - [[mvc-config]] == MVC Config -[.small]#<># +[.small]#<># -The MVC Java config and the MVC XML namespace provide default configuration suitable for most -applications along with a configuration API to customize it. +The MVC Java configuration and the MVC XML namespace provide default configuration suitable for most +applications and a configuration API to customize it. -For more advanced customizations, not available in the configuration API, see +For more advanced customizations, which are not available in the configuration API, see <> and <>. -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 <> +You do not need to understand the underlying beans created by the MVC Java configuration and +the MVC namespace. If you want to learn more, see <> and <>. + [[mvc-config-enable]] -=== Enable MVC Config -[.small]#<># +=== Enable MVC Configuration +[.small]#<># -In Java config use the `@EnableWebMvc` annotation: +In Java configuration, you can use the `@EnableWebMvc` annotation to enable MVC +configuration, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3950,9 +4149,12 @@ In Java config use the `@EnableWebMvc` annotation: public class WebConfig { } ---- +==== -In XML use the `` element: +In XML configuration, you can use the `` element to enable MVC +configuration, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -3970,19 +4172,22 @@ In XML use the `` element: ---- +==== -The above registers a number of Spring MVC -<> also adapting to dependencies -available on the classpath: e.g. payload converters for JSON, XML, etc. +The preceding example registers a number of Spring MVC +<> and adapts to dependencies +available on the classpath (for example, payload converters for JSON, XML, and others). [[mvc-config-customize]] -=== MVC Config API -[.small]#<># +=== The MVC Configuration API +[.small]#<># -In Java config implement `WebMvcConfigurer` interface: +In Java configuration, you can implement the `WebMvcConfigurer` interface, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -3993,8 +4198,9 @@ In Java config implement `WebMvcConfigurer` interface: // Implement configuration methods... } ---- +==== -In XML check attributes and sub-elements of ``. You can +In XML, you can check attributes and sub-elements of ``. 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. @@ -4002,15 +4208,17 @@ sub-elements are available. [[mvc-config-conversion]] -=== Type conversion -[.small]#<># +=== Type Conversion +[.small]#<># -By default formatters for `Number` and `Date` types are installed, including support for +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: +In Java configuration, you can register custom formatters and converters, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4024,9 +4232,11 @@ In Java config, register custom formatters and converters: } } ---- +==== -In XML, the same: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4064,26 +4274,26 @@ In XML, the same: ---- - -[NOTE] -==== -See <> -and the `FormattingConversionServiceFactoryBean` for more information on when to use FormatterRegistrars. ==== +NOTE: See <> +and the `FormattingConversionServiceFactoryBean` for more information on when to use FormatterRegistrar implementations. + [[mvc-config-validation]] === Validation -[.small]#<># +[.small]#<># -By default if <> is present -on the classpath -- e.g. Hibernate Validator, the `LocalValidatorFactoryBean` is registered +By default, if <> is present +on the classpath (for example, Hibernate Validator), the `LocalValidatorFactoryBean` is registered as a global <> for use with `@Valid` and `Validated` on controller method arguments. -In Java config, you can customize the global `Validator` instance: +In Java configuration, you can customize the global `Validator` instance, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4097,9 +4307,11 @@ In Java config, you can customize the global `Validator` instance: } } ---- +==== -In XML, the same: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4117,9 +4329,12 @@ In XML, the same: ---- +==== -Note that you can also register ``Validator``'s locally: +Note that you can also register `Validator` implementations locally, as the following +example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4133,20 +4348,20 @@ Note that you can also register ``Validator``'s locally: } ---- - -[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. ==== +TIP: If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and +mark it with `@Primary` in order to avoid conflict with the one declared in the MVC configuration. + [[mvc-config-interceptors]] === Interceptors -In Java config, register interceptors to apply to incoming requests: +In Java configuration, you can register interceptors to apply to incoming requests, as +the following example shows: +==== [source,java,indent=0] [subs="verbatim"] ---- @@ -4162,9 +4377,11 @@ In Java config, register interceptors to apply to incoming requests: } } ---- +==== -In XML, the same: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim"] ---- @@ -4181,27 +4398,30 @@ In XML, the same: ---- +==== [[mvc-config-content-negotiation]] === Content Types -[.small]#<># +[.small]#<># -You can configure how Spring MVC determines the requested media types from the request -- -e.g. `Accept` header, URL path extension, query parameter, etc. +You can configure how Spring MVC determines the requested media types from the request +(for example, `Accept` header, URL path extension, query parameter, and others). -By default 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 +By default, the URL path extension is checked first -- with `json`, `xml`, `rss`, and `atom` +registered as known extensions (depending on classpath dependencies). 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 +Consider changing those defaults to `Accept` header only, and, if you must use URL-based +content type resolution, consider using the query parameter strategy over path extensions. See <> and <> for more details. -In Java config, customize requested content type resolution: +In Java configuration, you can customize requested content type resolution, as the +following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4216,9 +4436,11 @@ In Java config, customize requested content type resolution: } } ---- +==== -In XML, the same: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4233,22 +4455,24 @@ In XML, the same: ---- +==== [[mvc-config-message-converters]] === Message Converters -[.small]#<># +[.small]#<># -Customization of `HttpMessageConverter` can be achieved in Java config by overriding +You can customize `HttpMessageConverter` in Java configuration 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 +(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. +(to customize the default converters 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: +The following example adds XML and Jackson JSON converters with a customized +`ObjectMapper` instead of the default ones: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4267,41 +4491,40 @@ Below is an example that adds Jackson JSON and XML converters with a customized } } ---- +==== -In this example, -{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[Jackson2ObjectMapperBuilder] +In the preceding example, +{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`] is used to create a common configuration for both `MappingJackson2HttpMessageConverter` and -`MappingJackson2XmlHttpMessageConverter` with indentation enabled, a customized date format +`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). +https://github.com/FasterXML/jackson-module-parameter-names[`jackson-module-parameter-names`], +Which adds support for accessing parameter names (a feature added in Java 8). -This builder customizes Jackson's default properties with the following ones: +This builder customizes Jackson's default properties as follows: -. 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. +* 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`. +* https://github.com/FasterXML/jackson-datatype-jdk7[jackson-datatype-jdk7]: Support for Java 7 types, such as `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 and Time API types. +* https://github.com/FasterXML/jackson-datatype-jdk8[jackson-datatype-jdk8]: Support for other Java 8 types, such as `Optional`. -[NOTE] -==== -Enabling indentation with Jackson XML support requires +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) +* 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: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4323,6 +4546,7 @@ It is also possible to do the same in XML: ---- +==== @@ -4330,11 +4554,12 @@ It is also possible to do the same in XML: === 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 +forwards to a view when invoked. You can 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: +The following example of Java configuration forwards a request for `/` to a view called `home`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4348,26 +4573,31 @@ An example of forwarding a request for `"/"` to a view called `"home"` in Java: } } ---- +==== -And the same in XML use the `` element: +The following example achieves the same thing as the preceding example, but with XML, by +using the `` element: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- ---- +==== [[mvc-config-view-resolvers]] === View Resolvers -[.small]#<># +[.small]#<># -The MVC config simplifies the registration of view resolvers. +The MVC configuration simplifies the registration of view resolvers. -The following is a Java config example that configures content negotiation view -resolution using JSP and Jackson as a default `View` for JSON rendering: +The following Java configuration example configures content negotiation view +resolution by using JSP and Jackson as a default `View` for JSON rendering: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4382,9 +4612,11 @@ resolution using JSP and Jackson as a default `View` for JSON rendering: } } ---- +==== -And the same in XML: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4397,12 +4629,14 @@ And the same in XML: ---- +==== -Note however that FreeMarker, Tiles, Groovy Markup and script templates also require +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: +The MVC namespace provides dedicated elements. The following example works with FreeMarker: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4421,9 +4655,12 @@ The MVC namespace provides dedicated elements. For example with FreeMarker: ---- +==== -In Java config simply add the respective "Configurer" bean: +In Java configuration, you can add the respective `Configurer` bean, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4445,25 +4682,27 @@ In Java config simply add the respective "Configurer" bean: } } ---- +==== [[mvc-config-static-resources]] === Static Resources -[.small]#<># +[.small]#<># This option provides a convenient way to serve static resources from a list of -{api-spring-framework}/core/io/Resource.html[Resource]-based locations. +{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 +In the next example, given a request that starts with `/resources`, the relative path is +used to find and serve static resources relative to `/public` under the web application +root or on the classpath under `/static`. The resources are served with a one-year future expiration to ensure maximum use of the browser cache and a reduction in HTTP requests -made by the browser. The `Last-Modified` header is also evaluated and if present a `304` +made by the browser. The `Last-Modified` header is also evaluated and, if present, a `304` status code is returned. -In Java config: +The following listing shows how to do so with Java configuration: +==== [source,java,indent=0] [subs="verbatim"] ---- @@ -4479,9 +4718,11 @@ In Java config: } } ---- +==== -In XML: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4489,22 +4730,24 @@ In XML: location="/public, classpath:/static/" cache-period="31556926" /> ---- +==== See also <>. The resource handler also supports a chain of -{api-spring-framework}/web/servlet/resource/ResourceResolver.html[ResourceResolver]s and -{api-spring-framework}/web/servlet/resource/ResourceTransformer.html[ResourceTransformer]s. -which can be used to create a toolchain for working with optimized resources. +{api-spring-framework}/web/servlet/resource/ResourceResolver.html[`ResourceResolver`] implementations and +{api-spring-framework}/web/servlet/resource/ResourceTransformer.html[`ResourceTransformer`] implementations, +which you can use to create a toolchain for working with optimized resources. -The `VersionResourceResolver` can be used for versioned resource URLs based on an MD5 hash +You can use the `VersionResourceResolver` for versioned resource URLs based on an MD5 hash computed from the content, a fixed application version, or other. A -`ContentVersionStrategy` (MD5 hash) is a good choice with some notable exceptions such as +`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; +The following example shows how to use `VersionResourceResolver` in Java configuration: +==== [source,java,indent=0] [subs="verbatim"] ---- @@ -4521,9 +4764,11 @@ For example in Java config; } } ---- +==== -In XML, the same: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim"] ---- @@ -4538,42 +4783,44 @@ In XML, the same: ---- +==== You can then 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 +transformers -- for example, to insert versions. The MVC configuration provides a `ResourceUrlProvider` +bean so that it can be injected into others. You can also make the rewrite transparent with the `ResourceUrlEncodingFilter` for Thymeleaf, JSPs, FreeMarker, and others with URL tags that rely on `HttpServletResponse#encodeURL`. -Note that when using both `EncodedResourceResolver` (e.g. for serving gzipped or brotli -encoded resources) and `VersionedResourceResolver`, they must be registered in this order. -That ensures content based versions are always computed reliably based on the unencoded file. +Note that, when using both `EncodedResourceResolver` (for example, for serving gzipped or +brotli-encoded resources) and `VersionedResourceResolver`, you must register them in this order. +That ensures content-based versions are always computed reliably, based on the unencoded file. -http://www.webjars.org/documentation[WebJars] is also supported via `WebJarsResourceResolver` -and automatically registered when `"org.webjars:webjars-locator"` is present on the +http://www.webjars.org/documentation[WebJars] is also supported through `WebJarsResourceResolver` +and is 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"`. +match to incoming URLs without versions -- for example, `/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 +Spring MVC allows for mapping the `DispatcherServlet` to `/` (thus overriding the mapping of the container's default Servlet), while still allowing static resource requests to be handled by the container's default Servlet. It configures a -`DefaultServletHttpRequestHandler` with a URL mapping of "/**" and the lowest priority +`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 `` or alternatively if you are setting up your -own customized `HandlerMapping` instance be sure to set its `order` property to a value +This handler forwards all requests to the default Servlet. Therefore, it must +remain last in the order of all other URL `HandlerMappings`. That is the +case if you use ``. Alternatively, if you set up your +own customized `HandlerMapping` instance, be sure to set its `order` property to a value lower than that of the `DefaultServletHttpRequestHandler`, which is `Integer.MAX_VALUE`. -To enable the feature using the default setup use: +The following example shows how to enable the feature by using the default setup: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4587,24 +4834,28 @@ To enable the feature using the default setup use: } } ---- +==== -Or in XML: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- ---- +==== -The caveat to overriding the "/" Servlet mapping is that the `RequestDispatcher` for the +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 +`DefaultServletHttpRequestHandler` tries to auto-detect the default Servlet for the container at startup time, using a list of known names for most of the major Servlet containers (including Tomcat, Jetty, GlassFish, JBoss, Resin, WebLogic, and WebSphere). -If the default Servlet has been custom configured with a different name, or if a +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: +then you must explicitly provide the default Servlet's name, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4619,27 +4870,31 @@ then the default Servlet's name must be explicitly provided as in the following } ---- +==== -Or in XML: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- ---- +==== [[mvc-config-path-matching]] === Path Matching -[.small]#<># +[.small]#<># -Customize options related to path matching, and treatment of the URL. +You can customize options related to path matching and treatment of the URL. For details on the individual options, see the -{api-spring-framework}/web/servlet/config/annotation/PathMatchConfigurer.html[PathMatchConfigurer] Javadoc. +{api-spring-framework}/web/servlet/config/annotation/PathMatchConfigurer.html[`PathMatchConfigurer`] Javadoc. -Example in Java config: +The following example shows how to customize path matching in Java configuration: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4671,9 +4926,11 @@ Example in Java config: } ---- +==== -In XML, the same: +The following example shows how to achieve the same configuration in XML: +==== [source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -4689,20 +4946,24 @@ In XML, the same: ---- +==== [[mvc-config-advanced-java]] -=== Advanced Java Config -[.small]#<># +=== Advanced Java Configuration +[.small]#<># + +`@EnableWebMvc` imports `DelegatingWebMvcConfiguration`, which: -`@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. +* Provides default Spring configuration for Spring MVC applications +* Detects and delegates to `WebMvcConfigurer` implementations to customize that configuration. -For advanced mode, remove `@EnableWebMvc` and extend directly from -`DelegatingWebMvcConfiguration` instead of implementing `WebMvcConfigurer`: +For advanced mode, you can remove `@EnableWebMvc` and extend directly from +`DelegatingWebMvcConfiguration` instead of implementing `WebMvcConfigurer`, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4713,20 +4974,22 @@ For advanced mode, remove `@EnableWebMvc` and extend directly from } ---- +==== -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 +You can keep existing methods in `WebConfig`, but you can now also override bean declarations +from the base class, and you can still have any number of other `WebMvcConfigurer` implementations on the classpath. [[mvc-config-advanced-xml]] -=== Advanced XML Config +=== Advanced XML Configuration 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`: +a bean that you cannot change otherwise, you can use the `BeanPostProcessor` lifecycle +hook of the Spring `ApplicationContext`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -4738,23 +5001,23 @@ hook of the Spring `ApplicationContext`: } } ---- +==== -Note that `MyPostProcessor` needs to be declared as a bean either explicitly in XML or -detected through a `` declaration. - +Note that you need to declare `MyPostProcessor` as a bean, either explicitly in XML or +by letting it be detected through a `` declaration. [[mvc-http2]] == HTTP/2 -[.small]#<># +[.small]#<># -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 +Servlet 4 containers are required to support HTTP/2, and Spring Framework 5 is compatible +with Servlet API 4. From a programming model perspective, there is nothing specific that +applications need to do. However, there are considerations related to server configuration. +For more details, see the https://github.com/spring-projects/spring-framework/wiki/HTTP-2-support[HTTP/2 wiki page]. -The Servlet API does expose one construct related to HTTP/2. The -`javax.servlet.http.PushBuilder` can used to proactively push resources to clients and it +The Servlet API does expose one construct related to HTTP/2. You can use the +`javax.servlet.http.PushBuilder` proactively push resources to clients, and it is supported as a <> to `@RequestMapping` methods. diff --git a/src/docs/asciidoc/web/websocket-intro.adoc b/src/docs/asciidoc/web/websocket-intro.adoc index b74775706c5..27aba897866 100644 --- a/src/docs/asciidoc/web/websocket-intro.adoc +++ b/src/docs/asciidoc/web/websocket-intro.adoc @@ -1,28 +1,35 @@ [[websocket-intro]] -= Introduction += Introduction to WebSocket -The WebSocket protocol http://tools.ietf.org/html/rfc6455[RFC 6455] provides a standardized +The WebSocket protocol, http://tools.ietf.org/html/rfc6455[RFC 6455], provides a standardized way to establish a full-duplex, two-way communication channel between client and server over a single TCP connection. It is a different TCP protocol from HTTP but is designed to work over HTTP, using ports 80 and 443 and allowing re-use of existing firewall rules. -A WebSocket interaction begins with an HTTP request that uses the HTTP `"Upgrade"` header -to upgrade, or in this case to switch, to the WebSocket protocol: +A WebSocket interaction begins with an HTTP request that uses the HTTP `Upgrade` header +to upgrade or, in this case, to switch to the WebSocket protocol. The following example +shows such an interaction: +==== [subs="quotes"] ---- GET /spring-websocket-portfolio/portfolio HTTP/1.1 Host: localhost:8080 -**Upgrade: websocket** -**Connection: Upgrade** +Upgrade: websocket <1> +Connection: Upgrade <2> Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg== Sec-WebSocket-Protocol: v10.stomp, v11.stomp Sec-WebSocket-Version: 13 Origin: http://localhost:8080 ---- +<1> The `Upgrade` header. +<2> Using the `Upgrade` connection. +==== -Instead of the usual 200 status code, a server with WebSocket support returns: +Instead of the usual 200 status code, a server with WebSocket support returns output +similar to the following: +==== [subs="quotes"] ---- **HTTP/1.1 101 Switching Protocols** @@ -31,65 +38,65 @@ Connection: Upgrade Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0= Sec-WebSocket-Protocol: v10.stomp ---- +==== -After a successful handshake the TCP socket underlying the HTTP upgrade request remains -open for both client and server to continue to send and receive messages. +After a successful handshake, the TCP socket underlying the HTTP upgrade request remains +open for both the client and the server to continue to send and receive messages. A complete introduction of how WebSockets work is beyond the scope of this document. -Please read RFC 6455, the WebSocket chapter of HTML5, or one of many introductions and +See RFC 6455, the WebSocket chapter of HTML5, or any of the many introductions and tutorials on the Web. -Note that if a WebSocket server is running behind a web server (e.g. nginx) you will +Note that, if a WebSocket server is running behind a web server (e.g. nginx), you likely need to configure it to pass WebSocket upgrade requests on to the WebSocket -server. Likewise if the application runs in a cloud environment, check the +server. Likewise, if the application runs in a cloud environment, check the instructions of the cloud provider related to WebSocket support. - [[websocket-intro-architecture]] -== HTTP vs WebSocket +== HTTP Versus WebSocket -Even though WebSocket is designed to be HTTP compatible and starts with an HTTP request, +Even though WebSocket is designed to be HTTP-compatible and starts with an HTTP request, it is important to understand that the two protocols lead to very different architectures and application programming models. -In HTTP and REST, an application is modeled as many URLs. To interact with the application +In HTTP and REST, an application is modeled as many URLs. To interact with the application, clients access those URLs, request-response style. Servers route requests to the appropriate handler based on the HTTP URL, method, and headers. -By contrast in WebSockets there is usually just one URL for the initial connect and -subsequently all application messages flow on that same TCP connection. This points to +By contrast, in WebSockets, there is usually only one URL for the initial connect. +Subsequently, all application messages flow on that same TCP connection. This points to an entirely different asynchronous, event-driven, messaging architecture. -WebSocket is also a low-level transport protocol which unlike HTTP does not prescribe -any semantics to the content of messages. That means there is no way to route or process -a message unless client and server agree on message semantics. +WebSocket is also a low-level transport protocol, which, unlike HTTP, does not prescribe +any semantics to the content of messages. That means that there is no way to route or process +a message unless the client and the server agree on message semantics. WebSocket clients and servers can negotiate the use of a higher-level, messaging protocol -(e.g. STOMP), via the `"Sec-WebSocket-Protocol"` header on the HTTP handshake request, -or in the absence of that they need to come up with their own conventions. +(for example, STOMP), through the `Sec-WebSocket-Protocol` header on the HTTP handshake request. +In the absence of that, they need to come up with their own conventions. [[websocket-intro-when-to-use]] -== When to use it? +== When to Use WebSockets -WebSockets can make a web page dynamic and interactive. However in many cases -a combination of Ajax and HTTP streaming and/or long polling could provide a simple and +WebSockets can make a web page be dynamic and interactive. However, in many cases, +a combination of Ajax and HTTP streaming or long polling can provide a simple and effective solution. -For example news, mail, and social feeds need to update dynamically but it may be -perfectly okay to do so every few minutes. Collaboration, games, and financial apps on -the other hand need to be much closer to real time. +For example, news, mail, and social feeds need to update dynamically, but it may be +perfectly okay to do so every few minutes. Collaboration, games, and financial apps, on +the other hand, need to be much closer to real-time. -Latency alone is not a deciding factor. If the volume of messages is relatively low (e.g. -monitoring network failures) HTTP streaming or polling may provide an effective solution. -It is the combination of low latency, high frequency and high volume that make the best -case for the use WebSocket. +Latency alone is not a deciding factor. If the volume of messages is relatively low (for example, +monitoring network failures) HTTP streaming or polling can provide an effective solution. +It is the combination of low latency, high frequency, and high volume that make the best +case for the use of WebSocket. -Keep in mind also that over the Internet, restrictive proxies outside your control, -may preclude WebSocket interactions either because they are not configured to pass on the -`Upgrade` header or because they close long lived connections that appear idle? This +Keep in mind also that over the Internet, restrictive proxies that are outside of your control +may preclude WebSocket interactions, either because they are not configured to pass on the +`Upgrade` header or because they close long-lived connections that appear idle. This means that the use of WebSocket for internal applications within the firewall is a more -straight-forward decision than it is for public facing applications. +straightforward decision than it is for public facing applications. diff --git a/src/docs/asciidoc/web/websocket.adoc b/src/docs/asciidoc/web/websocket.adoc index 76b53381b69..c65754a2acb 100644 --- a/src/docs/asciidoc/web/websocket.adoc +++ b/src/docs/asciidoc/web/websocket.adoc @@ -1,11 +1,12 @@ [[websocket]] = WebSockets :doc-spring-security: {doc-root}/spring-security/site/docs/current/reference -[.small]#<># +[.small]#<># This part of the reference documentation covers support for Servlet stack, WebSocket -messaging that includes raw WebSocket interactions, WebSocket emulation via SockJS, and -pub-sub messaging via STOMP as a sub-protocol over WebSocket. +messaging that includes raw WebSocket interactions, WebSocket emulation through SockJS, and +publish-subscribe messaging through STOMP as a sub-protocol over WebSocket. + include::websocket-intro.adoc[leveloffset=+1] @@ -14,20 +15,22 @@ include::websocket-intro.adoc[leveloffset=+1] [[websocket-server]] == WebSocket API -[.small]#<># +[.small]#<># -The Spring Framework provides a WebSocket API that can be used to write client and -server side applications that handle WebSocket messages. +The Spring Framework provides a WebSocket API that you can use to write client- and +server-side applications that handle WebSocket messages. [[websocket-server-handler]] -=== WebSocketHandler -[.small]#<># +=== Using `WebSocketHandler` +[.small]#<># -Creating a WebSocket server is as simple as implementing `WebSocketHandler` or more -likely extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`: +Creating a WebSocket server is as simple as implementing `WebSocketHandler` or, more +likely, extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`. The following +example uses `TextWebSocketHandler`: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -44,10 +47,12 @@ likely extending either `TextWebSocketHandler` or `BinaryWebSocketHandler`: } ---- +==== -There is dedicated WebSocket Java-config and XML namespace support for mapping the above -WebSocket handler to a specific URL: +There is dedicated WebSocket Java configuration and XML namespace support for mapping the preceding +WebSocket handler to a specific URL, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -71,9 +76,11 @@ WebSocket handler to a specific URL: } ---- +==== -XML configuration equivalent: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -94,25 +101,27 @@ XML configuration equivalent: ---- +==== -The above is for use in Spring MVC applications and should be included in the -configuration of a <>. However, Spring's WebSocket +The pereceding example is for use in Spring MVC applications and should be included in the +configuration of a <>. However, Spring's WebSocket support does not depend on Spring MVC. It is relatively simple to integrate a `WebSocketHandler` -into other HTTP serving environments with the help of -{api-spring-framework}/web/socket/server/support/WebSocketHttpRequestHandler.html[WebSocketHttpRequestHandler]. +into other HTTP-serving environments with the help of +{api-spring-framework}/web/socket/server/support/WebSocketHttpRequestHandler.html[`WebSocketHttpRequestHandler`]. [[websocket-server-handshake]] === WebSocket Handshake -[.small]#<># +[.small]#<># The easiest way to customize the initial HTTP WebSocket handshake request is through -a `HandshakeInterceptor`, which exposes "before" and "after" the handshake methods. -Such an interceptor can be used to preclude the handshake or to make any attributes -available to the `WebSocketSession`. For example, there is a built-in interceptor -for passing HTTP session attributes to the WebSocket session: +a `HandshakeInterceptor`, which exposes methiods for "`before`" and "`after`" the handshake. +You can use such an interceptor to preclude the handshake or to make any attributes +available to the `WebSocketSession`. The following example uses a built-in interceptor +to pass HTTP session attributes to the WebSocket session: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -128,9 +137,11 @@ for passing HTTP session attributes to the WebSocket session: } ---- +==== -And the XML configuration equivalent: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -154,26 +165,24 @@ And the XML configuration equivalent: ---- +==== A more advanced option is to extend the `DefaultHandshakeHandler` that performs the steps of the WebSocket handshake, including validating the client origin, -negotiating a sub-protocol, and others. An application may also need to use this +negotiating a sub-protocol, and other details. An application may also need to use this option if it needs to configure a custom `RequestUpgradeStrategy` in order to adapt to a WebSocket server engine and version that is not yet supported -(also see <> for more on this subject). -Both the Java-config and XML namespace make it possible to configure a custom +(see <> for more on this subject). +Both the Java configuration and XML namespace make it possible to configure a custom `HandshakeHandler`. -[TIP] -==== -Spring provides a `WebSocketHandlerDecorator` base class that can be used to decorate +TIP: Spring provides a `WebSocketHandlerDecorator` base class that you can use to decorate a `WebSocketHandler` with additional behavior. Logging and exception handling -implementations are provided and added by default when using the WebSocket Java-config +implementations are provided and added by default when using the WebSocket Java configuration or XML namespace. The `ExceptionWebSocketHandlerDecorator` catches all uncaught -exceptions arising from any WebSocketHandler method and closes the WebSocket -session with status `1011` that indicates a server error. -==== +exceptions that arise from any `WebSocketHandler` method and closes the WebSocket +session with status `1011`, which indicates a server error. @@ -181,42 +190,40 @@ session with status `1011` that indicates a server error. === Deployment The Spring WebSocket API is easy to integrate into a Spring MVC application where -the `DispatcherServlet` serves both HTTP WebSocket handshake as well as other +the `DispatcherServlet` serves both HTTP WebSocket handshake and other HTTP requests. It is also easy to integrate into other HTTP processing scenarios by invoking `WebSocketHttpRequestHandler`. This is convenient and easy to understand. However, special considerations apply with regards to JSR-356 runtimes. The Java WebSocket API (JSR-356) provides two deployment mechanisms. The first -involves a Servlet container classpath scan (Servlet 3 feature) at startup; and -the other is a registration API to use at Servlet container initialization. -Neither of these mechanism makes it possible to use a single "front controller" +involves a Servlet container classpath scan (a Servlet 3 feature) at startup. +The other is a registration API to use at Servlet container initialization. +Neither of these mechanism makes it possible to use a single "`front controller`" for all HTTP processing -- including WebSocket handshake and all other HTTP requests -- such as Spring MVC's `DispatcherServlet`. -This is a significant limitation of JSR-356 that Spring's WebSocket support addresses -server-specific ``RequestUpgradeStrategy``'s even when running in a JSR-356 runtime. +This is a significant limitation of JSR-356 that Spring's WebSocket support addresses with +server-specific `RequestUpgradeStrategy` implementations even when running in a JSR-356 runtime. Such strategies currently exist for Tomcat, Jetty, GlassFish, WebLogic, WebSphere, and Undertow (and WildFly). -[NOTE] -==== -A request to overcome the above limitation in the Java WebSocket API has been +NOTE: A request to overcome the preceding limitation in the Java WebSocket API has been created and can be followed at https://github.com/eclipse-ee4j/websocket-api/issues/211[WEBSOCKET_SPEC-211]. -Tomcat, Undertow and WebSphere provide their own API alternatives that -makes it possible to this, and it's also possible with Jetty. We are hopeful -that more servers will follow do the same. -==== +Tomcat, Undertow, and WebSphere provide their own API alternatives that +make it possible to do this, and it is also possible with Jetty. We are hopeful +that more servers will do the same. A secondary consideration is that Servlet containers with JSR-356 support are expected to perform a `ServletContainerInitializer` (SCI) scan that can slow down application -startup, in some cases dramatically. If a significant impact is observed after an +startup -- in some cases, dramatically. If a significant impact is observed after an upgrade to a Servlet container version with JSR-356 support, it should be possible to selectively enable or disable web fragments (and SCI scanning) -through the use of the `` element in `web.xml`: +through the use of the `` element in `web.xml`, as the following example shows: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -231,11 +238,13 @@ through the use of the `` element in `web.xml`: ---- +==== You can then selectively enable web fragments by name, such as Spring's own `SpringServletContainerInitializer` that provides support for the Servlet 3 -Java initialization API, if required: +Java initialization API. The following example shows how to do so: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -252,20 +261,22 @@ Java initialization API, if required: ---- +==== [[websocket-server-runtime-configuration]] -=== Server config -[.small]#<># +=== Server Configuration +[.small]#<># Each underlying WebSocket engine exposes configuration properties that control -runtime characteristics such as the size of message buffer sizes, idle timeout, +runtime characteristics, such as the size of message buffer sizes, idle timeout, and others. -For Tomcat, WildFly, and GlassFish add a `ServletServerContainerFactoryBean` to your -WebSocket Java config: +For Tomcat, WildFly, and GlassFish, you can add a `ServletServerContainerFactoryBean` to your +WebSocket Java config, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -283,9 +294,11 @@ WebSocket Java config: } ---- +==== -or WebSocket XML namespace: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -305,16 +318,16 @@ or WebSocket XML namespace: ---- - -[NOTE] -==== -For client side WebSocket configuration, you should use `WebSocketContainerFactoryBean` -(XML) or `ContainerProvider.getWebSocketContainer()` (Java config). ==== -For Jetty, you'll need to supply a pre-configured Jetty `WebSocketServerFactory` and plug -that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config: +NOTE: For client-side WebSocket configuration, you should use `WebSocketContainerFactoryBean` +(XML) or `ContainerProvider.getWebSocketContainer()` (Java configuration). +For Jetty, you need to supply a pre-configured Jetty `WebSocketServerFactory` and plug +that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config. +The following example shows how to do so: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -341,9 +354,11 @@ that into Spring's `DefaultHandshakeHandler` through your WebSocket Java config: } ---- +==== -or WebSocket XML namespace: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -381,34 +396,36 @@ or WebSocket XML namespace: ---- +==== [[websocket-server-allowed-origins]] -=== Allowed origins -[.small]#<># +=== Allowed Origins +[.small]#<># As of Spring Framework 4.1.5, the default behavior for WebSocket and SockJS is to accept -only _same origin_ requests. It is also possible to allow _all_ or a specified list of origins. -This check is mostly designed for browser clients. There is nothing preventing other types +only same-origin requests. It is also possible to allow all or a specified list of origins. +This check is mostly designed for browser clients. Nothing prevents other types of clients from modifying the `Origin` header value (see https://tools.ietf.org/html/rfc6454[RFC 6454: The Web Origin Concept] for more details). -The 3 possible behaviors are: +The three possible behaviors are: - * Allow only same origin requests (default): in this mode, when SockJS is enabled, the + * Allow only same-origin requests (default): In this mode, when SockJS is enabled, the Iframe HTTP response header `X-Frame-Options` is set to `SAMEORIGIN`, and JSONP - transport is disabled since it does not allow to check the origin of a request. + transport is disabled, since it does not allow checking the origin of a request. As a consequence, IE6 and IE7 are not supported when this mode is enabled. - * Allow a specified list of origins: each provided _allowed origin_ must start with `http://` + * Allow a specified list of origins: Each allowed origin must start with `http://` or `https://`. In this mode, when SockJS is enabled, IFrame transport is disabled. As a consequence, IE6 through IE9 are not supported when this mode is enabled. - * Allow all origins: to enable this mode, you should provide `{asterisk}` as the allowed origin + * Allow all origins: To enable this mode, you should provide `{asterisk}` as the allowed origin value. In this mode, all transports are available. -WebSocket and SockJS allowed origins can be configured as shown bellow: +You can configure WebSocket and SockJS allowed origins, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -432,9 +449,11 @@ WebSocket and SockJS allowed origins can be configured as shown bellow: } ---- +==== -XML configuration equivalent: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -455,7 +474,7 @@ XML configuration equivalent: ---- - +==== @@ -463,22 +482,23 @@ XML configuration equivalent: == SockJS Fallback Over the public Internet, restrictive proxies outside your control may preclude WebSocket -interactions either because they are not configured to pass on the `Upgrade` header or -because they close long lived connections that appear idle. +interactions, either because they are not configured to pass on the `Upgrade` header or +because they close long-lived connections that appear to be idle. -The solution to this problem is WebSocket emulation, i.e. attempting to use WebSocket +The solution to this problem is WebSocket emulation -- that is, attempting to use WebSocket first and then falling back on HTTP-based techniques that emulate a WebSocket interaction and expose the same application-level API. -On the Servlet stack the Spring Framework provides both server (and also client) support +On the Servlet stack, the Spring Framework provides both server (and also client) support for the SockJS protocol. + [[websocket-fallback-sockjs-overview]] === Overview The goal of SockJS is to let applications use a WebSocket API but fall back to -non-WebSocket alternatives when necessary at runtime, i.e. without the need to +non-WebSocket alternatives when necessary at runtime, without the need to change application code. SockJS consists of: @@ -486,61 +506,67 @@ SockJS consists of: * The https://github.com/sockjs/sockjs-protocol[SockJS protocol] defined in the form of executable http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html[narrated tests]. -* The https://github.com/sockjs/sockjs-client/[SockJS JavaScript client] - a client library for use in browsers. -* SockJS server implementations including one in the Spring Framework `spring-websocket` module. -* As of 4.1 `spring-websocket` also provides a SockJS Java client. +* The https://github.com/sockjs/sockjs-client/[SockJS JavaScript client] -- a client library for use in browsers. +* SockJS server implementations, including one in the Spring Framework `spring-websocket` module. +* A SockJS Java client in the `spring-websocket` module (since version 4.1). -SockJS is designed for use in browsers. It goes to great lengths -to support a wide range of browser versions using a variety of techniques. -For the full list of SockJS transport types and browsers see the +SockJS is designed for use in browsers. It uses a variety of techniques +to support a wide range of browser versions. +For the full list of SockJS transport types and browsers, see the https://github.com/sockjs/sockjs-client/[SockJS client] page. Transports -fall in 3 general categories: WebSocket, HTTP Streaming, and HTTP Long Polling. -For an overview of these categories see +fall in three general categories: WebSocket, HTTP Streaming, and HTTP Long Polling. +For an overview of these categories, see https://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[this blog post]. -The SockJS client begins by sending `"GET /info"` to -obtain basic information from the server. After that it must decide what transport -to use. If possible WebSocket is used. If not, in most browsers -there is at least one HTTP streaming option and if not then HTTP (long) +The SockJS client begins by sending `GET /info` to +obtain basic information from the server. After that, it must decide what transport +to use. If possible, WebSocket is used. If not, in most browsers, +there is at least one HTTP streaming option. If not, then HTTP (long) polling is used. All transport requests have the following URL structure: + +==== ---- http://host:port/myApp/myEndpoint/{server-id}/{session-id}/{transport} ---- -* `{server-id}` - useful for routing requests in a cluster but not used otherwise. -* `{session-id}` - correlates HTTP requests belonging to a SockJS session. -* `{transport}` - indicates the transport type, e.g. "websocket", "xhr-streaming", etc. +where: + +* `{server-id}` is useful for routing requests in a cluster but is not used otherwise. +* `{session-id}` correlates HTTP requests belonging to a SockJS session. +* `{transport}` indicates the transport type (for example, `websocket`, `xhr-streaming`, and others). +==== The WebSocket transport needs only a single HTTP request to do the WebSocket handshake. All messages thereafter are exchanged on that socket. -HTTP transports require more requests. Ajax/XHR streaming for example relies on +HTTP transports require more requests. Ajax/XHR streaming, for example, relies on one long-running request for server-to-client messages and additional HTTP POST -requests for client-to-server messages. Long polling is similar except it +requests for client-to-server messages. Long polling is similar, except that it ends the current request after each server-to-client send. -SockJS adds minimal message framing. For example the server sends the letter +o+ -("open" frame) initially, messages are sent as +a["message1","message2"]+ -(JSON-encoded array), the letter +h+ ("heartbeat" frame) if no messages flow -for 25 seconds by default, and the letter +c+ ("close" frame) to close the session. +SockJS adds minimal message framing. For example, the server sends the letter `o` +("`open`" frame) initially, messages are sent as `a["message1","message2"]` +(JSON-encoded array), the letter `h` ("`heartbeat`" frame) if no messages flow +for 25 seconds (by default), and the letter `c` ("`close`" frame) to close the session. To learn more, run an example in a browser and watch the HTTP requests. -The SockJS client allows fixing the list of transports so it is possible to -see each transport one at a time. The SockJS client also provides a debug flag -which enables helpful messages in the browser console. On the server side enable +The SockJS client allows fixing the list of transports, so it is possible to +see each transport one at a time. The SockJS client also provides a debug flag, +which enables helpful messages in the browser console. On the server side, you can enable `TRACE` logging for `org.springframework.web.socket`. -For even more detail refer to the SockJS protocol +For even more detail, see the SockJS protocol http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html[narrated test]. [[websocket-fallback-sockjs-enable]] -=== Enable SockJS +=== Enabling SockJS -SockJS is easy to enable through Java configuration: +You can enable SockJS through Java configuration, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -560,9 +586,11 @@ SockJS is easy to enable through Java configuration: } ---- +==== -and the XML configuration equivalent: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -584,49 +612,50 @@ and the XML configuration equivalent: ---- +==== -The above is for use in Spring MVC applications and should be included in the -configuration of a <>. However, Spring's WebSocket +The preceding example is for use in Spring MVC applications and should be included in the +configuration of a <>. However, Spring's WebSocket and SockJS support does not depend on Spring MVC. It is relatively simple to integrate into other HTTP serving environments with the help of -{api-spring-framework}/web/socket/sockjs/support/SockJsHttpRequestHandler.html[SockJsHttpRequestHandler]. +{api-spring-framework}/web/socket/sockjs/support/SockJsHttpRequestHandler.html[`SockJsHttpRequestHandler`]. On the browser side, applications can use the -https://github.com/sockjs/sockjs-client/[sockjs-client] (version 1.0.x) that +https://github.com/sockjs/sockjs-client/[`sockjs-client`] (version 1.0.x). It emulates the W3C WebSocket API and communicates with the server to select the best -transport option depending on the browser it's running in. Review the +transport option, depending on the browser in which it runs. See the https://github.com/sockjs/sockjs-client/[sockjs-client] page and the list of transport types supported by browser. The client also provides several -configuration options, for example, to specify which transports to include. +configuration options -- for example, to specify which transports to include. [[websocket-fallback-xhr-vs-iframe]] -=== IE 8, 9 +=== IE 8 and 9 -Internet Explorer 8 and 9 are and will remain common for some time. They are +Internet Explorer 8 and 9 remain in use. They are a key reason for having SockJS. This section covers important considerations about running in those browsers. -The SockJS client supports Ajax/XHR streaming in IE 8 and 9 via Microsoft's -http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx[XDomainRequest]. +The SockJS client supports Ajax/XHR streaming in IE 8 and 9 by using Microsoft's +http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx[`XDomainRequest`]. That works across domains but does not support sending cookies. -Cookies are very often essential for Java applications. -However since the SockJS client can be used with many server +Cookies are often essential for Java applications. +However, since the SockJS client can be used with many server types (not just Java ones), it needs to know whether cookies matter. -If so the SockJS client prefers Ajax/XHR for streaming or otherwise it -relies on a iframe-based technique. +If so, the SockJS client prefers Ajax/XHR for streaming. Otherwise, it +relies on an iframe-based technique. -The very first `"/info"` request from the SockJS client is a request for +The first `/info` request from the SockJS client is a request for information that can influence the client's choice of transports. -One of those details is whether the server application relies on cookies, -e.g. for authentication purposes or clustering with sticky sessions. +One of those details is whether the server application relies on cookies +(for example, for authentication purposes or clustering with sticky sessions). Spring's SockJS support includes a property called `sessionCookieNeeded`. -It is enabled by default since most Java applications rely on the `JSESSIONID` -cookie. If your application does not need it, you can turn off this option -and the SockJS client should choose `xdr-streaming` in IE 8 and 9. +It is enabled by default, since most Java applications rely on the `JSESSIONID` +cookie. If your application does not need it, you can turn off this option, +and SockJS client should then choose `xdr-streaming` in IE 8 and 9. -If you do use an iframe-based transport, and in any case, it is good to know +If you do use an iframe-based transport, keep in mind that browsers can be instructed to block the use of IFrames on a given page by setting the HTTP response header `X-Frame-Options` to `DENY`, `SAMEORIGIN`, or `ALLOW-FROM `. This is used to prevent @@ -635,27 +664,27 @@ https://www.owasp.org/index.php/Clickjacking[clickjacking]. [NOTE] ==== Spring Security 3.2+ provides support for setting `X-Frame-Options` on every -response. By default the Spring Security Java config sets it to `DENY`. -In 3.2 the Spring Security XML namespace does not set that header by default -but may be configured to do so, and in the future it may set it by default. +response. By default, the Spring Security Java configuration sets it to `DENY`. +In 3.2, the Spring Security XML namespace does not set that header by default +but can be configured to do so. In the future, it may set it by default. -See {doc-spring-security}/htmlsingle/#headers[Section 7.1. "Default Security Headers"] +See {doc-spring-security}/htmlsingle/#headers[Default Security Headers] of the Spring Security documentation for details on how to configure the -setting of the `X-Frame-Options` header. You may also check or watch +setting of the `X-Frame-Options` header. You can also see https://jira.spring.io/browse/SEC-2501[SEC-2501] for additional background. ==== If your application adds the `X-Frame-Options` response header (as it should!) -and relies on an iframe-based transport, you will need to set the header value to -`SAMEORIGIN` or `ALLOW-FROM `. Along with that the Spring SockJS -support also needs to know the location of the SockJS client because it is loaded -from the iframe. By default the iframe is set to download the SockJS client -from a CDN location. It is a good idea to configure this option to +and relies on an iframe-based transport, you need to set the header value to +`SAMEORIGIN` or `ALLOW-FROM `. The Spring SockJS +support also needs to know the location of the SockJS client, because it is loaded +from the iframe. By default, the iframe is set to download the SockJS client +from a CDN location. It is a good idea to configure this option to use a URL from the same origin as the application. -In Java config this can be done as shown below. The XML namespace provides a -similar option via the `` element: +The following example shows how to do so in Java configuration: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -673,14 +702,14 @@ similar option via the `` element: } ---- - -[NOTE] ==== -During initial development, do enable the SockJS client `devel` mode that prevents + +The XML namespace provides a similar option through the `` element. + +NOTE: During initial development, do enable the SockJS client `devel` mode that prevents the browser from caching SockJS requests (like the iframe) that would otherwise be cached. For details on how to enable it see the https://github.com/sockjs/sockjs-client/[SockJS client] page. -==== @@ -688,52 +717,46 @@ https://github.com/sockjs/sockjs-client/[SockJS client] page. === Heartbeats The SockJS protocol requires servers to send heartbeat messages to preclude proxies -from concluding a connection is hung. The Spring SockJS configuration has a property -called `heartbeatTime` that can be used to customize the frequency. By default a -heartbeat is sent after 25 seconds assuming no other messages were sent on that -connection. This 25 seconds value is in line with the following +from concluding that a connection is hung. The Spring SockJS configuration has a property +called `heartbeatTime` that you can use to customize the frequency. By default, a +heartbeat is sent after 25 seconds, assuming no other messages were sent on that +connection. This 25-second value is in line with the following http://tools.ietf.org/html/rfc6202[IETF recommendation] for public Internet applications. -[NOTE] -==== -When using STOMP over WebSocket/SockJS, if the STOMP client and server negotiate +NOTE: When using STOMP over WebSocket and SockJS, if the STOMP client and server negotiate heartbeats to be exchanged, the SockJS heartbeats are disabled. -==== -The Spring SockJS support also allows configuring the `TaskScheduler` to use -for scheduling heartbeats tasks. The task scheduler is backed by a thread pool -with default settings based on the number of available processors. Applications -should consider customizing the settings according to their specific needs. +The Spring SockJS support also lets you configure the `TaskScheduler` to +schedule heartbeats tasks. The task scheduler is backed by a thread pool, +with default settings based on the number of available processors. Your +should consider customizing the settings according to your specific needs. [[websocket-fallback-sockjs-servlet3-async]] -=== Client disconnects +=== Client Disconnects HTTP streaming and HTTP long polling SockJS transports require a connection to remain -open longer than usual. For an overview of these techniques see +open longer than usual. For an overview of these techniques, see https://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[this blog post]. -In Servlet containers this is done through Servlet 3 async support that -allows exiting the Servlet container thread processing a request and continuing +In Servlet containers, this is done through Servlet 3 asynchronous support that +allows exiting the Servlet container thread, processing a request, and continuing to write to the response from another thread. A specific issue is that the Servlet API does not provide notifications for a client -that has gone away, see https://java.net/jira/browse/SERVLET_SPEC-44[SERVLET_SPEC-44]. +that has gone away. See https://java.net/jira/browse/SERVLET_SPEC-44[SERVLET_SPEC-44]. However, Servlet containers raise an exception on subsequent attempts to write -to the response. Since Spring's SockJS Service supports sever-sent heartbeats (every +to the response. Since Spring's SockJS Service supports server-sent heartbeats (every 25 seconds by default), that means a client disconnect is usually detected within that -time period or earlier if messages are sent more frequently. +time period (or earlier, if messages are sent more frequently). -[NOTE] -==== -As a result network IO failures may occur simply because a client has disconnected, which +NOTE: As a result, network I/O failures can occur because a client has disconnected, which can fill the log with unnecessary stack traces. Spring makes a best effort to identify such network failures that represent client disconnects (specific to each server) and log -a minimal message using the dedicated log category `DISCONNECTED_CLIENT_LOG_CATEGORY` -defined in `AbstractSockJsSession`. If you need to see the stack traces, set that +a minimal message by using the dedicated log category, `DISCONNECTED_CLIENT_LOG_CATEGORY` +(defined in `AbstractSockJsSession`). If you need to see the stack traces, you can set that log category to TRACE. -==== @@ -741,57 +764,58 @@ log category to TRACE. === SockJS and CORS If you allow cross-origin requests (see <>), the SockJS protocol -uses CORS for cross-domain support in the XHR streaming and polling transports. Therefore -CORS headers are added automatically unless the presence of CORS headers in the response -is detected. So if an application is already configured to provide CORS support, e.g. -through a Servlet Filter, Spring's SockJsService will skip this part. +uses CORS for cross-domain support in the XHR streaming and polling transports. Therefore, +CORS headers are added automatically, unless the presence of CORS headers in the response +is detected. So, if an application is already configured to provide CORS support (for example, +through a Servlet Filter), Spring's `SockJsService` skips this part. -It is also possible to disable the addition of these CORS headers via the +It is also possible to disable the addition of these CORS headers by setting the `suppressCors` property in Spring's SockJsService. -The following is the list of headers and values expected by SockJS: +SockJS expects the following headers and values: -* `"Access-Control-Allow-Origin"` - initialized from the value of the "Origin" request header. -* `"Access-Control-Allow-Credentials"` - always set to `true`. -* `"Access-Control-Request-Headers"` - initialized from values from the equivalent request header. -* `"Access-Control-Allow-Methods"` - the HTTP methods a transport supports (see `TransportType` enum). -* `"Access-Control-Max-Age"` - set to 31536000 (1 year). +* `Access-Control-Allow-Origin`: Initialized from the value of the `Origin` request header. +* `Access-Control-Allow-Credentials`: Always set to `true`. +* `Access-Control-Request-Headers`: Initialized from values from the equivalent request header. +* `Access-Control-Allow-Methods`: The HTTP methods a transport supports (see `TransportType` enum). +* `Access-Control-Max-Age`: Set to 31536000 (1 year). -For the exact implementation see `addCorsHeaders` in `AbstractSockJsService` as well -as the `TransportType` enum in the source code. +For the exact implementation, see `addCorsHeaders` in `AbstractSockJsService` and +the `TransportType` enum in the source code. -Alternatively if the CORS configuration allows it consider excluding URLs with the -SockJS endpoint prefix thus letting Spring's `SockJsService` handle it. +Alternatively, if the CORS configuration allows it, consider excluding URLs with the +SockJS endpoint prefix, thus letting Spring's `SockJsService` handle it. [[websocket-fallback-sockjs-client]] -=== SockJsClient +=== Using `SockJsClient` -A SockJS Java client is provided in order to connect to remote SockJS endpoints without +Spring provides a SockJS Java client to connect to remote SockJS endpoints without using a browser. This can be especially useful when there is a need for bidirectional -communication between 2 servers over a public network, i.e. where network proxies may -preclude the use of the WebSocket protocol. A SockJS Java client is also very useful -for testing purposes, for example to simulate a large number of concurrent users. +communication between two servers over a public network (that is, where network proxies can +preclude the use of the WebSocket protocol). A SockJS Java client is also very useful +for testing purposes (for example, to simulate a large number of concurrent users). -The SockJS Java client supports the "websocket", "xhr-streaming", and "xhr-polling" +The SockJS Java client supports the `websocket`, `xhr-streaming`, and `xhr-polling` transports. The remaining ones only make sense for use in a browser. -The `WebSocketTransport` can be configured with: +You can configure the `WebSocketTransport` with: -* `StandardWebSocketClient` in a JSR-356 runtime -* `JettyWebSocketClient` using the Jetty 9+ native WebSocket API -* Any implementation of Spring's `WebSocketClient` +* `StandardWebSocketClient` in a JSR-356 runtime. +* `JettyWebSocketClient` by using the Jetty 9+ native WebSocket API. +* Any implementation of Spring's `WebSocketClient`. -An `XhrTransport` by definition supports both "xhr-streaming" and "xhr-polling" since -from a client perspective there is no difference other than in the URL used to connect +An `XhrTransport`, by definition, supports both `xhr-streaming` and `xhr-polling`, since, +from a client perspective, there is no difference other than in the URL used to connect to the server. At present there are two implementations: * `RestTemplateXhrTransport` uses Spring's `RestTemplate` for HTTP requests. * `JettyXhrTransport` uses Jetty's `HttpClient` for HTTP requests. -The example below shows how to create a SockJS client and connect to a SockJS endpoint: +The following example shows how to create a SockJS client and connect to a SockJS endpoint: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -802,18 +826,17 @@ The example below shows how to create a SockJS client and connect to a SockJS en SockJsClient sockJsClient = new SockJsClient(transports); sockJsClient.doHandshake(new MyWebSocketHandler(), "ws://example.com:8080/sockjs"); ---- - -[NOTE] ==== -SockJS uses JSON formatted arrays for messages. By default Jackson 2 is used and needs -to be on the classpath. Alternatively you can configure a custom implementation of + +NOTE: SockJS uses JSON formatted arrays for messages. By default, Jackson 2 is used and needs +to be on the classpath. Alternatively, you can configure a custom implementation of `SockJsMessageCodec` and configure it on the `SockJsClient`. -==== -To use the SockJsClient for simulating a large number of concurrent users you will +To use `SockJsClient` to simulate a large number of concurrent users, you need to configure the underlying HTTP client (for XHR transports) to allow a sufficient -number of connections and threads. For example with Jetty: +number of connections and threads. The following example shows how to do so with Jetty: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -821,9 +844,11 @@ HttpClient jettyHttpClient = new HttpClient(); jettyHttpClient.setMaxConnectionsPerDestination(1000); jettyHttpClient.setExecutor(new QueuedThreadPool(1000)); ---- +==== -Consider also customizing these server-side SockJS related properties (see Javadoc for details): +The following example shows the server-side SockJS-related properties (see Javadoc for details) that you should also consider customizing: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -833,44 +858,49 @@ Consider also customizing these server-side SockJS related properties (see Javad @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/sockjs").withSockJS() - .setStreamBytesLimit(512 * 1024) - .setHttpMessageCacheSize(1000) - .setDisconnectDelay(30 * 1000); + .setStreamBytesLimit(512 * 1024) <1> + .setHttpMessageCacheSize(1000) <2> + .setDisconnectDelay(30 * 1000); <3> } // ... } ---- - +<1> Set the `streamBytesLimit` property to 512KB (the default is 128KB -- `128 * 1024`). +<2> Set the `httpMessageCacheSize` property to 1,000 (the default is `100`). +<3> Set the `disconnectDelay` property to 30 property seconds (the default is five seconds +-- `5 * 1000`). +==== [[websocket-stomp]] == STOMP -The WebSocket protocol defines two types of messages, text and binary, but their -content is undefined. The defines a mechanism for client and server to negotiate a -sub-protocol -- i.e. a higher level messaging protocol, to use on top of WebSocket to -define what kind of messages each can send, what is the format and content for each -message, and so on. The use of a sub-protocol is optional but either way client and -server will need to agree on some protocol that defines message content. +The WebSocket protocol defines two types of messages (text and binary), but their +content is undefined. The protocol defines a mechanism for client and server to negotiate a +sub-protocol (that is, a higher-level messaging protocol) to use on top of WebSocket to +define what kind of messages each can send, what the format is, the content of each +message, and so on. The use of a sub-protocol is optional but, either way, the client and +the server need to agree on some protocol that defines message content. [[websocket-stomp-overview]] === Overview -http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple, -text-oriented messaging protocol that was originally created for scripting languages -such as Ruby, Python, and Perl to connect to enterprise message brokers. It is +http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] (Simple +Text Oriented Messaging Protocol) was originally created for scripting languages +(such as Ruby, Python, and Perl) to connect to enterprise message brokers. It is designed to address a minimal subset of commonly used messaging patterns. STOMP can be -used over any reliable, 2-way streaming network protocol such as TCP and WebSocket. +used over any reliable two-way streaming network protocol, such as TCP and WebSocket. Although STOMP is a text-oriented protocol, message payloads can be either text or binary. -STOMP is a frame based protocol whose frames are modeled on HTTP. The structure +STOMP is a frame-based protocol whose frames are modeled on HTTP. The following listing shows the structure of a STOMP frame: +==== ---- COMMAND header1:value1 @@ -878,29 +908,31 @@ header2:value2 Body^@ ---- +==== -Clients can use the +SEND+ or +SUBSCRIBE+ commands to send or subscribe for -messages along with a +"destination"+ header that describes what the +Clients can use the `SEND` or `SUBSCRIBE` commands to send or subscribe for +messages, along with a `destination` header that describes what the message is about and who should receive it. This enables a simple -publish-subscribe mechanism that can be used to send messages through the broker +publish-subscribe mechanism that you can use to send messages through the broker to other connected clients or to send messages to the server to request that some work be performed. -When using Spring's STOMP support, the Spring WebSocket application acts +When you use Spring's STOMP support, the Spring WebSocket application acts as the STOMP broker to clients. Messages are routed to `@Controller` message-handling -methods or to a simple, in-memory broker that keeps track of subscriptions and +methods or to a simple in-memory broker that keeps track of subscriptions and broadcasts messages to subscribed users. You can also configure Spring to work -with a dedicated STOMP broker (e.g. RabbitMQ, ActiveMQ, etc) for the actual -broadcasting of messages. In that case Spring maintains -TCP connections to the broker, relays messages to it, and also passes messages -from it down to connected WebSocket clients. Thus Spring web applications can +with a dedicated STOMP broker (such as RabbitMQ, ActiveMQ, and others) for the actual +broadcasting of messages. In that case, Spring maintains +TCP connections to the broker, relays messages to it, and passes messages +from it down to connected WebSocket clients. Thus, Spring web applications can rely on unified HTTP-based security, common validation, and a familiar programming -model message-handling work. +model for message handling. -Here is an example of a client subscribing to receive stock quotes which -the server may emit periodically e.g. via a scheduled task sending messages -through a `SimpMessagingTemplate` to the broker: +The following example shows a client subscribing to receive stock quotes, which +the server may emit periodically (for example, via a scheduled task that sends messages +through a `SimpMessagingTemplate` to the broker): +==== ---- SUBSCRIBE id:sub-1 @@ -908,11 +940,12 @@ destination:/topic/price.stock.* ^@ ---- +==== -Here is an example of a client sending a trade request, which the server -may handle through an `@MessageMapping` method and later on, after the execution, -broadcast a trade confirmation message and details down to the client: +The following example shows a client that sends a trade request, which the server +can handle through an `@MessageMapping` method: +==== ---- SEND destination:/queue/trade @@ -921,17 +954,22 @@ content-length:44 {"action":"BUY","ticker":"MMM","shares",44}^@ ---- +==== + +After the execution, the server can +broadcast a trade confirmation message and details down to the client. The meaning of a destination is intentionally left opaque in the STOMP spec. It can -be any string, and it's entirely up to STOMP servers to define the semantics and +be any string, and it is entirely up to STOMP servers to define the semantics and the syntax of the destinations that they support. It is very common, however, for -destinations to be path-like strings where `"/topic/.."` implies publish-subscribe -(__one-to-many__) and `"/queue/"` implies point-to-point (__one-to-one__) message +destinations to be path-like strings where `/topic/..` implies publish-subscribe +(one-to-many) and `/queue/` implies point-to-point (one-to-one) message exchanges. -STOMP servers can use the +MESSAGE+ command to broadcast messages to all subscribers. -Here is an example of a server sending a stock quote to a subscribed client: +STOMP servers can use the `MESSAGE` command to broadcast messages to all subscribers. +The following example shows a server sending a stock quote to a subscribed client: +==== ---- MESSAGE message-id:nxahklf6-1 @@ -940,14 +978,15 @@ destination:/topic/price.stock.MMM {"ticker":"MMM","price":129.45}^@ ---- +==== -It is important to know that a server cannot send unsolicited messages. All messages +A server cannot send unsolicited messages. All messages from a server must be in response to a specific client subscription, and the -+"subscription-id"+ header of the server message must match the +"id"+ header of the +`subscription-id` header of the server message must match the `id` header of the client subscription. -The above overview is intended to provide the most basic understanding of the -STOMP protocol. It is recommended to review the protocol +The preceding overview is intended to provide the most basic understanding of the +STOMP protocol. We recommended reviewing the protocol http://stomp.github.io/stomp-specification-1.2.html[specification] in full. @@ -955,30 +994,31 @@ http://stomp.github.io/stomp-specification-1.2.html[specification] in full. [[websocket-stomp-benefits]] === Benefits -Use of STOMP as a sub-protocol enables the Spring Framework and Spring Security to -provide a richer programming model vs using raw WebSockets. The same point can be -made about how HTTP vs raw TCP and how it enables Spring MVC and other web frameworks -to provide rich functionality. The following is a list of benefits: +Using STOMP as a sub-protocol lets the Spring Framework and Spring Security +provide a richer programming model versus using raw WebSockets. The same point can be +made about HTTP versus raw TCP and how it lets Spring MVC and other web frameworks +provide rich functionality. The following is a list of benefits: * No need to invent a custom messaging protocol and message format. -* STOMP clients are available including a <> -in the Spring Framework. -* Message brokers such as RabbitMQ, ActiveMQ, and others can be used (optionally) to +* STOMP clients, including a <> +in the Spring Framework, are available. +* You can (optionally) use message brokers (such as RabbitMQ, ActiveMQ, and others) to manage subscriptions and broadcast messages. -* Application logic can be organized in any number of ``@Controller``'s and messages -routed to them based on the STOMP destination header vs handling raw WebSocket messages +* Application logic can be organized in any number of `@Controller` instances and messages can be +routed to them based on the STOMP destination header versus handling raw WebSocket messages with a single `WebSocketHandler` for a given connection. -* Use Spring Security to secure messages based on STOMP destinations and message types. +* You can use Spring Security to secure messages based on STOMP destinations and message types. [[websocket-stomp-enable]] === Enable STOMP -STOMP over WebSocket support is available in the `spring-messaging` and the +STOMP over WebSocket support is available in the `spring-messaging` and `spring-websocket` modules. Once you have those dependencies, you can expose a STOMP -endpoints, over WebSocket with <>, as shown below: +endpoints, over WebSocket with <>, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1002,15 +1042,17 @@ endpoints, over WebSocket with <>, as shown below: } ---- -<1> `"/portfolio"` is the HTTP URL for the endpoint to which a WebSocket (or SockJS) -client will need to connect to for the WebSocket handshake. -<2> STOMP messages whose destination header begins with `"/app"` are routed to +<1> `/portfolio` is the HTTP URL for the endpoint to which a WebSocket (or SockJS) +client needs to connect for the WebSocket handshake. +<2> STOMP messages whose destination header begins with `/app` are routed to `@MessageMapping` methods in `@Controller` classes. -<3> Use the built-in, message broker for subscriptions and broadcasting; -Route messages whose destination header begins with "/topic" or "/queue" to the broker. +<3> Use the built-in message broker for subscriptions and broadcasting and +route messages whose destination header begins with `/topic `or `/queue` to the broker. +==== -The same configuration in XML: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -1032,25 +1074,24 @@ The same configuration in XML: ---- - -[NOTE] ==== -For the built-in, simple broker the "/topic" and "/queue" prefixes do not have any special -meaning. They're merely a convention to differentiate between pub-sub vs point-to-point -messaging (i.e. many subscribers vs one consumer). When using an external broker, please + +NOTE: For the built-in simple broker, the `/topic` and `/queue` prefixes do not have any special +meaning. They are merely a convention to differentiate between pub-sub versus point-to-point +messaging (that is, many subscribers versus one consumer). When you use an external broker, check the STOMP page of the broker to understand what kind of STOMP destinations and prefixes it supports. -==== -To connect from a browser, for SockJS you can use the -https://github.com/sockjs/sockjs-client[sockjs-client]. For STOMP many applications have +To connect from a browser, for SockJS, you can use the +https://github.com/sockjs/sockjs-client[`sockjs-client`]. For STOMP, many applications have used the https://github.com/jmesnil/stomp-websocket[jmesnil/stomp-websocket] library -(also known as stomp.js) which is feature complete and has been used in production for +(also known as stomp.js), which is feature-complete and has been used in production for years but is no longer maintained. At present the https://github.com/JSteunou/webstomp-client[JSteunou/webstomp-client] is the most -actively maintained and evolving successor of that library and the example code below +actively maintained and evolving successor of that library. The following example code is based on it: +==== [source,javascript,indent=0] [subs="verbatim,quotes"] ---- @@ -1060,9 +1101,11 @@ is based on it: stompClient.connect({}, function(frame) { } ---- +==== -Or if connecting via WebSocket (without SockJS): +Alternatively, if you connect through WebSocket (without SockJS), you can use the following code: +==== [source,javascript,indent=0] [subs="verbatim,quotes"] ---- @@ -1072,17 +1115,18 @@ Or if connecting via WebSocket (without SockJS): stompClient.connect({}, function(frame) { } ---- +==== -Note that the `stompClient` above does not need to specify `login` and `passcode` headers. -Even if it did, they would be ignored, or rather overridden, on the server side. See the -sections <> and +Note that `stompClient` in the preceding example does not need to specify `login` and `passcode` headers. +Even if it did, they would be ignored (or, rather, overridden) on the server side. See +<> and <> for more information on authentication. -For a more example code see: +For more example code see: * https://spring.io/guides/gs/messaging-stomp-websocket/[Using WebSocket to build an -interactive web application] getting started guide. -* https://github.com/rstoyanchev/spring-websocket-portfolio[Stock Portfolio] sample +interactive web application] -- a getting started guide. +* https://github.com/rstoyanchev/spring-websocket-portfolio[Stock Portfolio] -- a sample application. @@ -1097,58 +1141,59 @@ The `spring-messaging` module contains foundational support for messaging applic that originated in https://spring.io/spring-integration[Spring Integration] and was later extracted and incorporated into the Spring Framework for broader use across many https://spring.io/projects[Spring projects] and application scenarios. -Below is a list of a few of the available messaging abstractions: - -* {api-spring-framework}/messaging/Message.html[Message] -- -simple representation for a message including headers and payload. -* {api-spring-framework}/messaging/MessageHandler.html[MessageHandler] -- -contract for handling a message. -* {api-spring-framework}/messaging/MessageChannel.html[MessageChannel] -- -contract for sending a message that enables loose coupling between producers and consumers. -* {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel] -- +The following list briefly describes a few of the available messaging abstractions: + +* {api-spring-framework}/messaging/Message.html[Message]: +Simple representation for a message, including headers and payload. +* {api-spring-framework}/messaging/MessageHandler.html[MessageHandler]: +Contract for handling a message. +* {api-spring-framework}/messaging/MessageChannel.html[MessageChannel]: +Contract for sending a message that enables loose coupling between producers and consumers. +* {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel]: `MessageChannel` with `MessageHandler` subscribers. -* {api-spring-framework}/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel] -- +* {api-spring-framework}/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel]: `SubscribableChannel` that uses an `Executor` for delivering messages. -Both the Java config (i.e. `@EnableWebSocketMessageBroker`) and the XML namespace config -(i.e. ``) use the above components to assemble a message -workflow. The diagram below shows the components used when the simple, built-in message +Both the Java configuration (that is, `@EnableWebSocketMessageBroker`) and the XML namespace configuration +(that is,``) use the preceding components to assemble a message +workflow. The following diagram shows the components used when the simple built-in message broker is enabled: image::images/message-flow-simple-broker.png[] -There are 3 message channels in the above diagram: +The preceding diagram shows three message channels: -* `"clientInboundChannel"` -- for passing messages received from WebSocket clients. -* `"clientOutboundChannel"` -- for sending server messages to WebSocket clients. -* `"brokerChannel"` -- for sending messages to the message broker from within -server-side, application code. +* `clientInboundChannel`: For passing messages received from WebSocket clients. +* `clientOutboundChannel`: For sending server messages to WebSocket clients. +* `brokerChannel`: For sending messages to the message broker from within +server-side application code. -The next diagram shows the components used when an external broker (e.g. RabbitMQ) +The next diagram shows the components used when an external broker (such as RabbitMQ) is configured for managing subscriptions and broadcasting messages: image::images/message-flow-broker-relay.png[] -The main difference in the above diagram is the use of the "broker relay" for passing -messages up to the external STOMP broker over TCP, and for passing messages down from the +The main difference between the two preceding diagrams is the use of the "`broker relay`" for passing +messages up to the external STOMP broker over TCP and for passing messages down from the broker to subscribed clients. -When messages are received from a WebSocket connectin, they're decoded to STOMP frames, -then turned into a Spring `Message` representation, and sent to the -`"clientInboundChannel"` for further processing. For example STOMP messages whose -destination header starts with `"/app"` may be routed to `@MessageMapping` methods in -annotated controllers, while `"/topic"` and `"/queue"` messages may be routed directly +When messages are received from a WebSocket connection, they are decoded to STOMP frames, +turned into a Spring `Message` representation, and sent to the +`clientInboundChannel` for further processing. For example, STOMP messages whose +destination headers start with `/app` may be routed to `@MessageMapping` methods in +annotated controllers, while `/topic` and `/queue` messages may be routed directly to the message broker. -An annotated `@Controller` handling a STOMP message from a client may send a message to -the message broker through the `"brokerChannel"`, and the broker will broadcast the -message to matching subscribers through the `"clientOutboundChannel"`. The same -controller can also do the same in response to HTTP requests, so a client may perform an -HTTP POST and then an `@PostMapping` method can send a message to the message broker +An annotated `@Controller` that handles a STOMP message from a client may send a message to +the message broker through the `brokerChannel`, and the broker broadcasts the +message to matching subscribers through the `clientOutboundChannel`. The same +controller can also do the same in response to HTTP requests, so a client can perform an +HTTP POST, and then a `@PostMapping` method can send a message to the message broker to broadcast to subscribed clients. -Let's trace the flow through a simple example. Given the following server setup: +We can trace the flow through a simple example. Consider the following example, which sets up a server: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1180,26 +1225,29 @@ Let's trace the flow through a simple example. Given the following server setup: } ---- +==== + +The preceding example supports the following flow: -. Client connects to `"http://localhost:8080/portfolio"` and once a WebSocket connection +. The client connects to `http://localhost:8080/portfolio` and, once a WebSocket connection is established, STOMP frames begin to flow on it. -. Client sends SUBSCRIBE frame with destination header `"/topic/greeting"`. Once received -and decoded, the message is sent to the `"clientInboundChannel"`, then routed to the -message broker which stores the client subscription. -. Client sends SEND frame to `"/app/greeting"`. The `"/app"` prefix helps to route it to -annotated controllers. After the `"/app"` prefix is stripped, the remaining `"/greeting"` +. The client sends a SUBSCRIBE frame with a destination header of `/topic/greeting`. Once received +and decoded, the message is sent to the `clientInboundChannel` and is then routed to the +message broker, which stores the client subscription. +. The client sends a aSEND frame to `/app/greeting`. The `/app` prefix helps to route it to +annotated controllers. After the `/app` prefix is stripped, the remaining `/greeting` part of the destination is mapped to the `@MessageMapping` method in `GreetingController`. . The value returned from `GreetingController` is turned into a Spring `Message` with a payload based on the return value and a default destination header of -`"/topic/greeting"` (derived from the input destination with `"/app"` replaced by -`"/topic"`). The resulting message is sent to the "brokerChannel" and handled +`/topic/greeting` (derived from the input destination with `/app` replaced by +`/topic`). The resulting message is sent to the `brokerChannel` and handled by the message broker. -. The message broker finds all matching subscribers, and sends a MESSAGE frame to each -through the `"clientOutboundChannel"` from where messages are encoded as STOMP frames +. The message broker finds all matching subscribers and sends a MESSAGE frame to each one +through the `clientOutboundChannel`, from where messages are encoded as STOMP frames and sent on the WebSocket connection. -The next section provides more details on annotated methods including the -kinds of arguments and return values supported. +The next section provides more details on annotated methods, including the +kinds of arguments and return values that are supported. @@ -1208,23 +1256,30 @@ kinds of arguments and return values supported. Applications can use annotated `@Controller` classes to handle messages from clients. Such classes can declare `@MessageMapping`, `@SubscribeMapping`, and `@ExceptionHandler` -methods as described next. +methods, as described in the following topics: + +* <> +* <> +* <> + [[websocket-stomp-message-mapping]] -==== `@MessageMapping` +==== Using `@MessageMapping` -`@MessageMapping` can be used to annotate methods to route messages based on their -destination. It is supported at the method level as well as at the type level. At type -level `@MessageMapping` is used to express shared mappings across all methods in a +You can use `@MessageMapping` to annotate methods that route messages based on their +destination. It is supported at the method level as well as at the type level. At the type +level, `@MessageMapping` is used to express shared mappings across all methods in a controller. -The mapping values are Ant-style path patterns by default, e.g. "/foo*", "/foo/**" -including support for template variables, e.g. "/foo/{id}", that can be referenced via +By default, the mapping values are Ant-style path patterns (for example `/thing*`, `/thing/**`), +including support for template variables (for example, `/thing/{id}`). The values can be referenced through `@DestinationVariable` method arguments. Applications can also switch to a dot-separated destination convention for mappings, as explained in <>. -*Supported Method Arguments* +===== Supported Method Arguments + +The following table describes the method arguments: [cols="1,2", options="header"] |=== @@ -1236,22 +1291,22 @@ destination convention for mappings, as explained in <>. +You can use the `@SendTo` and `@SendToUser` annotations to customize the destination of +the output message. `@SendTo` is used to customize the target destination or to +specify multiple destinations. `@SendToUser` is used to direct the output message +to only the user associated with the input message. See <>. -`@SendTo` and `@SendToUser` may both be used at the same time on the same method, and both -are supported at the class level in which case they act as a default for methods in the -class. However keep in mind that _any_ method-level `@SendTo` or `@SendToUser` annotations -override _any_ such annotations at the class level. +You can use both `@SendTo` and `@SendToUser` at the same time on the same method, and both +are supported at the class level, in which case they act as a default for methods in the +class. However, keep in mind that any method-level `@SendTo` or `@SendToUser` annotations +override any such annotations at the class level. -Messages may be handled asynchronously and a `@MessageMapping` method may return +Messages can be handled asynchronously and a `@MessageMapping` method can return `ListenableFuture`, `CompletableFuture`, or `CompletionStage`. Note that `@SendTo` and `@SendToUser` are merely a convenience that amounts to using the `SimpMessagingTemplate` to send messages. If necessary, for more advanced scenarios, `@MessageMapping` methods can fall back on using the `SimpMessagingTemplate` directly. -This can be done instead of, or possibly in addition to returning a value. +This can be done instead of, or possibly in addition to, returning a value. See <>. [[websocket-stomp-subscribe-mapping]] -==== `@SubscribeMapping` +==== Using `@SubscribeMapping` `@SubscribeMapping` is similar to `@MessageMapping` but narrows the mapping to subscription messages only. It supports the same -<> as `@MessageMapping` does. However -for the return value, by default a message is sent directly to the client via -"clientOutboundChannel" in response to the subscription, and not to the broker via -"brokerChannel" as a broadcast to matching subscriptions. Adding `@SendTo` or +<> as `@MessageMapping`. However +for the return value, by default, a message is sent directly to the client (through +`clientOutboundChannel`, in response to the subscription) and not to the broker (through +`brokerChannel`, as a broadcast to matching subscriptions). Adding `@SendTo` or `@SendToUser` overrides this behavior and sends to the broker instead. -When is this useful? Let's assume the broker is mapped to "/topic" and "/queue" while -application controllers are mapped to "/app". In this setup, the broker *stores* all -subscriptions to "/topic" and "/queue" that are intended for *repeated* broadcasts, and +When is this useful? Assume that the broker is mapped to `/topic` and `/queue`, while +application controllers are mapped to `/app`. In this setup, the broker stores all +subscriptions to `/topic` and `/queue` that are intended for repeated broadcasts, and there is no need for the application to get involved. A client could also also subscribe to -some "/app" destination and a controller could return a value in response to that -subscription without involving the broker, effectively a *one-off*, *request-reply* exchange, -without storing or using the subscription again. One case for this is populating a UI +some `/app` destination, and a controller could return a value in response to that +subscription without involving the broker +without storing or using the subscription again (effectively a one-time request-reply exchange). One use case for this is populating a UI with initial data on startup. When is this not useful? Do not try to map broker and controllers to the same destination -prefix unless you want both to process messages, including subscriptions, independently +prefix unless you want both to independently process messages, including subscriptions, for some reason. Inbound messages are handled in parallel. There are no guarantees whether -broker or controller will process a given message first. If the goal is to be notified -when a subscription is stored and ready for broadcasts, then a client should ask for a -receipt if the server supports it (simple broker does not). For example with the Java -<>: +a broker or a controller processes a given message first. If the goal is to be notified +when a subscription is stored and ready for broadcasts, a client should ask for a +receipt if the server supports it (simple broker does not). For example, with the Java +<>, you could do the following to add a receipt: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1339,6 +1395,7 @@ receipt if the server supports it (simple broker does not). For example with the // Subscription ready... }); ---- +==== A server side option is <> an `ExecutorChannelInterceptor` on the `brokerChannel` and implement the `afterMessageHandled` @@ -1346,12 +1403,14 @@ method that is invoked after messages, including subscriptions, have been handle [[websocket-stomp-exception-handler]] -==== `@MessageExceptionHandler` +==== Using `@MessageExceptionHandler` An application can use `@MessageExceptionHandler` methods to handle exceptions from -`@MessageMapping` methods. Exceptions of interest can be declared in the annotation -itself, or through a method argument if you want to get access to the exception instance: +`@MessageMapping` methods. You can declare exceptions in the annotation +itself or through a method argument if you want to get access to the exception instance. +The following example declares an exception through a method argument: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1367,26 +1426,28 @@ itself, or through a method argument if you want to get access to the exception } } ---- +==== `@MessageExceptionHandler` methods support flexible method signatures and support the same -method argument types and return values as <> methods. +method argument types and return values as <> methods. -Typically `@MessageExceptionHandler` 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`. -This is comparable to <> in Spring MVC. +Typically, `@MessageExceptionHandler` methods apply within the `@Controller` class (or +class hierarchy) in which they are declared. If you want such methods to apply more globally +(across controllers), you can declare them in a class marked with `@ControllerAdvice`. +This is comparable to <> that is available in Spring MVC. [[websocket-stomp-handle-send]] -=== Send Messages +=== Sending Messages What if you want to send messages to connected clients from any part of the -application? Any application component can send messages to the `"brokerChannel"`. -The easiest way to do that is to have a `SimpMessagingTemplate` injected, and -use it to send messages. Typically it should be easy to have it injected by -type, for example: +application? Any application component can send messages to the `brokerChannel`. +The easiest way to do so is to inject a `SimpMessagingTemplate` and +use it to send messages. Typically, you would inject it by +type, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1408,8 +1469,9 @@ type, for example: } ---- +==== -But it can also be qualified by its name "brokerMessagingTemplate" if another +However, you can also qualify it by its name (`brokerMessagingTemplate`), if another bean of the same type exists. @@ -1417,22 +1479,20 @@ bean of the same type exists. [[websocket-stomp-handle-simple-broker]] === Simple Broker -The built-in, simple message broker handles subscription requests from clients, -stores them in memory, and broadcasts messages to connected clients with matching +The built-in simple message broker handles subscription requests from clients, +stores them in memory, and broadcasts messages to connected clients that have matching destinations. The broker supports path-like destinations, including subscriptions to Ant-style destination patterns. -[NOTE] -==== -Applications can also use dot-separated destinations (vs slash). +NOTE: Applications can also use dot-separated (rather than slash-separated) destinations. See <>. -==== If configured with a task scheduler, the simple broker supports https://stomp.github.io/stomp-specification-1.2.html#Heart-beating[STOMP heartbeats]. -For that you can declare your own scheduler, or use the one that's automatically -declared and used internally: +For that, you can declare your own scheduler or use the one that is automatically +declared and used internally. The following example shows how to declare your own scheduler: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1458,6 +1518,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { } } ---- +==== @@ -1465,18 +1526,19 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { === External Broker The simple broker is great for getting started but supports only a subset of -STOMP commands (e.g. no acks, receipts, etc.), relies on a simple message -sending loop, and is not suitable for clustering. As an alternative, applications -can upgrade to using a full-featured message broker. +STOMP commands (it does not support acks, receipts, and some other features), relies on a simple +message-sending loop, and is not suitable for clustering. As an alternative, you can upgrade your applications +to use a full-featured message broker. -Check the STOMP documentation for your message broker of choice (e.g. +See the STOMP documentation for your message broker of choice (such as http://www.rabbitmq.com/stomp.html[RabbitMQ], -http://activemq.apache.org/stomp.html[ActiveMQ], etc.), install the broker, -and run it with STOMP support enabled. Then enable the STOMP broker relay in the -Spring configuration instead of the simple broker. +http://activemq.apache.org/stomp.html[ActiveMQ], and others), install the broker, +and run it with STOMP support enabled. Then you can enable the STOMP broker relay (instead of the simple broker) in the +Spring configuration. -Below is example configuration that enables a full-featured broker: +The following example configuration enables a full-featured broker: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1497,9 +1559,11 @@ Below is example configuration that enables a full-featured broker: } ---- +==== -XML configuration equivalent: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -1521,24 +1585,22 @@ XML configuration equivalent: ---- +==== -The "STOMP broker relay" in the above configuration is a Spring -{api-spring-framework}/messaging/MessageHandler.html[MessageHandler] +The STOMP broker relay in the preceding configuration is a Spring +{api-spring-framework}/messaging/MessageHandler.html[`MessageHandler`] that handles messages by forwarding them to an external message broker. -To do so it establishes TCP connections to the broker, forwards all +To do so, it establishes TCP connections to the broker, forwards all messages to it, and then forwards all messages received -from the broker to clients through their WebSocket sessions. Essentially -it acts as a "relay" that forwards messages in both directions. +from the broker to clients through their WebSocket sessions. Essentially, +it acts as a "`relay`" that forwards messages in both directions. -[NOTE] -==== -Please add `io.projectreactor.netty:reactor-netty` and `io.netty:netty-all` +NOTE: Add `io.projectreactor.netty:reactor-netty` and `io.netty:netty-all` dependencies to your project for TCP connection management. -==== -Furthermore, application components (e.g. HTTP request handling methods, -business services, etc.) can also send messages to the broker relay, as described -in <>, in order to broadcast messages to +Furthermore, application components (such as HTTP request handling methods, +business services, and others) can also send messages to the broker relay, as described +in <>, to broadcast messages to subscribed WebSocket clients. In effect, the broker relay enables robust and scalable message broadcasting. @@ -1546,46 +1608,44 @@ In effect, the broker relay enables robust and scalable message broadcasting. [[websocket-stomp-handle-broker-relay-configure]] -=== Connect to Broker +=== Connecting to a Broker -A STOMP broker relay maintains a single "system" TCP connection to the broker. +A STOMP broker relay maintains a single "`system`" TCP connection to the broker. This connection is used for messages originating from the server-side application -only, not for receiving messages. You can configure the STOMP credentials -for this connection, i.e. the STOMP frame `login` and `passcode` headers. This -is exposed in both the XML namespace and the Java config as the -``systemLogin``/``systemPasscode`` properties with default values ``guest``/``guest``. +only, not for receiving messages. You can configure the STOMP credentials (that is, the STOMP frame `login` and `passcode` headers) +for this connection. This +is exposed in both the XML namespace and Java configuration as the +`systemLogin` and `systemPasscode` properties with default values of `guest` and `guest`. The STOMP broker relay also creates a separate TCP connection for every connected -WebSocket client. You can configure the STOMP credentials to use for all TCP +WebSocket client. You can configure the STOMP credentials that are used for all TCP connections created on behalf of clients. This is exposed in both the XML namespace -and the Java config as the ``clientLogin``/``clientPasscode`` properties with default -values ``guest``/``guest``. +and Java configuration as the `clientLogin and `clientPasscode` properties with default +values of `guest`and `guest`. -[NOTE] -==== -The STOMP broker relay always sets the `login` and `passcode` headers on every `CONNECT` -frame that it forwards to the broker on behalf of clients. Therefore WebSocket clients -need not set those headers; they will be ignored. As the <> -section explains, instead WebSocket clients should rely on HTTP authentication to protect +NOTE: The STOMP broker relay always sets the `login` and `passcode` headers on every `CONNECT` +frame that it forwards to the broker on behalf of clients. Therefore, WebSocket clients +need not set those headers. They are ignored. As the <> +section explains, WebSocket clients should instead rely on HTTP authentication to protect the WebSocket endpoint and establish the client identity. -==== The STOMP broker relay also sends and receives heartbeats to and from the message -broker over the "system" TCP connection. You can configure the intervals for sending +broker over the "`system`" TCP connection. You can configure the intervals for sending and receiving heartbeats (10 seconds each by default). If connectivity to the broker -is lost, the broker relay will continue to try to reconnect, every 5 seconds, +is lost, the broker relay continues to try to reconnect, every 5 seconds, until it succeeds. -Any Spring bean can implement `ApplicationListener` in order -to receive notifications when the "system" connection to the broker is lost and -re-established. For example a Stock Quote service broadcasting stock quotes can -stop trying to send messages when there is no active "system" connection. +Any Spring bean can implement `ApplicationListener` +to receive notifications when the "`system`" connection to the broker is lost and +re-established. For example, a Stock Quote service that broadcasts stock quotes can +stop trying to send messages when there is no active "`system`" connection. -By default, the STOMP broker relay always connects, and reconnects as needed if -connectivity is lost, to the same host and port. If you wish to supply multiple addresses, +By default, the STOMP broker relay always connects (and reconnects as needed if +connectivity is lost) to the same host and port. If you wish to supply multiple addresses, on each attempt to connect, you can configure a supplier of addresses, instead of a -fixed host and port. For example: +fixed host and port. The following example shows how to do so: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1613,25 +1673,27 @@ public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { } } ---- +==== -The STOMP broker relay can also be configured with a `virtualHost` property. -The value of this property will be set as the `host` header of every `CONNECT` frame -and may be useful for example in a cloud environment where the actual host to which -the TCP connection is established is different from the host providing the -cloud-based STOMP service. +You can also configure the STOMP broker relay with a `virtualHost` property. +The value of this property is set as the `host` header of every `CONNECT` frame +and can be useful (for example, in a cloud environment where the actual host to which +the TCP connection is established differs from the host that provides the +cloud-based STOMP service). [[websocket-stomp-destination-separator]] -=== Dot as Separator +=== Dots as Separators -When messages are routed to `@MessageMapping` methods, they're matched with -`AntPathMatcher` and by default patterns are expected to use slash "/" as separator. -This is a good convention in a web applications and similar to HTTP URLs. However if -you are more used to messaging conventions, you can switch to using dot "." as separator. +When messages are routed to `@MessageMapping` methods, they are matched with +`AntPathMatcher`. By default, patterns are expected to use slash (`/`) as the separator. +This is a good convention in web applications and similar to HTTP URLs. However, if +you are more used to messaging conventions, you can switch to using dot (`.`) as the separator. -In Java config: +The following example shows how to do so in Java configuration: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1649,9 +1711,11 @@ In Java config: } } ---- +==== -In XML: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -1677,31 +1741,35 @@ In XML: ---- +==== -After that a controller may use dot "." as separator in `@MessageMapping` methods: +After that, a controller can use a dot (`.`) as the separator in `@MessageMapping` methods, +as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller - @MessageMapping("foo") - public class FooController { + @MessageMapping("erd") + public class RedController { - @MessageMapping("bar.{baz}") - public void handleBaz(@DestinationVariable String baz) { + @MessageMapping("blue.{green}") + public void handleGreen(@DestinationVariable String green) { // ... } } ---- +==== -The client can now send a message to `"/app/foo.bar.baz123"`. +The client can now send a message to `/app/red.blue.green123`. -In the example above we did not change the prefixes on the "broker relay" because those -depend entirely on the external message broker. Check the STOMP documentation pages of -the broker you're using to see what conventions it supports for the destination header. +In the preceding example, we did not change the prefixes on the "`broker relay`", because those +depend entirely on the external message broker. See the STOMP documentation pages for +the broker you use to see what conventions it supports for the destination header. -The "simple broker" on the other hand does rely on the configured `PathMatcher` so if -you switch the separator that will also apply to the broker and the way matches +The "`simple broker`", on the other hand, does rely on the configured `PathMatcher`, so, if +you switch the separator, that change also applies to the broker and the way the broker matches destinations from a message to patterns in subscriptions. @@ -1709,45 +1777,42 @@ destinations from a message to patterns in subscriptions. [[websocket-stomp-authentication]] === Authentication -Every STOMP over WebSocket messaging session begins with an HTTP request -- -that can be a request to upgrade to WebSockets (i.e. a WebSocket handshake) -or in the case of SockJS fallbacks a series of SockJS HTTP transport requests. +Every STOMP over WebSocket messaging session begins with an HTTP request. +That can be a request to upgrade to WebSockets (that is, a WebSocket handshake) +or, in the case of SockJS fallbacks, a series of SockJS HTTP transport requests. -Web applications already have authentication and authorization in place to -secure HTTP requests. Typically a user is authenticated via Spring Security -using some mechanism such as a login page, HTTP basic authentication, or other. +Many web applications already have authentication and authorization in place to +secure HTTP requests. Typically, a user is authenticated through Spring Security +by using some mechanism such as a login page, HTTP basic authentication, or another way. The security context for the authenticated user is saved in the HTTP session and is associated with subsequent requests in the same cookie-based session. -Therefore for a WebSocket handshake, or for SockJS HTTP transport requests, -typically there will already be an authenticated user accessible via +Therefore, for a WebSocket handshake or for SockJS HTTP transport requests, +typically, there is already an authenticated user accessible through `HttpServletRequest#getUserPrincipal()`. Spring automatically associates that user -with a WebSocket or SockJS session created for them and subsequently with all +with a WebSocket or SockJS session created for them and, subsequently, with all STOMP messages transported over that session through a user header. -In short there is nothing special a typical web application needs to do above -and beyond what it already does for security. The user is authenticated at -the HTTP request level with a security context maintained through a cookie-based -HTTP session which is then associated with WebSocket or SockJS sessions created -for that user and results in a user header stamped on every `Message` flowing +In short, a typical web application needs to do nothing +beyond what it already does for security. The user is authenticated at +the HTTP request level with a security context that is maintained through a cookie-based +HTTP session (which is then associated with WebSocket or SockJS sessions created +for that user) and results in a user header being stamped on every `Message` flowing through the application. -Note that the STOMP protocol does have a "login" and "passcode" headers -on the `CONNECT` frame. Those were originally designed for and are still needed -for example for STOMP over TCP. However for STOMP over WebSocket by default -Spring ignores authorization headers at the STOMP protocol level and assumes -the user is already authenticated at the HTTP transport level and expects that +Note that the STOMP protocol does have `login` and `passcode` headers +on the `CONNECT` frame. Those were originally designed for and are still needed, +for example, for STOMP over TCP. However, for STOMP over WebSocket, by default, +Spring ignores authorization headers at the STOMP protocol level, assumes that +the user is already authenticated at the HTTP transport level, and expects that the WebSocket or SockJS session contain the authenticated user. -[NOTE] -==== -Spring Security provides +NOTE: Spring Security provides https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#websocket[WebSocket sub-protocol authorization] that uses a `ChannelInterceptor` to authorize messages based on the user header in them. -Also Spring Session provides a +Also, Spring Session provides a http://docs.spring.io/spring-session/docs/current/reference/html5/#websocket[WebSocket integration] that ensures the user HTTP session does not expire when the WebSocket session is still active. -==== @@ -1755,46 +1820,45 @@ that ensures the user HTTP session does not expire when the WebSocket session is === Token Authentication https://github.com/spring-projects/spring-security-oauth[Spring Security OAuth] -provides support for token based security including JSON Web Token (JWT). -This can be used as the authentication mechanism in Web applications -including STOMP over WebSocket interactions just as described in the previous -section, i.e. maintaining identity through a cookie-based session. - -At the same time cookie-based sessions are not always the best fit for example -in applications that don't wish to maintain a server-side session at all or in -mobile applications where it's common to use headers for authentication. - -The https://tools.ietf.org/html/rfc6455#section-10.5[WebSocket protocol RFC 6455] -"doesn't prescribe any particular way that servers can authenticate clients during -the WebSocket handshake." In practice however browser clients can only use standard -authentication headers (i.e. basic HTTP authentication) or cookies and cannot for example -provide custom headers. Likewise the SockJS JavaScript client does not provide -a way to send HTTP headers with SockJS transport requests, see +provides support for token based security, including JSON Web Token (JWT). +You can use this as the authentication mechanism in Web applications, +including STOMP over WebSocket interactions, as described in the previous +section (that is, to maintain identity through a cookie-based session). + +At the same time, cookie-based sessions are not always the best fit (for example, +in applications that do not maintain a server-side session or in +mobile applications where it is common to use headers for authentication). + +The https://tools.ietf.org/html/rfc6455#section-10.5[WebSocket protocol, RFC 6455] +"`doesn't prescribe any particular way that servers can authenticate clients during +the WebSocket handshake.`" In practice, however, browser clients can use only standard +authentication headers (that is, basic HTTP authentication) or cookies and cannot (for example) +provide custom headers. Likewise, the SockJS JavaScript client does not provide +a way to send HTTP headers with SockJS transport requests. See https://github.com/sockjs/sockjs-client/issues/196[sockjs-client issue 196]. -Instead it does allow sending query parameters that can be used to send a token -but that has its own drawbacks, for example as the token may be inadvertently -logged with the URL in server logs. +Instead, it does allow sending query parameters that you can use to send a token, +but that has its own drawbacks (for example, the token may be inadvertently +logged with the URL in server logs). -[NOTE] -==== -The above limitations are for browser-based clients and do not apply to the -Spring Java-based STOMP client which does support sending headers with both +NOTE: The preceding limitations are for browser-based clients and do not apply to the +Spring Java-based STOMP client, which does support sending headers with both WebSocket and SockJS requests. -==== -Therefore applications that wish to avoid the use of cookies may not have any good -alternatives for authentication at the HTTP protocol level. Instead of using cookies +Therefore, applications that wish to avoid the use of cookies may not have any good +alternatives for authentication at the HTTP protocol level. Instead of using cookies, they may prefer to authenticate with headers at the STOMP messaging protocol level -There are 2 simple steps to doing that: +Doing so requires two simple steps: -1. Use the STOMP client to pass authentication header(s) at connect time. -2. Process the authentication header(s) with a `ChannelInterceptor`. +. Use the STOMP client to pass authentication headers at connect time. +. Process the authentication headers with a `ChannelInterceptor`. -Below is the example server-side configuration to register a custom authentication -interceptor. Note that an interceptor only needs to authenticate and set -the user header on the CONNECT `Message`. Spring will note and save the authenticated -user and associate it with subsequent STOMP messages on the same session: +The next example uses server-side configuration to register a custom authentication +interceptor. Note that an interceptor needs only to authenticate and set +the user header on the CONNECT `Message`. Spring notes and saves the authenticated +user and associate it with subsequent STOMP messages on the same session. The following +example shows how register a custom authentication interceptor: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1819,11 +1883,12 @@ user and associate it with subsequent STOMP messages on the same session: } } ---- +==== -Also note that when using Spring Security's authorization for messages, at present -you will need to ensure that the authentication `ChannelInterceptor` config is ordered +Also, note that, when you use Spring Security's authorization for messages, at present, +you need to ensure that the authentication `ChannelInterceptor` config is ordered ahead of Spring Security's. This is best done by declaring the custom interceptor in -its own implementation of `WebSocketMessageBrokerConfigurer` marked with +its own implementation of `WebSocketMessageBrokerConfigurer` that is marked with `@Order(Ordered.HIGHEST_PRECEDENCE + 99)`. @@ -1831,28 +1896,29 @@ its own implementation of `WebSocketMessageBrokerConfigurer` marked with [[websocket-stomp-user-destination]] === User Destinations -An application can send messages targeting a specific user, and Spring's STOMP support -recognizes destinations prefixed with `"/user/"` for this purpose. -For example, a client might subscribe to the destination `"/user/queue/position-updates"`. -This destination will be handled by the `UserDestinationMessageHandler` and -transformed into a destination unique to the user session, -e.g. `"/queue/position-updates-user123"`. This provides the convenience of subscribing -to a generically named destination while at the same time ensuring no collisions -with other users subscribing to the same destination so that each user can receive +An application can send messages that target a specific user, and Spring's STOMP support +recognizes destinations prefixed with `/user/` for this purpose. +For example, a client might subscribe to the `/user/queue/position-updates` destination. +This destination is handled by the `UserDestinationMessageHandler` and +transformed into a destination unique to the user session +(such as `/queue/position-updates-user123`). This provides the convenience of subscribing +to a generically named destination while, at the same time, ensuring no collisions +with other users who subscribe to the same destination so that each user can receive unique stock position updates. -On the sending side messages can be sent to a destination such as -`"/user/{username}/queue/position-updates"`, which in turn will be translated +On the sending side, messages can be sent to a destination such as +`/user/{username}/queue/position-updates`, which in turn is translated by the `UserDestinationMessageHandler` into one or more destinations, one for each -session associated with the user. This allows any component within the application to -send messages targeting a specific user without necessarily knowing anything more +session associated with the user. This lets any component within the application +send messages that target a specific user without necessarily knowing anything more than their name and the generic destination. This is also supported through an -annotation as well as a messaging template. +annotation and a messaging template. -For example, a message-handling method can send messages to the user associated with +A message-handling method can send messages to the user associated with the message being handled through the `@SendToUser` annotation (also supported on -the class-level to share a common destination): +the class-level to share a common destination), as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1867,12 +1933,14 @@ the class-level to share a common destination): } } ---- +==== -If the user has more than one session, by default all of the sessions subscribed -to the given destination are targeted. However sometimes, it may be necessary to -target only the session that sent the message being handled. This can be done by -setting the `broadcast` attribute to false, for example: +If the user has more than one session, by default, all of the sessions subscribed +to the given destination are targeted. However, sometimes, it may be necessary to +target only the session that sent the message being handled. You can do so by +setting the `broadcast` attribute to false, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1892,21 +1960,20 @@ setting the `broadcast` attribute to false, for example: } } ---- - -[NOTE] -==== -While user destinations generally imply an authenticated user, it isn't required -strictly. A WebSocket session that is not associated with an authenticated user -can subscribe to a user destination. In such cases the `@SendToUser` annotation -will behave exactly the same as with `broadcast=false`, i.e. targeting only the -session that sent the message being handled. ==== -It is also possible to send a message to user destinations from any application -component by injecting the `SimpMessagingTemplate` created by the Java config or -XML namespace, for example (the bean name is `"brokerMessagingTemplate"` if required -for qualification with `@Qualifier`): +NOTE: While user destinations generally imply an authenticated user, it is not strictly required. +A WebSocket session that is not associated with an authenticated user +can subscribe to a user destination. In such cases, the `@SendToUser` annotation +behaves exactly the same as with `broadcast=false` (that is, targeting only the +session that sent the message being handled). +You can send a message to user destinations from any application +component by, for example, injecting the `SimpMessagingTemplate` created by the Java configuration or +the XML namespace. (The bean name is `"brokerMessagingTemplate"` if required +for qualification with `@Qualifier`.) The following example shows how to do so: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1928,24 +1995,22 @@ public class TradeServiceImpl implements TradeService { } } ---- - -[NOTE] ==== -When using user destinations with an external message broker, check the broker -documentation on how to manage inactive queues, so that when the user session is + +NOTE: When you use user destinations with an external message broker, you should check the broker +documentation on how to manage inactive queues, so that, when the user session is over, all unique user queues are removed. For example, RabbitMQ creates auto-delete -queues when destinations like `/exchange/amq.direct/position-updates` are used. -So in that case the client could subscribe to `/user/exchange/amq.direct/position-updates`. +queues when you use destinations such as `/exchange/amq.direct/position-updates`. +So, in that case, the client could subscribe to `/user/exchange/amq.direct/position-updates`. Similarly, ActiveMQ has http://activemq.apache.org/delete-inactive-destinations.html[configuration options] for purging inactive destinations. -==== -In a multi-application server scenario a user destination may remain unresolved because -the user is connected to a different server. In such cases you can configure a -destination to broadcast unresolved messages to so that other servers have a chance to try. +In a multi-application server scenario, a user destination may remain unresolved because +the user is connected to a different server. In such cases, you can configure a +destination to broadcast unresolved messages so that other servers have a chance to try. This can be done through the `userDestinationBroadcast` property of the -`MessageBrokerRegistry` in Java config and the `user-destination-broadcast` attribute +`MessageBrokerRegistry` in Java configuration and the `user-destination-broadcast` attribute of the `message-broker` element in XML. @@ -1953,13 +2018,14 @@ of the `message-broker` element in XML. [[websocket-stomp-ordered-messages]] === Order of Messages -Messages from the broker are published to the "clientOutboundChannel" from where they are -written to WebSocket sessions. As the channel is backed by a `ThreadPoolExecutor` messages +Messages from the broker are published to the `clientOutboundChannel`, from where they are +written to WebSocket sessions. As the channel is backed by a `ThreadPoolExecutor`, messages are processed in different threads, and the resulting sequence received by the client may not match the exact order of publication. -If this is an issue, enable the following flag: +If this is an issue, enable the `setPreservePublishOrder` flag, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -1975,9 +2041,11 @@ If this is an issue, enable the following flag: } ---- +==== -The same in XML: +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -1996,53 +2064,51 @@ The same in XML: ---- +==== When the flag is set, messages within the same client session are published to the -"clientOutboundChannel" one at a time, so that the order of publication is guaranteed. -Note that this incurs a small performance overhead, so enable it only if required. +`clientOutboundChannel` one at a time, so that the order of publication is guaranteed. +Note that this incurs a small performance overhead, so you should enable it only if it is required. [[websocket-stomp-appplication-context-events]] === Events -Several `ApplicationContext` events (listed below) are published and can be -received by implementing Spring's `ApplicationListener` interface. +Several `ApplicationContext` events are published and can be +received by implementing Spring's `ApplicationListener` interface: -* `BrokerAvailabilityEvent` -- indicates when the broker becomes available/unavailable. -While the "simple" broker becomes available immediately on startup and remains so while -the application is running, the STOMP "broker relay" may lose its connection -to the full featured broker, for example if the broker is restarted. The broker relay -has reconnect logic and will re-establish the "system" connection to the broker -when it comes back, hence this event is published whenever the state changes from connected -to disconnected and vice versa. Components using the `SimpMessagingTemplate` should +* `BrokerAvailabilityEvent`: Indicates when the broker becomes available or unavailable. +While the "`simple`" broker becomes available immediately on startup and remains so while +the application is running, the STOMP "`broker relay`" can lose its connection +to the full featured broker (for example, if the broker is restarted). The broker relay +has reconnect logic and re-establishes the "`system`" connection to the broker +when it comes back. As a result, this event is published whenever the state changes from connected +to disconnected and vice-versa. Components that use the `SimpMessagingTemplate` should subscribe to this event and avoid sending messages at times when the broker is not -available. In any case they should be prepared to handle `MessageDeliveryException` +available. In any case, they should be prepared to handle `MessageDeliveryException` when sending a message. -* `SessionConnectEvent` -- published when a new STOMP CONNECT is received -indicating the start of a new client session. The event contains the message representing the -connect including the session id, user information (if any), and any custom headers the client -may have sent. This is useful for tracking client sessions. Components subscribed -to this event can wrap the contained message using `SimpMessageHeaderAccessor` or +* `SessionConnectEvent`: Published when a new STOMP CONNECT is received to +indicate the start of a new client session. The event contains the message that represents the +connect, including the session ID, user information (if any), and any custom headers the client +sent. This is useful for tracking client sessions. Components subscribed +to this event can wrap the contained message with `SimpMessageHeaderAccessor` or `StompMessageHeaderAccessor`. -* `SessionConnectedEvent` -- published shortly after a `SessionConnectEvent` when the -broker has sent a STOMP CONNECTED frame in response to the CONNECT. At this point the +* `SessionConnectedEvent`: Published shortly after a `SessionConnectEvent` when the +broker has sent a STOMP CONNECTED frame in response to the CONNECT. At this point, the STOMP session can be considered fully established. -* `SessionSubscribeEvent` -- published when a new STOMP SUBSCRIBE is received. -* `SessionUnsubscribeEvent` -- published when a new STOMP UNSUBSCRIBE is received. -* `SessionDisconnectEvent` -- published when a STOMP session ends. The DISCONNECT may -have been sent from the client, or it may also be automatically generated when the -WebSocket session is closed. In some cases this event may be published more than once +* `SessionSubscribeEvent`: Published when a new STOMP SUBSCRIBE is received. +* `SessionUnsubscribeEvent`: Published when a new STOMP UNSUBSCRIBE is received. +* `SessionDisconnectEvent`: Published when a STOMP session ends. The DISCONNECT may +have been sent from the client or it may be automatically generated when the +WebSocket session is closed. In some cases, this event is published more than once per session. Components should be idempotent with regard to multiple disconnect events. -[NOTE] -==== -When using a full-featured broker, the STOMP "broker relay" automatically reconnects the -"system" connection in case the broker becomes temporarily unavailable. Client connections -however are not automatically reconnected. Assuming heartbeats are enabled, the client -will typically notice the broker is not responding within 10 seconds. Clients need to -implement their own reconnect logic. -==== +NOTE: When you use a full-featured broker, the STOMP "`broker relay`" automatically reconnects the +"`system`" connection if broker becomes temporarily unavailable. Client connections, +however, are not automatically reconnected. Assuming heartbeats are enabled, the client +typically notices the broker is not responding within 10 seconds. Clients need to +implement their own reconnecting logic. @@ -2050,10 +2116,11 @@ implement their own reconnect logic. === Interception <> provide notifications for the lifecycle -of a STOMP connection and not for every client message. Applications can also register a -`ChannelInterceptor` to intercept any message, and in any part of the processing chain. -For example to intercept inbound messages from clients: +of a STOMP connection but not for every client message. Applications can also register a +`ChannelInterceptor` to intercept any message and in any part of the processing chain. +The following example shows how to intercept inbound messages from clients: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2067,10 +2134,12 @@ For example to intercept inbound messages from clients: } } ---- +==== A custom `ChannelInterceptor` can use `StompHeaderAccessor` or `SimpMessageHeaderAccessor` -to access information about the message. +to access information about the message, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2085,17 +2154,18 @@ to access information about the message. } } ---- +==== -Applications may also implement `ExecutorChannelInterceptor` which is a sub-interface +Applications can also implement `ExecutorChannelInterceptor`, which is a sub-interface of `ChannelInterceptor` with callbacks in the thread in which the messages are handled. -While a `ChannelInterceptor` is invoked once for per message sent to a channel, the +While a `ChannelInterceptor` is invoked once for each message sent to a channel, the `ExecutorChannelInterceptor` provides hooks in the thread of each `MessageHandler` subscribed to messages from the channel. -Note that just like with the `SesionDisconnectEvent` above, a DISCONNECT message -may have been sent from the client, or it may also be automatically generated when -the WebSocket session is closed. In some cases an interceptor may intercept this -message more than once per session. Components should be idempotent with regard to +Note that, as with the `SesionDisconnectEvent` described earlier, a DISCONNECT message +can be from the client or it can also be automatically generated when +the WebSocket session is closed. In some cases, an interceptor may intercept this +message more than once for each session. Components should be idempotent with regard to multiple disconnect events. @@ -2105,8 +2175,9 @@ multiple disconnect events. Spring provides a STOMP over WebSocket client and a STOMP over TCP client. -To begin create and configure `WebSocketStompClient`: +To begin, you can create and configure `WebSocketStompClient`, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2115,14 +2186,16 @@ To begin create and configure `WebSocketStompClient`: stompClient.setMessageConverter(new StringMessageConverter()); stompClient.setTaskScheduler(taskScheduler); // for heartbeats ---- +==== -In the above example `StandardWebSocketClient` could be replaced with `SockJsClient` +In the preceding example, you could replace `StandardWebSocketClient` with `SockJsClient`, since that is also an implementation of `WebSocketClient`. The `SockJsClient` can -use WebSocket or HTTP-based transport as a fallback. For more details see +use WebSocket or HTTP-based transport as a fallback. For more details, see <>. -Next establish a connection and provide a handler for the STOMP session: +Next, you can establish a connection and provide a handler for the STOMP session, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2130,9 +2203,11 @@ Next establish a connection and provide a handler for the STOMP session: StompSessionHandler sessionHandler = new MyStompSessionHandler(); stompClient.connect(url, sessionHandler); ---- +==== -When the session is ready for use the handler is notified: +When the session is ready for use, the handler is notified, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2144,25 +2219,29 @@ public class MyStompSessionHandler extends StompSessionHandlerAdapter { } } ---- +==== -Once the session is established any payload can be sent and that will be -serialized with the configured `MessageConverter`: - +Once the session is established, any payload can be sent and is +serialized with the configured `MessageConverter`, as the following example shows: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- -session.send("/topic/foo", "payload"); +session.send("/topic/something", "payload"); ---- +==== You can also subscribe to destinations. The `subscribe` methods require a handler -for messages on the subscription and return a `Subscription` handle that can be -used to unsubscribe. For each received message the handler can specify the target -Object type the payload should be deserialized to: - +for messages on the subscription and returns a `Subscription` handle that you can +use to unsubscribe. For each received message, the handler can specify the target +`Object` type to which the payload should be deserialized, as the following example shows: + +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- -session.subscribe("/topic/foo", new StompFrameHandler() { +session.subscribe("/topic/something", new StompFrameHandler() { @Override public Type getPayloadType(StompHeaders headers) { @@ -2176,35 +2255,32 @@ session.subscribe("/topic/foo", new StompFrameHandler() { }); ---- +==== -To enable STOMP heartbeat configure `WebSocketStompClient` with a `TaskScheduler` -and optionally customize the heartbeat intervals, 10 seconds for write inactivity -which causes a heartbeat to be sent and 10 seconds for read inactivity which -closes the connection. +To enable STOMP heartbeat, you can configure `WebSocketStompClient` with a `TaskScheduler` +and optionally customize the heartbeat intervals (10 seconds for write inactivity, +which causes a heartbeat to be sent, and 10 seconds for read inactivity, which +closes the connection). -[NOTE] -==== -When using `WebSocketStompClient` for performance tests to simulate thousands -of clients from the same machine consider turning off heartbeats since each -connection schedules its own heartbeat tasks and that's not optimized for a +NOTE: When you use `WebSocketStompClient` for performance tests to simulate thousands +of clients from the same machine, consider turning off heartbeats, since each +connection schedules its own heartbeat tasks and that is not optimized for a large number of clients running on the same machine. -==== - -The STOMP protocol also supports receipts where the client must add a "receipt" +The STOMP protocol also supports receipts, where the client must add a `receipt` header to which the server responds with a RECEIPT frame after the send or -subscribe are processed. To support this the `StompSession` offers -`setAutoReceipt(boolean)` that causes a "receipt" header to be -added on every subsequent send or subscribe. -Alternatively you can also manually add a "receipt" header to the `StompHeaders`. +subscribe are processed. To support this, the `StompSession` offers +`setAutoReceipt(boolean)` that causes a `receipt` header to be +added on every subsequent send or subscribe event. +Alternatively, you can also manually add a receipt header to the `StompHeaders`. Both send and subscribe return an instance of `Receiptable` -that can be used to register for receipt success and failure callbacks. -For this feature the client must be configured with a `TaskScheduler` +that you can use to register for receipt success and failure callbacks. +For this feature, you must configure the client with a `TaskScheduler` and the amount of time before a receipt expires (15 seconds by default). -Note that `StompSessionHandler` itself is a `StompFrameHandler` which allows -it to handle ERROR frames in addition to the `handleException` callback for -exceptions from the handling of messages, and `handleTransportError` for +Note that `StompSessionHandler` itself is a `StompFrameHandler`, which lets +it handle ERROR frames in addition to the `handleException` callback for +exceptions from the handling of messages and `handleTransportError` for transport-level errors including `ConnectionLostException`. @@ -2213,8 +2289,9 @@ transport-level errors including `ConnectionLostException`. === WebSocket Scope Each WebSocket session has a map of attributes. The map is attached as a header to -inbound client messages and may be accessed from a controller method, for example: +inbound client messages and may be accessed from a controller method, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2228,13 +2305,15 @@ public class MyController { } } ---- +==== -It is also possible to declare a Spring-managed bean in the `websocket` scope. -WebSocket-scoped beans can be injected into controllers and any channel interceptors -registered on the "clientInboundChannel". Those are typically singletons and live -longer than any individual WebSocket session. Therefore you will need to use a -scope proxy mode for WebSocket-scoped beans: +You can declare a Spring-managed bean in the `websocket` scope. +You can inject WebSocket-scoped beans into controllers and any channel interceptors +registered on the `clientInboundChannel`. Those are typically singletons and live +longer than any individual WebSocket session. Therefore, you need to use a +scope proxy mode for WebSocket-scoped beans, as the following example shows: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2271,80 +2350,82 @@ scope proxy mode for WebSocket-scoped beans: } } ---- +==== As with any custom scope, Spring initializes a new `MyBean` instance the first time it is accessed from the controller and stores the instance in the WebSocket -session attributes. The same instance is returned subsequently until the session -ends. WebSocket-scoped beans will have all Spring lifecycle methods invoked as -shown in the examples above. +session attributes. The same instance is subsequently returned until the session +ends. WebSocket-scoped beans have all Spring lifecycle methods invoked, as +shown in the preceding examples. [[websocket-stomp-configuration-performance]] === Performance -There is no silver bullet when it comes to performance. Many factors may -affect it including the size of messages, the volume, whether application -methods perform work that requires blocking, as well as external factors -such as network speed and others. The goal of this section is to provide +There is no silver bullet when it comes to performance. Many factors +affect it, including the size and volume of messages, whether application +methods perform work that requires blocking, and external factors +(such as network speed and other issues). The goal of this section is to provide an overview of the available configuration options along with some thoughts on how to reason about scaling. -In a messaging application messages are passed through channels for asynchronous -executions backed by thread pools. Configuring such an application requires -good knowledge of the channels and the flow of messages. Therefore it is +In a messaging application, messages are passed through channels for asynchronous +executions that are backed by thread pools. Configuring such an application requires +good knowledge of the channels and the flow of messages. Therefore, it is recommended to review <>. -The obvious place to start is to configure the thread pools backing the -`"clientInboundChannel"` and the `"clientOutboundChannel"`. By default both +The obvious place to start is to configure the thread pools that back the +`clientInboundChannel` and the `clientOutboundChannel`. By default, both are configured at twice the number of available processors. -If the handling of messages in annotated methods is mainly CPU bound then the -number of threads for the `"clientInboundChannel"` should remain close to the -number of processors. If the work they do is more IO bound and requires blocking -or waiting on a database or other external system then the thread pool size -will need to be increased. +If the handling of messages in annotated methods is mainly CPU-bound, the +number of threads for the `clientInboundChannel` should remain close to the +number of processors. If the work they do is more IO-bound and requires blocking +or waiting on a database or other external system, the thread pool size +probably needs to be increased. [NOTE] ==== -`ThreadPoolExecutor` has 3 important properties. Those are the core and -the max thread pool size as well as the capacity for the queue to store +`ThreadPoolExecutor` has three important properties: the core thread pool size, +the max thread pool size, and the capacity for the queue to store tasks for which there are no available threads. -A common point of confusion is that configuring the core pool size (e.g. 10) -and max pool size (e.g. 20) results in a thread pool with 10 to 20 threads. -In fact if the capacity is left at its default value of Integer.MAX_VALUE -then the thread pool will never increase beyond the core pool size since -all additional tasks will be queued. +A common point of confusion is that configuring the core pool size (for example, 10) +and max pool size (for example, 20) results in a thread pool with 10 to 20 threads. +In fact, if the capacity is left at its default value of Integer.MAX_VALUE, +the thread pool never increases beyond the core pool size, since +all additional tasks are queued. -Please review the Javadoc of `ThreadPoolExecutor` to learn how these +See the Javadoc of `ThreadPoolExecutor` to learn how these properties work and understand the various queuing strategies. ==== -On the `"clientOutboundChannel"` side it is all about sending messages to WebSocket -clients. If clients are on a fast network then the number of threads should +On the `clientOutboundChannel` side, it is all about sending messages to WebSocket +clients. If clients are on a fast network, the number of threads should remain close to the number of available processors. If they are slow or on -low bandwidth they will take longer to consume messages and put a burden on the -thread pool. Therefore increasing the thread pool size will be necessary. - -While the workload for the "clientInboundChannel" is possible to predict -- -after all it is based on what the application does -- how to configure the -"clientOutboundChannel" is harder as it is based on factors beyond -the control of the application. For this reason there are two additional -properties related to the sending of messages. Those are the `"sendTimeLimit"` -and the `"sendBufferSizeLimit"`. Those are used to configure how long a +low bandwidth, they take longer to consume messages and put a burden on the +thread pool. Therefore, increasing the thread pool size becomes necessary. + +While the workload for the `clientInboundChannel` is possible to predict -- +after all, it is based on what the application does -- how to configure the +"clientOutboundChannel" is harder, as it is based on factors beyond +the control of the application. For this reason, two additional +properties relate to the sending of messages: `sendTimeLimit` +and `sendBufferSizeLimit`. You can use those methods to configure how long a send is allowed to take and how much data can be buffered when sending messages to a client. -The general idea is that at any given time only a single thread may be used -to send to a client. All additional messages meanwhile get buffered and you +The general idea is that, at any given time, only a single thread can be used +to send to a client. All additional messages, meanwhile, get buffered, and you can use these properties to decide how long sending a message is allowed to -take and how much data can be buffered in the mean time. Please review the +take and how much data can be buffered in the meantime. See the Javadoc and documentation of the XML schema for this configuration for important additional details. -Here is example configuration: +The following example shows a possible configuration: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2361,7 +2442,11 @@ Here is example configuration: } ---- +==== + +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -2381,23 +2466,25 @@ Here is example configuration: ---- +==== -The WebSocket transport configuration shown above can also be used to configure the -maximum allowed size for incoming STOMP messages. Although in theory a WebSocket -message can be almost unlimited in size, in practice WebSocket servers impose -limits -- for example, 8K on Tomcat and 64K on Jetty. For this reason STOMP clients -such as the JavaScript https://github.com/JSteunou/webstomp-client[webstomp-client] -and others split larger STOMP messages at 16K boundaries and send them as multiple -WebSocket messages thus requiring the server to buffer and re-assemble. - -Spring's STOMP over WebSocket support does this so applications can configure the -maximum size for STOMP messages irrespective of WebSocket server specific message -sizes. Do keep in mind that the WebSocket message size will be automatically -adjusted if necessary to ensure they can carry 16K WebSocket messages at a +You can also use the WebSocket transport configuration shown earlier to configure the +maximum allowed size for incoming STOMP messages. In theory, a WebSocket +message can be almost unlimited in size. In practice, WebSocket servers impose +limits -- for example, 8K on Tomcat and 64K on Jetty. For this reason, STOMP clients +(such as the JavaScript https://github.com/JSteunou/webstomp-client[webstomp-client] +and others) split larger STOMP messages at 16K boundaries and send them as multiple +WebSocket messages, which requires the server to buffer and re-assemble. + +Spring's STOMP-over-WebSocket support does this ,so applications can configure the +maximum size for STOMP messages irrespective of WebSocket server-specific message +sizes. Keep in mind that the WebSocket message size is automatically +adjusted, if necessary, to ensure they can carry 16K WebSocket messages at a minimum. -Here is example configuration: +The following example shows one possible configuration: +==== [source,java,indent=0] [subs="verbatim,quotes"] ---- @@ -2414,7 +2501,11 @@ Here is example configuration: } ---- +==== + +The following example shows the XML configuration equivalent of the preceding example: +==== [source,xml,indent=0] [subs="verbatim,quotes,attributes"] ---- @@ -2434,11 +2525,12 @@ Here is example configuration: ---- +==== -An important point about scaling is using multiple application instances. -Currently it is not possible to do that with the simple broker. -However when using a full-featured broker such as RabbitMQ, each application -instance connects to the broker and messages broadcast from one application +An important point about scaling involves using multiple application instances. +Currently, you cannot do that with the simple broker. +However, when you use a full-featured broker (such as RabbitMQ), each application +instance connects to the broker, and messages broadcast from one application instance can be broadcast through the broker to WebSocket clients connected through any other application instances. @@ -2447,90 +2539,90 @@ through any other application instances. [[websocket-stomp-stats]] === Monitoring -When using `@EnableWebSocketMessageBroker` or `` key -infrastructure components automatically gather stats and counters that provide +When you use `@EnableWebSocketMessageBroker` or ``, key +infrastructure components automatically gather statisticss and counters that provide important insight into the internal state of the application. The configuration also declares a bean of type `WebSocketMessageBrokerStats` that gathers all -available information in one place and by default logs it at `INFO` level once +available information in one place and by, default logs, it at the `INFO` level once every 30 minutes. This bean can be exported to JMX through Spring's -`MBeanExporter` for viewing at runtime, for example through JDK's `jconsole`. -Below is a summary of the available information. +`MBeanExporter` for viewing at runtime (for example, through JDK's `jconsole`). +The following list summarizes the available information: Client WebSocket Sessions:: - Current::: indicates how many client sessions there are - currently with the count further broken down by WebSocket vs HTTP + Current::: Indicates how many client sessions there are + currently, with the count further broken down by WebSocket versus HTTP streaming and polling SockJS sessions. - Total::: indicates how many total sessions have been established. + Total::: Indicates how many total sessions have been established. Abnormally Closed::: - Connect Failures:::: these are sessions that got established but were + Connect Failures:::: Sessions that got established but were closed after not having received any messages within 60 seconds. This is usually an indication of proxy or network issues. - Send Limit Exceeded:::: sessions closed after exceeding the configured send - timeout or the send buffer limits which can occur with slow clients + Send Limit Exceeded:::: Sessions closed after exceeding the configured send + timeout or the send buffer limits, which can occur with slow clients (see previous section). - Transport Errors:::: sessions closed after a transport error such as + Transport Errors:::: Sessions closed after a transport error, such as failure to read or write to a WebSocket connection or - HTTP request/response. - STOMP Frames::: the total number of CONNECT, CONNECTED, and DISCONNECT frames - processed indicating how many clients connected on the STOMP level. Note that + HTTP request or response. + STOMP Frames::: The total number of CONNECT, CONNECTED, and DISCONNECT frames + processed, indicating how many clients connected on the STOMP level. Note that the DISCONNECT count may be lower when sessions get closed abnormally or when clients close without sending a DISCONNECT frame. STOMP Broker Relay:: - TCP Connections::: indicates how many TCP connections on behalf of client + TCP Connections::: Indicates how many TCP connections on behalf of client WebSocket sessions are established to the broker. This should be equal to the - number of client WebSocket sessions + 1 additional shared "system" connection + number of client WebSocket sessions + 1 additional shared "`system`" connection for sending messages from within the application. - STOMP Frames::: the total number of CONNECT, CONNECTED, and DISCONNECT frames + STOMP Frames::: The total number of CONNECT, CONNECTED, and DISCONNECT frames forwarded to or received from the broker on behalf of clients. Note that a DISCONNECT frame is sent to the broker regardless of how the client WebSocket - session was closed. Therefore a lower DISCONNECT frame count is an indication - that the broker is pro-actively closing connections, may be because of a - heartbeat that didn't arrive in time, an invalid input frame, or other. -Client Inbound Channel:: stats from thread pool backing the "clientInboundChannel" - providing insight into the health of incoming message processing. Tasks queueing - up here is an indication the application may be too slow to handle messages. - If there I/O bound tasks (e.g. slow database query, HTTP request to 3rd party - REST API, etc) consider increasing the thread pool size. -Client Outbound Channel:: stats from the thread pool backing the "clientOutboundChannel" - providing insight into the health of broadcasting messages to clients. Tasks + session was closed. Therefore, a lower DISCONNECT frame count is an indication + that the broker is pro-actively closing connections (maybe because of a + heartbeat that did not arrive in time, an invalid input frame, or other issue). +Client Inbound Channel:: Statistics from the thread pool that backs the `clientInboundChannel` + that provide insight into the health of incoming message processing. Tasks queueing + up here is an indication that the application may be too slow to handle messages. + If there I/O bound tasks (for example, slow database queries, HTTP requests to third party + REST API, and so on), consider increasing the thread pool size. +Client Outbound Channel:: Statistics from the thread pool that backs the `clientOutboundChannel` + that provides insight into the health of broadcasting messages to clients. Tasks queueing up here is an indication clients are too slow to consume messages. One way to address this is to increase the thread pool size to accommodate the - number of concurrent slow clients expected. Another option is to reduce the + expected number of concurrent slow clients. Another option is to reduce the send timeout and send buffer size limits (see the previous section). -SockJS Task Scheduler:: stats from thread pool of the SockJS task scheduler which - is used to send heartbeats. Note that when heartbeats are negotiated on the - STOMP level the SockJS heartbeats are disabled. +SockJS Task Scheduler:: Statistics from the thread pool of the SockJS task scheduler that + is used to send heartbeats. Note that, when heartbeats are negotiated on the + STOMP level, the SockJS heartbeats are disabled. [[websocket-stomp-testing]] === Testing -There are two main approaches to testing applications using Spring's STOMP over -WebSocket support. The first is to write server-side tests verifying the functionality -of controllers and their annotated message handling methods. The second is to write +There are two main approaches to testing applications when you use Spring's STOMP-over-WebSocket +support. The first is to write server-side tests to verify the functionality +of controllers and their annotated message-handling methods. The second is to write full end-to-end tests that involve running a client and a server. -The two approaches are not mutually exclusive. On the contrary each has a place +The two approaches are not mutually exclusive. On the contrary, each has a place in an overall test strategy. Server-side tests are more focused and easier to write -and maintain. End-to-end integration tests on the other hand are more complete and -test much more, but they're also more involved to write and maintain. +and maintain. End-to-end integration tests, on the other hand, are more complete and +test much more, but they are also more involved to write and maintain. -The simplest form of server-side tests is to write controller unit tests. However -this is not useful enough since much of what a controller does depends on its -annotations. Pure unit tests simply can't test that. +The simplest form of server-side tests is to write controller unit tests. However, +this is not useful enough, since much of what a controller does depends on its +annotations. Pure unit tests simply cannot test that. -Ideally controllers under test should be invoked as they are at runtime, much like -the approach to testing controllers handling HTTP requests using the Spring MVC Test -framework. i.e. without running a Servlet container but relying on the Spring Framework -to invoke the annotated controllers. Just like with Spring MVC Test here there are two -two possible alternatives, either using a "context-based" or "standalone" setup: +Ideally, controllers under test should be invoked as they are at runtime, much like +the approach to testing controllers that handle HTTP requests by using the Spring MVC Test +framework -- that is, without running a Servlet container but relying on the Spring Framework +to invoke the annotated controllers. As with Spring MVC Test, you have two +two possible alternatives here, either use a "`context-based`" or use a "`standalone`" setup: -1. Load the actual Spring configuration with the help of the -Spring TestContext framework, inject "clientInboundChannel" as a test field, and +* Load the actual Spring configuration with the help of the +Spring TestContext framework, inject `clientInboundChannel` as a test field, and use it to send messages to be handled by controller methods. -2. Manually set up the minimum Spring framework infrastructure required to invoke +* Manually set up the minimum Spring framework infrastructure required to invoke controllers (namely the `SimpAnnotationMethodMessageHandler`) and pass messages for controllers directly to it. @@ -2538,9 +2630,9 @@ Both of these setup scenarios are demonstrated in the https://github.com/rstoyanchev/spring-websocket-portfolio/tree/master/src/test/java/org/springframework/samples/portfolio/web[tests for the stock portfolio] sample application. -The second approach is to create end-to-end integration tests. For that you will need +The second approach is to create end-to-end integration tests. For that, you need to run a WebSocket server in embedded mode and connect to it as a WebSocket client -sending WebSocket messages containing STOMP frames. +that sends WebSocket messages containing STOMP frames. The https://github.com/rstoyanchev/spring-websocket-portfolio/tree/master/src/test/java/org/springframework/samples/portfolio/web[tests for the stock portfolio] -sample application also demonstrates this approach using Tomcat as the embedded +sample application also demonstrate this approach by using Tomcat as the embedded WebSocket server and a simple STOMP client for test purposes.