Edit the Web chapter of the reference documentation

I edited for spelling, punctuation, grammar, usage,
and corporate voice. I also added links and cross-references.
This commit is contained in:
Jay Bryant 2018-09-17 09:36:43 -05:00 committed by Brian Clozel
parent b29a278b2a
commit 00a3afcda8
17 changed files with 4882 additions and 4061 deletions

View File

@ -7,12 +7,12 @@
:tabsize: 4 :tabsize: 4
:docinfo1: :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 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 <<webflux,Spring WebFlux>> framework, the <<webflux,Spring WebFlux>> framework,
the reactive <<webflux-client,WebClient>>, support for <<webflux-test>>, the reactive <<webflux-client,`WebClient`>>, support for <<webflux-test, testing>>,
and <<webflux-reactive-libraries>>. For Servlet stack, web applications, please see and <<webflux-reactive-libraries, reactive libraries>>. For Servlet-stack web applications, see
<<web.adoc#spring-web,Web on Servlet Stack>>. <<web.adoc#spring-web,Web on Servlet Stack>>.
include::web/webflux.adoc[leveloffset=+1] 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`, The `spring-test` module provides mock implementations of `ServerHttpRequest`,
`ServerHttpResponse`, and `ServerWebExchange`. `ServerHttpResponse`, and `ServerWebExchange`.
See <<testing.adoc#mock-objects-web-reactive,Spring Web Reactive>> mock objects. See <<testing.adoc#mock-objects-web-reactive,Spring Web Reactive>> for a discussion of mock objects.
The <<testing.adoc#webtestclient,WebTestClient>> builds on these mock request and <<testing.adoc#webtestclient,`WebTestClient`>> builds on these mock request and
response objects to provide support for testing WebFlux applications without and HTTP response objects to provide support for testing WebFlux applications without an HTTP
server. The `WebTestClient` can be used for end-to-end integration tests too. server. You can use the `WebTestClient` for end-to-end integration tests, too.
//[[webflux-threading-model]]
[[webflux-threading-model]] //=== 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 == Reactive Libraries
`spring-webflux` depends on `reactor-core` and uses it internally to compose asynchronous `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 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 `Mono` (since those are used internally) and leniently accept any Reactive Streams
`Publisher` implementation as input. The use of `Flux` vs `Mono` is important because it `Publisher` implementation as input. The use of `Flux` versus `Mono` is important, because it
helps to express cardinality -- e.g. whether a single or multiple async values are 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 expected, and that can be essential for making decisions (for example, when encoding or
decoding HTTP messages. decoding HTTP messages).
For annotated controllers, WebFlux transparently adapts to the reactive library chosen by For annotated controllers, WebFlux transparently adapts to the reactive library chosen by
the application. This is done with the help of the 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 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 <<webflux-fn>>, the `WebClient`, and others, the general rules For functional APIs (such as <<webflux-fn>>, the `WebClient`, and others), the general rules
for WebFlux APIs apply -- `Flux` and `Mono` as return values, and Reactive Streams 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, `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 the semantics are known, you can wrap it with `Flux` or `Mono.from(Publisher)` instead
of passing the raw `Publisher`. of passing the raw `Publisher`.
[NOTE]
====
For example, given a `Publisher` that is not a `Mono`, the Jackson JSON message writer 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. expects multiple values. If the media type implies an infinite stream (for example,
`"application/json+stream"`, values are written and flushed individually; otherwise `application/json+stream`), values are written and flushed individually. Otherwise,
values are buffered into a list and rendered as a JSON array. values are buffered into a list and rendered as a JSON array.
====

View File

@ -7,10 +7,10 @@
:tabsize: 4 :tabsize: 4
:docinfo1: :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 <<mvc,Spring MVC>>, Servlet API and deployed to Servlet containers. Individual chapters include <<mvc,Spring MVC>>,
<<mvc-view,View Technologies>>, <<mvc-cors,CORS Support>>, and <<websocket,WebSocket Support>>. <<mvc-view,View Technologies>>, <<mvc-cors,CORS Support>>, and <<websocket,WebSocket Support>>.
For reactive stack, web applications, go to <<web-reactive.adoc#spring-web-reactive,Web on Reactive Stack>>. For reactive-stack web applications, see <<web-reactive.adoc#spring-web-reactive,Web on Reactive Stack>>.
include::web/webmvc.adoc[leveloffset=+1] 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/websocket.adoc[leveloffset=+1]
include::web/integration.adoc[leveloffset=+1] include::web/integration.adoc[leveloffset=+1]

View File

@ -1,53 +1,47 @@
[[web-integration]] [[web-integration]]
= Other Web Frameworks = Other Web Frameworks
[[intro]]
== Introduction
This chapter details Spring's integration with third party web frameworks. 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 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 particular architecture, technology, or methodology (although it certainly recommends
some over others). This freedom to pick and choose the architecture, technology, or 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 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 arguably most evident in the web area, where Spring provides its own web framework
(<<mvc,Spring MVC>>), while at the same time providing integration with a number of (<<mvc,Spring MVC>>) while, at the same time, providing integration with a number of
popular third party web frameworks. popular third party web frameworks.
[[web-integration-common]] [[web-integration-common]]
== Common config == Common Configuration
Before diving into the integration specifics of each supported web framework, let us 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 framework. (This section is equally applicable to Spring's own web framework, Spring
MVC.) MVC.)
One of the concepts (for want of a better word) espoused by (Spring's) lightweight 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' 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 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 entry points into a server-side application, and it delegates to service objects
(facades) defined in a service layer to satisfy business specific (and (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 presentation-technology agnostic) use cases. In Spring, these service objects, any other
business-specific objects, data access objects, etc. exist in a distinct 'business business-specific objects, data-access objects, and others exist in a distinct "`business
context', which contains __no__ web or presentation layer objects (presentation objects context`", which contains no web or presentation layer objects (presentation objects
such as Spring MVC controllers are typically configured in a distinct 'presentation ,such as Spring MVC controllers, are typically configured in a distinct "`presentation
context'). This section details how one configures a Spring container (a context`"). This section details how you can configure a Spring container (a
`WebApplicationContext`) that contains all of the 'business beans' in one's application. `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`] {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`<context-param/> section (in the same file) that defines which `contextConfigLocation`<context-param/> section (in the same file) that defines which
set of Spring XML configuration files to load. set of Spring XML configuration files to load.
Find below the <listener/> configuration: Consider the following `<listener/>` configuration:
====
[source,xml,indent=0] [source,xml,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -55,9 +49,11 @@ Find below the <listener/> configuration:
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> </listener>
---- ----
====
Find below the <context-param/> configuration: Further consider the following `<context-param/>` configuration:
====
[source,xml,indent=0] [source,xml,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -66,30 +62,35 @@ Find below the <context-param/> configuration:
<param-value>/WEB-INF/applicationContext*.xml</param-value> <param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param> </context-param>
---- ----
====
If you don't specify the `contextConfigLocation` context parameter, the If you do not specify the `contextConfigLocation` context parameter, the
`ContextLoaderListener` will look for a file called `/WEB-INF/applicationContext.xml` to `ContextLoaderListener` looks for a file called `/WEB-INF/applicationContext.xml` to
load. Once the context files are loaded, Spring creates a load. Once the context files are loaded, Spring creates a
{api-spring-framework}/web/context/WebApplicationContext.html[`WebApplicationContext`] {api-spring-framework}/web/context/WebApplicationContext.html[`WebApplicationContext`]
object based on the bean definitions and stores it in the `ServletContext` of the web object based on the bean definitions and stores it in the `ServletContext` of the web
application. application.
All Java web frameworks are built on top of the Servlet API, and so one can use the 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` following code snippet to get access to this "`business context`" `ApplicationContext`
created by the `ContextLoaderListener`. created by the `ContextLoaderListener`.
The following example shows how to get the `WebApplicationContext`:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext); WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
---- ----
====
The The
{api-spring-framework}/web/context/support/WebApplicationContextUtils.html[`WebApplicationContextUtils`] {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` class is for convenience, so you need not remember the name of the `ServletContext`
attribute. Its __getWebApplicationContext()__ method will return `null` if an object attribute. Its `getWebApplicationContext()` method returns `null` if an object
doesn't exist under the `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE` does not exist under the `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE`
key. Rather than risk getting `NullPointerExceptions` in your application, it's better key. Rather than risk getting `NullPointerExceptions` in your application, it is better
to use the `getRequiredWebApplicationContext()` method. This method throws an exception to use the `getRequiredWebApplicationContext()` method. This method throws an exception
when the `ApplicationContext` is missing. 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 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 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. has more detail on its specific integration strategies.
[[jsf]] [[jsf]]
== JSF == JSF
JavaServer Faces (JSF) is the JCP's standard component-based, event-driven web user 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. 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 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 http://myfaces.apache.org/[Apache MyFaces project]. The MyFaces project also provides
common JSF extensions such as http://myfaces.apache.org/orchestra/[MyFaces Orchestra]: common JSF extensions, such as http://myfaces.apache.org/orchestra/[MyFaces Orchestra]
a Spring-based JSF extension that provides rich conversation scope support. (a Spring-based JSF extension that provides rich conversation scope support).
[NOTE] NOTE: Spring Web Flow 2.0 provides rich JSF support through its newly established Spring Faces
====
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 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 usage (using JSF views within a Spring MVC dispatcher). See the
http://projects.spring.io/spring-webflow[Spring Web Flow website] for details! 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. 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]] [[jsf-springbeanfaceselresolver]]
=== Spring Bean Resolver === Spring Bean Resolver
`SpringBeanFacesELResolver` is a JSF 1.2+ compliant `ELResolver` implementation, `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 integrating with the standard Unified EL as used by JSF 1.2 and JSP 2.1. As
`SpringBeanVariableResolver`, it delegates to the Spring's 'business context' `SpringBeanVariableResolver`, it delegates to Spring's "`business context`"
`WebApplicationContext` __first__, then to the default resolver of the underlying JSF `WebApplicationContext` first and then to the default resolver of the underlying JSF
implementation. implementation.
Configuration-wise, simply define `SpringBeanFacesELResolver` in your JSF Configuration-wise, you can define `SpringBeanFacesELResolver` in your JSF
__faces-context.xml__ file: `faces-context.xml` file, as the following example shows:
====
[source,xml,indent=0] [source,xml,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -148,33 +148,39 @@ __faces-context.xml__ file:
</application> </application>
</faces-config> </faces-config>
---- ----
====
[[jsf-facescontextutils]] [[jsf-facescontextutils]]
=== FacesContextUtils === Using `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 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`] {api-spring-framework}/web/jsf/FacesContextUtils.html[`FacesContextUtils`]
class makes this easy. It is similar to `WebApplicationContextUtils`, except that it class makes this easy. It is similar to `WebApplicationContextUtils`, except that it
takes a `FacesContext` parameter rather than a `ServletContext` parameter. takes a `FacesContext` parameter rather than a `ServletContext` parameter.
The following example shows how to use `FacesContextUtils`:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance()); ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
---- ----
====
[[struts]] [[struts]]
== Apache Struts 2.x == 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 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 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 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 beer), and it had a large community, which let the project grow and become popular
among Java web developers. among Java web developers.
Check out the Struts Check out the Struts
@ -183,28 +189,25 @@ built-in Spring integration shipped with Struts.
[[tapestry]] [[tapestry]]
== Tapestry 5.x == Tapestry 5.x
From the http://tapestry.apache.org/[Tapestry homepage]:
Tapestry is a "__Component oriented framework for creating dynamic, robust, http://tapestry.apache.org/[Tapestry] is a ""Component oriented framework for creating dynamic, robust,
highly scalable web applications in Java.__" highly scalable web applications in Java.""
While Spring has its own <<mvc,powerful web layer>>, there are a number of unique While Spring has its own <<mvc,powerful web layer>>, 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 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 https://tapestry.apache.org/integrating-with-spring-framework.html[integration module for
Spring]. Spring].
[[web-integration-resources]] [[web-integration-resources]]
== Further 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. chapter.
* The http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF] homepage * The http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html[JSF] homepage

View File

@ -3,8 +3,9 @@
= UriComponents = UriComponents
[.small]#Spring MVC and Spring WebFlux# [.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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -17,13 +18,16 @@
URI uri = uriComponents.expand("Westin", "123").toUri(); // <5> URI uri = uriComponents.expand("Westin", "123").toUri(); // <5>
---- ----
<1> Static factory method with a URI template. <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. <3> Request to have the URI template and URI variables encoded.
<4> Build a `UriComponents`. <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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -34,9 +38,12 @@ The above can be consolidated into one chain and shortened with `buildAndExpand`
.buildAndExpand("Westin", "123") .buildAndExpand("Westin", "123")
.toUri(); .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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -45,9 +52,11 @@ It can be shortened further by going directly to URI (which implies encoding):
.queryParam("q", "{q}") .queryParam("q", "{q}")
.build("Westin", "123"); .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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -55,24 +64,27 @@ Or shorter further yet, with a full URI template:
.fromUriString("http://example.com/hotels/{hotel}?q={q}") .fromUriString("http://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123"); .build("Westin", "123");
---- ----
====
[[web-uribuilder]] [[web-uribuilder]]
= UriBuilder = UriBuilder
[.small]#Spring MVC and Spring WebFlux# [.small]#Spring MVC and Spring WebFlux#
<<web-uricomponents,UriComponentsBuilder>> implements `UriBuilder`. A `UriBuilder` in turn <<web-uricomponents,`UriComponentsBuilder`>> implements `UriBuilder`. You can create a `UriBuilder`, in turn,
can be created with a `UriBuilderFactory`. Together `UriBuilderFactory` and `UriBuilder` with a `UriBuilderFactory`. Together, `UriBuilderFactory` and `UriBuilder`
provide a pluggable mechanism to build URIs from URI templates, based on shared 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 to customize the preparation of URIs. `DefaultUriBuilderFactory` is a default
implementation of `UriBuilderFactory` that uses `UriComponentsBuilder` internally and implementation of `UriBuilderFactory` that uses `UriComponentsBuilder` internally and
exposes shared configuration options. exposes shared configuration options.
`RestTemplate` example: The following example shows how to configure a `RestTemplate`:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -85,9 +97,11 @@ exposes shared configuration options.
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory); restTemplate.setUriTemplateHandler(factory);
---- ----
====
`WebClient` example: The following example configures a `WebClient`:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -99,11 +113,13 @@ exposes shared configuration options.
WebClient client = WebClient.builder().uriBuilderFactory(factory).build(); WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
---- ----
====
In addition `DefaultUriBuilderFactory` can also be used directly. It is similar to using 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 `UriComponentsBuilder` but, instead of static factory methods, it is an actual instance
that holds configuration and preferences: that holds configuration and preferences, as the following example shows:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -114,35 +130,35 @@ that holds configuration and preferences:
.queryParam("q", "{q}") .queryParam("q", "{q}")
.build("Westin", "123"); .build("Westin", "123");
---- ----
====
[[web-uri-encoding]] [[web-uri-encoding]]
= URI Encoding = URI Encoding
[.small]#Spring MVC and Spring WebFlux# [.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()] - * {api-spring-framework}/web/util/UriComponentsBuilder.html#encode--[UriComponentsBuilder#encode()]:
pre-encodes the URI template first, then strictly encodes URI variables when expanded. Pre-encodes the URI template first and then strictly encodes URI variables when expanded.
. {api-spring-framework}/web/util/UriComponents.html#encode--[UriComponents#encode()] - * {api-spring-framework}/web/util/UriComponents.html#encode--[UriComponents#encode()]:
encodes URI components _after_ URI variables are expanded. Encodes URI components _after_ URI variables are expanded.
Both options replace non-ASCII and illegal characters with escaped octets, however option Both options replace non-ASCII and illegal characters with escaped octets. However, the first option
1 also replaces characters with reserved meaning that appear in URI variables. also replaces characters with reserved meaning that appear in URI variables.
[TIP] 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
Consider ";" which is legal in a path but has reserved meaning. Option 1 replaces replaces ";", since it is a legal character in a path.
";" 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.
====
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 variables as opaque data to be fully encoded, while option 2 is useful only if
URI variables intentionally contain reserved characters. URI variables intentionally contain reserved characters.
Example usage using option 1: The following example uses the first option:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -154,9 +170,12 @@ URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
// Result is "/hotel%20list/New%20York?q=foo%2Bbar" // 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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -164,19 +183,24 @@ URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}") .queryParam("q", "{q}")
.build("New York", "foo+bar") .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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}?q={q}") URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}?q={q}")
.build("New York", "foo+bar") .build("New York", "foo+bar")
---- ----
====
The `WebClient` and the `RestTemplate` expand and encode URI templates internally through 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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -191,22 +215,23 @@ the `UriBuilderFactory` strategy. Both can be configured with a custom strategy:
// Customize the WebClient.. // Customize the WebClient..
WebClient client = WebClient.builder().uriBuilderFactory(factory).build(); WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
---- ----
====
The `DefaultUriBuilderFactory` implementation uses `UriComponentsBuilder` internally to The `DefaultUriBuilderFactory` implementation uses `UriComponentsBuilder` internally to
expand and encode URI templates. As a factory it provides a single place to configure 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: the approach to encoding, based on one of the below encoding modes:
* `TEMPLATE_AND_VALUES` -- uses `UriComponentsBuilder#encode()`, corresponding to * `TEMPLATE_AND_VALUES`: Uses `UriComponentsBuilder#encode()`, corresponding to
option 1 above, to pre-encode the URI template and strictly encode URI variables when the first option in the earlier list, to pre-encode the URI template and strictly encode URI variables when
expanded. expanded.
* `VALUES_ONLY` -- does not encode the URI template and instead applies strict encoding * `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 to URI variables through `UriUtils#encodeUriUriVariables` prior to expanding them into the
template. 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. 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 reasons and for backwards compatibility. The `WebClient` relies on the default value
in `DefaultUriBuilderFactory` which was changed from `EncodingMode.URI_COMPONENTS` in in `DefaultUriBuilderFactory`, which was changed from `EncodingMode.URI_COMPONENTS` in
5.0.x to `EncodingMode.TEMPLATE_AND_VALUES` in 5.1. 5.0.x to `EncodingMode.TEMPLATE_AND_VALUES` in 5.1.

View File

@ -1,84 +1,86 @@
[[webflux-cors]] [[webflux-cors]]
= CORS = CORS
[.small]#<<web.adoc#mvc-cors,Same in Spring MVC>># [.small]#<<web.adoc#mvc-cors,Same as in Spring MVC>>#
Spring WebFlux lets you handle CORS (Cross-Origin Resource Sharing). This section
describes how to do so.
[[webflux-cors-intro]] [[webflux-cors-intro]]
== Introduction == Introduction
[.small]#<<web.adoc#mvc-cors-intro,Same in Spring MVC>># [.small]#<<web.adoc#mvc-cors-intro,Same as in Spring MVC>>#
For security reasons browsers prohibit AJAX calls to resources outside the current origin. 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 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 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] 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 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 what kind of cross-domain requests are authorized, rather than using less secure and less
powerful workarounds based on IFRAME or JSONP. powerful workarounds based on IFRAME or JSONP.
[[webflux-cors-processing]] [[webflux-cors-processing]]
== Processing == Processing
[.small]#<<web.adoc#mvc-cors-processing,Same in Spring MVC>># [.small]#<<web.adoc#mvc-cors-processing,Same as in Spring MVC>>#
The CORS specification distinguishes between preflight, simple, and actual requests. The CORS specification distinguishes between preflight, simple, and actual requests.
To learn how CORS works, you can read To learn how CORS works, you can read
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[this article], among 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 Spring WebFlux `HandlerMapping` implementations provide built-in support for CORS. After successfully
mapping a request to a handler, ``HandlerMapping``'s check the CORS configuration for the mapping a request to a handler, a `HandlerMapping` checks the CORS configuration for the
given request and handler and take further actions. Preflight requests are handled given request and handler and takes 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 the
required CORS response headers set. required CORS response headers set.
In order to enable cross-origin requests (i.e. the `Origin` header is present and 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 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 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 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 Each `HandlerMapping` can be
{api-spring-framework}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured] {api-spring-framework}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
individually with URL pattern based `CorsConfiguration` mappings. In most cases applications 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, use the WebFlux Java configuration to declare such mappings, which results in a single,
global map passed to all ``HadlerMappping``'s. global map passed to all `HadlerMappping` implementations.
Global CORS configuration at the `HandlerMapping` level can be combined with more You can combine global CORS configuration at the `HandlerMapping` level with more
fine-grained, handler-level CORS configuration. For example annotated controllers can use fine-grained, handler-level CORS configuration. For example, annotated controllers can use
class or method-level `@CrossOrigin` annotations (other handlers can implement class- or method-level `@CrossOrigin` annotations (other handlers can implement
`CorsConfigurationSource`). `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 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)`] {api-spring-framework}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`]
for more details. for more details.
[TIP] [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` * `CorsConfiguration`
* `CorsProcessor`, `DefaultCorsProcessor` * `CorsProcessor` and `DefaultCorsProcessor`
* `AbstractHandlerMapping` * `AbstractHandlerMapping`
==== ====
[[webflux-cors-controller]] [[webflux-cors-controller]]
== @CrossOrigin == Using `@CrossOrigin`
[.small]#<<web.adoc#mvc-cors-controller,Same in Spring MVC>># [.small]#<<web.adoc#mvc-cors-controller,Same as in Spring MVC>>#
The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`] 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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -98,19 +100,25 @@ public class AccountController {
} }
} }
---- ----
====
By default `@CrossOrigin` allows: By default, `@CrossOrigin` allows:
* All origins. * All origins.
* All headers. * All headers.
* All HTTP methods to which the controller method is mapped. * 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] [source,java,indent=0]
[subs="verbatim,quotes"] [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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@CrossOrigin(maxAge = 3600) @CrossOrigin(maxAge = 3600) <1>
@RestController @RestController
@RequestMapping("/account") @RequestMapping("/account")
public class AccountController { public class AccountController {
@CrossOrigin("http://domain2.com") @CrossOrigin("http://domain2.com") <2>
@GetMapping("/{id}") @GetMapping("/{id}")
public Mono<Account> retrieve(@PathVariable Long id) { public Mono<Account> 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]] [[webflux-cors-global]]
== Global Config == Global Configuration
[.small]#<<web.adoc#mvc-cors-global,Same in Spring MVC>># [.small]#<<web.adoc#mvc-cors-global,Same as in Spring MVC>>#
In addition to fine-grained, controller method level configuration you'll probably want to 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` define some global CORS configuration, too. You can set URL-based `CorsConfiguration`
mappings individually on any `HandlerMapping`. Most applications however will use the mappings individually on any `HandlerMapping`. Most applications, however, use the
WebFlux Java config to do that. WebFlux Java configuration to do that.
By default global configuration enables the following: By default global configuration enables the following:
* All origins. * All origins.
* All headers. * All headers.
* `GET`, `HEAD`, and `POST` methods. * `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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -199,19 +216,22 @@ public class WebConfig implements WebFluxConfigurer {
} }
} }
---- ----
====
[[webflux-cors-webfilter]] [[webflux-cors-webfilter]]
== CORS WebFilter == CORS `WebFilter`
[.small]#<<web.adoc#mvc-cors-filter,Same in Spring MVC>># [.small]#<<web.adoc#mvc-cors-filter,Same as in Spring MVC>>#
You can apply CORS support through the built-in You can apply CORS support through the built-in
{api-spring-framework}/web/cors/reactive/CorsWebFilter.html[`CorsWebFilter`], which is a {api-spring-framework}/web/cors/reactive/CorsWebFilter.html[`CorsWebFilter`], which is a
good fit with <<webflux-fn,functional endpoints>>. good fit with <<webflux-fn,functional endpoints>>.
To configure the filter, you can declare a `CorsWebFilter` bean and pass a 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] [source,java,indent=0]
[subs="verbatim"] [subs="verbatim"]
---- ----
@ -234,4 +254,4 @@ CorsWebFilter corsFilter() {
return new CorsWebFilter(source); return new CorsWebFilter(source);
} }
---- ----
====

View File

@ -1,31 +1,31 @@
[[webflux-fn]] [[webflux-fn]]
= Functional Endpoints = 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. 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 It is an alternative to the annotation-based programming model but otherwise runs on
the same <<web-reactive.adoc#webflux-reactive-spring-web>> foundation the same <<web-reactive.adoc#webflux-reactive-spring-web>> foundation.
[[webflux-fn-overview]] [[webflux-fn-overview]]
== Overview == Overview
An HTTP request is handled with a **`HandlerFunction`** that takes `ServerRequest` and An HTTP request is handled with a `HandlerFunction` that takes `ServerRequest` and
returns `Mono<ServerResponse>`, both of which are immutable contracts that offer JDK-8 returns `Mono<ServerResponse>`, both of which are immutable contracts that offer
friendly access to the HTTP request and response. `HandlerFunction` is the equivalent of JDK 8-friendly access to the HTTP request and response. `HandlerFunction` is the equivalent of
an `@RequestMapping` method in the annotation-based programming model. 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<HandlerFunction>`. When a request is matched to a `ServerRequest` and returns `Mono<HandlerFunction>`. When a request is matched to a
particular route, the `HandlerFunction` mapped to the route is used. `RouterFunction` is 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 `RouterFunctions.route(RequestPredicate, HandlerFunction)` provides a router function
default implementation that can be used with a number of built-in request predicates. default implementation that can be used with a number of built-in request predicates,
For example: as the following example shows:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [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 One way to run a `RouterFunction` is to turn it into an `HttpHandler` and install it
through one of the built-in <<web-reactive.adoc#webflux-httphandler,server adapters>>: through one of the built-in <<web-reactive.adoc#webflux-httphandler,server adapters>>:
@ -66,96 +67,137 @@ through one of the built-in <<web-reactive.adoc#webflux-httphandler,server adapt
* `RouterFunctions.toHttpHandler(RouterFunction)` * `RouterFunctions.toHttpHandler(RouterFunction)`
* `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)` * `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)`
Most applications can run through the WebFlux Java configuration, see <<webflux-fn-running>>.
Most applications will run through the WebFlux Java config, see <<webflux-fn-running>>.
[[webflux-fn-handler-functions]] [[webflux-fn-handler-functions]]
== HandlerFunction == 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 access to the HTTP request and response with
http://www.reactive-streams.org[Reactive Streams] back pressure against the request 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`. 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` 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
<<web-reactive.adoc#webflux-reactive-libraries,Reactive Libraries>>. <<web-reactive.adoc#webflux-reactive-libraries,Reactive Libraries>>.
[[webflux-fn-request]] [[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. while access to the body is provided through the `body` methods.
To extract the request body to a `Mono<String>`: The following example extracts the request body to a `Mono<String>`:
Mono<String> string = request.bodyToMono(String.class); ====
[source,java]
----
Mono<String> string = request.bodyToMono(String.class);
----
====
To extract the body to a `Flux<Person>`, where `Person` objects are decoded from some The following example extracts the body to a `Flux<Person>`, where `Person` objects are decoded from some
serialized form, such as JSON or XML: serialized form, such as JSON or XML:
Flux<Person> people = request.bodyToFlux(Person.class); ====
[source,java]
----
Flux<Person> people = request.bodyToFlux(Person.class);
----
====
The above are shortcuts that use the more general `ServerRequest.body(BodyExtractor)` The preceding examples are shortcuts that use the more general `ServerRequest.body(BodyExtractor)`,
which accepts the `BodyExtractor` functional, strategy interface. The utility class which accepts the `BodyExtractor` functional strategy interface. The utility class
`BodyExtractors` provides access to a number of instances. For example, the above can `BodyExtractors` provides access to a number of instances. For example, the preceding examples can
also be written as follows: also be written as follows:
Mono<String> string = request.body(BodyExtractors.toMono(String.class)); ====
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class)); [source,java]
----
Mono<String> string = request.body(BodyExtractors.toMono(String.class));
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
----
====
To access form data: The following example shows how to access form data:
Mono<MultiValueMap<String, String> map = request.body(BodyExtractors.toFormData()); ====
[source,java]
----
Mono<MultiValueMap<String, String> 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<MultiValueMap<String, Part> map = request.body(BodyExtractors.toMultipartData()); ====
[source,java]
----
Mono<MultiValueMap<String, Part> 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<Part> parts = request.body(BodyExtractos.toParts()); ====
[source,java]
----
Flux<Part> parts = request.body(BodyExtractos.toParts());
----
====
[[webflux-fn-response]] [[webflux-fn-response]]
=== ServerResponse === Using `ServerResponse`
`ServerResponse` provides access to the HTTP response and since it is immutable, you use `ServerResponse` provides access to the HTTP response and, since it is immutable, you can use
a build to create it. The builder can be used to set the response status, to add response 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. Below is an example with a 200 (OK) response with JSON headers, or to provide a body. The following example creates a 200 (OK) response with JSON
content: content:
Mono<Person> person = ... ====
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class); [source,java]
----
Mono<Person> 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]] [[webflux-fn-handler-classes]]
=== 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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
HandlerFunction<ServerResponse> helloWorld = HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().body(fromObject("Hello World")); request -> ServerResponse.ok().body(fromObject("Hello World"));
---- ----
====
That is convenient but in an application we need multiple functions and useful to group 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 an `@Controller`). For example, related handler functions together into a handler (like a `@Controller`). For example,
here is a class that exposes a reactive `Person` repository: the following class exposes a reactive `Person` repository:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [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 <1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
JSON. JSON.
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body. <2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
Note that `PersonRepository.savePerson(Person)` returns `Mono<Void>`: an empty Mono that emits Note that `PersonRepository.savePerson(Person)` returns `Mono<Void>`: an empty `Mono` that emits
a completion signal when the person has been read from the request and stored. So we use the a completion signal when the person has been read from the request and stored. So we use the
`build(Publisher<Void>)` method to send a response when that completion signal is received, i.e. `build(Publisher<Void>)` method to send a response when that completion signal is received (that is,
when the `Person` has been saved. when the `Person` has been saved).
<3> `getPerson` is a handler function that returns a single person, identified via the path <3> `getPerson` is a handler function that returns a single person, identified by the `id` path
variable `id`. We retrieve that `Person` via the repository, and create a JSON response if it is 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<T>)` to return a 404 Not Found response. found. If it is not found, we use `switchIfEmpty(Mono<T>)` to return a 404 Not Found response.
====
[[webflux-fn-router-functions]] [[webflux-fn-router-functions]]
== RouterFunction == Using `RouterFunction`
`RouterFunction` is used to route requests to a `HandlerFunction`. Typically, you do not `RouterFunction` is used to route requests to a `HandlerFunction`. Typically, you do not
write router functions yourself, but rather use write router functions yourself, but rather use
`RouterFunctions.route(RequestPredicate, HandlerFunction)`. If the predicate applies, the `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. and that would translate to a 404 (Not Found) response.
[[webflux-fn-predicates]] [[webflux-fn-predicates]]
=== Predicates === Using Predicates
You can write your own `RequestPredicate`, but the `RequestPredicates` utility class You can write your own `RequestPredicate`, but the `RequestPredicates` utility class
offers commonly implementations, based on the request path, HTTP method, content-type, offers commonly used implementations, based on the request path, HTTP method, content-type,
and so on. For example: and so on. The following example creates a request predicate based on a path:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -228,18 +271,19 @@ RouterFunction<ServerResponse> route =
RouterFunctions.route(RequestPredicates.path("/hello-world"), RouterFunctions.route(RequestPredicates.path("/hello-world"),
request -> Response.ok().body(fromObject("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.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)` `RequestPredicates.GET(String)` is composed from `RequestPredicates.method(HttpMethod)`
and `RequestPredicates.path(String)`. and `RequestPredicates.path(String)`.
You can compose multiple router functions into one, such that they're evaluated in order, You can compose multiple router functions into one, such that they are evaluated in order,
and if the first route doesn't match, the second is evaluated. You can declare more and, if the first route does not match, the second is evaluated. You can declare more
specific routes before more general ones. specific routes before more general ones.
@ -247,16 +291,17 @@ specific routes before more general ones.
[[webflux-fn-routes]] [[webflux-fn-routes]]
=== Routes === Routes
You can compose multiple router functions together via: You can compose multiple router functions together by using:
* `RouterFunction.and(RouterFunction)` * `RouterFunction.and(RouterFunction)`
* `RouterFunction.andRoute(RequestPredicate, HandlerFunction)` -- shortcut for * `RouterFunction.andRoute(RequestPredicate, HandlerFunction)` -- shortcut for
`RouterFunction.and()` with nested `RouterFunctions.route()`. `RouterFunction.and()` with nested `RouterFunctions.route()`.
Using composed routes and predicates, we can then declare the following routes, referring Using composed routes and predicates, we can then declare the following routes, referring
to methods in the `PersonHandler`, shown in <<webflux-fn-handler-class>>, through to methods in the `PersonHandler` (shown in <<webflux-fn-handler-class>>) through
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html[method-references]: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html[method-references]:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -271,45 +316,45 @@ RouterFunction<ServerResponse> personRoute =
.andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople) .andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople)
.andRoute(POST("/person"), handler::createPerson); .andRoute(POST("/person"), handler::createPerson);
---- ----
====
[[webflux-fn-running]] [[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 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)`
* `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)` * `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
<<web-reactive.adoc#webflux-httphandler,HttpHandler>> for server-specific instructions. <<web-reactive.adoc#webflux-httphandler,HttpHandler>> for server-specific instructions.
A more advanced option is to run with a A more advanced option is to run with a
<<web-reactive.adoc#webflux-dispatcher-handler,DispatcherHandler>>-based setup through the <<web-reactive.adoc#webflux-dispatcher-handler,`DispatcherHandler`>>-based setup through the
<<web-reactive.adoc#webflux-config>> which uses Spring configuration to declare the <<web-reactive.adoc#webflux-config>>, which uses Spring configuration to declare the
components quired to process requests. The WebFlux Java config declares the following components required to process requests. The WebFlux Java configuration declares the following
infrastructure components to support functional endpoints: infrastructure components to support functional endpoints:
* `RouterFunctionMapping` -- detects one or more `RouterFunction<?>` beans in the Spring * `RouterFunctionMapping`: Detects one or more `RouterFunction<?>` beans in the Spring
configuration, combines them via `RouterFunction.andOther`, and routes requests to the configuration, combines them through `RouterFunction.andOther`, and routes requests to the
resulting composed `RouterFunction`. 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. 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`. `HandlerFunction` by invoking the `writeTo` method of the `ServerResponse`.
The above components allow functional endpoints to fit within the `DispatcherHandler` request The preceding components let functional endpoints fit within the `DispatcherHandler` request
processing lifecycle, and also potentially run side by side with annotated controllers, if 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 any are declared. It is also how functional endpoints are enabled by the Spring Boot WebFlux
starter. starter.
Below is example WebFlux Java config (see The following example shows a WebFlux Java configuration (see
<<web-reactive.adoc#webflux-dispatcher-handler,DispatcherHandler>> for how to run): <<web-reactive.adoc#webflux-dispatcher-handler,DispatcherHandler>> for how to run it):
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -345,23 +390,24 @@ public class WebConfig implements WebFluxConfigurer {
} }
} }
---- ----
====
[[webflux-fn-handler-filter-function]] [[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 `RouterFunction.filter(HandlerFilterFunction)`, where `HandlerFilterFunction` is essentially a
function that takes a `ServerRequest` and `HandlerFunction`, and returns a `ServerResponse`. 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 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 `HandlerFunction` that is routed to, but it can also be another `FilterFunction` if multiple filters
are applied. are applied.
With annotations, similar functionality can be achieved using `@ControllerAdvice` and/or a `ServletFilter`. With annotations, you can achieve similar functionality by using `@ControllerAdvice`, a `ServletFilter`, or both.
Let's add a simple security filter to our route, assuming that we have a `SecurityManager` that 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: can determine whether a particular path is allowed. The following example shows how to do so:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -380,11 +426,9 @@ RouterFunction<ServerResponse> 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.
[NOTE]
====
CORS support for functional endpoints is provided via a dedicated <<webflux-cors-webfilter,`CorsWebFilter`>>.
==== ====
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 through a dedicated <<webflux-cors-webfilter,`CorsWebFilter`>>.

View File

@ -1,52 +1,51 @@
[[webflux-view]] [[webflux-view]]
= View Technologies = View Technologies
[.small]#<<web.adoc#mvc-view,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view,Same as in Spring MVC>>#
The use of view technologies in Spring WebFlux is pluggable, whether you decide to 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. use Thymeleaf, FreeMarker, or some other view technology is primarily a matter of a configuration change.
This chapter covers view technologies integrated with Spring WebFlux. We assume you are This chapter covers the view technologies integrated with Spring WebFlux. We assume you are
already familiar with <<webflux-viewresolution>>. already familiar with <<webflux-viewresolution>>.
[[webflux-view-thymeleaf]] [[webflux-view-thymeleaf]]
== Thymeleaf == Thymeleaf
[.small]#<<web.adoc#mvc-view-thymeleaf,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view-thymeleaf,Same as in Spring MVC>>#
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 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 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 running server. Thymeleaf offers an extensive set of features, and it is actively developed
and maintained. For a more complete introduction see the and maintained. For a more complete introduction, see the
http://www.thymeleaf.org/[Thymeleaf] project home page. http://www.thymeleaf.org/[Thymeleaf] project home page.
The Thymeleaf integration with Spring WebFlux is managed by the Thymeleaf project. The 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 `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://www.thymeleaf.org/documentation.html[Thymeleaf+Spring] and the WebFlux integration
http://forum.thymeleaf.org/Thymeleaf-3-0-8-JUST-PUBLISHED-td4030687.html[announcement]. http://forum.thymeleaf.org/Thymeleaf-3-0-8-JUST-PUBLISHED-td4030687.html[announcement].
[[webflux-view-freemarker]] [[webflux-view-freemarker]]
== FreeMarker == FreeMarker
[.small]#<<web.adoc#mvc-view-freemarker,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view-freemarker,Same as in Spring MVC>>#
http://www.freemarker.org[Apache FreeMarker] is a template engine for generating any 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. integration for using Spring WebFlux with FreeMarker templates.
[[webflux-view-freemarker-contextconfig]] [[webflux-view-freemarker-contextconfig]]
=== View config === View Configuration
[.small]#<<web.adoc#mvc-view-freemarker-contextconfig,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view-freemarker-contextconfig,Same as in Spring MVC>>#
To configure FreeMarker as a view technology: The following example shows how to configure FreeMarker as a view technology:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [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` 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 shown in the preceding example. Given the preceding configuration, if your controller returns the view name,
"welcome" then the resolver will look for the `welcome`, the resolver looks for the
`classpath:/templates/freemarker/welcome.ftl` template. `classpath:/templates/freemarker/welcome.ftl` template.
[[webflux-views-freemarker]] [[webflux-views-freemarker]]
=== FreeMarker config === FreeMarker Configuration
[.small]#<<web.adoc#mvc-views-freemarker,Same in Spring MVC>># [.small]#<<web.adoc#mvc-views-freemarker,Same as in Spring MVC>>#
FreeMarker 'Settings' and 'SharedVariables' can be passed directly to the FreeMarker You can pass FreeMarker 'Settings' and 'SharedVariables' directly to the FreeMarker
`Configuration` object managed by Spring by setting the appropriate bean properties on `Configuration` object (managed by Spring) by setting the appropriate bean properties on
the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a the `FreeMarkerConfigurer` bean. The `freemarkerSettings` property requires a
`java.util.Properties` object and the `freemarkerVariables` property requires a `java.util.Properties` object, and the `freemarkerVariables` property requires a
`java.util.Map`. `java.util.Map`. The following example shows how to use a `FreeMarkerConfigurer`:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [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 See the FreeMarker documentation for details of settings and variables as they apply to
the `Configuration` object. the `Configuration` object.
[[webflux-view-script]] [[webflux-view-script]]
== Script Views == Script Views
[.small]#<<web.adoc#mvc-view-script,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view-script,Same as in Spring MVC>>#
The Spring Framework has a built-in integration for using Spring WebFlux with any The Spring Framework has a built-in integration for using Spring WebFlux with any
templating library that can run on top of the 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 https://www.jcp.org/en/jsr/detail?id=223[JSR-223] Java scripting engine.
of templating libraries we've tested on different script engines: The following table shows the templating libraries that we have tested on different script engines:
[horizontal] [%header]
http://handlebarsjs.com/[Handlebars] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |===
https://mustache.github.io/[Mustache] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |Scripting Library |Scripting Engine
http://facebook.github.io/react/[React] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |http://handlebarsjs.com/[Handlebars] |http://openjdk.java.net/projects/nashorn/[Nashorn]
http://www.embeddedjs.com/[EJS] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |https://mustache.github.io/[Mustache] |http://openjdk.java.net/projects/nashorn/[Nashorn]
http://www.stuartellis.eu/articles/erb/[ERB] :: http://jruby.org[JRuby] |http://facebook.github.io/react/[React] |http://openjdk.java.net/projects/nashorn/[Nashorn]
https://docs.python.org/2/library/string.html#template-strings[String templates] :: http://www.jython.org/[Jython] |http://www.embeddedjs.com/[EJS] |http://openjdk.java.net/projects/nashorn/[Nashorn]
https://github.com/sdeleuze/kotlin-script-templating[Kotlin Script templating] :: http://kotlinlang.org/[Kotlin] |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] TIP: The basic rule for integrating any other script engine is that it must implement the
====
The basic rule for integrating any other script engine is that it must implement the
`ScriptEngine` and `Invocable` interfaces. `ScriptEngine` and `Invocable` interfaces.
====
[[webflux-view-script-dependencies]] [[webflux-view-script-dependencies]]
=== Requirements === Requirements
[.small]#<<web.adoc#mvc-view-script-dependencies,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view-script-dependencies,Same as in Spring MVC>>#
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. 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://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. * 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` * `org.jetbrains.kotlin:kotlin-script-util` dependency and a `META-INF/services/javax.script.ScriptEngineFactory`
file containing a `org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory` 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. 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 You need to have the script templating library. One way to do that for Javascript is
through http://www.webjars.org/[WebJars]. through http://www.webjars.org/[WebJars].
@ -162,13 +163,14 @@ through http://www.webjars.org/[WebJars].
[[webflux-view-script-integrate]] [[webflux-view-script-integrate]]
=== Script templates === Script Templates
[.small]#<<web.adoc#mvc-view-script-integrate,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view-script-integrate,Same as in Spring MVC>>#
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. 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] [source,java,indent=0]
[subs="verbatim,quotes"] [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 * `String template`: The template content
* `Map model`: the view model * `Map model`: The view model
* `RenderingContext renderingContext`: the * `RenderingContext renderingContext`: The
{api-spring-framework}/web/servlet/view/script/RenderingContext.html[RenderingContext] {api-spring-framework}/web/servlet/view/script/RenderingContext.html[`RenderingContext`]
that gives access to the application context, the locale, the template loader and the that gives access to the application context, the locale, the template loader, and the
url (since 5.0) URL (since 5.0)
`Mustache.render()` is natively compatible with this signature, so you can call it directly. `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] 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 http://en.wikipedia.org/wiki/Polyfill[polyfill] in order to emulate some
browser facilities not available in the server-side script engine. 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] [source,java,indent=0]
[subs="verbatim,quotes"] [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 NOTE: Setting the `sharedEngine` property to `false` is required when using non-thread-safe
React running on Nashorn for example. In that case, Java 8u60 or greater is required due 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]. to https://bugs.openjdk.java.net/browse/JDK-8076099[this bug].
`polyfill.js` defines only the `window` object needed by Handlebars to run properly,
as the following snippet shows:
==== ====
`polyfill.js` only defines the `window` object needed by Handlebars to run properly:
[source,javascript,indent=0] [source,javascript,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
var window = {}; var window = {};
---- ----
====
This basic `render.js` implementation compiles the template before using it. A production 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 This can be done on the script side, as well as any customization you need (managing
template engine configuration for example). template engine configuration for example).
The following example shows how compile a template:
====
[source,javascript,indent=0] [source,javascript,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -263,34 +271,31 @@ template engine configuration for example).
return compiledTemplate(model); return compiledTemplate(model);
} }
---- ----
====
Check out the Spring Framework unit tests, 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], 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. for more configuration examples.
[[webflux-view-httpmessagewriter]] [[webflux-view-httpmessagewriter]]
== JSON, XML == JSON and XML
[.small]#<<web.adoc#mvc-view-jackson,Same in Spring MVC>># [.small]#<<web.adoc#mvc-view-jackson,Same as in Spring MVC>>#
For <<webflux-multiple-representations>> purposes it is useful to be able to alternate For <<webflux-multiple-representations>> 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, 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 depending on the content type requested by the client. To support doing so, Spring WebFlux
provides the `HttpMessageWriterView` that can be used to plug in any of the available provides the `HttpMessageWriterView`, which you can use to plug in any of the available
<<webflux-codecs>> from `spring-web` such as `Jackson2JsonEncoder`, <<webflux-codecs>> from `spring-web`, such as `Jackson2JsonEncoder`,
`Jackson2SmileEncoder`, or `Jaxb2XmlEncoder`. `Jackson2SmileEncoder`, or `Jaxb2XmlEncoder`.
Unlike other view technologies, `HttpMessageWriterView` does not require a `ViewResolver`, Unlike other view technologies, `HttpMessageWriterView` does not require a `ViewResolver`
but instead is <<webflux-config-view-resolvers,configured>> as a default view. You can but is instead <<webflux-config-view-resolvers,configured>> as a default view. You can
configure one more such default views, wrapping different ``HttpMessageWriter``'s or configure one or more such default views, wrapping different `HttpMessageWriter` instances or
``Encoder``'s. The one that matches the requested content type is used at runtime. `Encoder` instances. 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.
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.

View File

@ -7,14 +7,13 @@ using a functional-style API that exposes Reactor `Flux` and `Mono` types, see
<<web-reactive.adoc#webflux-codecs,codecs>> that WebFlux server applications use to work <<web-reactive.adoc#webflux-codecs,codecs>> that WebFlux server applications use to work
with request and response content. 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 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], the Jetty https://github.com/jetty-project/jetty-reactive-httpclient[reactive HtpClient],
and others can be plugged in through a `ClientHttpConnector`. and others can be plugged in through a `ClientHttpConnector`.
[[webflux-client-builder]] [[webflux-client-builder]]
== Configuration == Configuration
@ -23,22 +22,23 @@ The simplest way to create a `WebClient` is through one of the static factory me
* `WebClient.create()` * `WebClient.create()`
* `WebClient.create(String baseUrl)` * `WebClient.create(String baseUrl)`
The above uses Reactor Netty `HttpClient` from "io.projectreactor.netty:reactor-netty" The preceding methods use Reactor Netty `HttpClient` from `io.projectreactor.netty:reactor-netty`
with default settings and participates in global resources such for event loop threads and with default settings and participates in global resources for event loop threads and
a connection pool, see <<webflux-client-builder-reactor, Reactor Netty configuration>>. a connection pool. See <<webflux-client-builder-reactor, Reactor Netty configuration>>.
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. * `uriBuilderFactory`: Customized `UriBuilderFactory` to use as a base URL.
* `defaultHeader` -- headers for every request. * `defaultHeader`: Headers for every request.
* `defaultCookie)` -- cookies for every request. * `defaultCookie)`: Cookies for every request.
* `defaultRequest` -- `Consumer` to customize every request. * `defaultRequest`: `Consumer` to customize every request.
* `filter` -- client filter for every request. * `filter`: Client filter for every request.
* `exchangeStrategies` -- HTTP message reader/writer customizations. * `exchangeStrategies`: HTTP message reader/writer customizations.
* `clientConnector` -- HTTP client library settings. * `clientConnector`: HTTP client library settings.
For example, to configure <<web-reactive.adoc#webflux-codecs,HTTP codecs>>: The following example configures <<web-reactive.adoc#webflux-codecs,HTTP codecs>>:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -52,10 +52,12 @@ For example, to configure <<web-reactive.adoc#webflux-codecs,HTTP codecs>>:
.exchangeStrategies(strategies) .exchangeStrategies(strategies)
.build(); .build();
---- ----
====
Once built a `WebClient` instance is immutable. However, you can clone it, and build a Once built, a `WebClient` instance is immutable. However, you can clone it and build a
modified copy without affecting the original instance: modified copy without affecting the original instance, as the following example shows:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -69,14 +71,16 @@ modified copy without affecting the original instance:
// client2 has filterA, filterB, filterC, filterD // client2 has filterA, filterB, filterC, filterD
---- ----
====
[[webflux-client-builder-reactor]] [[webflux-client-builder-reactor]]
=== Reactor Netty === Reactor Netty
To customize Reactor Netty settings: You can customize Reactor Netty settings:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -85,18 +89,21 @@ To customize Reactor Netty settings:
WebClient webClient = WebClient.builder().clientConnector(connector).build(); 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. `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. 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 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 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 application deployed as a WAR), you can declare a Spring-managed bean of type
`ReactorResourceFactory` with `useGlobalResources=true` (the default) to ensure the Reactor `ReactorResourceFactory` with `globalResources=true` (the default) to ensure that the Reactor
Netty global resources are shut down when the Spring `ApplicationContext` is closed: Netty global resources are shut down when the Spring `ApplicationContext` is closed,
as the following example shows:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -105,11 +112,13 @@ Netty global resources are shut down when the Spring `ApplicationContext` is clo
return new ReactorResourceFactory(); return new ReactorResourceFactory();
} }
---- ----
====
You may also choose not to participate in the global Reactor Netty resources. However keep You can also choose not to participate in the global Reactor Netty resources. However,
in mind in this mode the burden is on you to ensure all Reactor Netty client and server in this mode, the burden is on you to ensure that all Reactor Netty client and server
instances use shared resources: instances use shared resources, as the following example shows:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -134,15 +143,18 @@ instances use shared resources:
} }
---- ----
<1> Create resources independent of global ones. <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`. <3> Plug the connector into the `WebClient.Builder`.
====
[[webflux-client-builder-jetty]] [[webflux-client-builder-jetty]]
=== Jetty === Jetty
To customize Jetty `HttpClient` settings: The following example shows how to customize Jetty `HttpClient` settings:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -152,14 +164,16 @@ To customize Jetty `HttpClient` settings:
WebClient webClient = WebClient.builder().clientConnector(connector).build(); 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. 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 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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -183,16 +197,19 @@ Spring-managed bean of type `JettyResourceFactory`:
---- ----
<1> Create shared resources. <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`. <3> Plug the connector into the `WebClient.Builder`.
====
[[webflux-client-retrieve]] [[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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -203,9 +220,11 @@ The `retrieve()` method is the easiest way to get a response body and decode it:
.retrieve() .retrieve()
.bodyToMono(Person.class); .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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -214,12 +233,15 @@ You can also get a stream of objects decoded from the response:
.retrieve() .retrieve()
.bodyToFlux(Quote.class); .bodyToFlux(Quote.class);
---- ----
====
By default, responses with 4xx or 5xx status codes result in an 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. `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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -230,16 +252,17 @@ You can also use the `onStatus` method to customize the resulting exception:
.onStatus(HttpStatus::is5xxServerError, response -> ...) .onStatus(HttpStatus::is5xxServerError, response -> ...)
.bodyToMono(Person.class); .bodyToMono(Person.class);
---- ----
====
[[webflux-client-exchange]] [[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`: to `retrieve()` but also provides access to the `ClientResponse`:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -248,9 +271,11 @@ to `retrieve()` but also provides access to the `ClientResponse`:
.exchange() .exchange()
.flatMap(response -> response.bodyToMono(Person.class)); .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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -259,27 +284,25 @@ At this level you can also create a full `ResponseEntity`:
.exchange() .exchange()
.flatMap(response -> response.toEntity(Person.class)); .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. 4xx and 5xx responses. You have to check the status code and decide how to proceed.
[CAUTION] CAUTION: When you use `exchange()`, you must always use any of the `body` or `toEntity` methods of
====
When using `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 `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 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 expected. However, if the response does have content, the connection
will be closed and will not be placed back in the pool. is closed and is not placed back in the pool.
====
[[webflux-client-body]] [[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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -292,9 +315,11 @@ The request body can be encoded from an Object:
.retrieve() .retrieve()
.bodyToMono(Void.class); .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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -307,9 +332,12 @@ You can also have a stream of objects encoded:
.retrieve() .retrieve()
.bodyToMono(Void.class); .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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -322,16 +350,18 @@ Or if you have the actual value, use the `syncBody` shortcut method:
.retrieve() .retrieve()
.bodyToMono(Void.class); .bodyToMono(Void.class);
---- ----
====
[[webflux-client-body-form]] [[webflux-client-body-form]]
=== Form data === Form Data
To send form data, provide a `MultiValueMap<String, String>` as the body. Note that the To send form data, you can provide a `MultiValueMap<String, String>` as the body. Note that the
content is automatically set to `"application/x-www-form-urlencoded"` by the content is automatically set to `application/x-www-form-urlencoded` by the
`FormHttpMessageWriter`: `FormHttpMessageWriter`. The following example shows how to use `MultiValueMap<String, String>`:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -343,9 +373,11 @@ content is automatically set to `"application/x-www-form-urlencoded"` by the
.retrieve() .retrieve()
.bodyToMono(Void.class); .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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -357,16 +389,17 @@ You can also supply form data in-line via `BodyInserters`:
.retrieve() .retrieve()
.bodyToMono(Void.class); .bodyToMono(Void.class);
---- ----
====
[[webflux-client-body-multipart]] [[webflux-client-body-multipart]]
=== Multipart data === Multipart Data
To send multipart data, you need to provide a `MultiValueMap<String, ?>` whose values are To send multipart data, you need to provide a `MultiValueMap<String, ?>` 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 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<String, ?>`:
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
@ -379,15 +412,16 @@ multipart request:
MultiValueMap<String, HttpEntity<?>> parts = builder.build(); MultiValueMap<String, HttpEntity<?>> parts = builder.build();
---- ----
In most cases you do not have to specify the `Content-Type` for each part. The content 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, 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 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 explicitly provide the `MediaType` to use for each part through one of the overloaded
builder `part` methods. builder `part` methods.
Once a `MultiValueMap` is prepared, the easiest way to pass it to the the `WebClient` is 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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -399,15 +433,17 @@ through the `syncBody` method:
.retrieve() .retrieve()
.bodyToMono(Void.class); .bodyToMono(Void.class);
---- ----
====
If the `MultiValueMap` contains at least one non-String value, which could also be If the `MultiValueMap` contains at least one non-`String` value, which could also
represent regular form data (i.e. "application/x-www-form-urlencoded"), you don't have to 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 set the `Content-Type` to `multipart/form-data`. This is always the case when using
`MultipartBodyBuilder` which ensures an `HttpEntity` wrapper. `MultipartBodyBuilder`, which ensures an `HttpEntity` wrapper.
As an alternative to `MultipartBodyBuilder`, you can also provide multipart content, 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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -419,7 +455,7 @@ inline-style, through the built-in `BodyInserters`. For example:
.retrieve() .retrieve()
.bodyToMono(Void.class); .bodyToMono(Void.class);
---- ----
====
@ -427,8 +463,9 @@ inline-style, through the built-in `BodyInserters`. For example:
== Client Filters == Client Filters
You can register a client filter (`ExchangeFilterFunction`) through the `WebClient.Builder` 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] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -443,10 +480,12 @@ WebClient client = WebClient.builder()
}) })
.build(); .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: a filter for basic authentication through a static factory method:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -457,11 +496,13 @@ WebClient client = WebClient.builder()
.filter(basicAuthentication("user", "password")) .filter(basicAuthentication("user", "password"))
.build(); .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 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] [source,java,intent=0]
[subs="verbatim,quotes"] [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 You can also replicate an existing `WebClient`, insert new filters, or remove already
registered filters. In the example below, a basic authentication filter is inserted at registered filters. The following example, inserts a basic authentication filter at
index 0: index 0:
====
[source,java,intent=0] [source,java,intent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -496,17 +539,17 @@ WebClient client = webClient.mutate()
}) })
.build(); .build();
---- ----
====
[[webflux-client-testing]] [[webflux-client-testing]]
== Testing == Testing
To test code that uses the `WebClient`, you can use a mock web server such as the 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 https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer]. To see an example
use, check 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] 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 in the Spring Framework tests or the
https://github.com/square/okhttp/tree/master/samples/static-server[static-server] https://github.com/square/okhttp/tree/master/samples/static-server[`static-server`]
sample in the OkHttp repository. sample in the OkHttp repository.

View File

@ -1,30 +1,33 @@
[[webflux-websocket]] [[webflux-websocket]]
= WebSockets = WebSockets
[.small]#<<web.adoc#websocket,Same in Servlet stack>># [.small]#<<web.adoc#websocket,Same as in the Servlet stack>>#
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. messaging.
include::websocket-intro.adoc[leveloffset=+1] include::websocket-intro.adoc[leveloffset=+1]
[[webflux-websocket-server]] [[webflux-websocket-server]]
== WebSocket API == WebSocket API
[.small]#<<web.adoc#websocket-server,Same in Servlet stack>># [.small]#<<web.adoc#websocket-server,Same as in the Servlet stack>>#
The Spring Framework provides a WebSocket API that can be used to write client and The Spring Framework provides a WebSocket API that you can use to write client- and
server side applications that handle WebSocket messages. server-side applications that handle WebSocket messages.
[[webflux-websocket-server-handler]] [[webflux-websocket-server-handler]]
=== Server === Server
[.small]#<<web.adoc#websocket-server-handler,Same in Servlet stack>># [.small]#<<web.adoc#websocket-server-handler,Same as in the Servlet stack>>#
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] [source,java,indent=0]
[subs="verbatim,quotes"] [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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -65,22 +70,24 @@ Then map it to a URL and add a `WebSocketHandlerAdapter`:
} }
} }
---- ----
====
[[webflux-websockethandler]] [[webflux-websockethandler]]
=== WebSocketHandler === Using `WebSocketHandler`
The `handle` method of `WebSocketHandler` takes `WebSocketSession` and returns `Mono<Void>` The `handle` method of `WebSocketHandler` takes `WebSocketSession` and returns `Mono<Void>`
to indicate when application handling of the session is complete. The session is handled 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"] [options="header"]
|=== |===
| WebSocketSession method | Description | `WebSocketSession` method | Description
| `Flux<WebSocketMessage> receive()` | `Flux<WebSocketMessage> 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<Void> send(Publisher<WebSocketMessage>)` | `Mono<Void> send(Publisher<WebSocketMessage>)`
| Takes a source for outgoing messages, writes the messages, and returns a `Mono<Void>` that | Takes a source for outgoing messages, writes the messages, and returns a `Mono<Void>` 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<Void>` that reflects the completion of that flow. Depending on application return a `Mono<Void>` that reflects the completion of that flow. Depending on application
requirements, the unified flow completes when: requirements, the unified flow completes when:
* Either inbound or outbound message streams complete. * Either the inbound or the outbound message stream completes.
* Inbound stream completes (i.e. connection closed), while outbound is infinite. * 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`. * At a chosen point, through the `close` method of `WebSocketSession`.
When inbound and outbound message streams are composed together, there is no need to 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. check if the connection is open, since Reactive Streams signals terminate activity.
The inbound stream receives a completion/error signal, and the outbound stream receives The inbound stream receives a completion or error signal, and the outbound stream
receives a cancellation signal. 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] [source,java,indent=0]
[subs="verbatim,quotes"] [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. <2> Do something with each message.
<3> Perform nested async operation using message content. <3> Perform nested asynchronous operations that use the message content.
<4> Return `Mono<Void>` that completes when receiving completes. <4> Return a `Mono<Void>` that completes when receiving completes.
[TIP]
==== ====
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 TIP: For nested, asynchronous operations, you may need to call `message.retain()` on underlying
released before you've had a chance to read the data. For more background see 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
<<core.adoc#databuffers,Data Buffers and Codecs>>. <<core.adoc#databuffers,Data Buffers and Codecs>>.
The following implementation combines the inbound and outbound streams:
==== ====
The below implementation combines the inbound with the outbound streams:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -157,12 +165,15 @@ class ExampleHandler implements WebSocketHandler {
} }
} }
---- ----
<1> Handle inbound message stream. <1> Handle the inbound message stream.
<2> Create outbound message, producing a combined flow. <2> Create the outbound message, producing a combined flow.
<3> Return `Mono<Void>` that doesn't complete while we continue to receive. <3> Return a `Mono<Void>` 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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -189,17 +200,18 @@ class ExampleHandler implements WebSocketHandler {
---- ----
<1> Handle inbound message stream. <1> Handle inbound message stream.
<2> Send outgoing messages. <2> Send outgoing messages.
<3> Join the streams and return `Mono<Void>` that completes when _either_ stream ends. <3> Join the streams and return a `Mono<Void>` that completes when either stream ends.
====
[[webflux-websocket-server-handshake]] [[webflux-websocket-server-handshake]]
=== Handshake === Handshake
[.small]#<<web.adoc#websocket-server-handshake,Same in Servlet stack>># [.small]#<<web.adoc#websocket-server-handshake,Same as in the Servlet stack>>#
`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 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. support for Reactor Netty, Tomcat, Jetty, and Undertow.
`HandshakeWebSocketService` exposes a `sessionAttributePredicate` property that allows `HandshakeWebSocketService` exposes a `sessionAttributePredicate` property that allows
@ -208,15 +220,15 @@ into the attributes of the `WebSocketSession`.
[[webflux-websocket-server-config]] [[webflux-websocket-server-config]]
=== Server config === Server Configation
[.small]#<<web.adoc#websocket-server-runtime-configuration,Same in Servlet stack>># [.small]#<<web.adoc#websocket-server-runtime-configuration,Same as in the Servlet stack>>#
The `RequestUpgradeStrategy` for each server exposes WebSocket-related configuration 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: WebSocket options when running on Tomcat:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [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. only Tomcat and Jetty expose such options.
[[webflux-websocket-server-cors]] [[webflux-websocket-server-cors]]
=== CORS === CORS
[.small]#<<web.adoc#websocket-server-allowed-origins,Same in Servlet stack>># [.small]#<<web.adoc#websocket-server-allowed-origins,Same as in the Servlet stack>>#
The easiest way to configure CORS and restrict access to a WebSocket endpoint is to The easiest way to configure CORS and restrict access to a WebSocket endpoint is to
have your `WebSocketHandler` implement `CorsConfigurationSource` and return a 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 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`. `combine` method on `CorsConfiguration`.
@ -259,18 +272,16 @@ specify CORS settings by URL pattern. If both are specified they're combined via
=== Client === Client
Spring WebFlux provides a `WebSocketClient` abstraction with implementations for 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] 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
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
API to suspend receiving messages for back pressure. 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: methods:
====
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -282,7 +293,8 @@ client.execute(url, session ->
.doOnNext(System.out::println) .doOnNext(System.out::println)
.then()); .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 before you can use them. All clients have constructor options related to configuration
of the underlying WebSocket client. of the underlying WebSocket client.

File diff suppressed because it is too large Load Diff

View File

@ -5,40 +5,35 @@ This section describes options for client-side access to REST endpoints.
[[webmvc-resttemplate]] [[webmvc-resttemplate]]
== RestTemplate == Using `RestTemplate`
`RestTemplate` is a synchronous client to perform HTTP requests. It is the original `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. libraries.
[NOTE] 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
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
scenarios. The `RestTemplate` will be deprecated in a future version and will not have scenarios. The `RestTemplate` will be deprecated in a future version and will not have
major new features added going forward. major new features added going forward.
====
See <<integration.adoc#rest-client-access,RestTemplate>> for details.
See <<integration.adoc#rest-client-access,REST Endpoints>> for details.
[[webmvc-webclient]] [[webmvc-webclient]]
== WebClient == Using `WebClient`
`WebClient` is a non-blocking, reactive client to perform HTTP requests. It was `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. 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. * Non-blocking I/O.
* Reactive Streams back pressure. * Reactive Streams back pressure.
* High concurrency with less hardware resources. * High concurrency with fewer hardware resources.
* Functional-style, fluent API taking advantage of Java 8 lambdas. * Functional-style, fluent API that takes advantage of Java 8 lambdas.
* Synchronous and asynchronous interactions. * Synchronous and asynchronous interactions.
* Streaming up to or streaming down from a server. * Streaming up to or streaming down from a server.

View File

@ -1,68 +1,69 @@
[[mvc-cors]] [[mvc-cors]]
= CORS = CORS
[.small]#<<web-reactive.adoc#webflux-cors,Same in Spring WebFlux>># [.small]#<<web-reactive.adoc#webflux-cors,Same as in Spring WebFlux>>#
Spring MVC lets you handle CORS (Cross-Origin Resource Sharing). This section
describes how to do so.
[[mvc-cors-intro]] [[mvc-cors-intro]]
== Introduction == Introduction
[.small]#<<web-reactive.adoc#webflux-cors-intro,Same in Spring WebFlux>># [.small]#<<web-reactive.adoc#webflux-cors-intro,Same as in Spring WebFlux>>#
For security reasons browsers prohibit AJAX calls to resources outside the current origin. 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 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 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] 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 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 what kind of cross-domain requests are authorized, rather than using less secure and less
powerful workarounds based on IFRAME or JSONP. powerful workarounds based on IFRAME or JSONP.
[[mvc-cors-processing]] [[mvc-cors-processing]]
== Processing == Processing
[.small]#<<web-reactive.adoc#webflux-cors-processing,Same in Spring WebFlux>># [.small]#<<web-reactive.adoc#webflux-cors-processing,Same as in Spring WebFlux>>#
The CORS specification distinguishes between preflight, simple, and actual requests. The CORS specification distinguishes between preflight, simple, and actual requests.
To learn how CORS works, you can read To learn how CORS works, you can read
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[this article], among 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 Spring MVC `HandlerMapping` implementations provide built-in support for CORS. After successfully
mapping a request to a handler, ``HandlerMapping``'s check the CORS configuration for the 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 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. required CORS response headers set.
In order to enable cross-origin requests (i.e. the `Origin` header is present and 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 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 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 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 Each `HandlerMapping` can be
{api-spring-framework}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured] {api-spring-framework}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
individually with URL pattern based `CorsConfiguration` mappings. In most cases applications 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 use the MVC Java configuration or the XML namespace to declare such mappings, which results
in a single, global map passed to all ``HadlerMappping``'s. in a single global map being passed to all `HandlerMappping` instances.
Global CORS configuration at the `HandlerMapping` level can be combined with more You can combine global CORS configuration at the `HandlerMapping` level with more
fine-grained, handler-level CORS configuration. For example annotated controllers can use fine-grained, handler-level CORS configuration. For example, annotated controllers can use
class or method-level `@CrossOrigin` annotations (other handlers can implement class- or method-level `@CrossOrigin` annotations (other handlers can implement
`CorsConfigurationSource`). `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 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)`] {api-spring-framework}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`]
for more details. for more details.
[TIP] [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` * `CorsConfiguration`
* `CorsProcessor`, `DefaultCorsProcessor` * `CorsProcessor`, `DefaultCorsProcessor`
@ -71,14 +72,15 @@ To learn more from the source or make advanced customizations, check:
[[mvc-cors-controller]] [[mvc-cors-controller]]
== @CrossOrigin == Using `@CrossOrigin`
[.small]#<<web-reactive.adoc#webflux-cors-controller,Same in Spring WebFlux>># [.small]#<<web-reactive.adoc#webflux-cors-controller,Same as in Spring WebFlux>>#
The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`] 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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -98,19 +100,24 @@ public class AccountController {
} }
} }
---- ----
====
By default `@CrossOrigin` allows: By default, `@CrossOrigin` allows:
* All origins. * All origins.
* All headers. * All headers.
* All HTTP methods to which the controller method is mapped. * 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. 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] [source,java,indent=0]
[subs="verbatim,quotes"] [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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -153,38 +163,43 @@ public class AccountController {
} }
} }
---- ----
====
[[mvc-cors-global]] [[mvc-cors-global]]
== Global Config == Global Configuration
[.small]#<<web-reactive.adoc#webflux-cors-global,Same in Spring WebFlux>># [.small]#<<web-reactive.adoc#webflux-cors-global,Same as in Spring WebFlux>>#
In addition to fine-grained, controller method level configuration you'll probably want to 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` define some global CORS configuration, too. You can set URL-based `CorsConfiguration`
mappings individually on any `HandlerMapping`. Most applications however will use the mappings individually on any `HandlerMapping`. Most applications, however, use the
MVC Java config or the MVC XNM namespace to do that. 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 origins.
* All headers. * All headers.
* `GET`, `HEAD`, and `POST` methods. * `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. should only be used where appropriate.
* `maxAge` is set to 30 minutes.
`maxAge` is set to 30 minutes.
[[mvc-cors-global-java]] [[mvc-cors-global-java]]
=== Java Config === Java Configuration
[.small]#<<web-reactive.adoc#webflux-cors-global,Same in Spring WebFlux>># [.small]#<<web-reactive.adoc#webflux-cors-global,Same as in Spring WebFlux>>#
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] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
@ -206,14 +221,17 @@ public class WebConfig implements WebMvcConfigurer {
} }
} }
---- ----
====
[[mvc-cors-global-xml]] [[mvc-cors-global-xml]]
=== XML Config === XML Configuration
To enable CORS in the XML namespace, use the `<mvc:cors>` element: To enable CORS in the XML namespace, you can use the `<mvc:cors>` element,
as the following example shows:
====
[source,xml,indent=0] [source,xml,indent=0]
[subs="verbatim"] [subs="verbatim"]
---- ----
@ -231,28 +249,26 @@ To enable CORS in the XML namespace, use the `<mvc:cors>` element:
</mvc:cors> </mvc:cors>
---- ----
====
[[mvc-cors-filter]] [[mvc-cors-filter]]
== CORS Filter == CORS Filter
[.small]#<<web-reactive.adoc#webflux-cors-webfilter,Same in Spring WebFlux>># [.small]#<<web-reactive.adoc#webflux-cors-webfilter,Same as in Spring WebFlux>>#
You can apply CORS support through the built-in You can apply CORS support through the built-in
{api-spring-framework}/web/filter/CorsFilter.html[`CorsFilter`]. {api-spring-framework}/web/filter/CorsFilter.html[`CorsFilter`].
[NOTE] NOTE: If you try to use the `CorsFilter` with Spring Security, keep in mind that Spring
====
If you're trying to use the `CorsFilter` with Spring Security, keep in mind that Spring
Security has Security has
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors[built-in support] https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors[built-in support]
for CORS. for CORS.
To configure the filter, pass a
`CorsConfigurationSource` to its constructor, as the following example shows:
==== ====
To configure the filter pass a
`CorsConfigurationSource` to its constructor:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim"] [subs="verbatim"]
---- ----
@ -271,4 +287,4 @@ source.registerCorsConfiguration("/**", config);
CorsFilter filter = new CorsFilter(source); CorsFilter filter = new CorsFilter(source);
---- ----
====

View File

@ -4,35 +4,25 @@
This section summarizes the options available in `spring-test` for Spring MVC applications. This section summarizes the options available in `spring-test` for Spring MVC applications.
**Servlet API Mocks** * Servlet API Mocks: Mock implementations of Servlet API contracts for unit testing controllers, filters, and
Mock implementations of Servlet API contracts for unit testing controllers, filters, and
other web components. See <<testing.adoc#mock-objects-servlet,Servlet API>> mock objects other web components. See <<testing.adoc#mock-objects-servlet,Servlet API>> mock objects
for more details. for more details.
**TestContext Framework** * TestContext Framework: Support for loading Spring configuration in JUnit and TestNG tests, including efficient
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 caching of the loaded configuration across test methods and support for loading a
`WebApplicationContext` with a `MockServletContext`. `WebApplicationContext` with a `MockServletContext`.
See <<testing.adoc#testcontext-framework,TestContext Framework>> for more details. See <<testing.adoc#testcontext-framework,TestContext Framework>> for more details.
**Spring MVC Test** * 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
A framework, also known as `MockMvc`, for testing annotated controllers through the infrastructure but without an HTTP server. See
`DispatcherServlet`, i.e. supporting annotations and complete with Spring MVC
infrastructure, but without an HTTP server. See
<<testing.adoc#spring-mvc-test-framework,Spring MVC Test>> for more details. <<testing.adoc#spring-mvc-test-framework,Spring MVC Test>> for more details.
**Client-side REST** * Client-side REST: `spring-test` provides a `MockRestServiceServer` that you can use as a mock server for
`spring-test` provides a `MockRestServiceServer` that can be used as a mock server for
testing client-side code that internally uses the `RestTemplate`. testing client-side code that internally uses the `RestTemplate`.
See <<testing.adoc#spring-mvc-test-client,Client REST Tests>> for more details. See <<testing.adoc#spring-mvc-test-client,Client REST Tests>> for more details.
**WebTestClient** * `WebTestClient`: Built for testing WebFlux applications, but it can also be used for
`WebTestClient` was 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 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. scenarios.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,35 @@
[[websocket-intro]] [[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 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 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. 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 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: to upgrade or, in this case, to switch to the WebSocket protocol. The following example
shows such an interaction:
====
[subs="quotes"] [subs="quotes"]
---- ----
GET /spring-websocket-portfolio/portfolio HTTP/1.1 GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080 Host: localhost:8080
**Upgrade: websocket** Upgrade: websocket <1>
**Connection: Upgrade** Connection: Upgrade <2>
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg== Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13 Sec-WebSocket-Version: 13
Origin: http://localhost:8080 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"] [subs="quotes"]
---- ----
**HTTP/1.1 101 Switching Protocols** **HTTP/1.1 101 Switching Protocols**
@ -31,65 +38,65 @@ Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0= Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp Sec-WebSocket-Protocol: v10.stomp
---- ----
====
After a successful handshake the TCP socket underlying the HTTP upgrade request remains 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. 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. 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. 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 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. instructions of the cloud provider related to WebSocket support.
[[websocket-intro-architecture]] [[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 it is important to understand that the two protocols lead to very different
architectures and application programming models. 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 clients access those URLs, request-response style. Servers route requests to the
appropriate handler based on the HTTP URL, method, and headers. 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 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 Subsequently, all application messages flow on that same TCP connection. This points to
an entirely different asynchronous, event-driven, messaging architecture. an entirely different asynchronous, event-driven, messaging architecture.
WebSocket is also a low-level transport protocol which unlike HTTP does not prescribe 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 any semantics to the content of messages. That means that there is no way to route or process
a message unless client and server agree on message semantics. 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 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, (for example, STOMP), through 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. In the absence of that, they need to come up with their own conventions.
[[websocket-intro-when-to-use]] [[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 WebSockets can make a web page be dynamic and interactive. However, in many cases,
a combination of Ajax and HTTP streaming and/or long polling could provide a simple and a combination of Ajax and HTTP streaming or long polling can provide a simple and
effective solution. effective solution.
For example news, mail, and social feeds need to update dynamically but it may be 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 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. 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. Latency alone is not a deciding factor. If the volume of messages is relatively low (for example,
monitoring network failures) HTTP streaming or polling may provide an effective solution. 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 It is the combination of low latency, high frequency, and high volume that make the best
case for the use WebSocket. case for the use of WebSocket.
Keep in mind also that over the Internet, restrictive proxies outside your control, 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 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 `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 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.

File diff suppressed because it is too large Load Diff