2492 lines
87 KiB
Plaintext
2492 lines
87 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]]
|
|
=== Why a new web framework?
|
|
|
|
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]]
|
|
=== Reactive: what and why?
|
|
|
|
We touched on non-blocking and functional but why reactive and what do we 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 http://download.java.net/java/jdk9/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]]
|
|
=== Choosing a web framework
|
|
|
|
Should you use Spring MVC or WebFlux? Let's cover a few different perspectives.
|
|
|
|
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 is not an oxymoron) and its effects.
|
|
The tag line is "scale with less hardware" but that effect is not guaranteed, not without
|
|
some network I/O that can be slow or unpredictable. This Netflix
|
|
https://medium.com/netflix-techblog/zuul-2-the-netflix-journey-to-asynchronous-non-blocking-systems-45947377fb5c[blog post]
|
|
is a good resource.
|
|
|
|
|
|
|
|
[[webflux-server-choice]]
|
|
=== Choosing a server
|
|
|
|
Spring WebFlux is supported on Netty, Undertow, Tomcat, Jetty, and Servlet 3.1+ containers.
|
|
Each server is adapted to a common Reactive Streams API. The Spring WebFlux programming
|
|
models are built on that common API.
|
|
|
|
[NOTE]
|
|
====
|
|
*Common question: how can Tomcat and Jetty be used in both stacks?* +
|
|
Tomcat and Jetty are non-blocking at their core. It's the Servlet API that adds a
|
|
blocking facade. Starting in version 3.1 the Servlet API adds a choice for non-blocking I/O.
|
|
However its use requires care to avoid other synchronous and blocking parts. For this
|
|
reason Spring's reactive web stack has a low-level Servlet adapter to bridge to Reactive
|
|
Streams but the Servlet API is otherwise not exposed for direct use.
|
|
====
|
|
|
|
Spring Boot 2 uses Netty by default with WebFlux because Netty is more widely used in the
|
|
async, non-blocking space and also provides both client and server that can share resources.
|
|
By comparison Servlet 3.1 non-blocking I/O hasn't seen much use because the bar to use it
|
|
is so high. Spring WebFlux opens one practical path to adoption.
|
|
|
|
The default server choice in Spring Boot is mainly about the out-of-the-box experience.
|
|
Applications can still choose any of the other supported servers which are also highly
|
|
optimized for performance, fully non-blocking, and adapted to Reactive Streams back
|
|
pressure. In Spring Boot it is trivial to make the switch.
|
|
|
|
|
|
|
|
[[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-reactive-spring-web]]
|
|
== Reactive Spring Web
|
|
|
|
The `spring-web` module provides low level infrastructure and HTTP abstractions -- client
|
|
and server, to build reactive web applications. All public APIs are build around Reactive
|
|
Streams with Reactor as a backing implementation.
|
|
|
|
Server support is organized in two layers:
|
|
|
|
* <<webflux-httphandler,HttpHandler>> and server adapters -- the most basic, common API
|
|
for HTTP request handling with Reactive Streams back pressure.
|
|
* <<webflux-web-handler-api,WebHandler API>> -- slightly higher level but still general
|
|
purpose server web API with filter chain style processing.
|
|
|
|
|
|
|
|
[[webflux-httphandler]]
|
|
=== HttpHandler
|
|
|
|
Every HTTP server has some API for HTTP request handling.
|
|
{api-spring-framework}/http/server/reactive/HttpHandler.html[HttpHandler]
|
|
is a simple contract with one method to handle a request and response.
|
|
It is intentionally minimal. Its main purpose is to provide a common, Reactive Streams
|
|
based API for HTTP request handling over different servers.
|
|
|
|
The `spring-web` module contains adapters for every supported server. The table below shows
|
|
the server APIs are used and where Reactive Streams support comes from:
|
|
|
|
[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
|
|
|===
|
|
|
|
Here are required dependencies,
|
|
https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-the-Spring-Framework[supported versions],
|
|
and code snippets for each server:
|
|
|
|
|===
|
|
|Server name|Group id|Artifact name
|
|
|
|
|Reactor Netty
|
|
|io.projectreactor.ipc
|
|
|reactor-netty
|
|
|
|
|Undertow
|
|
|io.undertow
|
|
|undertow-core
|
|
|
|
|Tomcat
|
|
|org.apache.tomcat.embed
|
|
|tomcat-embed-core
|
|
|
|
|Jetty
|
|
|org.eclipse.jetty
|
|
|jetty-server, jetty-servlet
|
|
|===
|
|
|
|
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();
|
|
----
|
|
|
|
[NOTE]
|
|
====
|
|
To deploy as a WAR to a Servlet 3.1+ container, wrap `HttpHandler` with
|
|
`ServletHttpHandlerAdapter` and register that as a `Servlet`.
|
|
This can be automated through the use of
|
|
{api-spring-framework}/web/server/adapter/AbstractReactiveWebInitializer.html[AbstractReactiveWebInitializer].
|
|
====
|
|
|
|
|
|
|
|
[[webflux-web-handler-api]]
|
|
=== WebHandler API
|
|
|
|
`HttpHandler` is the lowest level contract for running on different HTTP servers.
|
|
On top of that foundation, the WebHandler API provides a slightly higher level, but
|
|
still general purpose, set of components that form a chain of
|
|
{api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler's],
|
|
{api-spring-framework}/web/server/WebFilter.html[WebFilter's], and a
|
|
{api-spring-framework}/web/server/WebHandler.html[WebHandler].
|
|
|
|
All WebHandler API components take `ServerWebExchange` as input which goes beyond
|
|
`ServerHttpRequest` and `ServerHttpResponse` to provide extra building blocks for
|
|
use in web applications such as request attributes, session attributes, access to parsed
|
|
form data, multipart data, and more.
|
|
|
|
`WebHttpHandlerBuilder` is used to assemble a request processing chain. You can use
|
|
methods on the builder to add components manually, or more likely have them detected from
|
|
a Spring `ApplicationContext`, with the resulting `HttpHandler` ready to run via a
|
|
<<webflux-httphandler,server adapter>>:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
ApplicationContext context = ...
|
|
HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build()
|
|
----
|
|
|
|
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
|
|
|Exception handlers to apply after all ``WebFilter``'s and the target `WebHandler`.
|
|
|
|
|<any>
|
|
|`WebFilter`
|
|
|0..N
|
|
|Filters to invoke before and after the target `WebHandler`.
|
|
|
|
|"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.
|
|
|===
|
|
|
|
|
|
[[webflux-form-data]]
|
|
==== Form Reader
|
|
|
|
`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 Reader
|
|
[.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-codecs]]
|
|
=== HTTP Message Codecs
|
|
[.small]#<<integration.adoc#rest-message-conversion,Same in Spring MVC>>#
|
|
|
|
The `spring-web` module defines the
|
|
{api-spring-framework}/http/codec/HttpMessageReader.html[HttpMessageReader] and
|
|
{api-spring-framework}/http/codec/HttpMessageWriter.html[HttpMessageWriter] contracts
|
|
for encoding and decoding the body of HTTP requests and responses via Rective Streams
|
|
``Publisher``'s. These contacts are used on the client side, e.g. in the `WebClient`,
|
|
and on the server side, e.g. in annotated controllers and functional endpoints.
|
|
|
|
The `spring-core` module defines the
|
|
{api-spring-framework}/core/codec/Encoder.html[Encoder] and
|
|
{api-spring-framework}/core/codec/Decoder.html[Decoder] contracts that are independent of
|
|
HTTP and rely on the {api-spring-framework}/core/io/buffer/DataBuffer.html[DataBuffer]
|
|
contract that abstracts different byte buffer representations such as the Netty `ByteBuf`
|
|
and `java.nio.ByteBuffer` (see <<core#databuffers, Data Buffers and Codecs>>).
|
|
An `Encoder` can be wrapped with `EncoderHttpMessageWriter` to be used as an
|
|
`HttpMessageWriter` while a `Decoder` can be wrapped with `DecoderHttpMessageReader` to
|
|
be used as an `HttpMessageReader`.
|
|
|
|
The `spring-core` module contains basic `Encoder` and `Decoder` implementations for
|
|
`byte[]`, `ByteBuffer`, `DataBuffer`, `Resource`, and `String`. The `spring-web` module
|
|
adds ``Encoder``'s and ``Decoder``'s for Jackson JSON, Jackson Smile, and JAXB2.
|
|
The `spring-web` module also contains some web-specific readers and writers for
|
|
server-sent events, form data, and multipart requests.
|
|
|
|
To configure or customize the readers and writers to use applications will typically use
|
|
`ClientCodecConfigurer` or `ServerCodecConfigurer`.
|
|
|
|
|
|
|
|
[[webflux-codecs-jackson-json]]
|
|
==== Jackson JSON
|
|
|
|
The decoder relies on Jackson's non-blocking, byte array parser to parse a stream of byte
|
|
chunks into a `TokenBuffer` stream, which can then be turned into Objects with Jackson's
|
|
`ObjectMapper`.
|
|
|
|
The encoder processes a `Publisher<?>` as follows:
|
|
|
|
* if the `Publisher` is a `Mono` (i.e. single value), the value is encoded to JSON.
|
|
* if media type is `application/stream+json`, each value produced by the
|
|
`Publisher` is encoded individually to JSON followed by a new line.
|
|
* otherwise all items from the `Publisher` are gathered in with `Flux#collectToList()`
|
|
and the resulting collection is encoded as a JSON 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-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,WebHandler 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 one of the framework contracts listed in the table below.
|
|
Spring WebFlux provides built-in implementations of these contracts but you can also
|
|
customize, extend, or replace them.
|
|
|
|
[[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` based on
|
|
`@RequestMapping` annotated methods, `RouterFunctionMapping` based on functional
|
|
endpoint routes, and `SimpleUrlHandlerMapping` based on explicit registrations of URI
|
|
path patterns to handlers.
|
|
|
|
| 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.
|
|
|
|
The built-in `HandlerResultHandler` implementations are `ResponseEntityResultHandler`
|
|
supporting `ResponseEntity` return values, `ResponseBodyResultHandler`
|
|
supporting `@ResponseBody` methods, `ServerResponseResultHandler`
|
|
supporting the `ServerResponse` returned from functional endpoints, and
|
|
`ViewResolutionResultHandler` supporting rendering with a view and a model.
|
|
|===
|
|
|
|
|
|
|
|
[[webflux-framework-config]]
|
|
=== Framework Config
|
|
[.small]#<<web.adoc#mvc-servlet-config,Same in Spring MVC>>#
|
|
|
|
The `DispatcherHandler` detects the special beans it needs in the `ApplicationContext`.
|
|
Applications can declare the special beans they wish to have. However most applications
|
|
will find a better starting point in the WebFlux Java config which provide a higher level
|
|
configuration API that in turn make the necessary bean declarations.
|
|
See <<webflux-config>> for more details.
|
|
|
|
|
|
|
|
[[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-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 composed annotation that is itself annotated with
|
|
`@Controller` and `@ResponseBody` indicating a controller whose every method inherits the type-level
|
|
`@ResponseBody` annotation and therefore writes to the response body (vs model-and-vew
|
|
rendering).
|
|
|
|
|
|
|
|
[[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 shortcut variants are
|
|
https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model#composed-annotations[composed annotations]
|
|
-- themselves annotated with `@RequestMapping`. They are commonly used at the method level.
|
|
At the class level an `@RequestMapping` is more useful for expressing shared 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
|
|
will overrides rather than extend the class level declaration.
|
|
|
|
[TIP]
|
|
====
|
|
`MediaType` provides constants for commonly used media types -- e.g.
|
|
`APPLICATION_JSON_VALUE`, `APPLICATION_JSON_UTF8_VALUE`.
|
|
====
|
|
|
|
|
|
[[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".
|
|
|
|
You can declare a shared produces attribute at the class level. Unlike most other request
|
|
mapping attributes however when used at the class level, a method-level produces attribute
|
|
will overrides rather than extend the class level declaration.
|
|
|
|
[TIP]
|
|
====
|
|
`MediaType` provides constants for commonly used media types -- e.g.
|
|
`APPLICATION_JSON_VALUE`, `APPLICATION_JSON_UTF8_VALUE`.
|
|
====
|
|
|
|
|
|
[[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-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 forcing 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 6+: `java.util.TimeZone` +
|
|
Java 8+: `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.
|
|
<<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, context path, and
|
|
the literal part of the servlet mapping also taking into account `Forwarded` and
|
|
`X-Forwarded-*` headers.
|
|
// 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. Reactive types --
|
|
Reactor, RxJava, <<webflux-reactive-libraries,or other>> are 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 paramater" concept that conflate 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 paramerters, 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 an `java.util.Optional`
|
|
wrapper.
|
|
|
|
Type conversion is applied automatically if the target method parameter type is not
|
|
`String`. See <<mvc-ann-typeconversion>>.
|
|
|
|
When an `@RequestParam` annotation is declared as `Map<String, String>` or
|
|
`MultiValueMap<String, String>` argument, the map is populated with all query parameters.
|
|
|
|
Note that use of `@RequestParam` is optional, e.g. to set its attributes.
|
|
By default any argument that is a simple value type, as determined by
|
|
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty],
|
|
and is not resolved by any other argument resolver, is treated as if it was annotated
|
|
with `@RequestParam`.
|
|
|
|
|
|
[[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 the "meta-data" part with `@RequestPart` which would deserialize it from
|
|
JSON (similar to `@RequestBody`) through one of the configured <<webflux-codecs>>:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@PostMapping("/")
|
|
public String handle(**@RequestPart("meta-data") MetaData metadata,
|
|
@RequestPart("file-data") FilePart file**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
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**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
`@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**) {
|
|
// ...
|
|
}
|
|
----
|
|
|
|
|
|
[[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 on JSON rendering see <<webflux-codecs-jackson-json>>.
|
|
|
|
`@ResponseBody` methods can be combined with JSON serialization views.
|
|
See <<mvc-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 Methods
|
|
[.small]#<<web.adoc#mvc-ann-modelattrib-methods,Same in Spring MVC>>#
|
|
|
|
The `@ModelAttribute` annotation can be used on `@RequestMapping`
|
|
<<webflux-ann-modelattrib-method-args,method arguments>> to create or access an Object
|
|
from the model and bind it to the request. `@ModelAttribute` can also be used as a
|
|
method-level annotation on controller methods whose purpose is not to handle requests
|
|
but to add commonly needed model attributes prior to request handling.
|
|
|
|
A controller can have any number of `@ModelAttribute` methods. All such methods are
|
|
invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute`
|
|
method can also be shared across controllers via `@ControllerAdvice`. See the section on
|
|
<<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]]
|
|
=== Binder Methods
|
|
[.small]#<<web.adoc#mvc-ann-initbinder,Same in Spring MVC>>#
|
|
|
|
`@InitBinder` methods in an `@Controller` or `@ControllerAdvice` class can be used to
|
|
customize type conversion for method arguments that represent String-based request values
|
|
(e.g. request parameters, path variables, headers, cookies, and others). Type conversion
|
|
also applies during data binding of request parameters onto `@ModelAttribute` arguments
|
|
(i.e. command objects).
|
|
|
|
`@InitBinder` methods can register controller-specific `java.bean.PropertyEditor`, or
|
|
Spring `Converter` and `Formatter` components. In addition, the
|
|
<<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-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]
|
|
|
|
|
|
|
|
|
|
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]
|
|
|
|
|
|
|
|
|
|
[[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) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
Note that FreeMarker also requires configuration of the underlying view technology:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
// ...
|
|
|
|
@Bean
|
|
public FreeMarkerConfigurer freeMarkerConfigurer() {
|
|
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
|
|
configurer.setTemplateLoaderPath("classpath:/templates");
|
|
return configurer;
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
|
|
|
|
[[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/")
|
|
.setCachePeriod(31556926);
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
// 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 transparely rewrite static
|
|
resource URLs since the are no view technologies that can make use of a non-blocking chain
|
|
of resolvers and transformers (e.g. resources on Amazon S3). When serving only local
|
|
resources the workaround is to use `ResourceUrlProvider` directly (e.g. through a custom
|
|
tag) and block for 0 seconds.
|
|
|
|
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>>#
|
|
|
|
Spring WebFlux uses parsed representation of path patterns -- i.e. `PathPattern`, and also
|
|
the incoming request path -- i.e. `RequestPath`, which eliminates the need to indicate
|
|
whether to decode the request path, or remove semicolon content, since `PathPattern`
|
|
can now access decoded path segment values and match safely.
|
|
|
|
Spring WebFlux also does not support suffix pattern matching so effectively there are only two
|
|
minor options to customize related to path matching -- whether to match trailing slashes
|
|
(`true` by default) and whether the match is case-sensitive (`false`).
|
|
|
|
To customize those options:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@Configuration
|
|
@EnableWebFlux
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
@Override
|
|
public void configurePathMatch(PathMatchConfigurer configurer) {
|
|
// ...
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
|
|
|
|
[[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.
|