From cdf2ab973783062ba55cc9fbd2ad881eae094647 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 10 Jan 2018 16:00:50 -0500 Subject: [PATCH] Expand docs on WebFlux.fn + @EnableWebFlux Issue: SPR-16360 --- src/docs/asciidoc/web/webflux-functional.adoc | 107 +++++++++++++----- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/src/docs/asciidoc/web/webflux-functional.adoc b/src/docs/asciidoc/web/webflux-functional.adoc index 5f43ee3b54..3507fab8dc 100644 --- a/src/docs/asciidoc/web/webflux-functional.adoc +++ b/src/docs/asciidoc/web/webflux-functional.adoc @@ -1,10 +1,10 @@ [[webflux-fn]] = Functional Endpoints -Spring WebFlux provides a lightweight, functional programming model where functions -are used to route and handle requests and where contracts are designed for immutability. -It is an alternative to the annotated-based programming model but runs on the same -<> foundation +Spring WebFlux includes a lightweight, functional programming model in which functions +are used to route and handle requests and contracts are designed for immutability. +It is an alternative to the annotated-based programming model but otherwise running on +the same <> foundation @@ -13,19 +13,19 @@ It is an alternative to the annotated-based programming model but runs on the sa == HandlerFunction Incoming HTTP requests are handled by a **`HandlerFunction`**, which is essentially a function that -takes a `ServerRequest` and returns a `Mono`. The annotation counterpart to a -handler function is an `@RequestMapping` method. +takes a `ServerRequest` and returns a `Mono`. If you're familiar with the +annotation-based programming model, a handler function is the equivalent of an +`@RequestMapping` method. `ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK-8 friendly access to the underlying HTTP messages with http://www.reactive-streams.org[Reactive Streams] non-blocking back pressure. The request exposes the body as Reactor `Flux` or `Mono` -types; the response accepts any Reactive Streams `Publisher` as body (see -<>). - +types; the response accepts any Reactive Streams `Publisher` as body. The rational for this +is explained in <>. `ServerRequest` gives access to various HTTP request elements: -the method, URI, query parameters, and -- through the separate `ServerRequest.Headers` interface --- the headers. Access to the body is provided through the `body` methods. For instance, this is +the method, URI, query parameters, and headers (via a separate `ServerRequest.Headers` +interface. Access to the body is provided through the `body` methods. For instance, this is how to extract the request body into a `Mono`: Mono string = request.bodyToMono(String.class); @@ -36,11 +36,11 @@ contains JSON, or JAXB if XML). Flux people = request.bodyToFlux(Person.class); -The above -- `bodyToMono` and `bodyToFlux`, are, in fact, convenience methods that use the +The `bodyToMono` and `bodyToFlux` used above are in fact convenience methods that use the generic `ServerRequest.body(BodyExtractor)` method. `BodyExtractor` is a functional strategy interface that allows you to write your own extraction logic, but common `BodyExtractor` instances can be found in the `BodyExtractors` utility class. So, the above -examples can be replaced with: +examples can also be written as follows: Mono string = request.body(BodyExtractors.toMono(String.class); Flux people = request.body(BodyExtractors.toFlux(Person.class); @@ -103,7 +103,7 @@ public class PersonHandler { public Mono getPerson(ServerRequest request) { // <3> int personId = Integer.valueOf(request.pathVariable("id")); Mono notFound = ServerResponse.notFound().build(); - Mono personMono = this.repository.getPerson(personId); + Mono personMono = repository.getPerson(personId); return personMono .flatMap(person -> ServerResponse.ok().contentType(APPLICATION_JSON).body(fromObject(person))) .switchIfEmpty(notFound); @@ -129,8 +129,9 @@ found. If it is not found, we use `switchIfEmpty(Mono)` to return a 404 Not F Incoming requests are routed to handler functions with a **`RouterFunction`**, which is a function that takes a `ServerRequest`, and returns a `Mono`. If a request matches a -particular route, a handler function is returned; otherwise it returns an empty `Mono`. The -`RouterFunction` has a similar purpose as the `@RequestMapping` annotation in `@Controller` classes. +particular route, a handler function is returned, or otherwise an empty `Mono` is returned. +`RouterFunction` has a similar purpose as the `@RequestMapping` annotation in the +annotation-based programming model. Typically, you do not write router functions yourself, but rather use `RouterFunctions.route(RequestPredicate, HandlerFunction)` to @@ -192,17 +193,71 @@ For instance, `RequestPredicates.GET(String)` is a composition of [[webflux-fn-running]] == Running a server -How do you run a router function in an HTTP server? A simple option is to convert a -router function to an `HttpHandler` via `RouterFunctions.toHttpHandler(RouterFunction)`. -The `HttpHandler` can then be used with a number of servers adapters. -See <> for server-specific -instructions. +How do you run a router function in an HTTP server? A simple option is to convert a router +function to an `HttpHandler` using one of the following: -it is also possible to run with a -<> setup -- side by side -with annotated controllers. The easiest way to do that is through the -<> which creates the necessary configuration to -handle requests with router and handler functions. +* `RouterFunctions.toHttpHandler(RouterFunction)` +* `RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)` + +The returned `HttpHandler` can then be used with a number of servers adapters by following +<> for server-specific instructions. + +A more advanced option is to run with a +<>-based setup through the +<> which uses Spring configuration to declare the +components process requests. The WebFlux Java config declares the following components +related to functional endpoints: + +* `RouterFunctionMapping` -- this detects one or more `RouterFunction` beans in the +Spring configuration, combines them via `RouterFunction.andOther`, and routes requests to +the resulting, composed `RouterFunction`. +* `HandlerFunctionAdapter` -- simple adapter to invoke a `HandlerFunction` selected to +handle a request. +* `ServerResponseResultHandler` -- invokes the `writeTo` method of the `ServerResponse` +returned by the `HandlerFunction`. + +The above allows functional endpoints to fit within the `DispatcherHandler` request +processing lifecycle, and potentially to run side by side with annotated controllers, if +any are declared. This is also the mechanism used in the Spring Boot WebFlux starter. + +Below is example WebFlux Java config (see +<> for how to run): + +[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 + default void addCorsMappings(CorsRegistry registry) { + // configure CORS... + } + + @Override + public void configureViewResolvers(ViewResolverRegistry registry) { + // configure view resolution for HTML rendering... + } +} +----