2017-08-17 18:45:11 +08:00
|
|
|
[[webflux-fn]]
|
2017-09-14 09:02:28 +08:00
|
|
|
= Functional Endpoints
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
Spring WebFlux includes a lightweight functional programming model in which functions
|
2018-01-11 05:00:50 +08:00
|
|
|
are used to route and handle requests and contracts are designed for immutability.
|
2018-09-17 22:36:43 +08:00
|
|
|
It is an alternative to the annotation-based programming model but otherwise runs on
|
|
|
|
the same <<web-reactive.adoc#webflux-reactive-spring-web>> foundation.
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2017-10-19 02:24:17 +08:00
|
|
|
|
|
|
|
|
2018-03-10 08:57:20 +08:00
|
|
|
[[webflux-fn-overview]]
|
|
|
|
== Overview
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
An HTTP request is handled with a `HandlerFunction` that takes `ServerRequest` and
|
|
|
|
returns `Mono<ServerResponse>`, both of which are immutable contracts that offer
|
|
|
|
JDK 8-friendly access to the HTTP request and response. `HandlerFunction` is the equivalent of
|
|
|
|
a `@RequestMapping` method in the annotation-based programming model.
|
2018-03-10 08:57:20 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
Requests are routed to a `HandlerFunction` with a `RouterFunction` that takes
|
2018-03-10 08:57:20 +08:00
|
|
|
`ServerRequest` and returns `Mono<HandlerFunction>`. When a request is matched to a
|
|
|
|
particular route, the `HandlerFunction` mapped to the route is used. `RouterFunction` is
|
2018-09-17 22:36:43 +08:00
|
|
|
the equivalent of a `@RequestMapping` annotation.
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
`RouterFunctions.route(RequestPredicate, HandlerFunction)` provides a router function
|
2018-09-17 22:36:43 +08:00
|
|
|
default implementation that can be used with a number of built-in request predicates,
|
|
|
|
as the following example shows:
|
2018-03-10 08:57:20 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2018-03-10 08:57:20 +08:00
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
|
|
|
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
|
2018-05-31 21:10:46 +08:00
|
|
|
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
PersonRepository repository = ...
|
|
|
|
PersonHandler handler = new PersonHandler(repository);
|
|
|
|
|
|
|
|
RouterFunction<ServerResponse> route =
|
|
|
|
route(GET("/person/{id}").and(accept(APPLICATION_JSON)), handler::getPerson)
|
|
|
|
.andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople)
|
|
|
|
.andRoute(POST("/person"), handler::createPerson);
|
|
|
|
|
|
|
|
|
|
|
|
public class PersonHandler {
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
public Mono<ServerResponse> listPeople(ServerRequest request) {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
public Mono<ServerResponse> createPerson(ServerRequest request) {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
public Mono<ServerResponse> getPerson(ServerRequest request) {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
----
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
One way to run a `RouterFunction` is to turn it into an `HttpHandler` and install it
|
|
|
|
through one of the built-in <<web-reactive.adoc#webflux-httphandler,server adapters>>:
|
|
|
|
|
|
|
|
* `RouterFunctions.toHttpHandler(RouterFunction)`
|
|
|
|
* `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)`
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
Most applications can run through the WebFlux Java configuration, see <<webflux-fn-running>>.
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-17 18:45:11 +08:00
|
|
|
[[webflux-fn-handler-functions]]
|
2017-09-14 09:02:28 +08:00
|
|
|
== HandlerFunction
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
`ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK 8-friendly
|
2018-03-10 08:57:20 +08:00
|
|
|
access to the HTTP request and response with
|
|
|
|
http://www.reactive-streams.org[Reactive Streams] back pressure against the request
|
|
|
|
and response body stream. The request body is represented with a Reactor `Flux` or `Mono`.
|
|
|
|
The response body is represented with any Reactive Streams `Publisher`, including `Flux`
|
2018-09-17 22:36:43 +08:00
|
|
|
and `Mono`. For more on that, see
|
2018-03-10 08:57:20 +08:00
|
|
|
<<web-reactive.adoc#webflux-reactive-libraries,Reactive Libraries>>.
|
|
|
|
|
|
|
|
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-03-10 08:57:20 +08:00
|
|
|
[[webflux-fn-request]]
|
2018-09-17 22:36:43 +08:00
|
|
|
=== Using `ServerRequest`
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
`ServerRequest` provides access to the HTTP method, URI, headers, and query parameters,
|
2018-03-10 10:14:07 +08:00
|
|
|
while access to the body is provided through the `body` methods.
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The following example extracts the request body to a `Mono<String>`:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
Mono<String> string = request.bodyToMono(String.class);
|
|
|
|
----
|
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The following example extracts the body to a `Flux<Person>`, where `Person` objects are decoded from some
|
2018-03-10 10:14:07 +08:00
|
|
|
serialized form, such as JSON or XML:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
Flux<Person> people = request.bodyToFlux(Person.class);
|
|
|
|
----
|
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The preceding examples are shortcuts that use the more general `ServerRequest.body(BodyExtractor)`,
|
|
|
|
which accepts the `BodyExtractor` functional strategy interface. The utility class
|
|
|
|
`BodyExtractors` provides access to a number of instances. For example, the preceding examples can
|
2018-03-10 10:14:07 +08:00
|
|
|
also be written as follows:
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
Mono<String> string = request.body(BodyExtractors.toMono(String.class));
|
|
|
|
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
|
|
|
|
----
|
|
|
|
====
|
2018-03-10 10:14:07 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The following example shows how to access form data:
|
2018-03-10 10:14:07 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
Mono<MultiValueMap<String, String> map = request.body(BodyExtractors.toFormData());
|
|
|
|
----
|
|
|
|
====
|
2018-03-10 10:14:07 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The following example shows how to access multipart data as a map:
|
2018-03-10 10:14:07 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
Mono<MultiValueMap<String, Part> map = request.body(BodyExtractors.toMultipartData());
|
|
|
|
----
|
|
|
|
====
|
2018-03-10 10:14:07 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The following example shows how to access multiparts, one at a time, in streaming fashion:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
Flux<Part> parts = request.body(BodyExtractos.toParts());
|
|
|
|
----
|
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
|
|
|
|
[[webflux-fn-response]]
|
2018-09-17 22:36:43 +08:00
|
|
|
=== Using `ServerResponse`
|
2018-03-10 08:57:20 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
`ServerResponse` provides access to the HTTP response and, since it is immutable, you can use
|
|
|
|
a `build` method to create it. You can use the builder to set the response status, to add response
|
|
|
|
headers, or to provide a body. The following example creates a 200 (OK) response with JSON
|
2018-03-10 08:57:20 +08:00
|
|
|
content:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
Mono<Person> person = ...
|
|
|
|
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
|
|
|
|
----
|
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The following example shows how to build a 201 (CREATED) response with a `Location` header and no body:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
|
|
|
[source,java]
|
|
|
|
----
|
|
|
|
URI location = ...
|
|
|
|
ServerResponse.created(location).build();
|
|
|
|
----
|
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
|
|
|
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
[[webflux-fn-handler-classes]]
|
|
|
|
=== Handler Classes
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
We can write a handler function as a lambda, as the following example shows:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
HandlerFunction<ServerResponse> helloWorld =
|
|
|
|
request -> ServerResponse.ok().body(fromObject("Hello World"));
|
|
|
|
----
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
That is convenient, but, in an application, we need multiple functions, and it is useful to group
|
|
|
|
related handler functions together into a handler (like a `@Controller`). For example,
|
|
|
|
the following class exposes a reactive `Person` repository:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
2018-03-10 08:57:20 +08:00
|
|
|
import static org.springframework.web.reactive.function.ServerResponse.ok;
|
2017-02-28 18:48:46 +08:00
|
|
|
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
|
|
|
|
|
|
|
|
public class PersonHandler {
|
|
|
|
|
|
|
|
private final PersonRepository repository;
|
|
|
|
|
|
|
|
public PersonHandler(PersonRepository repository) {
|
|
|
|
this.repository = repository;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Mono<ServerResponse> listPeople(ServerRequest request) { // <1>
|
|
|
|
Flux<Person> people = repository.allPeople();
|
2018-03-10 08:57:20 +08:00
|
|
|
return ok().contentType(APPLICATION_JSON).body(people, Person.class);
|
2017-02-28 18:48:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public Mono<ServerResponse> createPerson(ServerRequest request) { // <2>
|
|
|
|
Mono<Person> person = request.bodyToMono(Person.class);
|
2018-03-10 08:57:20 +08:00
|
|
|
return ok().build(repository.savePerson(person));
|
2017-02-28 18:48:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public Mono<ServerResponse> getPerson(ServerRequest request) { // <3>
|
|
|
|
int personId = Integer.valueOf(request.pathVariable("id"));
|
2018-03-10 08:57:20 +08:00
|
|
|
return repository.getPerson(personId)
|
|
|
|
.flatMap(person -> ok().contentType(APPLICATION_JSON).body(fromObject(person)))
|
|
|
|
.switchIfEmpty(ServerResponse.notFound().build());
|
2017-02-28 18:48:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
----
|
|
|
|
<1> `listPeople` is a handler function that returns all `Person` objects found in the repository as
|
|
|
|
JSON.
|
|
|
|
<2> `createPerson` is a handler function that stores a new `Person` contained in the request body.
|
2018-09-17 22:36:43 +08:00
|
|
|
Note that `PersonRepository.savePerson(Person)` returns `Mono<Void>`: an empty `Mono` that emits
|
2017-02-28 18:48:46 +08:00
|
|
|
a completion signal when the person has been read from the request and stored. So we use the
|
2018-09-17 22:36:43 +08:00
|
|
|
`build(Publisher<Void>)` method to send a response when that completion signal is received (that is,
|
|
|
|
when the `Person` has been saved).
|
|
|
|
<3> `getPerson` is a handler function that returns a single person, identified by the `id` path
|
|
|
|
variable. We retrieve that `Person` from the repository and create a JSON response, if it is
|
2017-07-04 19:47:05 +08:00
|
|
|
found. If it is not found, we use `switchIfEmpty(Mono<T>)` to return a 404 Not Found response.
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-10-19 02:24:17 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-17 18:45:11 +08:00
|
|
|
[[webflux-fn-router-functions]]
|
2018-09-17 22:36:43 +08:00
|
|
|
== Using `RouterFunction`
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-03-10 08:57:20 +08:00
|
|
|
`RouterFunction` is used to route requests to a `HandlerFunction`. Typically, you do not
|
|
|
|
write router functions yourself, but rather use
|
|
|
|
`RouterFunctions.route(RequestPredicate, HandlerFunction)`. If the predicate applies, the
|
2018-09-17 22:36:43 +08:00
|
|
|
request is routed to the given `HandlerFunction`. Otherwise, no routing is performed,
|
2018-03-10 08:57:20 +08:00
|
|
|
and that would translate to a 404 (Not Found) response.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[webflux-fn-predicates]]
|
2018-09-17 22:36:43 +08:00
|
|
|
=== Using Predicates
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
You can write your own `RequestPredicate`, but the `RequestPredicates` utility class
|
2018-09-17 22:36:43 +08:00
|
|
|
offers commonly used implementations, based on the request path, HTTP method, content-type,
|
|
|
|
and so on. The following example creates a request predicate based on a path:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
----
|
2018-03-10 08:57:20 +08:00
|
|
|
RouterFunction<ServerResponse> route =
|
2017-02-28 18:48:46 +08:00
|
|
|
RouterFunctions.route(RequestPredicates.path("/hello-world"),
|
|
|
|
request -> Response.ok().body(fromObject("Hello World")));
|
|
|
|
----
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
You can compose multiple request predicates together by using:
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
* `RequestPredicate.and(RequestPredicate)` -- both must match.
|
2018-09-17 22:36:43 +08:00
|
|
|
* `RequestPredicate.or(RequestPredicate)` -- either can match.
|
2018-03-10 08:57:20 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
Many of the predicates from `RequestPredicates` are composed. For example,
|
2018-03-10 08:57:20 +08:00
|
|
|
`RequestPredicates.GET(String)` is composed from `RequestPredicates.method(HttpMethod)`
|
|
|
|
and `RequestPredicates.path(String)`.
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
You can compose multiple router functions into one, such that they are evaluated in order,
|
|
|
|
and, if the first route does not match, the second is evaluated. You can declare more
|
2018-03-10 08:57:20 +08:00
|
|
|
specific routes before more general ones.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[webflux-fn-routes]]
|
|
|
|
=== Routes
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
You can compose multiple router functions together by using:
|
2018-03-10 08:57:20 +08:00
|
|
|
|
|
|
|
* `RouterFunction.and(RouterFunction)`
|
|
|
|
* `RouterFunction.andRoute(RequestPredicate, HandlerFunction)` -- shortcut for
|
|
|
|
`RouterFunction.and()` with nested `RouterFunctions.route()`.
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-03-10 08:57:20 +08:00
|
|
|
Using composed routes and predicates, we can then declare the following routes, referring
|
2018-09-17 22:36:43 +08:00
|
|
|
to methods in the `PersonHandler` (shown in <<webflux-fn-handler-class>>) through
|
2018-03-10 08:57:20 +08:00
|
|
|
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html[method-references]:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
|
|
|
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
|
|
|
|
|
|
|
|
PersonRepository repository = ...
|
|
|
|
PersonHandler handler = new PersonHandler(repository);
|
|
|
|
|
|
|
|
RouterFunction<ServerResponse> personRoute =
|
|
|
|
route(GET("/person/{id}").and(accept(APPLICATION_JSON)), handler::getPerson)
|
|
|
|
.andRoute(GET("/person").and(accept(APPLICATION_JSON)), handler::listPeople)
|
2018-03-10 08:57:20 +08:00
|
|
|
.andRoute(POST("/person"), handler::createPerson);
|
2017-02-28 18:48:46 +08:00
|
|
|
----
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2017-10-19 02:24:17 +08:00
|
|
|
|
|
|
|
|
2017-09-14 09:02:28 +08:00
|
|
|
[[webflux-fn-running]]
|
2018-09-17 22:36:43 +08:00
|
|
|
== Running a Server
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-01-11 05:00:50 +08:00
|
|
|
How do you run a router function in an HTTP server? A simple option is to convert a router
|
2018-09-17 22:36:43 +08:00
|
|
|
function to an `HttpHandler` by using one of the following:
|
2018-01-11 05:00:50 +08:00
|
|
|
|
|
|
|
* `RouterFunctions.toHttpHandler(RouterFunction)`
|
|
|
|
* `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)`
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
You can then use the returned `HttpHandler` with a number of server adapters by following
|
2018-01-11 05:00:50 +08:00
|
|
|
<<web-reactive.adoc#webflux-httphandler,HttpHandler>> for server-specific instructions.
|
|
|
|
|
|
|
|
A more advanced option is to run with a
|
2018-09-17 22:36:43 +08:00
|
|
|
<<web-reactive.adoc#webflux-dispatcher-handler,`DispatcherHandler`>>-based setup through the
|
|
|
|
<<web-reactive.adoc#webflux-config>>, which uses Spring configuration to declare the
|
|
|
|
components required to process requests. The WebFlux Java configuration declares the following
|
2018-01-12 00:55:37 +08:00
|
|
|
infrastructure components to support functional endpoints:
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
* `RouterFunctionMapping`: Detects one or more `RouterFunction<?>` beans in the Spring
|
|
|
|
configuration, combines them through `RouterFunction.andOther`, and routes requests to the
|
2018-01-12 00:55:37 +08:00
|
|
|
resulting composed `RouterFunction`.
|
2018-09-17 22:36:43 +08:00
|
|
|
* `HandlerFunctionAdapter`: Simple adapter that lets `DispatcherHandler` invoke
|
2018-01-12 00:55:37 +08:00
|
|
|
a `HandlerFunction` that was mapped to a request.
|
2018-09-17 22:36:43 +08:00
|
|
|
* `ServerResponseResultHandler`: Handles the result from the invocation of a
|
2018-01-12 00:55:37 +08:00
|
|
|
`HandlerFunction` by invoking the `writeTo` method of the `ServerResponse`.
|
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The preceding components let functional endpoints fit within the `DispatcherHandler` request
|
|
|
|
processing lifecycle and also (potentially) run side by side with annotated controllers, if
|
|
|
|
any are declared. It is also how functional endpoints are enabled by the Spring Boot WebFlux
|
2018-01-12 00:55:37 +08:00
|
|
|
starter.
|
2018-01-11 05:00:50 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The following example shows a WebFlux Java configuration (see
|
|
|
|
<<web-reactive.adoc#webflux-dispatcher-handler,DispatcherHandler>> for how to run it):
|
2018-01-11 05:00:50 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2018-01-11 05:00:50 +08:00
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
@Configuration
|
|
|
|
@EnableWebFlux
|
|
|
|
public class WebConfig implements WebFluxConfigurer {
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
public RouterFunction<?> routerFunctionA() {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
@Bean
|
|
|
|
public RouterFunction<?> routerFunctionB() {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
|
|
|
|
// configure message conversion...
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-06-27 16:29:54 +08:00
|
|
|
public void addCorsMappings(CorsRegistry registry) {
|
2018-01-11 05:00:50 +08:00
|
|
|
// configure CORS...
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void configureViewResolvers(ViewResolverRegistry registry) {
|
|
|
|
// configure view resolution for HTML rendering...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
----
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
|
|
|
|
2017-10-19 02:24:17 +08:00
|
|
|
|
2017-08-17 18:45:11 +08:00
|
|
|
[[webflux-fn-handler-filter-function]]
|
2018-09-17 22:36:43 +08:00
|
|
|
== Using `HandlerFilterFunction`
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
You can filter routes mapped by a router function by calling
|
2017-02-28 18:48:46 +08:00
|
|
|
`RouterFunction.filter(HandlerFilterFunction)`, where `HandlerFilterFunction` is essentially a
|
2018-09-17 22:36:43 +08:00
|
|
|
function that takes a `ServerRequest` and `HandlerFunction` and returns a `ServerResponse`.
|
|
|
|
The handler function parameter represents the next element in the chain. This is typically the
|
|
|
|
`HandlerFunction` that is routed to, but it can also be another `FilterFunction` if multiple filters
|
2017-02-28 18:48:46 +08:00
|
|
|
are applied.
|
2018-09-17 22:36:43 +08:00
|
|
|
With annotations, you can achieve similar functionality by using `@ControllerAdvice`, a `ServletFilter`, or both.
|
|
|
|
Now we can add a simple security filter to our route, assuming that we have a `SecurityManager` that
|
|
|
|
can determine whether a particular path is allowed. The following example shows how to do so:
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
[source,java,indent=0]
|
|
|
|
[subs="verbatim,quotes"]
|
|
|
|
----
|
|
|
|
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
|
|
|
|
|
|
|
|
SecurityManager securityManager = ...
|
|
|
|
RouterFunction<ServerResponse> route = ...
|
|
|
|
|
|
|
|
RouterFunction<ServerResponse> filteredRoute =
|
2017-12-15 19:03:14 +08:00
|
|
|
route.filter((request, next) -> {
|
2017-02-28 18:48:46 +08:00
|
|
|
if (securityManager.allowAccessTo(request.path())) {
|
|
|
|
return next.handle(request);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return ServerResponse.status(UNAUTHORIZED).build();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
----
|
2018-09-17 22:36:43 +08:00
|
|
|
====
|
2017-02-28 18:48:46 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
The preceding example demonstrates that invoking the `next.handle(ServerRequest)` is optional. We
|
|
|
|
allow only the handler function to be executed when access is allowed.
|
2017-11-15 23:16:01 +08:00
|
|
|
|
2018-09-17 22:36:43 +08:00
|
|
|
NOTE: CORS support for functional endpoints is provided through a dedicated <<webflux-cors-webfilter,`CorsWebFilter`>>.
|