3239 lines
116 KiB
Plaintext
3239 lines
116 KiB
Plaintext
[[webflux]]
|
|
= Spring WebFlux
|
|
:doc-spring-security: {doc-root}/spring-security/site/docs/current/reference
|
|
|
|
|
|
|
|
|
|
[[webflux-introduction]]
|
|
== Introduction
|
|
The original web framework included in the Spring Framework, Spring Web MVC, was purpose
|
|
built for the Servlet API and Servlet containers. The reactive stack, web framework,
|
|
Spring WebFlux, was added later in version 5.0. It is fully non-blocking, supports
|
|
http://www.reactive-streams.org/[Reactive Streams] back pressure, and runs on servers such as
|
|
Netty, Undertow, and Servlet 3.1+ containers.
|
|
|
|
Both web frameworks mirror the names of their source modules
|
|
https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc[spring-webmvc] and
|
|
https://github.com/spring-projects/spring-framework/tree/master/spring-webflux[spring-webflux]
|
|
and co-exist side by side in the Spring Framework. Each module is optional.
|
|
Applications may use one or the other module, or in some cases both --
|
|
e.g. Spring MVC controllers with the reactive `WebClient`.
|
|
|
|
|
|
|
|
[[webflux-new-framework]]
|
|
=== Motivation
|
|
|
|
Why was Spring WebFlux created?
|
|
|
|
Part of the answer is the need for a non-blocking web stack to handle concurrency with a
|
|
small number of threads and scale with less hardware resources. Servlet 3.1 did provide
|
|
an API for non-blocking I/O. However, using it leads away from the rest of the Servlet API
|
|
where contracts are synchronous (`Filter`, `Servlet`) or blocking (`getParameter`,
|
|
`getPart`). This was the motivation for a new common API to serve as a foundation across
|
|
any non-blocking runtime. That is important because of servers such as Netty that are well
|
|
established in the async, non-blocking space.
|
|
|
|
The other part of the answer is functional programming. Much like the addition of annotations
|
|
in Java 5 created opportunities -- e.g. annotated REST controllers or unit tests, the addition
|
|
of lambda expressions in Java 8 created opportunities for functional APIs in Java.
|
|
This is a boon for non-blocking applications and continuation style APIs -- as popularized
|
|
by `CompletableFuture` and http://reactivex.io/[ReactiveX], that allow declarative
|
|
composition of asynchronous logic. At the programming model level Java 8 enabled Spring
|
|
WebFlux to offer functional web endpoints alongside with annotated controllers.
|
|
|
|
|
|
|
|
[[webflux-why-reactive]]
|
|
=== Define "reactive"
|
|
|
|
We touched on non-blocking and functional but what does reactive mean?
|
|
|
|
The term "reactive" refers to programming models that are built around reacting to change --
|
|
network component reacting to I/O events, UI controller reacting to mouse events, etc.
|
|
In that sense non-blocking is reactive because instead of being blocked we are now in the mode
|
|
of reacting to notifications as operations complete or data becomes available.
|
|
|
|
There is also another important mechanism that we on the Spring team associate with "reactive"
|
|
and that is non-blocking back pressure. In synchronous, imperative code, blocking calls
|
|
serve as a natural form of back pressure that forces the caller to wait. In non-blocking
|
|
code it becomes important to control the rate of events so that a fast producer does not
|
|
overwhelm its destination.
|
|
|
|
Reactive Streams is a
|
|
https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.1/README.md#specification[small spec],
|
|
also https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/Flow.html[adopted] in Java 9,
|
|
that defines the interaction between asynchronous components with back pressure.
|
|
For example a data repository -- acting as
|
|
http://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/org/reactivestreams/Publisher.html[Publisher],
|
|
can produce data that an HTTP server -- acting as
|
|
http://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/org/reactivestreams/Subscriber.html[Subscriber],
|
|
can then write to the response. The main purpose of Reactive Streams is to allow the
|
|
subscriber to control how fast or how slow the publisher will produce data.
|
|
|
|
[NOTE]
|
|
====
|
|
*Common question: what if a publisher can't slow down?* +
|
|
The purpose of Reactive Streams is only to establish the mechanism and a boundary.
|
|
If a publisher can't slow down then it has to decide whether to buffer, drop, or fail.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-reactive-api]]
|
|
=== Reactive API
|
|
|
|
Reactive Streams plays an important role for interoperability. It is of interest to libraries
|
|
and infrastructure components but less useful as an application API because it is too
|
|
low level. What applications need is a higher level and richer, functional API to
|
|
compose async logic -- similar to the Java 8 `Stream` API but not only for collections.
|
|
This is the role that reactive libraries play.
|
|
|
|
https://github.com/reactor/reactor[Reactor] is the reactive library of choice for
|
|
Spring WebFlux. It provides the
|
|
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html[Mono] and
|
|
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html[Flux] API types
|
|
to work on data sequences of 0..1 and 0..N through a rich set of operators aligned with the
|
|
ReactiveX http://reactivex.io/documentation/operators.html[vocabulary of operators].
|
|
Reactor is a Reactive Streams library and therefore all of its operators support non-blocking back pressure.
|
|
Reactor has a strong focus on server-side Java. It is developed in close collaboration
|
|
with Spring.
|
|
|
|
WebFlux requires Reactor as a core dependency but it is interoperable with other reactive
|
|
libraries via Reactive Streams. As a general rule WebFlux APIs accept a plain `Publisher`
|
|
as input, adapt it to Reactor types internally, use those, and then return either
|
|
`Flux` or `Mono` as output. So you can pass any `Publisher` as input and you can apply
|
|
operations on the output, but you'll need to adapt the output for use with another reactive library.
|
|
Whenever feasible -- e.g. annotated controllers, WebFlux adapts transparently to the use
|
|
of RxJava or other reactive library. See <<webflux-reactive-libraries>> for more details.
|
|
|
|
|
|
|
|
[[webflux-programming-models]]
|
|
=== Programming models
|
|
|
|
The `spring-web` module contains the reactive foundation that underlies Spring WebFlux
|
|
including HTTP abstractions, Reactive Streams <<webflux-httphandler,adapters>> for supported
|
|
servers, <<webflux-codecs,codecs>>, and a core <<webflux-web-handler-api>> comparable to
|
|
the Servlet API but with non-blocking contracts.
|
|
|
|
On that foundation Spring WebFlux provides a choice of two programming models:
|
|
|
|
- <<webflux-controller>> -- consistent with Spring MVC, and based on the same annotations
|
|
from the `spring-web` module. Both Spring MVC and WebFlux controllers support reactive
|
|
(Reactor, RxJava) return types and as a result it is not easy to tell them apart. One notable
|
|
difference is that WebFlux also supports reactive `@RequestBody` arguments.
|
|
- <<webflux-fn>> -- lambda-based, lightweight, functional programming model. Think of
|
|
this as a small library or a set of utilities that an application can use to route and
|
|
handle requests. The big difference with annotated controllers is that the application
|
|
is in charge of request handling from start to finish vs declaring intent through
|
|
annotations and being called back.
|
|
|
|
|
|
|
|
[[webflux-framework-choice]]
|
|
=== Applicability
|
|
|
|
Spring MVC or WebFlux?
|
|
|
|
A natural question to ask but one that sets up an unsound dichotomy. It's actually both
|
|
working together to expand the range of available options. The two are designed for
|
|
continuity and consistency with each other, they are available side by side, and feedback
|
|
from each side benefits both sides. The diagram below shows how the two relate, what they
|
|
have in common, and what each supports uniquely:
|
|
|
|
image::images/spring-mvc-and-webflux-venn.png[]
|
|
|
|
Below are some specific points to consider:
|
|
|
|
* If you have a Spring MVC application that works fine, there is no need to change.
|
|
Imperative programming is the easiest way to write, understand, and debug code.
|
|
You have maximum choice of libraries since historically most are blocking.
|
|
|
|
* If you are already shopping for a non-blocking web stack, Spring WebFlux offers the same
|
|
execution model benefits as others in this space and also provides a choice of servers --
|
|
Netty, Tomcat, Jetty, Undertow, Servlet 3.1+ containers, a choice of programming models --
|
|
annotated controllers and functional web endpoints, and a choice of reactive libraries --
|
|
Reactor, RxJava, or other.
|
|
|
|
* If you are interested in a lightweight, functional web framework for use with Java 8 lambdas
|
|
or Kotlin then use the Spring WebFlux functional web endpoints. That can also be a good choice
|
|
for smaller applications or microservices with less complex requirements that can benefit
|
|
from greater transparency and control.
|
|
|
|
* In a microservice architecture you can have a mix of applications with either Spring MVC
|
|
or Spring WebFlux controllers, or with Spring WebFlux functional endpoints. Having support
|
|
for the same annotation-based programming model in both frameworks makes it easier to
|
|
re-use knowledge while also selecting the right tool for the right job.
|
|
|
|
* A simple way to evaluate an application is to check its dependencies. If you have blocking
|
|
persistence APIs (JPA, JDBC), or networking APIs to use, then Spring MVC is the best choice
|
|
for common architectures at least. It is technically feasible with both Reactor and
|
|
RxJava to perform blocking calls on a separate thread but you wouldn't be making the
|
|
most of a non-blocking web stack.
|
|
|
|
* If you have a Spring MVC application with calls to remote services, try the reactive `WebClient`.
|
|
You can return reactive types (Reactor, RxJava, <<webflux-reactive-libraries,or other>>)
|
|
directly from Spring MVC controller methods. The greater the latency per call, or the
|
|
interdependency among calls, the more dramatic the benefits. Spring MVC controllers
|
|
can call other reactive components too.
|
|
|
|
* If you have a large team, keep in mind the steep learning curve in the shift to non-blocking,
|
|
functional, and declarative programming. A practical way to start without a full switch
|
|
is to use the reactive `WebClient`. Beyond that start small and measure the benefits.
|
|
We expect that for a wide range of applications the shift is unnecessary. If you are
|
|
unsure what benefits to look for, start by learning about how non-blocking I/O works
|
|
(e.g. concurrency on single-threaded Node.js) and its effects.
|
|
|
|
|
|
|
|
[[webflux-server-choice]]
|
|
=== Servers
|
|
|
|
Spring WebFlux is supported on Tomcat, Jetty, Servlet 3.1+ containers, as well as on
|
|
non-Servlet runtimes such as Netty and Undertow. All servers are adapted to a low-level,
|
|
<<webflux-httphandler,common API>> so that higher level
|
|
<<webflux-programming-models,programming models>> can be supported across servers.
|
|
|
|
Spring WebFlux does not have built-in support to start or stop a server. However it is
|
|
easy to <<webflux-web-handler-api,assemble>> an application from Spring configuration, and
|
|
<<webflux-config,WebFlux infrastructure>>, and <<webflux-httphandler,run it>> with a few
|
|
lines of code.
|
|
|
|
Spring Boot has a WebFlux starter that automates these steps. By default the starter uses
|
|
Netty but it is easy to switch to Tomcat, Jetty, or Undertow simply by changing your
|
|
Maven or Gradle dependencies. Spring Boot defaults to Netty because it is more widely
|
|
used in the async, non-blocking space, and provides a client and a server share resources.
|
|
|
|
Tomcat and Jetty can be used with both Spring MVC and WebFlux. Keep in mind however that
|
|
the way they're used is very different. Spring MVC relies on Servlet blocking I/O and
|
|
allows applications to use the Servlet API directly if they need to. Spring WebFlux
|
|
relies on Servlet 3.1 non-blocking I/O and uses the Servlet API behind a low-level
|
|
adapter and not exposed for direct use.
|
|
|
|
For Undertow, Spring WebFlux uses Undertow APIs directly without the Servlet API.
|
|
|
|
|
|
|
|
[[webflux-performance]]
|
|
=== Performance vs scale
|
|
|
|
Performance has many characteristics and meanings. Reactive and non-blocking generally
|
|
do not make applications run faster. They can, in some cases, for example if using the
|
|
`WebClient` to execute remote calls in parallel. On the whole it requires more work to do
|
|
things the non-blocking way and that can increase slightly the required processing time.
|
|
|
|
The key expected benefit of reactive and non-blocking is the ability to scale with a small,
|
|
fixed number of threads and less memory. That makes applications more resilient under load
|
|
because they scale in a more predictable way. In order to observe those benefits however you
|
|
need to have some latency including a mix of slow and unpredictable network I/O.
|
|
That's where the reactive stack begins to show its strengths and the differences can be
|
|
dramatic.
|
|
|
|
|
|
|
|
[[webflux-concurrency-model]]
|
|
=== Concurrency Model
|
|
|
|
Both Spring MVC and Spring WebFlux support annotated controllers, but there is a key
|
|
difference in the concurrency model and default assumptions for blocking and threads.
|
|
|
|
In Spring MVC, and servlet applications in general, it is assumed that applications _may
|
|
block_ the current thread, e.g. for remote calls, and for this reason servlet containers
|
|
use a large thread pool, to absorb potential blocking during request handling.
|
|
|
|
In Spring WebFlux, and non-blocking servers in general, it is assumed that applications
|
|
_will not block_, and therefore non-blocking servers use a small, fixed-size thread pool
|
|
(event loop workers) to handle requests.
|
|
|
|
[TIP]
|
|
====
|
|
To "scale" and "small number of threads" may sound contradictory but to never block the
|
|
current thread, and rely on callbacks instead, means you don't need extra threads as
|
|
there are no blocking calls to absorb.
|
|
====
|
|
|
|
_Invoking a Blocking API_
|
|
|
|
What if you do need to use a blocking library? Both Reactor and RxJava provide the
|
|
`publishOn` operator to continue processing on a different thread. That means there is an
|
|
easy escape latch. Keep in mind however that blocking APIs are not a good fit for
|
|
this concurrency model.
|
|
|
|
_Mutable State_
|
|
|
|
In Reactor and RxJava, logic is declared through operators, and at runtime, a reactive
|
|
pipeline is formed where data is processed sequentially, in distinct stages. A key benefit
|
|
of that is that it frees applications from having to protect mutable state because
|
|
application code within that pipeline is never invoked concurrently.
|
|
|
|
_Threading Model_
|
|
|
|
What threads should you expect to see on a server running with Spring WebFlux?
|
|
|
|
* On a "vanilla" Spring WebFlux server (e.g. no data access, nor other optional
|
|
dependencies), you can expect one thread for the server, and several others for request
|
|
processing (typically as many as the number of CPU cores). Servlet containers, however,
|
|
may start with more threads (e.g. 10 on Tomcat), in support of both servlet, blocking I/O
|
|
and servlet 3.1, non-blocking I/O usage.
|
|
|
|
* The reactive `WebClient` operates in event loop style. So you'll see a small, fixed
|
|
number of processing threads related to that, e.g. "reactor-http-nio-" with the Reactor
|
|
Netty connector. However if Reactor Netty is used for both client and server, the two
|
|
will share event loop resources by default.
|
|
|
|
* Reactor and RxJava provide thread pool abstractions, called Schedulers, to use with the
|
|
`publishOn` operator that is used to switch processing to a different thread pool.
|
|
The schedulers have names that suggest a specific concurrency strategy, e.g. "parallel"
|
|
for CPU-bound work with a limited number of threads, or "elastic" for I/O-bound work with
|
|
a large number of threads. If you see such threads it means some code is using a
|
|
specific thread pool `Scheduler` strategy.
|
|
|
|
* Data access libraries and other 3rd party dependencies may also create and use threads
|
|
of their own.
|
|
|
|
_Configuring_
|
|
|
|
The Spring Framework does not provide support for starting and stopping
|
|
<<webflux-server-choice,servers>>. To configure the threading model for a server, you'll
|
|
need to use server-specific config APIs, or if using Spring Boot, check the Spring
|
|
Boot configuration options for each server. The WebClient
|
|
<<web-reactive.adoc#webflux-client-builder,can be configured>> directly. For all other
|
|
libraries, refer to their respective documentation.
|
|
|
|
|
|
|
|
|
|
[[webflux-reactive-spring-web]]
|
|
== Reactive Core
|
|
|
|
The `spring-web` module contains abstractions and infrastructure to build reactive web
|
|
applications. For server side processing this is organized in two distinct levels:
|
|
|
|
* <<webflux-httphandler,HttpHandler>> -- basic, common API for HTTP request handling with
|
|
non-blocking I/O and (Reactive Streams) back pressure, along with adapters for each
|
|
supported server.
|
|
* <<webflux-web-handler-api>> -- slightly higher level, but still general purpose API for
|
|
server request handling, which underlies higher level programming models such as annotated
|
|
controllers and functional endpoints.
|
|
|
|
The reactive core also includes <<webflux-codecs>> for client and server side use.
|
|
|
|
|
|
|
|
[[webflux-httphandler]]
|
|
=== HttpHandler
|
|
|
|
{api-spring-framework}/http/server/reactive/HttpHandler.html[HttpHandler]
|
|
is a simple contract with a single method to handle a request and response. It is
|
|
intentionally minimal as its main purpose is to provide an abstraction over different
|
|
server APIs for HTTP request handling.
|
|
|
|
Supported server APIs:
|
|
|
|
[cols="1,2,2", options="header"]
|
|
|===
|
|
| Server name | Server API used | Reactive Streams support
|
|
|
|
| Netty
|
|
| Netty API
|
|
| https://github.com/reactor/reactor-netty[Reactor Netty]
|
|
|
|
| Undertow
|
|
| Undertow API
|
|
| spring-web: Undertow to Reactive Streams bridge
|
|
|
|
| Tomcat
|
|
| Servlet 3.1 non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[]
|
|
| spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge
|
|
|
|
| Jetty
|
|
| Servlet 3.1 non-blocking I/O; Jetty API to write ByteBuffers vs byte[]
|
|
| spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge
|
|
|
|
| Servlet 3.1 container
|
|
| Servlet 3.1 non-blocking I/O
|
|
| spring-web: Servlet 3.1 non-blocking I/O to Reactive Streams bridge
|
|
|===
|
|
|
|
Server dependencies (and
|
|
https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-the-Spring-Framework[supported versions]):
|
|
|
|
|===
|
|
|Server name|Group id|Artifact name
|
|
|
|
|Reactor Netty
|
|
|io.projectreactor.netty
|
|
|reactor-netty
|
|
|
|
|Undertow
|
|
|io.undertow
|
|
|undertow-core
|
|
|
|
|Tomcat
|
|
|org.apache.tomcat.embed
|
|
|tomcat-embed-core
|
|
|
|
|Jetty
|
|
|org.eclipse.jetty
|
|
|jetty-server, jetty-servlet
|
|
|===
|
|
|
|
Code snippets to adapt `HttpHandler` to each server API:
|
|
|
|
*Reactor Netty*
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
HttpHandler handler = ...
|
|
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
|
|
HttpServer.create(host, port).newHandler(adapter).block();
|
|
----
|
|
|
|
*Undertow*
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
HttpHandler handler = ...
|
|
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
|
|
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
|
|
server.start();
|
|
----
|
|
|
|
*Tomcat*
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
HttpHandler handler = ...
|
|
Servlet servlet = new TomcatHttpHandlerAdapter(handler);
|
|
|
|
Tomcat server = new Tomcat();
|
|
File base = new File(System.getProperty("java.io.tmpdir"));
|
|
Context rootContext = server.addContext("", base.getAbsolutePath());
|
|
Tomcat.addServlet(rootContext, "main", servlet);
|
|
rootContext.addServletMappingDecoded("/", "main");
|
|
server.setHost(host);
|
|
server.setPort(port);
|
|
server.start();
|
|
----
|
|
|
|
*Jetty*
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
HttpHandler handler = ...
|
|
Servlet servlet = new JettyHttpHandlerAdapter(handler);
|
|
|
|
Server server = new Server();
|
|
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
|
|
contextHandler.addServlet(new ServletHolder(servlet), "/");
|
|
contextHandler.start();
|
|
|
|
ServerConnector connector = new ServerConnector(server);
|
|
connector.setHost(host);
|
|
connector.setPort(port);
|
|
server.addConnector(connector);
|
|
server.start();
|
|
----
|
|
|
|
*Servlet 3.1+ Container*
|
|
|
|
To deploy as a WAR to any Servlet 3.1+ container, simply extend and include
|
|
{api-spring-framework}/web/server/adapter/AbstractReactiveWebInitializer.html[AbstractReactiveWebInitializer]
|
|
in the WAR, which wraps an `HttpHandler` with `ServletHttpHandlerAdapter` and registers
|
|
that as a `Servlet`.
|
|
|
|
|
|
|
|
[[webflux-web-handler-api]]
|
|
=== WebHandler API
|
|
|
|
The WebHandler API is a general purpose, server, web API for processing requests through a
|
|
chain of {api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler],
|
|
{api-spring-framework}/web/server/WebFilter.html[WebFilter], and a target
|
|
{api-spring-framework}/web/server/WebHandler.html[WebHandler] components. The chain can be assembled
|
|
with `WebHttpHandlerBuilder` either by adding components to the builder or by having them
|
|
detected from a Spring `ApplicationContext`. The builder returns an
|
|
<<webflux-httphandler>> that can then be used to run on any of the supported servers.
|
|
|
|
While `HttpHandler` aims to be the most minimal contract across HTTP servers, the
|
|
WebHandler API provides essential features commonly used to build web applications.
|
|
For example, the `ServerWebExchange` available to WebHandler API components provides
|
|
access not only to the request and response, but also to request and session attributes,
|
|
access to parsed form data, multipart data, and more.
|
|
|
|
|
|
|
|
[[webflux-web-handler-api-special-beans]]
|
|
==== Special bean types
|
|
|
|
The table below lists the components that `WebHttpHandlerBuilder` detects:
|
|
|
|
[cols="2,2,1,3", options="header"]
|
|
|===
|
|
| Bean name | Bean type | Count | Description
|
|
|
|
| <any>
|
|
| `WebExceptionHandler`
|
|
| 0..N
|
|
| Provide handling for exceptions from the chain of ``WebFilter``'s and the target
|
|
`WebHandler`. For more details, see <<webflux-exception-handler>>.
|
|
|
|
| <any>
|
|
| `WebFilter`
|
|
| 0..N
|
|
| Apply interception style logic to before and after the rest of the filter chain and
|
|
the target `WebHandler`. For more details, see <<webflux-filters>>.
|
|
|
|
| "webHandler"
|
|
| `WebHandler`
|
|
| 1
|
|
| The handler for the request.
|
|
|
|
| "webSessionManager"
|
|
| `WebSessionManager`
|
|
| 0..1
|
|
| The manager for ``WebSession``'s exposed through a method on `ServerWebExchange`.
|
|
`DefaultWebSessionManager` by default.
|
|
|
|
| "serverCodecConfigurer"
|
|
| `ServerCodecConfigurer`
|
|
| 0..1
|
|
| For access to ``HttpMessageReader``'s for parsing form data and multipart data that's then
|
|
exposed through methods on `ServerWebExchange`. `ServerCodecConfigurer.create()` by default.
|
|
|
|
| "localeContextResolver"
|
|
| `LocaleContextResolver`
|
|
| 0..1
|
|
| The resolver for `LocaleContext` exposed through a method on `ServerWebExchange`.
|
|
`AcceptHeaderLocaleContextResolver` by default.
|
|
|
|
| "forwardedHeaderTransformer"
|
|
| `ForwardedHeaderTransformer`
|
|
| 0..1
|
|
| For processing Forwarded type headers, either extracting and removing, or removing them only.
|
|
Not used by default.
|
|
|===
|
|
|
|
|
|
[[webflux-form-data]]
|
|
==== Form data
|
|
|
|
`ServerWebExchange` exposes the following method for access to form data:
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
Mono<MultiValueMap<String, String>> getFormData();
|
|
----
|
|
|
|
The `DefaultServerWebExchange` uses the configured `HttpMessageReader` to parse form data
|
|
("application/x-www-form-urlencoded") into a `MultiValueMap`. By default
|
|
`FormHttpMessageReader` is configured for use via the `ServerCodecConfigurer` bean
|
|
(see <<webflux-web-handler-api,Web Handler API>>).
|
|
|
|
|
|
[[webflux-multipart]]
|
|
==== Multipart data
|
|
[.small]#<<web.adoc#mvc-multipart,Same in Spring MVC>>#
|
|
|
|
`ServerWebExchange` exposes the following method for access to multipart data:
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
Mono<MultiValueMap<String, Part>> getMultipartData();
|
|
----
|
|
|
|
The `DefaultServerWebExchange` uses the configured
|
|
`HttpMessageReader<MultiValueMap<String, Part>>` to parse "multipart/form-data" content
|
|
into a `MultiValueMap`. At present
|
|
https://github.com/synchronoss/nio-multipart[Synchronoss NIO Multipart] is the only 3rd
|
|
party library supported, and the only library we know for non-blocking parsing of
|
|
multipart requests. It is enabled through the `ServerCodecConfigurer` bean
|
|
(see <<webflux-web-handler-api,Web Handler API>>).
|
|
|
|
To parse multipart data in streaming fashion, use the `Flux<Part>` returned from an
|
|
`HttpMessageReader<Part>` instead. For example in an annotated controller use of
|
|
`@RequestPart` implies Map-like access to individual parts by name, and hence requires
|
|
parsing multipart data in full. By contrast `@RequestBody` can be used to decode the
|
|
content to `Flux<Part>` without collecting to a `MultiValueMap`.
|
|
|
|
|
|
[[webflux-forwarded-headers]]
|
|
==== Forwarded Headers
|
|
[.small]#<<web.adoc#filters-forwarded-headers,Same in Spring MVC>>#
|
|
|
|
As a request goes through proxies such as load balancers the host, port, and
|
|
scheme may change and that makes it a challenge to create links that point to the correct
|
|
host, port, and scheme from a client perspective.
|
|
|
|
https://tools.ietf.org/html/rfc7239[RFC 7239] defines the "Forwarded" HTTP header
|
|
that proxies can use to provide information about the original request. There are other
|
|
non-standard headers too including "X-Forwarded-Host", "X-Forwarded-Port",
|
|
"X-Forwarded-Proto", "X-Forwarded-Ssl", and "X-Forwarded-Prefix".
|
|
|
|
`ForwardedHeaderTransformer` is a component that modifies the host, port, and scheme of
|
|
the request, based on Forwarded headers, and then removes those headers. Simply declare
|
|
it as a bean with the name "forwardedHeaderTransformer" and it will be
|
|
<<webflux-web-handler-api-special-beans, detected>> and used.
|
|
|
|
There are security considerations for forwarded headers since an application can't know
|
|
if the headers were added by a proxy as intended, or with a malicious client. This is why
|
|
a proxy at the boundary of trust should be configured to remove untrusted Forwarded coming
|
|
from the outside. You can also configure the `ForwardedHeaderTransformer` with
|
|
`removeOnly=true` in which case it will remove but not use the headers.
|
|
|
|
[NOTE]
|
|
====
|
|
In 5.1 `ForwardedHeaderFilter` was deprecated and superceded by
|
|
`ForwardedHeaderTransformer` so forwarded headers can be processed earlier, before the
|
|
exchange is created. If the filter is configured anyway, it is taken out of the list of
|
|
filters, and `ForwardedHeaderTransformer` is used instead.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-filters]]
|
|
=== Filters
|
|
[.small]#<<web.adoc#filters,Same in Spring MVC>>#
|
|
|
|
In the <<webflux-web-handler-api>>, a `WebFilter` can be used to apply interception-style
|
|
logic before and after the rest of the processing chain of filters and the target
|
|
`WebHandler`. When using the <<webflux-config>>, registering a `WebFilter` is as simple
|
|
as declaring it as a Spring bean, and optionally expressing precedence via `@Order` on
|
|
the bean declaration or by implementing `Ordered`.
|
|
|
|
The following describe the available `WebFilter` implementations:
|
|
|
|
|
|
[[webflux-filters-cors]]
|
|
==== CORS
|
|
[.small]#<<web.adoc#filters-cors,Same in Spring MVC>>#
|
|
|
|
Spring WebFlux provides fine-grained support for CORS configuration through annotations on
|
|
controllers. However when used with Spring Security it is advisable to rely on the built-in
|
|
`CorsFilter` that must be ordered ahead of Spring Security's chain of filters.
|
|
|
|
See the section on <<webflux-cors>> and the <<webflux-cors-webfilter>> for more details.
|
|
|
|
|
|
|
|
[[webflux-exception-handler]]
|
|
=== Exceptions
|
|
[.small]#<<web.adoc#mvc-ann-customer-servlet-container-error-page,Same in Spring MVC>>#
|
|
|
|
In the <<webflux-web-handler-api>>, a `WebExceptionHandler` can be used to to handle
|
|
exceptions from the chain of ``WebFilter``'s and the target `WebHandler`. When using the
|
|
<<webflux-config>>, registering a `WebExceptionHandler` is as simple as declaring it as a
|
|
Spring bean, and optionally expressing precedence via `@Order` on the bean declaration or
|
|
by implementing `Ordered`.
|
|
|
|
Below are the available `WebExceptionHandler` implementations:
|
|
|
|
[cols="1,2", options="header"]
|
|
|===
|
|
| Exception Handler | Description
|
|
|
|
| `ResponseStatusExceptionHandler`
|
|
| Provides handling for exceptions of type
|
|
{api-spring-framework}/web/server/ResponseStatusException.html[ResponseStatusException]
|
|
by setting the response to the HTTP status code of the exception.
|
|
|
|
| `WebFluxResponseStatusExceptionHandler`
|
|
| Extension of `ResponseStatusExceptionHandler` that can also determine the HTTP status
|
|
code an `@ResponseStatus` annotation on any exception.
|
|
|
|
This handler is declared in the <<webflux-config>>.
|
|
|
|
|===
|
|
|
|
|
|
|
|
[[webflux-codecs]]
|
|
=== Codecs
|
|
[.small]#<<integration.adoc#rest-message-conversion,Same in Spring MVC>>#
|
|
|
|
{api-spring-framework}/http/codec/HttpMessageReader.html[HttpMessageReader] and
|
|
{api-spring-framework}/http/codec/HttpMessageWriter.html[HttpMessageWriter] are contracts
|
|
for encoding and decoding HTTP request and response content via non-blocking I/O with
|
|
(Rective Streams) back pressure.
|
|
|
|
{api-spring-framework}/core/codec/Encoder.html[Encoder] and
|
|
{api-spring-framework}/core/codec/Decoder.html[Decoder] are contracts for encoding and
|
|
decoding content, independent of HTTP. They can be wrapped with `EncoderHttpMessageWriter`
|
|
or `DecoderHttpMessageReader` and used for web processing.
|
|
|
|
All codecs are for client or server side use. All build on
|
|
{api-spring-framework}/core/io/buffer/DataBuffer.html[DataBuffer] which abstracts byte
|
|
buffer representations such as the Netty `ByteBuf` or `java.nio.ByteBuffer` (see
|
|
<<core#databuffers, Data Buffers and Codecs>> for more details). `ClientCodecConfigurer`
|
|
and `ServerCodecConfigurer` are typically used to configure and customize the codecs to
|
|
use in an application.
|
|
|
|
The `spring-core` module has encoders and decoders for `byte[]`, `ByteBuffer`, `DataBuffer`,
|
|
`Resource`, and `String`. The `spring-web` module adds encoders and decoders for Jackson
|
|
JSON, Jackson Smile, JAXB2, Protocol Buffers, along with other web-specific HTTP message
|
|
readers and writers for form data, multipart requests, and server-sent events.
|
|
|
|
|
|
[[webflux-codecs-jackson]]
|
|
==== Jackson
|
|
|
|
The decoder relies on Jackson's non-blocking, byte array parser to parse a stream of byte
|
|
chunks into a `TokenBuffer` stream, which can then be turned into Objects with Jackson's
|
|
`ObjectMapper`. JSON and https://github.com/FasterXML/smile-format-specification[Smile]
|
|
(binary JSON) data formats are currently supported.
|
|
|
|
The encoder processes a `Publisher<?>` as follows:
|
|
|
|
* if the `Publisher` is a `Mono` (i.e. single value), the value is encoded when available.
|
|
* if media type is `application/stream+json` for JSON or `application/stream+x-jackson-smile`
|
|
for Smile, each value produced by the `Publisher` is encoded individually (and followed
|
|
by a new line in JSON).
|
|
* otherwise all items from the `Publisher` are gathered in with `Flux#collectToList()`
|
|
and the resulting collection is encoded as an array.
|
|
|
|
As a special case to the above rules the `ServerSentEventHttpMessageWriter` feeds items
|
|
emitted from its input `Publisher` individually into the `Jackson2JsonEncoder` as a
|
|
`Mono<?>`.
|
|
|
|
Note that both the Jackson JSON encoder and decoder explicitly back out of rendering
|
|
elements of type `String`. Instead ``String``'s are treated as low level content, (i.e.
|
|
serialized JSON) and are rendered as-is by the `CharSequenceEncoder`. If you want a
|
|
`Flux<String>` rendered as a JSON array, you'll have to use `Flux#collectToList()` and
|
|
provide a `Mono<List<String>>` instead.
|
|
|
|
|
|
[[webflux-codecs-streaming]]
|
|
==== HTTP Streaming
|
|
[.small]#<<web.adoc#mvc-ann-async-http-streaming,Same in Spring MVC>>#
|
|
|
|
When a multi-value, reactive type such as `Flux` is used for response rendering, it may
|
|
be collected to a `List` and rendered as a whole (e.g. JSON array), or it may be treated
|
|
as an infinite stream with each item flushed immediately. The determination for which is
|
|
which is made based on content negotiation and the selected media type which may imply a
|
|
streaming format (e.g. "text/event-stream", "application/stream+json"), or not
|
|
(e.g. "application/json").
|
|
|
|
When streaming to the HTTP response, regardless of the media type (e.g. text/event-stream,
|
|
application/stream+json), it is important to send data periodically, since the write would
|
|
fail if the client has disconnected. The send could take the form of an empty
|
|
(comment-only) SSE event, or any other data that the other side would have to interpret as
|
|
a heartbeat and ignore.
|
|
|
|
|
|
|
|
[[webflux-logging]]
|
|
=== Logging
|
|
[.small]#<<web.adoc#mvc-logging,Same in Spring MVC>>#
|
|
|
|
DEBUG level logging in Spring WebFlux is designed to be compact, minimal, and
|
|
human-friendly. It focuses on high value bits of information that are useful over and
|
|
over again vs others that are useful only when debugging a specific issue.
|
|
|
|
TRACE level logging generally follows the same principles as DEBUG (and for example also
|
|
should not be a firehose) but can be used for debugging any issue. In addition some log
|
|
messages may show a different level of detail at TRACE vs DEBUG.
|
|
|
|
Good logging comes from the experience of using the logs. If you spot anything that does
|
|
not meet the stated goals, please let us know.
|
|
|
|
|
|
[[webflux-logging-id]]
|
|
==== Log Id
|
|
|
|
In WebFlux, a single request may be executed over multiple threads and the thread id
|
|
is not useful for correlating log messages that belong to a specific request. This is why
|
|
WebFlux log messages are prefixed with a request specific id by default.
|
|
|
|
On the server side the log id is stored in the `ServerWebExchange` attribute
|
|
{api-spring-framework}/web/server/ServerWebExchange.html#LOG_ID_ATTRIBUTE[LOG_ID_ATTRIBUTE]
|
|
while a fully formatted prefix based on that id is available via
|
|
`ServerWebExchange#getLogPrefix()`. On the `WebClient` side, the log id is stored in the
|
|
`ClientRequest` attribute
|
|
{api-spring-framework}/web/reactive/function/client/ClientRequest.html#LOG_ID_ATTRIBUTE[LOG_ID_ATTRIBUTE]
|
|
while a fully formatted prefix is available via `ClientRequest#logPrefix()`.
|
|
|
|
|
|
[[webflux-logging-sensitive-data]]
|
|
==== Sensitive Data
|
|
[.small]#<<web.adoc#mvc-logging-sensitive-data,Same in Spring MVC>>#
|
|
|
|
DEBUG and TRACE logging may log sensitive information. This is why form parameters and
|
|
headers are masked by default and their logging in full must be enabled explicitly.
|
|
|
|
For server side requests:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
class MyConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
|
|
configurer.defaultCodecs().enableLoggingRequestDetails(true);
|
|
}
|
|
}
|
|
----
|
|
|
|
For client side requests:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
Consumer<ClientCodecConfigurer> consumer = configurer ->
|
|
configurer.defaultCodecs().enableLoggingRequestDetails(true);
|
|
|
|
WebClient webClient = WebClient.builder()
|
|
.exchangeStrategies(ExchangeStrategies.builder().codecs(consumer).build())
|
|
.build();
|
|
----
|
|
|
|
|
|
|
|
|
|
[[webflux-dispatcher-handler]]
|
|
== DispatcherHandler
|
|
[.small]#<<web.adoc#mvc-servlet,Same in Spring MVC>>#
|
|
|
|
Spring WebFlux, like Spring MVC, is designed around the front controller pattern where a
|
|
central `WebHandler`, the `DispatcherHandler`, provides a shared algorithm for request
|
|
processing while actual work is performed by configurable, delegate components.
|
|
This model is flexible and supports diverse workflows.
|
|
|
|
`DispatcherHandler` discovers the delegate components it needs from Spring configuration.
|
|
It is also designed to be a Spring bean itself and implements `ApplicationContextAware`
|
|
for access to the context it runs in. If `DispatcherHandler` is declared with the bean
|
|
name "webHandler" it is in turn discovered by
|
|
{api-spring-framework}/web/server/adapter/WebHttpHandlerBuilder.html[WebHttpHandlerBuilder]
|
|
which puts together a request processing chain as described in
|
|
<<webflux-web-handler-api>>.
|
|
|
|
Spring configuration in a WebFlux application typically contains:
|
|
|
|
* `DispatcherHandler` with the bean name "webHandler"
|
|
* `WebFilter` and `WebExceptionHandler` beans
|
|
* <<webflux-special-bean-types,DispatcherHandler special beans>>
|
|
* Others
|
|
|
|
The configuration is given to `WebHttpHandlerBuilder` to build the processing chain:
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
ApplicationContext context = ...
|
|
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context);
|
|
----
|
|
|
|
The resulting `HttpHandler` is ready for use with a
|
|
<<webflux-httphandler,server adapter>>.
|
|
|
|
|
|
|
|
[[webflux-special-bean-types]]
|
|
=== Special bean types
|
|
[.small]#<<web.adoc#mvc-servlet-special-bean-types,Same in Spring MVC>>#
|
|
|
|
The `DispatcherHandler` delegates to special beans to process requests and render the
|
|
appropriate responses. By "special beans" we mean Spring-managed, Object instances that
|
|
implement WebFlux framework contracts. Those usually come with built-in contracts but
|
|
you can customize their properties, extend then, or replaced.
|
|
|
|
The table below lists the special beans detected by the `DispatcherHandler`. Note that
|
|
there are also some other beans detected at a lower level, see
|
|
<<webflux-web-handler-api-special-beans>> in the Web Handler API.
|
|
|
|
[[webflux-special-beans-table]]
|
|
[cols="1,2", options="header"]
|
|
|===
|
|
| Bean type | Explanation
|
|
|
|
| HandlerMapping
|
|
| Map a request to a handler. The mapping is based on some criteria the details of
|
|
which vary by `HandlerMapping` implementation -- annotated controllers, simple
|
|
URL pattern mappings, etc.
|
|
|
|
The main `HandlerMapping` implementations are `RequestMappingHandlerMapping` for
|
|
`@RequestMapping` annotated methods, `RouterFunctionMapping` for functional endpoint
|
|
routes, and `SimpleUrlHandlerMapping` for explicit registrations of URI path patterns
|
|
and ``WebHandler``'s.
|
|
|
|
| HandlerAdapter
|
|
| Help the `DispatcherHandler` to invoke a handler mapped to a request regardless of
|
|
how the handler is actually invoked. For example invoking an annotated controller
|
|
requires resolving annotations. The main purpose of a `HandlerAdapter` is to shield the
|
|
`DispatcherHandler` from such details.
|
|
|
|
| HandlerResultHandler
|
|
| Process the result from the handler invocation and finalize the response.
|
|
See <<webflux-resulthandling>>.
|
|
|
|
|===
|
|
|
|
|
|
|
|
[[webflux-framework-config]]
|
|
=== WebFlux Config
|
|
[.small]#<<web.adoc#mvc-servlet-config,Same in Spring MVC>>#
|
|
|
|
Applications can declare the infrastructure beans listed under
|
|
<<webflux-web-handler-api-special-beans,Web Handler API>> and
|
|
<<webflux-special-bean-types,DispatcherHandler>> that are required to process requests.
|
|
However in most cases the <<webflux-config>> is the best starting point. It declares the
|
|
required beans and provides a higher level configuration callback API to customize it.
|
|
|
|
[NOTE]
|
|
====
|
|
Spring Boot relies on the WebFlux config to configure Spring WebFlux and also provides
|
|
many extra convenient options.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-dispatcher-handler-sequence]]
|
|
=== Processing
|
|
[.small]#<<web.adoc#mvc-servlet-sequence,Same in Spring MVC>>#
|
|
|
|
The `DispatcherHandler` processes requests as follows:
|
|
|
|
* Each `HandlerMapping` is asked to find a matching handler and the first match is used.
|
|
* If a handler is found, it is executed through an appropriate `HandlerAdapter` which
|
|
exposes the return value from the execution as `HandlerResult`.
|
|
* The `HandlerResult` is given to an appropriate `HandlerResultHandler` to complete
|
|
processing by writing to the response directly or using a view to render.
|
|
|
|
|
|
|
|
[[webflux-resulthandling]]
|
|
=== Result Handling
|
|
|
|
The return value from the invocation of a handler, through a `HandlerAdapter`, is wrapped
|
|
as `HandlerResult`, along with some additional context, and passed to the first
|
|
`HandlerResultHandler` that claims support for it. The table below shows the available
|
|
`HandlerResultHandler` implementations all of which are declared in the <<webflux-config>>:
|
|
|
|
[cols="1,2,1", options="header"]
|
|
|===
|
|
| Result Handler Type | Return Values | Default Order
|
|
|
|
| `ResponseEntityResultHandler`
|
|
| `ResponseEntity`, typically from ``@Controller``'s.
|
|
| 0
|
|
|
|
| `ServerResponseResultHandler`
|
|
| `ServerResponse`, typically from functional endpoints.
|
|
| 0
|
|
|
|
| `ResponseBodyResultHandler`
|
|
| Handle return values from `@ResponseBody` methods or `@RestController` classes.
|
|
| 100
|
|
|
|
| `ViewResolutionResultHandler`
|
|
| `CharSequence` or {api-spring-framework}/web/reactive/result/view/View.html[View],
|
|
{api-spring-framework}/ui/Model.html[Model] or `Map`,
|
|
{api-spring-framework}/web/reactive/result/view/Rendering.html[Rendering],
|
|
or any other Object is treated as a model attribute.
|
|
|
|
Also see <<webflux-viewresolution>>.
|
|
| `Integer.MAX_VALUE`
|
|
|
|
|===
|
|
|
|
|
|
|
|
[[webflux-dispatcher-exceptions]]
|
|
=== Exceptions
|
|
[.small]#<<web.adoc#mvc-exceptionhandlers,Same in Spring MVC>>#
|
|
|
|
The `HandlerResult` returned from a `HandlerAdapter` may expose a function for error
|
|
handling based on some handler-specific mechanism. This error function is called if:
|
|
|
|
* the handler (e.g. `@Controller`) invocation fails.
|
|
* handling of the handler return value through a `HandlerResultHandler` fails.
|
|
|
|
The error function can change the response, e.g. to an error status, as long as an error
|
|
signal occurs before the reactive type returned from the handler produces any data items.
|
|
|
|
This is how `@ExceptionHandler` methods in `@Controller` classes are supported.
|
|
By contrast, support for the same in Spring MVC is built on a `HandlerExceptionResolver`.
|
|
This generally shouldn't matter, however, keep in mind that in WebFlux you cannot use a
|
|
`@ControllerAdvice` to handle exceptions that occur before a handler is chosen.
|
|
|
|
See also <<webflux-ann-controller-exceptions>> in the Annotated Controller section, or
|
|
<<webflux-exception-handler>> in the WebHandler API section.
|
|
|
|
|
|
|
|
[[webflux-viewresolution]]
|
|
=== View Resolution
|
|
[.small]#<<web.adoc#mvc-viewresolver,Same in Spring MVC>>#
|
|
|
|
View resolution enables rendering to a browser with an HTML template and a model without
|
|
tying you to a specific view technology. In Spring WebFlux, view resolution is
|
|
supported through a dedicated <<webflux-resulthandling,HandlerResultHandler>> that uses
|
|
``ViewResolver``'s to map a String, representing a logical view name, to a `View`
|
|
instance. The `View` is then used to render the response.
|
|
|
|
|
|
[[webflux-viewresolution-handling]]
|
|
==== Handling
|
|
[.small]#<<web.adoc#mvc-handling,Same in Spring MVC>>#
|
|
|
|
The `HandlerResult` passed into `ViewResolutionResultHandler` contains the return value
|
|
from the handler, and also the model that contains attributes added during request
|
|
handling. The return value is processed as one of the following:
|
|
|
|
* `String`, `CharSequence` -- a logical view name to be resolved to a `View` through
|
|
the list of configured ``ViewResolver``'s.
|
|
* `void` -- select a default view name based on the request path minus the leading and
|
|
trailing slash, and resolve it to a `View`. The same also happens when a view name
|
|
was not provided, e.g. model attribute was returned, or an async return value, e.g.
|
|
`Mono` completed empty.
|
|
* {api-spring-framework}/web/reactive/result/view/Rendering.html[Rendering] -- API for
|
|
view resolution scenarios; explore the options in your IDE with code completion.
|
|
* `Model`, `Map` -- extra model attributes to be added to the model for the request.
|
|
* Any other -- any other return value (except for simple types, as determined by
|
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
|
|
is treated as a model attribute to be added to the model. The attribute name is derived
|
|
from the Class name, using {api-spring-framework}/core/Conventions.html[Conventions],
|
|
unless a handler method `@ModelAttribute` annotation is present.
|
|
|
|
The model can contain asynchronous, reactive types (e.g. from Reactor, RxJava). Prior
|
|
to rendering, `AbstractView` resolves such model attributes into concrete values
|
|
and updates the model. Single-value reactive types are resolved to a single
|
|
value, or no value (if empty) while multi-value reactive types, e.g. `Flux<T>` are
|
|
collected and resolved to `List<T>`.
|
|
|
|
To configure view resolution is as simple as adding a `ViewResolutionResultHandler` bean
|
|
to your Spring configuration. <<webflux-config-view-resolvers,WebFlux Config>> provides a
|
|
dedicated configuration API for view resolution.
|
|
|
|
See <<webflux-view>> for more on the view technologies integrated with Spring WebFlux.
|
|
|
|
|
|
[[webflux-redirecting-redirect-prefix]]
|
|
==== Redirecting
|
|
[.small]#<<web.adoc#mvc-redirecting-redirect-prefix,Same in Spring MVC>>#
|
|
|
|
The special `redirect:` prefix in a view name allows you to perform a redirect. The
|
|
`UrlBasedViewResolver` (and sub-classes) recognize this as an instruction that a
|
|
redirect is needed. The rest of the view name is the redirect URL.
|
|
|
|
The net effect is the same as if the controller had returned a `RedirectView` or
|
|
`Rendering.redirectTo("abc").build()`, but now the controller itself can simply
|
|
operate in terms of logical view names. A view name such as
|
|
`redirect:/some/resource` is relative to the current application, while the view name
|
|
`redirect:http://example.com/arbitrary/path` redirects to an absolute URL.
|
|
|
|
|
|
[[webflux-multiple-representations]]
|
|
==== Content negotiation
|
|
[.small]#<<web.adoc#mvc-multiple-representations,Same in Spring MVC>>#
|
|
|
|
`ViewResolutionResultHandler` supports content negotiation. It compares the request
|
|
media type(s) with the media type(s) supported by each selected `View`. The first `View`
|
|
that supports the requested media type(s) is used.
|
|
|
|
In order to support media types such as JSON and XML, Spring WebFlux provides
|
|
`HttpMessageWriterView` which is a special `View` that renders through an
|
|
<<webflux-codecs,HttpMessageWriter>>. Typically you would configure these as default
|
|
views through the <<webflux-config-view-resolvers,WebFlux Config>>. Default views are
|
|
always selected and used if they match the requested media type.
|
|
|
|
|
|
|
|
|
|
[[webflux-controller]]
|
|
== Annotated Controllers
|
|
[.small]#<<web.adoc#mvc-controller,Same in Spring MVC>>#
|
|
|
|
Spring WebFlux provides an annotation-based programming model where `@Controller` and
|
|
`@RestController` components use annotations to express request mappings, request input,
|
|
exception handling, and more. Annotated controllers have flexible method signatures and
|
|
do not have to extend base classes nor implement specific interfaces.
|
|
|
|
Here is a basic example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@RestController
|
|
public class HelloController {
|
|
|
|
@GetMapping("/hello")
|
|
public String handle() {
|
|
return "Hello WebFlux";
|
|
}
|
|
}
|
|
----
|
|
|
|
In this example the methods returns a String to be written to the response body.
|
|
|
|
|
|
|
|
[[webflux-ann-controller]]
|
|
=== @Controller
|
|
[.small]#<<web.adoc#mvc-ann-controller,Same in Spring MVC>>#
|
|
|
|
You can define controller beans using a standard Spring bean definition.
|
|
The `@Controller` stereotype allows for auto-detection, aligned with Spring general support
|
|
for detecting `@Component` classes in the classpath and auto-registering bean definitions
|
|
for them. It also acts as a stereotype for the annotated class, indicating its role as
|
|
a web component.
|
|
|
|
To enable auto-detection of such `@Controller` beans, you can add component scanning to
|
|
your Java configuration:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@ComponentScan("org.example.web")
|
|
public class WebConfig {
|
|
|
|
// ...
|
|
}
|
|
----
|
|
|
|
`@RestController` is a <<core.adoc#beans-meta-annotations,composed annotation>> that is
|
|
itself meta-annotated with `@Controller` and `@ResponseBody` indicating a controller whose
|
|
every method inherits the type-level `@ResponseBody` annotation and therefore writes
|
|
directly to the response body vs view resolution and rendering with an HTML template.
|
|
|
|
|
|
|
|
[[webflux-ann-requestmapping]]
|
|
=== Request Mapping
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping,Same in Spring MVC>>#
|
|
|
|
The `@RequestMapping` annotation is used to map requests to controllers methods. It has
|
|
various attributes to match by URL, HTTP method, request parameters, headers, and media
|
|
types. It can be used at the class-level to express shared mappings or at the method level
|
|
to narrow down to a specific endpoint mapping.
|
|
|
|
There are also HTTP method specific shortcut variants of `@RequestMapping`:
|
|
|
|
- `@GetMapping`
|
|
- `@PostMapping`
|
|
- `@PutMapping`
|
|
- `@DeleteMapping`
|
|
- `@PatchMapping`
|
|
|
|
The above are <<webflux-ann-requestmapping-composed>> that are provided out of the box
|
|
because arguably most controller methods should be mapped to a specific HTTP method vs
|
|
using `@RequestMapping` which by default matches to all HTTP methods. At the same an
|
|
`@RequestMapping` is still needed at the class level to express shared mappings.
|
|
|
|
Below is an example with type and method level mappings:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@RestController
|
|
@RequestMapping("/persons")
|
|
class PersonController {
|
|
|
|
@GetMapping("/{id}")
|
|
public Person getPerson(@PathVariable Long id) {
|
|
// ...
|
|
}
|
|
|
|
@PostMapping
|
|
@ResponseStatus(HttpStatus.CREATED)
|
|
public void add(@RequestBody Person person) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-requestmapping-uri-templates]]
|
|
==== URI Patterns
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-uri-templates,Same in Spring MVC>>#
|
|
|
|
You can map requests using glob patterns and wildcards:
|
|
|
|
* `?` matches one character
|
|
* `*` matches zero or more characters within a path segment
|
|
* `**` match zero or more path segments
|
|
|
|
You can also declare URI variables and access their values with `@PathVariable`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/owners/{ownerId}/pets/{petId}")
|
|
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
URI variables can be declared at the class and method level:
|
|
[source,java,intent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
@RequestMapping("/owners/{ownerId}")
|
|
public class OwnerController {
|
|
|
|
@GetMapping("/pets/{petId}")
|
|
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
URI variables are automatically converted to the appropriate type or`TypeMismatchException`
|
|
is raised. Simple types -- `int`, `long`, `Date`, are supported by default and you can
|
|
register support for any other data type.
|
|
See <<webflux-ann-typeconversion>> and <<webflux-ann-initbinder>>.
|
|
|
|
URI variables can be named explicitly -- e.g. `@PathVariable("customId")`, but you can
|
|
leave that detail out if the names are the same and your code is compiled with debugging
|
|
information or with the `-parameters` compiler flag on Java 8.
|
|
|
|
The syntax `{*varName}` declares a URI variable that matches zero or more remaining
|
|
path segments. For example `/resources/{*path}` matches all files `/resources/` and the
|
|
`"path"` variable captures the complete relative path.
|
|
|
|
The syntax `{varName:regex}` declares a URI variable with a regular expressions with the
|
|
syntax `{varName:regex}` -- e.g. given URL `"/spring-web-3.0.5 .jar"`, the below method
|
|
extracts the name, version, and file extension:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
|
|
public void handle(@PathVariable String version, @PathVariable String ext) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
URI path patterns can also have embedded `${...}` placeholders that are resolved on startup
|
|
via `PropertyPlaceHolderConfigurer` against local, system, environment, and other property
|
|
sources. This can be used for example to parameterize a base URL based on some external
|
|
configuration.
|
|
|
|
[NOTE]
|
|
====
|
|
Spring WebFlux uses `PathPattern` and the `PathPatternParser` for URI path matching support
|
|
both of which are located in `spring-web` and expressly designed for use with HTTP URL
|
|
paths in web applications where a large number of URI path patterns are matched at runtime.
|
|
====
|
|
|
|
Spring WebFlux does not support suffix pattern matching -- unlike Spring MVC, where a
|
|
mapping such as `/person` also matches to `/person.{asterisk}`. For URL based content
|
|
negotiation, if needed, we recommend using a query parameter, which is simpler, more
|
|
explicit, and less vulnerable to URL path based exploits.
|
|
|
|
|
|
[[webflux-ann-requestmapping-pattern-comparison]]
|
|
==== Pattern Comparison
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-pattern-comparison,Same in Spring MVC>>#
|
|
|
|
When multiple patterns match a URL, they must be compared to find the best match. This is done
|
|
with `PathPattern.SPECIFICITY_COMPARATOR` which looks for patterns that more specific.
|
|
|
|
For every pattern, a score is computed based the number of URI variables and wildcards
|
|
where a URI variable scores lower than a wildcard. A pattern with a lower total score
|
|
wins. If two patterns have the same score, then the longer is chosen.
|
|
|
|
Catch-all patterns, e.g. `**`, `{*varName}`, are excluded from the scoring and are always
|
|
sorted last instead. If two patterns are both catch-all, the longer is chosen.
|
|
|
|
|
|
[[webflux-ann-requestmapping-consumes]]
|
|
==== Consumable Media Types
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-consumes,Same in Spring MVC>>#
|
|
|
|
You can narrow the request mapping based on the `Content-Type` of the request:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping(path = "/pets", **consumes = "application/json"**)
|
|
public void addPet(@RequestBody Pet pet) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
The consumes attribute also supports negation expressions -- e.g. `!text/plain` means any
|
|
content type other than "text/plain".
|
|
|
|
You can declare a shared consumes attribute at the class level. Unlike most other request
|
|
mapping attributes however when used at the class level, a method-level consumes attribute
|
|
overrides rather than extend the class level declaration.
|
|
|
|
[TIP]
|
|
====
|
|
`MediaType` provides constants for commonly used media types -- e.g.
|
|
`APPLICATION_JSON_VALUE`, `APPLICATION_XML_VALUE`.
|
|
====
|
|
|
|
|
|
[[webflux-ann-requestmapping-produces]]
|
|
==== Producible Media Types
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-produces,Same in Spring MVC>>#
|
|
|
|
You can narrow the request mapping based on the `Accept` request header and the list of
|
|
content types that a controller method produces:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping(path = "/pets/{petId}", **produces = "application/json;charset=UTF-8"**)
|
|
@ResponseBody
|
|
public Pet getPet(@PathVariable String petId) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
The media type can specify a character set. Negated expressions are supported -- e.g.
|
|
`!text/plain` means any content type other than "text/plain".
|
|
|
|
[NOTE]
|
|
====
|
|
For JSON content type, the UTF-8 charset should be specified even if
|
|
https://tools.ietf.org/html/rfc7159#section-11[RFC7159]
|
|
clearly states that "no charset parameter is defined for this registration" because some
|
|
browsers require it for interpreting correctly UTF-8 special characters.
|
|
====
|
|
|
|
You can declare a shared produces attribute at the class level. Unlike most other request
|
|
mapping attributes however when used at the class level, a method-level produces attribute
|
|
overrides rather than extend the class level declaration.
|
|
|
|
[TIP]
|
|
====
|
|
`MediaType` provides constants for commonly used media types -- e.g.
|
|
`APPLICATION_JSON_UTF8_VALUE`, `APPLICATION_XML_VALUE`.
|
|
====
|
|
|
|
|
|
[[webflux-ann-requestmapping-params-and-headers]]
|
|
==== Parameters and Headers
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-params-and-headers,Same in Spring MVC>>#
|
|
|
|
You can narrow request mappings based on query parameter conditions. You can test for the
|
|
presence of a query parameter (`"myParam"`), for the absence (`"!myParam"`), or for a
|
|
specific value (`"myParam=myValue"`):
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping(path = "/pets/{petId}", **params = "myParam=myValue"**)
|
|
public void findPet(@PathVariable String petId) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
You can also use the same with request header conditions:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping(path = "/pets", **headers = "myHeader=myValue"**)
|
|
public void findPet(@PathVariable String petId) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-requestmapping-head-options]]
|
|
==== HTTP HEAD, OPTIONS
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-head-options,Same in Spring MVC>>#
|
|
|
|
`@GetMapping` -- and also `@RequestMapping(method=HttpMethod.GET)`, support HTTP HEAD
|
|
transparently for request mapping purposes. Controller methods don't need to change.
|
|
A response wrapper, applied in the `HttpHandler` server adapter, ensures a `"Content-Length"`
|
|
header is set to the number of bytes written and without actually writing to the response.
|
|
|
|
By default HTTP OPTIONS is handled by setting the "Allow" response header to the list of HTTP
|
|
methods listed in all `@RequestMapping` methods with matching URL patterns.
|
|
|
|
For a `@RequestMapping` without HTTP method declarations, the "Allow" header is set to
|
|
`"GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"`. Controller methods should always declare the
|
|
supported HTTP methods for example by using the HTTP method specific variants --
|
|
`@GetMapping`, `@PostMapping`, etc.
|
|
|
|
`@RequestMapping` method can be explicitly mapped to HTTP HEAD and HTTP OPTIONS, but that
|
|
is not necessary in the common case.
|
|
|
|
|
|
[[webflux-ann-requestmapping-composed]]
|
|
==== Custom Annotations
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-composed,Same in Spring MVC>>#
|
|
|
|
Spring WebFlux supports the use of <<core.adoc#beans-meta-annotations,composed annotations>>
|
|
for request mapping. Those are annotations that are themselves meta-annotated with
|
|
`@RequestMapping` and composed to redeclare a subset (or all) of the `@RequestMapping`
|
|
attributes with a narrower, more specific purpose.
|
|
|
|
`@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, and `@PatchMapping` are
|
|
examples of composed annotations. They're provided out of the box because arguably most
|
|
controller methods should be mapped to a specific HTTP method vs using `@RequestMapping`
|
|
which by default matches to all HTTP methods. If you need an example of composed
|
|
annotations, look at how those are declared.
|
|
|
|
Spring WebFlux also supports custom request mapping attributes with custom request matching
|
|
logic. This is a more advanced option that requires sub-classing
|
|
`RequestMappingHandlerMapping` and overriding the `getCustomMethodCondition` method where
|
|
you can check the custom attribute and return your own `RequestCondition`.
|
|
|
|
|
|
[[webflux-ann-requestmapping-registration]]
|
|
==== Explicit Registrations
|
|
[.small]#<<web.adoc#mvc-ann-requestmapping-registration,Same in Spring MVC>>#
|
|
|
|
Handler methods can be registered programmatically which can be used for dynamic
|
|
registrations, or for advanced cases such as different instances of the same handler
|
|
under different URLs. Below is an example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
public class MyConfig {
|
|
|
|
@Autowired
|
|
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) <1>
|
|
throws NoSuchMethodException {
|
|
|
|
RequestMappingInfo info = RequestMappingInfo
|
|
.paths("/user/{id}").methods(RequestMethod.GET).build(); <2>
|
|
|
|
Method method = UserHandler.class.getMethod("getUser", Long.class); <3>
|
|
|
|
mapping.registerMapping(info, handler, method); <4>
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
<1> Inject target handler(s) and the handler mapping for controllers.
|
|
<2> Prepare the request mapping meta data.
|
|
<3> Get the handler method.
|
|
<4> Add the registration.
|
|
|
|
|
|
|
|
[[webflux-ann-methods]]
|
|
=== Handler methods
|
|
[.small]#<<web.adoc#mvc-ann-methods,Same in Spring MVC>>#
|
|
|
|
`@RequestMapping` handler methods have a flexible signature and can choose from a range of
|
|
supported controller method arguments and return values.
|
|
|
|
|
|
[[webflux-ann-arguments]]
|
|
==== Method arguments
|
|
[.small]#<<web.adoc#mvc-ann-arguments,Same in Spring MVC>>#
|
|
|
|
The table below shows supported controller method arguments.
|
|
|
|
Reactive types (Reactor, RxJava, <<webflux-reactive-libraries,or other>>) are
|
|
supported on arguments that require blocking I/O, e.g. reading the request body, to
|
|
be resolved. This is marked in the description column. Reactive types are not expected
|
|
on arguments that don't require blocking.
|
|
|
|
JDK 1.8's `java.util.Optional` is supported as a method argument in combination with
|
|
annotations that have a `required` attribute -- e.g. `@RequestParam`, `@RequestHeader`,
|
|
etc, and is equivalent to `required=false`.
|
|
|
|
[cols="1,2", options="header"]
|
|
|===
|
|
| Controller method argument | Description
|
|
|
|
| `ServerWebExchange`
|
|
| Access to the full `ServerWebExchange` -- container for the HTTP request and response,
|
|
request and session attributes, `checkNotModified` methods, and others.
|
|
|
|
| `ServerHttpRequest`, `ServerHttpResponse`
|
|
| Access to the HTTP request or response.
|
|
|
|
| `WebSession`
|
|
| Access to the session; this does not force the start of a new session unless attributes
|
|
are added. Supports reactive types.
|
|
|
|
| `java.security.Principal`
|
|
| Currently authenticated user; possibly a specific `Principal` implementation class if known.
|
|
Supports reactive types.
|
|
|
|
| `org.springframework.http.HttpMethod`
|
|
| The HTTP method of the request.
|
|
|
|
| `java.util.Locale`
|
|
| The current request locale, determined by the most specific `LocaleResolver` available, in
|
|
effect, the configured `LocaleResolver`/`LocaleContextResolver`.
|
|
|
|
| `java.util.TimeZone` + `java.time.ZoneId`
|
|
| The time zone associated with the current request, as determined by a `LocaleContextResolver`.
|
|
|
|
| `@PathVariable`
|
|
| For access to URI template variables. See <<webflux-ann-requestmapping-uri-templates>>.
|
|
|
|
| `@MatrixVariable`
|
|
| For access to name-value pairs in URI path segments. See <<webflux-ann-matrix-variables>>.
|
|
|
|
| `@RequestParam`
|
|
| For access to Servlet request parameters. Parameter values are converted to the declared
|
|
method argument type. See <<webflux-ann-requestparam>>.
|
|
|
|
Note that use of `@RequestParam` is optional, e.g. to set its attributes.
|
|
See "Any other argument" further below in this table.
|
|
|
|
| `@RequestHeader`
|
|
| For access to request headers. Header values are converted to the declared method argument
|
|
type. See <<webflux-ann-requestheader>>.
|
|
|
|
| `@CookieValue`
|
|
| For access to cookies. Cookies values are converted to the declared method argument type.
|
|
See <<webflux-ann-cookievalue>>.
|
|
|
|
| `@RequestBody`
|
|
| For access to the HTTP request body. Body content is converted to the declared method
|
|
argument type using ``HttpMessageReader``'s. Supports reactive types.
|
|
See <<webflux-ann-requestbody>>.
|
|
|
|
| `HttpEntity<B>`
|
|
| For access to request headers and body. The body is converted with ``HttpMessageReader``'s.
|
|
Supports reactive types. See <<webflux-ann-httpentity>>.
|
|
|
|
| `@RequestPart`
|
|
| For access to a part in a "multipart/form-data" request. Supports reactive types.
|
|
See <<webflux-multipart-forms>> and <<webflux-multipart>>.
|
|
|
|
| `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap`
|
|
| For access to the model that is used in HTML controllers and exposed to templates as
|
|
part of view rendering.
|
|
|
|
| `@ModelAttribute`
|
|
| For access to an existing attribute in the model (instantiated if not present) with
|
|
data binding and validation applied. See <<webflux-ann-modelattrib-method-args>> as well
|
|
as <<webflux-ann-modelattrib-methods>> and <<webflux-ann-initbinder>>.
|
|
|
|
Note that use of `@ModelAttribute` is optional, e.g. to set its attributes.
|
|
See "Any other argument" further below in this table.
|
|
|
|
| `Errors`, `BindingResult`
|
|
| For access to errors from validation and data binding for a command object
|
|
(i.e. `@ModelAttribute` argument), or errors from the validation of an `@RequestBody` or
|
|
`@RequestPart` arguments; an `Errors`, or `BindingResult` argument must be declared
|
|
immediately after the validated method argument.
|
|
|
|
| `SessionStatus` + class-level `@SessionAttributes`
|
|
| For marking form processing complete which triggers cleanup of session attributes
|
|
declared through a class-level `@SessionAttributes` annotation.
|
|
See <<webflux-ann-sessionattributes>> for more details.
|
|
|
|
| `UriComponentsBuilder`
|
|
| For preparing a URL relative to the current request's host, port, scheme, and path.
|
|
See <<webflux-uri-building>>.
|
|
|
|
| `@SessionAttribute`
|
|
| For access to any session attribute; in contrast to model attributes stored in the session
|
|
as a result of a class-level `@SessionAttributes` declaration. See
|
|
<<webflux-ann-sessionattribute>> for more details.
|
|
|
|
| `@RequestAttribute`
|
|
| For access to request attributes. See <<webflux-ann-requestattrib>> for more details.
|
|
|
|
| Any other argument
|
|
| If a method argument is not matched to any of the above, by default it is resolved as
|
|
an `@RequestParam` if it is a simple type, as determined by
|
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
|
|
or as an `@ModelAttribute` otherwise.
|
|
|===
|
|
|
|
|
|
[[webflux-ann-return-types]]
|
|
==== Return values
|
|
[.small]#<<web.adoc#mvc-ann-return-types,Same in Spring MVC>>#
|
|
|
|
The table below shows supported controller method return values. Note that reactive types
|
|
from libraries such as Reactor, RxJava, <<webflux-reactive-libraries,or other>> are
|
|
generally supported for all return values.
|
|
|
|
[cols="1,2", options="header"]
|
|
|===
|
|
| Controller method return value | Description
|
|
|
|
| `@ResponseBody`
|
|
| The return value is encoded through ``HttpMessageWriter``'s and written to the response.
|
|
See <<webflux-ann-responsebody>>.
|
|
|
|
| `HttpEntity<B>`, `ResponseEntity<B>`
|
|
| The return value specifies the full response including HTTP headers and body be encoded
|
|
through ``HttpMessageWriter``'s and written to the response.
|
|
See <<webflux-ann-responseentity>>.
|
|
|
|
| `HttpHeaders`
|
|
| For returning a response with headers and no body.
|
|
|
|
| `String`
|
|
| A view name to be resolved with ``ViewResolver``'s and used together with the implicit
|
|
model -- determined through command objects and `@ModelAttribute` methods. The handler
|
|
method may also programmatically enrich the model by declaring a `Model` argument
|
|
(see above).
|
|
|
|
| `View`
|
|
| A `View` instance to use for rendering together with the implicit model -- determined
|
|
through command objects and `@ModelAttribute` methods. The handler method may also
|
|
programmatically enrich the model by declaring a `Model` argument (see above).
|
|
|
|
| `java.util.Map`, `org.springframework.ui.Model`
|
|
| Attributes to be added to the implicit model with the view name implicitly determined
|
|
based on the request path.
|
|
|
|
| `@ModelAttribute`
|
|
| An attribute to be added to the model with the view name implicitly determined based
|
|
on the request path.
|
|
|
|
Note that `@ModelAttribute` is optional. See "Any other return value" further below in
|
|
this table.
|
|
|
|
| `Rendering`
|
|
| An API for model and view rendering scenarios.
|
|
|
|
| `void`
|
|
| A method with a `void`, possibly async (e.g. `Mono<Void>`), return type (or a `null` return
|
|
value) is considered to have fully handled the response if it also has a `ServerHttpResponse`,
|
|
or a `ServerWebExchange` argument, or an `@ResponseStatus` annotation. The same is true also
|
|
if the controller has made a positive ETag or lastModified timestamp check.
|
|
// TODO: See <<webflux-caching-etag-lastmodified>> for details.
|
|
|
|
If none of the above is true, a `void` return type may also indicate "no response body" for
|
|
REST controllers, or default view name selection for HTML controllers.
|
|
|
|
| `Flux<ServerSentEvent>`, `Observable<ServerSentEvent>`, or other reactive type
|
|
| Emit server-sent events; the `SeverSentEvent` wrapper can be omitted when only data needs
|
|
to be written (however `text/event-stream` must be requested or declared in the mapping
|
|
through the produces attribute).
|
|
|
|
| Any other return value
|
|
| If a return value is not matched to any of the above, by default it is treated as a view
|
|
name, if it is `String` or `void` (default view name selection applies); or as a model
|
|
attribute to be added to the model, unless it is a simple type, as determined by
|
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
|
|
in which case it remains unresolved.
|
|
|===
|
|
|
|
|
|
[[webflux-ann-typeconversion]]
|
|
==== Type Conversion
|
|
[.small]#<<web.adoc#mvc-ann-typeconversion,Same in Spring MVC>>#
|
|
|
|
Some annotated controller method arguments that represent String-based request input -- e.g.
|
|
`@RequestParam`, `@RequestHeader`, `@PathVariable`, `@MatrixVariable`, and `@CookieValue`,
|
|
may require type conversion if the argument is declared as something other than `String`.
|
|
|
|
For such cases type conversion is automatically applied based on the configured converters.
|
|
By default simple types such as `int`, `long`, `Date`, etc. are supported. Type conversion
|
|
can be customized through a `WebDataBinder`, see <<mvc-ann-initbinder>>, or by registering
|
|
`Formatters` with the `FormattingConversionService`, see
|
|
<<core.adoc#format, Spring Field Formatting>>.
|
|
|
|
|
|
[[webflux-ann-matrix-variables]]
|
|
==== Matrix variables
|
|
[.small]#<<web.adoc#mvc-ann-matrix-variables,Same in Spring MVC>>#
|
|
|
|
http://tools.ietf.org/html/rfc3986#section-3.3[RFC 3986] discusses name-value pairs in
|
|
path segments. In Spring WebFlux we refer to those as "matrix variables" based on an
|
|
http://www.w3.org/DesignIssues/MatrixURIs.html["old post"] by Tim Berners-Lee but they
|
|
can be also be referred to as URI path parameters.
|
|
|
|
Matrix variables can appear in any path segment, each variable separated by semicolon and
|
|
multiple values separated by comma, e.g. `"/cars;color=red,green;year=2012"`. Multiple
|
|
values can also be specified through repeated variable names, e.g.
|
|
`"color=red;color=green;color=blue"`.
|
|
|
|
Unlike Spring MVC, in WebFlux the presence or absence of matrix variables in a URL does
|
|
not affect request mappings. In other words you're not required to use a URI variable
|
|
to mask variable content. That said if you want to access matrix variables from a
|
|
controller method you need to add a URI variable to the path segment where matrix
|
|
variables are expected. Below is an example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
// GET /pets/42;q=11;r=22
|
|
|
|
@GetMapping("/pets/{petId}")
|
|
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
|
|
|
|
// petId == 42
|
|
// q == 11
|
|
}
|
|
----
|
|
|
|
Given that all path segments may contain matrix variables, sometimes you may need to
|
|
disambiguate which path variable the matrix variable is expected to be in.
|
|
For example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
// GET /owners/42;q=11/pets/21;q=22
|
|
|
|
@GetMapping("/owners/{ownerId}/pets/{petId}")
|
|
public void findPet(
|
|
@MatrixVariable(name="q", pathVar="ownerId") int q1,
|
|
@MatrixVariable(name="q", pathVar="petId") int q2) {
|
|
|
|
// q1 == 11
|
|
// q2 == 22
|
|
}
|
|
----
|
|
|
|
A matrix variable may be defined as optional and a default value specified:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
// GET /pets/42
|
|
|
|
@GetMapping("/pets/{petId}")
|
|
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
|
|
|
|
// q == 1
|
|
}
|
|
----
|
|
|
|
To get all matrix variables, use a `MultiValueMap`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
|
|
|
|
@GetMapping("/owners/{ownerId}/pets/{petId}")
|
|
public void findPet(
|
|
@MatrixVariable MultiValueMap<String, String> matrixVars,
|
|
@MatrixVariable(pathVar="petId"") MultiValueMap<String, String> petMatrixVars) {
|
|
|
|
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
|
|
// petMatrixVars: ["q" : 22, "s" : 23]
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-requestparam]]
|
|
==== @RequestParam
|
|
[.small]#<<web.adoc#mvc-ann-requestparam,Same in Spring MVC>>#
|
|
|
|
Use the `@RequestParam` annotation to bind query parameters to a method argument in a
|
|
controller. The following code snippet shows the usage:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
@RequestMapping("/pets")
|
|
public class EditPetForm {
|
|
|
|
// ...
|
|
|
|
@GetMapping
|
|
public String setupForm(**@RequestParam("petId") int petId**, Model model) {
|
|
Pet pet = this.clinic.loadPet(petId);
|
|
model.addAttribute("pet", pet);
|
|
return "petForm";
|
|
}
|
|
|
|
// ...
|
|
|
|
}
|
|
----
|
|
|
|
[TIP]
|
|
====
|
|
Unlike the Servlet API "request parameter" concept that conflates query parameters, form
|
|
data, and multiparts into one, in WebFlux each is accessed individually through the
|
|
`ServerWebExchange`. While `@RequestParam` binds to query parameters only, you can use
|
|
data binding to apply query parameters, form data, and multiparts to a
|
|
<<webflux-ann-modelattrib-method-args,command object>>.
|
|
====
|
|
|
|
Method parameters using using the `@RequestParam` annotation are required by default, but
|
|
you can specify that a method parameter is optional by setting ``@RequestParam``'s
|
|
`required` flag to `false` or by declaring the argument with a `java.util.Optional`
|
|
wrapper.
|
|
|
|
Type conversion is applied automatically if the target method parameter type is not
|
|
`String`. See <<mvc-ann-typeconversion>>.
|
|
|
|
When an `@RequestParam` annotation is declared on a `Map<String, String>` or
|
|
`MultiValueMap<String, String>` argument, the map is populated with all query parameters.
|
|
|
|
Note that use of `@RequestParam` is optional -- for example, to set its attributes. By
|
|
default any argument that is a simple value type, as determined by
|
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
|
|
and is not resolved by any other argument resolver, is treated as if it was annotated
|
|
with `@RequestParam`.
|
|
|
|
|
|
[[webflux-ann-requestheader]]
|
|
==== @RequestHeader
|
|
[.small]#<<web.adoc#mvc-ann-requestheader,Same in Spring MVC>>#
|
|
|
|
Use the `@RequestHeader` annotation to bind a request header to a method argument in a
|
|
controller.
|
|
|
|
Given request with headers:
|
|
|
|
[literal]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
Host localhost:8080
|
|
Accept text/html,application/xhtml+xml,application/xml;q=0.9
|
|
Accept-Language fr,en-gb;q=0.7,en;q=0.3
|
|
Accept-Encoding gzip,deflate
|
|
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
|
|
Keep-Alive 300
|
|
----
|
|
|
|
The following gets the value of the `Accept-Encoding` and `Keep-Alive` headers:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/demo")
|
|
public void handle(
|
|
**@RequestHeader("Accept-Encoding")** String encoding,
|
|
**@RequestHeader("Keep-Alive")** long keepAlive) {
|
|
//...
|
|
}
|
|
----
|
|
|
|
Type conversion is applied automatically if the target method parameter type is not
|
|
`String`. See <<mvc-ann-typeconversion>>.
|
|
|
|
When an `@RequestHeader` annotation is used on a `Map<String, String>`,
|
|
`MultiValueMap<String, String>`, or `HttpHeaders` argument, the map is populated
|
|
with all header values.
|
|
|
|
[TIP]
|
|
====
|
|
Built-in support is available for converting a comma-separated string into an
|
|
array/collection of strings or other types known to the type conversion system. For
|
|
example a method parameter annotated with `@RequestHeader("Accept")` may be of type
|
|
`String` but also `String[]` or `List<String>`.
|
|
====
|
|
|
|
|
|
[[webflux-ann-cookievalue]]
|
|
==== @CookieValue
|
|
[.small]#<<web.adoc#mvc-ann-cookievalue,Same in Spring MVC>>#
|
|
|
|
Use the `@CookieValue` annotation to bind the value of an HTTP cookie to a method argument
|
|
in a controller.
|
|
|
|
Given request with the following cookie:
|
|
|
|
[literal]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
|
|
----
|
|
|
|
The following code sample demonstrates how to get the cookie value:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/demo")
|
|
public void handle(**@CookieValue("JSESSIONID")** String cookie) {
|
|
//...
|
|
}
|
|
----
|
|
|
|
Type conversion is applied automatically if the target method parameter type is not
|
|
`String`. See <<mvc-ann-typeconversion>>.
|
|
|
|
|
|
[[webflux-ann-modelattrib-method-args]]
|
|
==== @ModelAttribute
|
|
[.small]#<<web.adoc#mvc-ann-modelattrib-method-args,Same in Spring MVC>>#
|
|
|
|
Use the `@ModelAttribute` annotation on a method argument to access an attribute from the
|
|
model, or have it instantiated if not present. The model attribute is also overlaid with
|
|
values of query parameters and form fields whose names match to field names. This is
|
|
referred to as data binding and it saves you from having to deal with parsing and
|
|
converting individual query parameters and form fields. For example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
|
public String processSubmit(**@ModelAttribute Pet pet**) { }
|
|
----
|
|
|
|
The `Pet` instance above is resolved as follows:
|
|
|
|
* From the model if already added via <<webflux-ann-modelattrib-methods>>.
|
|
* From the HTTP session via <<webflux-ann-sessionattributes>>.
|
|
* From the invocation of a default constructor.
|
|
* From the invocation of a "primary constructor" with arguments matching to query
|
|
parameters or form fields; argument names are determined via JavaBeans
|
|
`@ConstructorProperties` or via runtime-retained parameter names in the bytecode.
|
|
|
|
After the model attribute instance is obtained, data binding is applied. The
|
|
`WebExchangeDataBinder` class matches names of query parameters and form fields to field
|
|
names on the target Object. Matching fields are populated after type conversion is applied
|
|
where necessary. For more on data binding (and validation) see
|
|
<<core.adoc#validation, Validation>>. For more on customizing data binding see
|
|
<<webflux-ann-initbinder>>.
|
|
|
|
Data binding may result in errors. By default a `WebExchangeBindException` is raised but
|
|
to check for such errors in the controller method, add a `BindingResult` argument
|
|
immediately next to the `@ModelAttribute` as shown below:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
|
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) {
|
|
if (result.hasErrors()) {
|
|
return "petForm";
|
|
}
|
|
// ...
|
|
}
|
|
----
|
|
|
|
Validation can be applied automatically after data binding by adding the
|
|
`javax.validation.Valid` annotation or Spring's `@Validated` annotation (also see
|
|
<<core.adoc#validation-beanvalidation, Bean validation>> and
|
|
<<core.adoc#validation, Spring validation>>). For example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
|
public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) {
|
|
if (result.hasErrors()) {
|
|
return "petForm";
|
|
}
|
|
// ...
|
|
}
|
|
----
|
|
|
|
Spring WebFlux, unlike Spring MVC, supports reactive types in the model, e.g.
|
|
`Mono<Account>` or `io.reactivex.Single<Account>`. An `@ModelAttribute` argument can be
|
|
declared with or without a reactive type wrapper, and it will be resolved accordingly,
|
|
to the actual value if necessary. Note however that in order to use a `BindingResult`
|
|
argument, you must declare the `@ModelAttribute` argument before it without a reactive
|
|
type wrapper, as shown earlier. Alternatively, you can handle any errors through the
|
|
reactive type:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
|
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
|
|
return petMono
|
|
.flatMap(pet -> {
|
|
// ...
|
|
})
|
|
.onErrorResume(ex -> {
|
|
// ...
|
|
});
|
|
}
|
|
----
|
|
|
|
Note that use of `@ModelAttribute` is optional, e.g. to set its attributes.
|
|
By default any argument that is not a simple value type, as determined by
|
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
|
|
and is not resolved by any other argument resolver, is treated as if it was annotated
|
|
with `@ModelAttribute`.
|
|
|
|
|
|
[[webflux-ann-sessionattributes]]
|
|
==== @SessionAttributes
|
|
[.small]#<<web.adoc#mvc-ann-sessionattributes,Same in Spring MVC>>#
|
|
|
|
`@SessionAttributes` is used to store model attributes in the `WebSession` between
|
|
requests. It is a type-level annotation that declares session attributes used by a
|
|
specific controller. This will typically list the names of model attributes or types of
|
|
model attributes which should be transparently stored in the session for subsequent
|
|
requests to access.
|
|
|
|
For example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
**@SessionAttributes("pet")**
|
|
public class EditPetForm {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
On the first request when a model attribute with the name "pet" is added to the model,
|
|
it is automatically promoted to and saved in the `WebSession`. It remains there until
|
|
another controller method uses a `SessionStatus` method argument to clear the storage:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
**@SessionAttributes("pet")**
|
|
public class EditPetForm {
|
|
|
|
// ...
|
|
|
|
@PostMapping("/pets/{id}")
|
|
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
|
|
if (errors.hasErrors) {
|
|
// ...
|
|
}
|
|
status.setComplete();
|
|
// ...
|
|
}
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-sessionattribute]]
|
|
==== @SessionAttribute
|
|
[.small]#<<web.adoc#mvc-ann-sessionattribute,Same in Spring MVC>>#
|
|
|
|
If you need access to pre-existing session attributes that are managed globally,
|
|
i.e. outside the controller (e.g. by a filter), and may or may not be present
|
|
use the `@SessionAttribute` annotation on a method parameter:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/")
|
|
public String handle(**@SessionAttribute** User user) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
For use cases that require adding or removing session attributes consider injecting
|
|
`WebSession` into the controller method.
|
|
|
|
For temporary storage of model attributes in the session as part of a controller
|
|
workflow consider using `SessionAttributes` as described in
|
|
<<webflux-ann-sessionattributes>>.
|
|
|
|
|
|
[[webflux-ann-requestattrib]]
|
|
==== @RequestAttribute
|
|
[.small]#<<web.adoc#mvc-ann-requestattrib,Same in Spring MVC>>#
|
|
|
|
Similar to `@SessionAttribute` the `@RequestAttribute` annotation can be used to
|
|
access pre-existing request attributes created earlier, e.g. by a `WebFilter`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/")
|
|
public String handle(**@RequestAttribute** Client client) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-multipart-forms]]
|
|
==== Multipart
|
|
[.small]#<<web.adoc#mvc-multipart-forms,Same in Spring MVC>>#
|
|
|
|
As explained in <<webflux-multipart>>, `ServerWebExchange` provides access to multipart
|
|
content. The best way to handle a file upload form (e.g. from a browser) in a controller
|
|
is through data binding to a <<webflux-ann-modelattrib-method-args,command object>>:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
class MyForm {
|
|
|
|
private String name;
|
|
|
|
private MultipartFile file;
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
@Controller
|
|
public class FileUploadController {
|
|
|
|
@PostMapping("/form")
|
|
public String handleFormUpload(MyForm form, BindingResult errors) {
|
|
// ...
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
Multipart requests can also be submitted from non-browser clients in a RESTful service
|
|
scenario. For example a file along with JSON:
|
|
|
|
[literal]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
POST /someUrl
|
|
Content-Type: multipart/mixed
|
|
|
|
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
|
|
Content-Disposition: form-data; name="meta-data"
|
|
Content-Type: application/json; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
{
|
|
"name": "value"
|
|
}
|
|
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
|
|
Content-Disposition: form-data; name="file-data"; filename="file.properties"
|
|
Content-Type: text/xml
|
|
Content-Transfer-Encoding: 8bit
|
|
... File Data ...
|
|
----
|
|
|
|
You can access individual parts with `@RequestPart`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/")
|
|
public String handle(**@RequestPart("meta-data") Part metadata,
|
|
@RequestPart("file-data") FilePart file**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
To deserialize the raw part content, for example to JSON (similar to `@RequestBody`),
|
|
simply declare a concrete target Object, instead of `Part`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/")
|
|
public String handle(**@RequestPart("meta-data") MetaData metadata**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
`@RequestPart` can be used in combination with `javax.validation.Valid`, or Spring's
|
|
`@Validated` annotation, which causes Standard Bean Validation to be applied.
|
|
By default validation errors cause a `WebExchangeBindException` which is turned
|
|
into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally
|
|
within the controller through an `Errors` or `BindingResult` argument:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/")
|
|
public String handle(**@Valid** @RequestPart("meta-data") MetaData metadata,
|
|
**BindingResult result**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
To access all multipart data in as a `MultiValueMap` use `@RequestBody`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/")
|
|
public String handle(**@RequestBody Mono<MultiValueMap<String, Part>> parts**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
To access multipart data sequentially, in streaming fashion, use `@RequestBody` with
|
|
`Flux<Part>` instead. For example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/")
|
|
public String handle(**@RequestBody Flux<Part> parts**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-requestbody]]
|
|
==== @RequestBody
|
|
[.small]#<<web.adoc#mvc-ann-requestbody,Same in Spring MVC>>#
|
|
|
|
Use the `@RequestBody` annotation to have the request body read and deserialized into an
|
|
Object through an <<webflux-codecs,HttpMessageReader>>.
|
|
Below is an example with an `@RequestBody` argument:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/accounts")
|
|
public void handle(@RequestBody Account account) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
Unlike Spring MVC, in WebFlux the `@RequestBody` method argument supports reactive types
|
|
and fully non-blocking reading and (client-to-server) streaming:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/accounts")
|
|
public void handle(@RequestBody Mono<Account> account) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
You can use the <<webflux-config-message-codecs>> option of the <<webflux-config>> to
|
|
configure or customize message readers.
|
|
|
|
`@RequestBody` can be used in combination with `javax.validation.Valid`, or Spring's
|
|
`@Validated` annotation, which causes Standard Bean Validation to be applied.
|
|
By default validation errors cause a `WebExchangeBindException` which is turned
|
|
into a 400 (BAD_REQUEST) response. Alternatively validation errors can be handled locally
|
|
within the controller through an `Errors` or `BindingResult` argument:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/accounts")
|
|
public void handle(@Valid @RequestBody Account account, BindingResult result) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-httpentity]]
|
|
==== HttpEntity
|
|
[.small]#<<web.adoc#mvc-ann-httpentity,Same in Spring MVC>>#
|
|
|
|
`HttpEntity` is more or less identical to using <<webflux-ann-requestbody>> but based on a
|
|
container object that exposes request headers and body. Below is an example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/accounts")
|
|
public void handle(HttpEntity<Account> entity) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-responsebody]]
|
|
==== @ResponseBody
|
|
[.small]#<<web.adoc#mvc-ann-responsebody,Same in Spring MVC>>#
|
|
|
|
Use the `@ResponseBody` annotation on a method to have the return serialized to the
|
|
response body through an <<webflux-codecs,HttpMessageWriter>>. For example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/accounts/{id}")
|
|
@ResponseBody
|
|
public Account handle() {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
`@ResponseBody` is also supported at the class level in which case it is inherited by
|
|
all controller methods. This is the effect of `@RestController` which is nothing more
|
|
than a meta-annotation marked with `@Controller` and `@ResponseBody`.
|
|
|
|
`@ResponseBody` supports reactive types which means you can return Reactor or RxJava
|
|
types and have the asynchronous values they produce rendered to the response.
|
|
For additional details, see <<webflux-codecs-streaming>> and
|
|
<<webflux-codecs-jackson,JSON rendering>>.
|
|
|
|
`@ResponseBody` methods can be combined with JSON serialization views.
|
|
See <<webflux-ann-jackson>> for details.
|
|
|
|
You can use the <<webflux-config-message-codecs>> option of the <<webflux-config>> to
|
|
configure or customize message writing.
|
|
|
|
|
|
[[webflux-ann-responseentity]]
|
|
==== ResponseEntity
|
|
[.small]#<<web.adoc#mvc-ann-responseentity,Same in Spring MVC>>#
|
|
|
|
`ResponseEntity` is more or less identical to using <<webflux-ann-responsebody>> but based
|
|
on a container object that specifies request headers and body. Below is an example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/something")
|
|
public ResponseEntity<String> handle() {
|
|
// ...
|
|
URI location = ...
|
|
return new ResponseEntity.created(location).build();
|
|
}
|
|
----
|
|
|
|
|
|
[[webflux-ann-jackson]]
|
|
==== Jackson JSON
|
|
|
|
[[webflux-ann-jsonview]]
|
|
===== Jackson serialization views
|
|
[.small]#<<web.adoc#mvc-ann-jackson,Same in Spring MVC>>#
|
|
|
|
Spring WebFlux provides built-in support for
|
|
http://wiki.fasterxml.com/JacksonJsonViews[Jackson's Serialization Views]
|
|
which allows rendering only a subset of all fields in an Object. To use it with
|
|
`@ResponseBody` or `ResponseEntity` controller methods, use Jackson's
|
|
`@JsonView` annotation to activate a serialization view class:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@RestController
|
|
public class UserController {
|
|
|
|
@GetMapping("/user")
|
|
@JsonView(User.WithoutPasswordView.class)
|
|
public User getUser() {
|
|
return new User("eric", "7!jd#h23");
|
|
}
|
|
}
|
|
|
|
public class User {
|
|
|
|
public interface WithoutPasswordView {};
|
|
public interface WithPasswordView extends WithoutPasswordView {};
|
|
|
|
private String username;
|
|
private String password;
|
|
|
|
public User() {
|
|
}
|
|
|
|
public User(String username, String password) {
|
|
this.username = username;
|
|
this.password = password;
|
|
}
|
|
|
|
@JsonView(WithoutPasswordView.class)
|
|
public String getUsername() {
|
|
return this.username;
|
|
}
|
|
|
|
@JsonView(WithPasswordView.class)
|
|
public String getPassword() {
|
|
return this.password;
|
|
}
|
|
}
|
|
----
|
|
|
|
[NOTE]
|
|
====
|
|
`@JsonView` allows an array of view classes but you can only specify only one per
|
|
controller method. Use a composite interface if you need to activate multiple views.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-ann-modelattrib-methods]]
|
|
=== Model
|
|
[.small]#<<web.adoc#mvc-ann-modelattrib-methods,Same in Spring MVC>>#
|
|
|
|
The `@ModelAttribute` annotation can be used:
|
|
|
|
* On a <<webflux-ann-modelattrib-method-args,method argument>> in `@RequestMapping` methods
|
|
to create or access an Object from the model, and to bind it to the request through a
|
|
`WebDataBinder`.
|
|
* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes helping
|
|
to initialize the model prior to any `@RequestMapping` method invocation.
|
|
* On a `@RequestMapping` method to mark its return value is a model attribute.
|
|
|
|
This section discusses `@ModelAttribute` methods, or the 2nd from the list above.
|
|
A controller can have any number of `@ModelAttribute` methods. All such methods are
|
|
invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute`
|
|
method can also be shared across controllers via `@ControllerAdvice`. See the section on
|
|
<<webflux-ann-controller-advice>> for more details.
|
|
|
|
`@ModelAttribute` methods have flexible method signatures. They support many of the same
|
|
arguments as `@RequestMapping` methods except for `@ModelAttribute` itself nor anything
|
|
related to the request body.
|
|
|
|
An example `@ModelAttribute` method:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@ModelAttribute
|
|
public void populateModel(@RequestParam String number, Model model) {
|
|
model.addAttribute(accountRepository.findAccount(number));
|
|
// add more ...
|
|
}
|
|
----
|
|
|
|
To add one attribute only:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@ModelAttribute
|
|
public Account addAccount(@RequestParam String number) {
|
|
return accountRepository.findAccount(number);
|
|
}
|
|
----
|
|
|
|
[NOTE]
|
|
====
|
|
When a name is not explicitly specified, a default name is chosen based on the Object
|
|
type as explained in the Javadoc for
|
|
{api-spring-framework}/core/Conventions.html[Conventions].
|
|
You can always assign an explicit name by using the overloaded `addAttribute` method or
|
|
through the name attribute on `@ModelAttribute` (for a return value).
|
|
====
|
|
|
|
Spring WebFlux, unlike Spring MVC, explicitly supports reactive types in the model,
|
|
e.g. `Mono<Account>` or `io.reactivex.Single<Account>`. Such asynchronous model
|
|
attributes may be transparently resolved (and the model updated) to their actual values
|
|
at the time of `@RequestMapping` invocation, providing a `@ModelAttribute` argument is
|
|
declared without a wrapper, for example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@ModelAttribute
|
|
public void addAccount(@RequestParam String number) {
|
|
Mono<Account> accountMono = accountRepository.findAccount(number);
|
|
model.addAttribute("account", accountMono);
|
|
}
|
|
|
|
@PostMapping("/accounts")
|
|
public String handle(@ModelAttribute Account account, BindingResult errors) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
In addition any model attributes that have a reactive type wrapper are resolved to their
|
|
actual values (and the model updated) just prior to view rendering.
|
|
|
|
`@ModelAttribute` can also be used as a method-level annotation on `@RequestMapping`
|
|
methods in which case the return value of the `@RequestMapping` method is interpreted as a
|
|
model attribute. This is typically not required, as it is the default behavior in HTML
|
|
controllers, unless the return value is a `String` which would otherwise be interpreted
|
|
as a view name. `@ModelAttribute` can also help to customize the model attribute name:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/accounts/{id}")
|
|
@ModelAttribute("myAccount")
|
|
public Account handle() {
|
|
// ...
|
|
return account;
|
|
}
|
|
----
|
|
|
|
|
|
|
|
[[webflux-ann-initbinder]]
|
|
=== DataBinder
|
|
[.small]#<<web.adoc#mvc-ann-initbinder,Same in Spring MVC>>#
|
|
|
|
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods in order to
|
|
initialize instances of `WebDataBinder`, and those in turn are used to:
|
|
|
|
* Bind request parameters (i.e. form data or query) to a model object.
|
|
* Convert String-based request values such as request parameters, path variables,
|
|
headers, cookies, and others, to the target type of controller method arguments.
|
|
* Format model object values as String values when rendering HTML forms.
|
|
|
|
`@InitBinder` methods can register controller-specific `java.bean.PropertyEditor`, or
|
|
Spring `Converter` and `Formatter` components. In addition, the
|
|
<<webflux-config-conversion,WebFlux Java config>> can be used to register `Converter` and
|
|
`Formatter` types in a globally shared `FormattingConversionService`.
|
|
|
|
`@InitBinder` methods support many of the same arguments that a `@RequestMapping` methods
|
|
do, except for `@ModelAttribute` (command object) arguments. Typically they're are declared
|
|
with a `WebDataBinder` argument, for registrations, and a `void` return value.
|
|
Below is an example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
public class FormController {
|
|
|
|
**@InitBinder**
|
|
public void initBinder(WebDataBinder binder) {
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
|
dateFormat.setLenient(false);
|
|
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
|
|
}
|
|
|
|
// ...
|
|
}
|
|
----
|
|
|
|
Alternatively when using a `Formatter`-based setup through a shared
|
|
`FormattingConversionService`, you could re-use the same approach and register
|
|
controller-specific ``Formatter``'s:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
public class FormController {
|
|
|
|
**@InitBinder**
|
|
protected void initBinder(WebDataBinder binder) {
|
|
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
|
|
}
|
|
|
|
// ...
|
|
}
|
|
----
|
|
|
|
|
|
|
|
[[webflux-ann-controller-exceptions]]
|
|
=== Exceptions
|
|
[.small]#<<web.adoc#mvc-ann-exceptionhandler,Same in Spring MVC>>#
|
|
|
|
`@Controller` and <<mvc-ann-controller-advice,@ControllerAdvice>> classes can have
|
|
`@ExceptionHandler` methods to handle exceptions from controller methods. For example:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
public class SimpleController {
|
|
|
|
// ...
|
|
|
|
@ExceptionHandler
|
|
public ResponseEntity<String> handle(IOException ex) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
The exception may match against a top-level exception being propagated (i.e. a direct
|
|
`IOException` thrown), or against the immediate cause within a top-level wrapper exception
|
|
(e.g. an `IOException` wrapped inside an `IllegalStateException`).
|
|
|
|
For matching exception types, preferably declare the target exception as a method argument
|
|
as shown above. Alternatively, the annotation declaration may narrow the exception types to
|
|
match. We generally recommend to be as specific as possible in the argument signature and to
|
|
declare your primary root exception mappings on a `@ControllerAdvice` prioritized with a
|
|
corresponding order. See <<web.adoc#mvc-ann-exceptionhandler,the MVC section>> for details.
|
|
|
|
[NOTE]
|
|
====
|
|
An `@ExceptionHandler` method in WebFlux supports the same method arguments and
|
|
return values as an `@RequestMapping` method, with the exception of request body
|
|
and `@ModelAttribute` related method arguments.
|
|
====
|
|
|
|
Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the
|
|
`HandlerAdapter` for `@RequestMapping` methods. See <<webflux-dispatcher-exceptions>>
|
|
under the `DispatcherHandler` section for more details.
|
|
|
|
|
|
[[webflux-ann-rest-exceptions]]
|
|
==== REST API exceptions
|
|
[.small]#<<web.adoc#mvc-ann-rest-exceptions,Same in Spring MVC>>#
|
|
|
|
A common requirement for REST services is to include error details in the body of the
|
|
response. The Spring Framework does not automatically do this because the representation
|
|
of error details in the response body is application specific. However a
|
|
`@RestController` may use `@ExceptionHandler` methods with a `ResponseEntity` return
|
|
value to set the status and the body of the response. Such methods may also be declared
|
|
in `@ControllerAdvice` classes to apply them globally.
|
|
|
|
[NOTE]
|
|
====
|
|
Note that Spring WebFlux does not have an equivalent for the Spring MVC
|
|
`ResponseEntityExceptionHandler` because WebFlux only raises `ResponseStatusException`
|
|
(or subclasses thereof), which and those do not need to be translated translation to
|
|
an HTTP status code.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-ann-controller-advice]]
|
|
=== Controller Advice
|
|
[.small]#<<web.adoc#mvc-ann-controller-advice,Same in Spring MVC>>#
|
|
|
|
Typically `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply within
|
|
the `@Controller` class (or class hierarchy) they are declared in. If you want such
|
|
methods to apply more globally, across controllers, you can declare them in a class
|
|
marked with `@ControllerAdvice` or `@RestControllerAdvice`.
|
|
|
|
`@ControllerAdvice` is marked with `@Component` which means such classes can be registered
|
|
as Spring beans via <<core.adoc#beans-java-instantiating-container-scan,component scanning>>.
|
|
`@RestControllerAdvice` is also a meta-annotation marked with both `@ControllerAdvice` and
|
|
`@ResponseBody` which essentially means `@ExceptionHandler` methods are rendered to the
|
|
response body via message conversion (vs view resolution/template rendering).
|
|
|
|
On startup, the infrastructure classes for `@RequestMapping` and `@ExceptionHandler` methods
|
|
detect Spring beans of type `@ControllerAdvice`, and then apply their methods at runtime.
|
|
Global `@ExceptionHandler` methods (from an `@ControllerAdvice`) are applied *after* local
|
|
ones (from the `@Controller`). By contrast global `@ModelAttribute` and `@InitBinder`
|
|
methods are applied *before* local ones.
|
|
|
|
By default `@ControllerAdvice` methods apply to every request, i.e. all controllers, but
|
|
you can narrow that down to a subset of controllers via attributes on the annotation:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
// Target all Controllers annotated with @RestController
|
|
@ControllerAdvice(annotations = RestController.class)
|
|
public class ExampleAdvice1 {}
|
|
|
|
// Target all Controllers within specific packages
|
|
@ControllerAdvice("org.example.controllers")
|
|
public class ExampleAdvice2 {}
|
|
|
|
// Target all Controllers assignable to specific classes
|
|
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
|
|
public class ExampleAdvice3 {}
|
|
----
|
|
|
|
Keep in mind the above selectors are evaluated at runtime and may negatively impact
|
|
performance if used extensively. See the
|
|
{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[@ControllerAdvice]
|
|
Javadoc for more details.
|
|
|
|
|
|
|
|
include::webflux-functional.adoc[leveloffset=+1]
|
|
|
|
|
|
|
|
|
|
[[webflux-uri-building]]
|
|
== URI Links
|
|
[.small]#<<web.adoc#mvc-uri-building,Same in Spring MVC>>#
|
|
|
|
This section describes various options available in the Spring Framework to prepare URIs.
|
|
|
|
|
|
include::web-uris.adoc[leveloffset=+2]
|
|
|
|
|
|
|
|
|
|
include::webflux-cors.adoc[leveloffset=+1]
|
|
|
|
|
|
|
|
|
|
[[webflux-web-security]]
|
|
== Web Security
|
|
[.small]#<<web.adoc#mvc-web-security,Same in Spring MVC>>#
|
|
|
|
The http://projects.spring.io/spring-security/[Spring Security] project provides support
|
|
for protecting web applications from malicious exploits. Check out the Spring Security
|
|
reference documentation including:
|
|
|
|
* {doc-spring-security}/html5/#jc-webflux[WebFlux Security]
|
|
* {doc-spring-security}/html5/#test-webflux["WebFlux Testing Support"]
|
|
* {doc-spring-security}/html5/#csrf[CSRF Protection]
|
|
* {doc-spring-security}/html5/#headers[Security Response Headers]
|
|
|
|
|
|
|
|
|
|
include::webflux-view.adoc[leveloffset=+1]
|
|
|
|
|
|
|
|
|
|
[[webflux-caching]]
|
|
== HTTP Caching
|
|
[.small]#<<web.adoc#mvc-caching,Same in Spring MVC>>#
|
|
|
|
HTTP caching can significantly improve the performance of a web application. HTTP caching
|
|
revolves around the "Cache-Control" response header and subsequently conditional request
|
|
headers such as "Last-Modified" and "ETag". "Cache-Control" advises private (e.g. browser)
|
|
and public (e.g. proxy) caches how to cache and re-use responses. An "ETag" header is used
|
|
to make a conditional request that may result in a 304 (NOT_MODIFIED) without a body,
|
|
if the content has not changed. "ETag" can be seen as a more sophisticated successor to
|
|
the `Last-Modified` header.
|
|
|
|
This section describes HTTP caching related options available in Spring Web MVC.
|
|
|
|
|
|
|
|
[[webflux-caching-cachecontrol]]
|
|
=== `CacheControl`
|
|
[.small]#<<web.adoc#mvc-caching-cachecontrol,Same in Spring MVC>>#
|
|
|
|
{api-spring-framework}/http/CacheControl.html[`CacheControl`] provides support for
|
|
configuring settings related to the "Cache-Control" header and is accepted as an argument
|
|
in a number of places:
|
|
|
|
* <<webflux-caching-etag-lastmodified>>
|
|
* <<webflux-caching-static-resources>>
|
|
|
|
While https://tools.ietf.org/html/rfc7234#section-5.2.2[RFC 7234] describes all possible
|
|
directives for the "Cache-Control" response header, the `CacheControl` type takes a
|
|
use case oriented approach focusing on the common scenarios:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
// Cache for an hour - "Cache-Control: max-age=3600"
|
|
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);
|
|
|
|
// Prevent caching - "Cache-Control: no-store"
|
|
CacheControl ccNoStore = CacheControl.noStore();
|
|
|
|
// Cache for ten days in public and private caches,
|
|
// public caches should not transform the response
|
|
// "Cache-Control: max-age=864000, public, no-transform"
|
|
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();
|
|
----
|
|
|
|
|
|
|
|
[[webflux-caching-etag-lastmodified]]
|
|
=== Controllers
|
|
[.small]#<<web.adoc#mvc-caching-etag-lastmodified,Same in Spring MVC>>#
|
|
|
|
Controllers can add explicit support for HTTP caching. This is recommended since the
|
|
lastModified or ETag value for a resource needs to be calculated before it can be compared
|
|
against conditional request headers. A controller can add an ETag and "Cache-Control"
|
|
settings to a `ResponseEntity`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@GetMapping("/book/{id}")
|
|
public ResponseEntity<Book> showBook(@PathVariable Long id) {
|
|
|
|
Book book = findBook(id);
|
|
String version = book.getVersion();
|
|
|
|
return ResponseEntity
|
|
.ok()
|
|
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
|
|
.eTag(version) // lastModified is also available
|
|
.body(book);
|
|
}
|
|
----
|
|
|
|
This will send an 304 (NOT_MODIFIED) response with an empty body, if the comparison
|
|
to the conditional request headers indicates the content has not changed. Otherwise the
|
|
"ETag" and "Cache-Control" headers will be added to the response.
|
|
|
|
The check against conditional request headers can also be made in the controller:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@RequestMapping
|
|
public String myHandleMethod(ServerWebExchange exchange, Model model) {
|
|
|
|
long eTag = ... <1>
|
|
|
|
if (exchange.checkNotModified(eTag)) {
|
|
return null; <2>
|
|
}
|
|
|
|
model.addAttribute(...); <3>
|
|
return "myViewName";
|
|
}
|
|
----
|
|
|
|
<1> Application-specific calculation.
|
|
<2> Response has been set to 304 (NOT_MODIFIED), no further processing.
|
|
<3> Continue with request processing.
|
|
|
|
There are 3 variants for checking conditional requests against eTag values, lastModified
|
|
values, or both. For conditional "GET" and "HEAD" requests, the response may be set to
|
|
304 (NOT_MODIFIED). For conditional "POST", "PUT", and "DELETE", the response would be set
|
|
to 409 (PRECONDITION_FAILED) instead to prevent concurrent modification.
|
|
|
|
|
|
|
|
[[webflux-caching-static-resources]]
|
|
=== Static resources
|
|
[.small]#<<web.adoc#mvc-caching-static-resources,Same in Spring MVC>>#
|
|
|
|
Static resources should be served with a "Cache-Control" and conditional response headers
|
|
for optimal performance. See section on configuring <<webflux-config-static-resources>>.
|
|
|
|
|
|
|
|
|
|
[[webflux-config]]
|
|
== WebFlux Config
|
|
[.small]#<<web.adoc#mvc-config,Same in Spring MVC>>#
|
|
|
|
The WebFlux Java config declares components required to process requests with annotated
|
|
controllers or functional endpoints, and it offers an API to customize the configuration.
|
|
That means you do not need to understand the underlying beans created by the Java config
|
|
but, if you want to, it's very easy to see them in `WebFluxConfigurationSupport` or read more
|
|
what they are in <<webflux-special-bean-types>>.
|
|
|
|
For more advanced customizations, not available in the configuration API, it is also
|
|
possible to gain full control over the configuration through the
|
|
<<webflux-config-advanced-java>>.
|
|
|
|
|
|
|
|
|
|
[[webflux-config-enable]]
|
|
=== Enable WebFlux config
|
|
[.small]#<<web.adoc#mvc-config-enable,Same in Spring MVC>>#
|
|
|
|
Use the `@EnableWebFlux` annotation in your Java config:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig {
|
|
}
|
|
----
|
|
|
|
The above registers a number of Spring WebFlux
|
|
<<mvc-webflux-special-bean-types,infrastructure beans>> also adapting to dependencies
|
|
available on the classpath -- for JSON, XML, etc.
|
|
|
|
|
|
|
|
[[webflux-config-customize]]
|
|
=== WebFlux config API
|
|
[.small]#<<web.adoc#mvc-config-customize,Same in Spring MVC>>#
|
|
|
|
In your Java config implement the `WebFluxConfigurer` interface:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
// Implement configuration methods...
|
|
|
|
}
|
|
----
|
|
|
|
|
|
|
|
[[webflux-config-conversion]]
|
|
=== Conversion, formatting
|
|
[.small]#<<web.adoc#mvc-config-conversion,Same in Spring MVC>>#
|
|
|
|
By default formatters for `Number` and `Date` types are installed, including support for
|
|
the `@NumberFormat` and `@DateTimeFormat` annotations. Full support for the Joda-Time
|
|
formatting library is also installed if Joda-Time is present on the classpath.
|
|
|
|
To register custom formatters and converters:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void addFormatters(FormatterRegistry registry) {
|
|
// ...
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
[NOTE]
|
|
====
|
|
See <<core.adoc#format-FormatterRegistrar-SPI,FormatterRegistrar SPI>>
|
|
and the `FormattingConversionServiceFactoryBean` for more information on when to use FormatterRegistrars.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-config-validation]]
|
|
=== Validation
|
|
[.small]#<<web.adoc#mvc-config-validation,Same in Spring MVC>>#
|
|
|
|
By default if <<core.adoc#validation-beanvalidation-overview,Bean Validation>> is present
|
|
on the classpath -- e.g. Hibernate Validator, the `LocalValidatorFactoryBean` is registered
|
|
as a global <<core.adoc#validator,Validator>> for use with `@Valid` and `Validated` on
|
|
`@Controller` method arguments.
|
|
|
|
In your Java config, you can customize the global `Validator` instance:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public Validator getValidator(); {
|
|
// ...
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
Note that you can also register ``Validator``'s locally:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Controller
|
|
public class MyController {
|
|
|
|
@InitBinder
|
|
protected void initBinder(WebDataBinder binder) {
|
|
binder.addValidators(new FooValidator());
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
[TIP]
|
|
====
|
|
If you need to have a `LocalValidatorFactoryBean` injected somewhere, create a bean and
|
|
mark it with `@Primary` in order to avoid conflict with the one declared in the MVC config.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-config-content-negotiation]]
|
|
=== Content type resolvers
|
|
[.small]#<<web.adoc#mvc-config-content-negotiation,Same in Spring MVC>>#
|
|
|
|
You can configure how Spring WebFlux determines the requested media types for
|
|
``@Controller``'s from the request. By default only the "Accept" header is checked but you
|
|
can also enable a query parameter based strategy.
|
|
|
|
To customize the requested content type resolution:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
|
|
[[webflux-config-message-codecs]]
|
|
=== HTTP message codecs
|
|
[.small]#<<web.adoc#mvc-config-message-converters,Same in Spring MVC>>#
|
|
|
|
To customize how the request and response body are read and written:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
`ServerCodecConfigurer` provides a set of default readers and writers. You can use it to add
|
|
more readers and writers, customize the default ones, or replace the default ones completely.
|
|
|
|
For Jackson JSON and XML, consider using the
|
|
{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[Jackson2ObjectMapperBuilder]
|
|
which customizes Jackson's default properties with the following ones:
|
|
|
|
. http://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/DeserializationFeature.html#FAIL_ON_UNKNOWN_PROPERTIES[`DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES`] is disabled.
|
|
. http://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/MapperFeature.html#DEFAULT_VIEW_INCLUSION[`MapperFeature.DEFAULT_VIEW_INCLUSION`] is disabled.
|
|
|
|
It also automatically registers the following well-known modules if they are detected on the classpath:
|
|
|
|
. https://github.com/FasterXML/jackson-datatype-jdk7[jackson-datatype-jdk7]: support for Java 7 types like `java.nio.file.Path`.
|
|
. https://github.com/FasterXML/jackson-datatype-joda[jackson-datatype-joda]: support for Joda-Time types.
|
|
. https://github.com/FasterXML/jackson-datatype-jsr310[jackson-datatype-jsr310]: support for Java 8 Date & Time API types.
|
|
. https://github.com/FasterXML/jackson-datatype-jdk8[jackson-datatype-jdk8]: support for other Java 8 types like `Optional`.
|
|
|
|
|
|
|
|
[[webflux-config-view-resolvers]]
|
|
=== View resolvers
|
|
[.small]#<<web.adoc#mvc-config-view-resolvers,Same in Spring MVC>>#
|
|
|
|
To configure view resolution:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void configureViewResolvers(ViewResolverRegistry registry) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
The `ViewResolverRegistry` has shortcuts for view technologies that the Spring Framework
|
|
integrates with. Here is an example with FreeMarker which also requires configuring the
|
|
underlying FreeMarker view technology:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
|
|
@Override
|
|
public void configureViewResolvers(ViewResolverRegistry registry) {
|
|
registry.freeMarker();
|
|
}
|
|
|
|
// Configure Freemarker...
|
|
|
|
@Bean
|
|
public FreeMarkerConfigurer freeMarkerConfigurer() {
|
|
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
|
|
configurer.setTemplateLoaderPath("classpath:/templates");
|
|
return configurer;
|
|
}
|
|
}
|
|
----
|
|
|
|
You can also plug in any `ViewResolver` implementation:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
|
|
@Override
|
|
public void configureViewResolvers(ViewResolverRegistry registry) {
|
|
ViewResolver resolver = ... ;
|
|
registry.viewResolver(resolver);
|
|
}
|
|
}
|
|
----
|
|
|
|
To support <<webflux-multiple-representations>> and rendering other formats
|
|
through view resolution, besides HTML, you can configure one or more default views based
|
|
on the `HttpMessageWriterView` implementation which accepts any of the available
|
|
<<webflux-codecs>> from `spring-web`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
|
|
@Override
|
|
public void configureViewResolvers(ViewResolverRegistry registry) {
|
|
registry.freeMarker();
|
|
|
|
Jackson2JsonEncoder encoder = new Jackson2JsonEncoder();
|
|
registry.defaultViews(new HttpMessageWriterView(encoder));
|
|
}
|
|
|
|
// ...
|
|
}
|
|
----
|
|
|
|
See <<webflux-view>> for more on the view technologies integrated with Spring WebFlux.
|
|
|
|
|
|
|
|
|
|
[[webflux-config-static-resources]]
|
|
=== Static resources
|
|
[.small]#<<web.adoc#mvc-config-static-resources,Same in Spring MVC>>#
|
|
|
|
This option provides a convenient way to serve static resources from a list of
|
|
{api-spring-framework}/core/io/Resource.html[Resource]-based locations.
|
|
|
|
In the example below, given a request that starts with `"/resources"`, the relative path is
|
|
used to find and serve static resources relative to `"/static"` on the classpath. Resources
|
|
will be served with a 1-year future expiration to ensure maximum use of the browser cache
|
|
and a reduction in HTTP requests made by the browser. The `Last-Modified` header is also
|
|
evaluated and if present a `304` status code is returned.
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
|
registry.addResourceHandler("/resources/**")
|
|
.addResourceLocations("/public", "classpath:/static/")
|
|
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
// TODO: See also <<webflux-caching-static-resources, HTTP caching support for static resources>>.
|
|
|
|
The resource handler also supports a chain of
|
|
{api-spring-framework}/web/reactive/resource/ResourceResolver.html[ResourceResolver]'s and
|
|
{api-spring-framework}/web/reactive/resource/ResourceTransformer.html[ResourceTransformer]'s.
|
|
which can be used to create a toolchain for working with optimized resources.
|
|
|
|
The `VersionResourceResolver` can be used for versioned resource URLs based on an MD5 hash
|
|
computed from the content, a fixed application version, or other. A
|
|
`ContentVersionStrategy` (MD5 hash) is a good choice with some notable exceptions such as
|
|
JavaScript resources used with a module loader.
|
|
|
|
For example in your Java config;
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
|
registry.addResourceHandler("/resources/**")
|
|
.addResourceLocations("/public/")
|
|
.resourceChain(true)
|
|
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
You can use `ResourceUrlProvider` to rewrite URLs and apply the full chain of resolvers and
|
|
transformers -- e.g. to insert versions. The WebFlux config provides a `ResourceUrlProvider`
|
|
so it can be injected into others.
|
|
|
|
Unlike Spring MVC at present in WebFlux there is no way to transparently rewrite static
|
|
resource URLs since there are no view technologies that can make use of a non-blocking chain
|
|
of resolvers and transformers. When serving only local resources the workaround is to use
|
|
`ResourceUrlProvider` directly (e.g. through a custom tag) and block.
|
|
|
|
Note that when using both `EncodedResourceResolver` (e.g. Gzip, Brotli encoded) and
|
|
`VersionedResourceResolver`, they must be registered in that order to ensure content
|
|
based versions are always computed reliably based on the unencoded file.
|
|
|
|
http://www.webjars.org/documentation[WebJars] is also supported via `WebJarsResourceResolver`
|
|
and automatically registered when `"org.webjars:webjars-locator"` is present on the
|
|
classpath. The resolver can re-write URLs to include the version of the jar and can also
|
|
match to incoming URLs without versions -- e.g. `"/jquery/jquery.min.js"` to
|
|
`"/jquery/1.2.0/jquery.min.js"`.
|
|
|
|
|
|
|
|
[[webflux-config-path-matching]]
|
|
=== Path Matching
|
|
[.small]#<<web.adoc#mvc-config-path-matching,Same in Spring MVC>>#
|
|
|
|
Customize options related to path matching. For details on the individual options, see the
|
|
{api-spring-framework}/web/reactive/config/PathMatchConfigurer.html[PathMatchConfigurer] Javadoc.
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void configurePathMatch(PathMatchConfigurer configurer) {
|
|
configurer
|
|
.setUseCaseSensitiveMatch(true)
|
|
.setUseTrailingSlashMatch(false)
|
|
.addPathPrefix("/api",
|
|
HandlerTypePredicate.forAnnotation(RestController.class));
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
[TIP]
|
|
====
|
|
Spring WebFlux relies on a parsed representation of the request path called
|
|
`RequestPath` for access to decoded path segment values, with semicolon content removed
|
|
(i.e. path/matrix variables). That means, unlike Spring MVC, there is no need to indicate
|
|
neither whether to decode the request path, nor whether to remove semicolon content for
|
|
path matching purposes.
|
|
|
|
Spring WebFlux also does not support suffix pattern matching, unlike Spring MVC, where we
|
|
also <<web.adoc#mvc-ann-requestmapping-suffix-pattern-match,recommend>> moving away from
|
|
reliance on it.
|
|
====
|
|
|
|
|
|
|
|
[[webflux-config-advanced-java]]
|
|
=== Advanced config mode
|
|
[.small]#<<web.adoc#mvc-config-advanced-java,Same in Spring MVC>>#
|
|
|
|
`@EnableWebFlux` imports `DelegatingWebFluxConfiguration` that (1) provides default
|
|
Spring configuration for WebFlux applications and (2) detects and delegates to
|
|
``WebFluxConfigurer``'s to customize that configuration.
|
|
|
|
For advanced mode, remove `@EnableWebFlux` and extend directly from
|
|
`DelegatingWebFluxConfiguration` instead of implementing `WebFluxConfigurer`:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
public class WebConfig extends DelegatingWebFluxConfiguration {
|
|
|
|
// ...
|
|
|
|
}
|
|
----
|
|
|
|
You can keep existing methods in `WebConfig` but you can now also override bean declarations
|
|
from the base class and you can still have any number of other ``WebMvcConfigurer``'s on
|
|
the classpath.
|
|
|
|
|
|
|
|
|
|
[[webflux-http2]]
|
|
== HTTP/2
|
|
[.small]#<<web.adoc#mvc-http2,Same in Spring MVC>>#
|
|
|
|
Servlet 4 containers are required to support HTTP/2 and Spring Framework 5 is compatible
|
|
with Servlet API 4. From a programming model perspective there is nothing specific that
|
|
applications need to do. However there are considerations related to server configuration.
|
|
For more details please check out the
|
|
https://github.com/spring-projects/spring-framework/wiki/HTTP-2-support[HTTP/2 wiki page].
|
|
|
|
Currently Spring WebFlux does not support HTTP/2 with Netty. There is also no support for
|
|
pushing resources programmatically to the client.
|