diff --git a/src/docs/asciidoc/images/webflux-overview.png b/src/docs/asciidoc/images/webflux-overview.png deleted file mode 100644 index 62774d0c64..0000000000 Binary files a/src/docs/asciidoc/images/webflux-overview.png and /dev/null differ diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index c48b4884ba..0a39fd540d 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -12,8 +12,8 @@ IoC container, with any web framework on top, but you can also use only the <>. The Spring Framework supports declarative transaction management, remote access to your logic through RMI or web services, and various options for persisting your data. -It offers full-featured web frameworks such as <> -and <>; and it enables you to +It offers full-featured web frameworks such as <> +and <>; and it enables you to integrate <> transparently into your software. This document is a reference guide to Spring Framework features. Questions on the @@ -29,7 +29,7 @@ This reference document provides the following sections: * <> -* The Web on <> or <> stacks +* The Web on <> or <> stacks * <> diff --git a/src/docs/asciidoc/integration.adoc b/src/docs/asciidoc/integration.adoc index 87d12e10da..0c430af97c 100644 --- a/src/docs/asciidoc/integration.adoc +++ b/src/docs/asciidoc/integration.adoc @@ -1369,68 +1369,8 @@ and writes the media type supported by the Java I/O API. [[rest-async-resttemplate]] ==== Async RestTemplate -Web applications often need to query external REST services those days. The very nature of -HTTP and synchronous calls can lead up to challenges when scaling applications for those -needs: multiple threads may be blocked, waiting for remote HTTP responses. - -`AsyncRestTemplate` and <>'s APIs are very similar; see -<>. The main difference between those APIs is -that `AsyncRestTemplate` returns -{api-spring-framework}/util/concurrent/ListenableFuture.html[`ListenableFuture`] -wrappers as opposed to concrete results. - -The previous `RestTemplate` example translates to: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - // async call - Future> futureEntity = template.getForEntity( - "http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21"); - - // get the concrete result - synchronous call - ResponseEntity entity = futureEntity.get(); ----- - -{api-spring-framework}/util/concurrent/ListenableFuture.html[`ListenableFuture`] -accepts completion callbacks: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - ListenableFuture> futureEntity = template.getForEntity( - "http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21"); - - // register a callback - futureEntity.addCallback(new ListenableFutureCallback>() { - @Override - public void onSuccess(ResponseEntity entity) { - //... - } - - @Override - public void onFailure(Throwable t) { - //... - } - }); ----- - -[NOTE] -==== -The default `AsyncRestTemplate` constructor registers a -{api-spring-framework}/core/task/SimpleAsyncTaskExecutor.html[`SimpleAsyncTaskExecutor` -] for executing HTTP requests. -When dealing with a large number of short-lived requests, a thread-pooling TaskExecutor -implementation like -{api-spring-framework}/scheduling/concurrent/ThreadPoolTaskExecutor.html[`ThreadPoolTaskExecutor`] -may be a good choice. -==== - -See the -{api-spring-framework}/util/concurrent/ListenableFuture.html[`ListenableFuture` javadocs] -and -{api-spring-framework}/web/client/AsyncRestTemplate.html[`AsyncRestTemplate` javadocs] -for more details. +The `AsyncRestTemplate` is deprecated. +Please use the <> instead. diff --git a/src/docs/asciidoc/kotlin.adoc b/src/docs/asciidoc/kotlin.adoc index fb771ddd48..b8472d168e 100644 --- a/src/docs/asciidoc/kotlin.adoc +++ b/src/docs/asciidoc/kotlin.adoc @@ -261,7 +261,7 @@ for more details and up-to-date information. Spring Framework now comes with a {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/-router-function-dsl/[Kotlin routing DSL] -that allows one to leverage the <> for writing clean and idiomatic Kotlin code: [source,kotlin] @@ -583,7 +583,7 @@ https://spring.io/blog/2017/08/01/spring-framework-5-kotlin-apis-the-functional- === Choosing the web flavor Spring Framework now comes with 2 different web stacks: <> and -<>. +<>. Spring WebFlux is recommended if one wants to create applications that will deal with latency, long-lived connections, streaming scenarios or simply if one wants to use the web functional diff --git a/src/docs/asciidoc/reactive-web.adoc b/src/docs/asciidoc/reactive-web.adoc deleted file mode 100644 index 59f078e9ea..0000000000 --- a/src/docs/asciidoc/reactive-web.adoc +++ /dev/null @@ -1,62 +0,0 @@ -[[spring-reactive-web]] -= Web on Reactive Stack -:doc-root: https://docs.spring.io -:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework -:toc: left -:toclevels: 3 -:docinfo1: - -This part of the documentation covers support for reactive stack, web applications built on -http://www.reactive-streams.org/[Reactive Streams] and adapted to non-blocking runtimes -such as Netty, Undertow, and Servlet containers via Servlet 3.1 non-blocking I/O. -Individual chapters cover <> and its -<>. The previous section covers support for -<> applications. - -[[spring-reactive-web-intro]] -== Introduction - - -[[spring-reactive-web-intro-reactive-programming]] -=== What is Reactive Programming? - -In plain terms reactive programming is about non-blocking applications that are asynchronous -and event-driven and require a small number of threads to scale vertically (i.e. within the -JVM) rather than horizontally (i.e. through clustering). - -A key aspect of reactive applications is the concept of backpressure which is -a mechanism to ensure producers don't overwhelm consumers. For example in a pipeline -of reactive components extending from the database to the HTTP response when the -HTTP connection is too slow the data repository can also slow down or stop completely -until network capacity frees up. - -Reactive programming also leads to a major shift from imperative to declarative async -composition of logic. It is comparable to writing blocking code vs using the -`CompletableFuture` from Java 8 to compose follow-up actions via lambda expressions. - -For a longer introduction check the blog series -https://spring.io/blog/2016/06/07/notes-on-reactive-programming-part-i-the-reactive-landscape["Notes on Reactive Programming"] -by Dave Syer. - - -[[spring-reactive-web-intro-reactive-api]] -=== Reactive API and Building Blocks - -Spring Framework 5 embraces -https://github.com/reactive-streams/reactive-streams-jvm#reactive-streams[Reactive Streams] -as the contract for communicating backpressure across async components and -libraries. Reactive Streams is a specification created through industry collaboration that -has also been adopted in Java 9 as `java.util.concurrent.Flow`. - -The Spring Framework uses https://projectreactor.io/[Reactor] internally for its own -reactive support. Reactor is a Reactive Streams implementation that further extends the -basic Reactive Streams `Publisher` contract with the `Flux` and `Mono` composable API -types to provide declarative operations on data sequences of `0..N` and `0..1`. - -The Spring Framework exposes `Flux` and `Mono` in many of its own reactive APIs. -At the application level however, as always, Spring provides choice and fully supports -the use of RxJava. For more on reactive types check the post -https://spring.io/blog/2016/04/19/understanding-reactive-types["Understanding Reactive Types"] -by Sebastien Deleuze. - -include::web/webflux.adoc[leveloffset=+1] \ No newline at end of file diff --git a/src/docs/asciidoc/web-reactive.adoc b/src/docs/asciidoc/web-reactive.adoc new file mode 100644 index 0000000000..e32c78ee45 --- /dev/null +++ b/src/docs/asciidoc/web-reactive.adoc @@ -0,0 +1,15 @@ +[[spring-web-reactive]] += Web on Reactive Stack +:doc-root: https://docs.spring.io +:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework +:toc: left +:toclevels: 4 +:docinfo1: + +This part of the documentation covers support for reactive stack, web applications built on a +http://www.reactive-streams.org/[Reactive Streams] API to run on top of non-blocking +servers such as Netty, Undertow, and Servlet 3.1+ containers. Individual chapters cover +<> and its <>. +For Servlet stack, web applications, to go <>. + +include::web/webflux.adoc[leveloffset=+1] \ No newline at end of file diff --git a/src/docs/asciidoc/web.adoc b/src/docs/asciidoc/web.adoc index 3baf3a6834..6ca3de62bb 100644 --- a/src/docs/asciidoc/web.adoc +++ b/src/docs/asciidoc/web.adoc @@ -9,7 +9,7 @@ This part of the documentation covers support for Servlet stack, web applications built on the Servlet API and deployed to Servlet containers. Individual chapters include <>, <>, <>, and <>. -The next section covers support for <> applications. +For reactive stack, web applications, go to <>. include::web/webmvc.adoc[leveloffset=+1] diff --git a/src/docs/asciidoc/web/webflux-functional.adoc b/src/docs/asciidoc/web/webflux-functional.adoc index 73159587aa..cfe80c6cc9 100644 --- a/src/docs/asciidoc/web/webflux-functional.adoc +++ b/src/docs/asciidoc/web/webflux-functional.adoc @@ -1,17 +1,25 @@ [[webflux-fn]] -= Functional Programming Model += 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 + [[webflux-fn-handler-functions]] -== HandlerFunctions +== 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 would be a method with `@RequestMapping`. +handler function is an `@RequestMapping` method. `ServerRequest` and `ServerResponse` are immutable interfaces that offer JDK-8 friendly access -to the underlying HTTP messages. Both are fully reactive by -building on top of Reactor: the request expose the body as `Flux` or `Mono`; the response accepts -any http://www.reactive-streams.org[Reactive Streams] `Publisher` as body. +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 +<>). + `ServerRequest` gives access to various HTTP request elements: the method, URI, query parameters, and -- through the separate `ServerRequest.Headers` interface @@ -26,7 +34,7 @@ contains JSON, or JAXB if XML). Flux people = request.bodyToFlux(Person.class); -The two methods above (`bodyToMono` and `bodyToFlux`) are, in fact, convenience methods that use the +The above -- `bodyToMono` and `bodyToFlux`, 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 @@ -43,7 +51,8 @@ a JSON content-type, and a body: Mono person = ... ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person); -And here is how to build a response with a 201 Created status, Location header, and empty body: +And here is how to build a response with a 201 CREATED status, a `"Location"` header, and +empty body: URI location = ... ServerResponse.created(location).build(); @@ -111,7 +120,7 @@ variable `id`. We retrieve that `Person` via the repository, and create a JSON r found. If it is not found, we use `switchIfEmpty(Mono)` to return a 404 Not Found response. [[webflux-fn-router-functions]] -== RouterFunctions +== RouterFunction 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 @@ -172,43 +181,23 @@ Most of the predicates found in `RequestPredicates` are compositions. For instance, `RequestPredicates.GET(String)` is a composition of `RequestPredicates.method(HttpMethod)` and `RequestPredicates.path(String)`. + [[webflux-fn-running]] -=== Running a Server +== Running a server -Now there is just one piece of the puzzle missing: running a router function in an HTTP server. -You can convert a router function into a `HttpHandler` by using -`RouterFunctions.toHttpHandler(RouterFunction)`. -The `HttpHandler` allows you to run on a wide variety of reactive runtimes: Reactor Netty, -Servlet 3.1+, and Undertow. -Here is how we run a router function in Reactor Netty, for instance: +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. -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -RouterFunction route = ... -HttpHandler httpHandler = RouterFunctions.toHttpHandler(route); -ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); -HttpServer server = HttpServer.create(HOST, PORT); -server.newHandler(adapter).block(); ----- - -For Tomcat it looks like this: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -RouterFunction route = ... -HttpHandler httpHandler = RouterFunctions.toHttpHandler(route); -HttpServlet servlet = new ServletHttpHandlerAdapter(httpHandler); -Tomcat server = new Tomcat(); -Context rootContext = server.addContext("", System.getProperty("java.io.tmpdir")); -Tomcat.addServlet(rootContext, "servlet", servlet); -rootContext.addServletMapping("/", "servlet"); -tomcatServer.start(); ----- +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. -// TODO: DispatcherHandler [[webflux-fn-handler-filter-function]] == HandlerFilterFunction diff --git a/src/docs/asciidoc/web/webflux.adoc b/src/docs/asciidoc/web/webflux.adoc index 15b4b410e6..152039c96d 100644 --- a/src/docs/asciidoc/web/webflux.adoc +++ b/src/docs/asciidoc/web/webflux.adoc @@ -1,332 +1,1688 @@ [[webflux]] = Spring WebFlux -Spring Framework 5 includes a new `spring-webflux` module. The module contains support -for reactive HTTP and WebSocket clients as well as for reactive server web applications -including REST, HTML browser, and WebSocket style interactions. +[[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 built on a +http://www.reactive-streams.org/[Reactive Streams] API and runs on non-blocking +servers such as Netty, Undertow, and Servlet 3.1+ containers. -[[webflux-server]] -== Server Side +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`. -On the server-side WebFlux supports 2 distinct programming models: +In addition to the WebFlux web framework, the `spring-webflux` module includes several +other reactive components such as a <> for performing HTTP requests, +a `WebTestClient` for testing web endpoints, and WebSocket support. -* Annotation-based with `@Controller` and the other annotations supported also with Spring MVC -* Functional, Java 8 lambda style routing and handling -Both programming models are executed on the same reactive foundation that adapts -non-blocking HTTP runtimes to the Reactive Streams API. The diagram -below shows the server-side stack including traditional, Servlet-based -Spring MVC on the left from the `spring-webmvc` module and also the -reactive stack on the right from the `spring-webflux` module. +[[webflux-new-framework]] +=== Why a new web framework? -image::images/webflux-overview.png[width=720] +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 the use of that leads away from using the rest of the +Servlet API which remains synchronous -- `Filter`, `Servlet`, and blocking -- `getParameter`, +`getPart`. On the positive side a new common API foundation makes it possible to support any +server and that is important because of runtimes such as Netty that are well established in +the async, non-blocking space. -WebFlux can run on Servlet containers with support for the -Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as -Netty and Undertow. Each runtime is adapted to a reactive -`ServerHttpRequest` and `ServerHttpResponse` exposing the body of the -request and response as `Flux`, rather than -`InputStream` and `OutputStream`, with reactive backpressure. -REST-style JSON and XML serialization and deserialization is supported on top -as a `Flux`, and so is HTML view rendering and Server-Sent Events. +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-server-annotation]] -=== Annotation-based Programming Model -The same `@Controller` programming model and the same annotations used in Spring MVC -are also supported in WebFlux. The main difference is that the underlying core, -framework contracts -- i.e. `HandlerMapping`, `HandlerAdapter`, are -non-blocking and operate on the reactive `ServerHttpRequest` and `ServerHttpResponse` -rather than on the `HttpServletRequest` and `HttpServletResponse`. -Below is an example with a reactive controller: +[[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 and feedback from Spring projects. + +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 <> for more details. + + +[[webflux-programming-models]] +=== Programming models + +The `spring-web` module contains the reactive foundation that underlies Spring WebFlux -- +HTTP abstractions, Reactive Streams server adapters, reactive codecs, and a +core Web API whose role is comparable to the Servlet API but with non-blocking semantics. + +On that foundation Spring WebFlux provides a choice of two programming models: + +- <> -- 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. +- <> -- 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, 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, <>) +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: + +* <> and server adapters -- the most basic, common API +for HTTP request handling with Reactive Streams back pressure. +* <> -- 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"] ---- -@RestController -public class PersonController { - - private final PersonRepository repository; - - public PersonController(PersonRepository repository) { - this.repository = repository; - } - - @PostMapping("/person") - Mono create(@RequestBody Publisher personStream) { - return this.repository.save(personStream).then(); - } - - @GetMapping("/person") - Flux list() { - return this.repository.findAll(); - } - - @GetMapping("/person/{id}") - Mono findById(@PathVariable String id) { - return this.repository.findOne(id); - } -} ----- - -include::webflux-functional.adoc[leveloffset=+2] - - -[[webflux-client]] -== Client Side - -WebFlux includes a functional, reactive `WebClient` that offers a fully -non-blocking and reactive alternative to the `RestTemplate`. It exposes network -input and output as a reactive `ClientHttpRequest` and `ClientHttpResponse` where -the body of the request and response is a `Flux` rather than an -`InputStream` and `OutputStream`. In addition it supports the same reactive JSON, XML, -and SSE serialization mechanism as on the server side so you can work with typed objects. -Below is an example of using the `WebClient` which requires a `ClientHttpConnector` -implementation to plug in a specific HTTP client such as Reactor Netty: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -WebClient client = WebClient.create("http://example.com"); - -Mono account = client.get() - .url("/accounts/{id}", 1L) - .accept(APPLICATION_JSON) - .exchange(request) - .flatMap(response -> response.bodyToMono(Account.class)); ----- - -As stated above, `WebClient` requires a `ClientHttpConnector` to operate, the default being the `ReactorClientHttpConnector`. -When constructing a `ReactorClientHttpConnector`, you can use the `HttpClientOptions.Builder` to further customize it. -For instance, to customize the Netty `SslContext`: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -SslContext sslContext = ... -ReactorClientHttpConnector connector = new ReactorClientHttpConnector(builder -> { - builder.sslContext(sslContext); -}); -WebClient webClient = WebClient.builder() - .clientConnector(connector) - .build(); ----- - -[[webflux-http-body]] -== Request and Response Body Conversion - -The `spring-core` module provides reactive `Encoder` and `Decoder` contracts -that enable the serialization of a `Flux` of bytes to and from typed objects. -The `spring-web` module adds JSON (Jackson) and XML (JAXB) implementations for use in -web applications as well as others for SSE streaming and zero-copy file transfer. - -The following Reactive APIs are supported: - -* Reactor 3.x is supported out of the box -* RxJava 2.x is supported when `io.reactivex.rxjava2:rxjava` dependency is on the classpath -* RxJava 1.x is supported when both `io.reactivex:rxjava` and `io.reactivex:rxjava-reactive-streams` (https://github.com/ReactiveX/RxJavaReactiveStreams[adapter between RxJava and Reactive Streams]) dependencies are on the classpath - -For example the request body can be one of the following way and it will be decoded -automatically in both the annotation and the functional programming models: - -* `Account account` -- the account is deserialized without blocking before the controller is invoked. -* `Mono account` -- the controller can use the `Mono` to declare logic to be executed after the account is deserialized. -* `Single account` -- same as with `Mono` but using RxJava -* `Flux accounts` -- input streaming scenario. -* `Observable accounts` -- input streaming with RxJava. - -The response body can be one of the following: - -* `Mono` -- serialize without blocking the given Account when the `Mono` completes. -* `Single` -- same but using RxJava. -* `Flux` -- streaming scenario, possibly SSE depending on the requested content type. -* `Observable` -- same but using RxJava `Observable` type. -* `Flowable` -- same but using RxJava 2 `Flowable` type. -* `Publisher` or `Flow.Publisher` -- any type implementing Reactive Streams `Publisher` is supported. -* `Flux` -- SSE streaming. -* `Mono` -- request handling completes when the `Mono` completes. -* `Account` -- serialize without blocking the given Account; implies a synchronous, non-blocking controller method. -* `void` -- specific to the annotation-based programming model, request handling completes -when the method returns; implies a synchronous, non-blocking controller method. - -When using stream types like `Flux` or `Observable`, the media type specified in the -request/response or at mapping/routing level is used to determine how the data should be serialized -and flushed. For example a REST endpoint that returns a `Flux` will be serialized by -default as following: - -* `application/json`: a `Flux` is handled as an asynchronous collection and - serialized as a JSON array with an explicit flush when the `complete` event is emitted. -* `application/stream+json`: a `Flux` will be handled as a stream of `Account` elements - serialized as individual JSON object separated by new lines and explicitly flushed after - each element. The `WebClient` supports JSON stream decoding so this is a good use case - for server to server use case. -* `text/event-stream`: a `Flux` or `Flux>` will be handled as - a stream of `Account` or `ServerSentEvent` elements serialized as individual SSE elements - using by default JSON for data encoding and explicit flush after each element. This - is well suited for exposing a stream to browser clients. `WebClient` supports - reading SSE streams as well. - - -[[webflux-websocket-support]] -== Reactive WebSocket Support - -WebFlux includes reactive WebSocket client and server support. -Both client and server are supported on the Java WebSocket API -(JSR-356), Jetty, Undertow, and Reactor Netty. - -On the server side, declare a `WebSocketHandlerAdapter` and then simply add -mappings to `WebSocketHandler`-based endpoints: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -@Bean -public HandlerMapping webSocketMapping() { - Map map = new HashMap<>(); - map.put("/foo", new FooWebSocketHandler()); - map.put("/bar", new BarWebSocketHandler()); - - SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); - mapping.setOrder(10); - mapping.setUrlMap(map); - return mapping; -} - -@Bean -public WebSocketHandlerAdapter handlerAdapter() { - return new WebSocketHandlerAdapter(); -} ----- - -On the client side create a `WebSocketClient` for one of the supported libraries -listed above: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -WebSocketClient client = new ReactorNettyWebSocketClient(); -client.execute("ws://localhost:8080/echo"), session -> {... }).blockMillis(5000); ----- - -[[webflux-tests]] -== Testing - -The `spring-test` module includes a `WebTestClient` that can be used to test -WebFlux server endpoints with or without a running server. - -Tests without a running server are comparable to `MockMvc` from Spring MVC -where mock request and response are used instead of connecting over the network -using a socket. The `WebTestClient` however can also perform tests against a -running server. - -For more see -https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples[sample tests] -in the framework. - - - -[[webflux-getting-started]] -= Getting Started - - -[[webflux-getting-started-boot]] -== Spring Boot Starter - -The -Spring Boot WebFlux starter available via http://start.spring.io is the fastest way to get started. -It does all that's necessary so you to start writing `@Controller` classes -just like with Spring MVC. Simply go to http://start.spring.io, choose -version 2.0.0.BUILD-SNAPSHOT, and type reactive in the dependencies box. -By default the starter runs with Reactor Netty but the dependencies can be changed as usual -with Spring Boot to switch to a different runtime. -See the Spring Boot reference documentation page for more details and instruction. - -[[webflux-getting-started-manual]] -== Manual Bootstrapping - -This section outlines the steps to get up and running manually. - -For dependencies start with `spring-webflux` and `spring-context`. -Then add `jackson-databind` and `io.netty:netty-buffer` -(temporarily see https://jira.spring.io/browse/SPR-14528[SPR-14528]) for JSON support. -Lastly add the dependencies for one of the supported runtimes: - -* Tomcat -- `org.apache.tomcat.embed:tomcat-embed-core` -* Jetty -- `org.eclipse.jetty:jetty-server` and `org.eclipse.jetty:jetty-servlet` -* Reactor Netty -- `io.projectreactor.ipc:reactor-netty` -* Undertow -- `io.undertow:undertow-core` - -For the **annotation-based programming model** bootstrap with: -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -ApplicationContext context = new AnnotationConfigApplicationContext(DelegatingWebFluxConfiguration.class); // (1) -HttpHandler handler = DispatcherHandler.toHttpHandler(context); // (2) ----- - -The above loads default Spring Web framework configuration (1), then creates a -`DispatcherHandler`, the main class driving request processing (2), and adapts -it to `HttpHandler` -- the lowest level Spring abstraction for reactive HTTP request handling. - -For the **functional programming model** bootstrap as follows: -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // (1) -context.registerBean(FooBean.class, () -> new FooBeanImpl()); // (2) -context.registerBean(BarBean.class); // (3) -context.refresh(); - -HttpHandler handler = WebHttpHandlerBuilder - .webHandler(RouterFunctions.toHttpHandler(...)) - .applicationContext(context) - .build(); // (4) ----- - -The above creates an `AnnotationConfigApplicationContext` instance (1) that can take advantage -of the new functional bean registration API (2) to register beans using a Java 8 `Supplier` -or just by specifying its class (3). The `HttpHandler` is created using `WebHttpHandlerBuilder` (4). - -The `HttpHandler` can then be installed in one of the supported runtimes: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -// Tomcat and Jetty (also see notes below) -HttpServlet servlet = new ServletHttpHandlerAdapter(handler); -... - -// Reactor Netty +HttpHandler handler = ... ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler); HttpServer.create(host, port).newHandler(adapter).block(); +---- -// Undertow +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(); +---- + +You can also deploy as a WAR to any Servlet 3.1 container by wrapping the handler with +`ServletHttpHandlerAdapter` as a `Servlet`. + + + +[[webflux-web-handler-api]] +=== WebHandler API + +`HttpHandler` is the basis for running on different servers. On that base the WebHandler +API provides a slightly higher level processing chain of +exception handlers +({api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler]), filters +({api-spring-framework}/web/server/WebFilter.html[WebFilter]), and a target handler +({api-spring-framework}/web/server/WebHandler.html[WebHandler]). + +All components work on `ServerWebExchange` -- a container for the HTTP request and +response that also adds request attributes, session attributes, access to form data, +multipart data, and more. + +The processing chain can be put together with `WebHttpHandlerBuilder` which builds an +`HttpHandler` that in turn can be run with a <>. +To use the builder either add components individually or point to an `ApplicationContext` +to have the following detected: + +[cols="2,2,1,3", options="header"] +|=== +|Bean name|Bean type|Count|Description + +|"webHandler" +|WebHandler +|1 +|Target handler after filters + +| +|WebFilter +|0..N +|Filters + +| +|WebExceptionHandler +|0..N +|Exception handlers after filter chain + +|"webSessionManager" +|WebSessionManager +|0..1 +|Custom session manager; `DefaultWebSessionManager` by default + +|"serverCodecConfigurer" +|ServerCodecConfigurer +|0..1 +|Custom form and multipart data decoders; `ServerCodecConfigurer.create()` by default + +|"localeContextResolver" +|LocaleContextResolver +|0..1 +|Custom resolver for `LocaleContext`; `AcceptHeaderLocaleContextResolver` by default +|=== + + +[[webflux-codecs]] +=== Codecs + +The `spring-web` module provides +{api-spring-framework}/http/codec/HttpMessageReader.html[HttpMessageReader] and +{api-spring-framework}/http/codec/HttpMessageWriter.html[HttpMessageWriter] +for encoding and decoding the HTTP request and response body with Reactive Streams. +It builds on lower level contracts from `spring-core`: + +* {api-spring-framework}/core/io/buffer/DataBuffer.html[DataBuffer] -- abstraction for +byte buffers -- e.g. Netty `ByteBuf`, `java.nio.ByteBuffer` +* {api-spring-framework}/core/codec/Encoder.html[Encoder] -- serialize a stream of Objects +to a stream of data buffers +* {api-spring-framework}/core/codec/Decoder.html[Decoder] -- deserialize a stream of data +buffers into a stream of Objects + +Basic `Encoder` and `Decoder` implementations exist in `spring-core` but `spring-web` adds +more for JSON, XML, and other formats. You can wrap any `Encoder` and `Decoder` as a reader +or writer with `EncoderHttpMessageWriter` and `DecoderHttpMessageReader`. There are some +additional, web-only reader and writer implementations for server-sent events, form data, +and more. + +Finally, `ClientCodecConfigurer` and `ServerCodecConfigurer` can be used to initialize +a list of readers and writers. They include support for classpath detection and a +of defaults along with the ability to override or replace those defaults. + + + +[[webflux-dispatcher-handler]] +== The DispatcherHandler +[.small]#<># + +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 +<>. + +Spring configuration in a WebFlux application typically contains: + +* `DispatcherHandler` with the bean name "webHandler" +* `WebFilter` and `WebExceptionHandler` 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-special-bean-types]] +=== Special bean types +[.small]#<># + +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. + +.Special bean types in the ApplicationContext +|=== +| 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. + +| HandlerAdapter +| Helps 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 various annotations. The main purpose of a `HandlerAdapter` is + to shield the `DispatcherHandler` from such details. + +| HandlerResultHandler +| Process the `HandlerResult` returned from a `HandlerAdapter`. +|=== + + +[[webflux-dispatcher-handler-sequence]] +=== Processing sequence +[.small]#<># + +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]#<># + +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 declaration +[.small]#<># + +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 { + + // ... + } +---- + [NOTE] ==== -For Servlet containers especially with WAR deployment you can use the -`AbstractAnnotationConfigDispatcherHandlerInitializer` which as a -`WebApplicationInitializer` and is auto-detected by Servlet containers. -It takes care of registering the `ServletHttpHandlerAdapter` as shown above. -You will need to implement one abstract method in order to point to your -Spring configuration. +`@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-getting-started-examples]] -== Examples -You will find code examples useful to build reactive Web application in the following projects: +[[webflux-ann-requestmapping]] +=== Mapping Requests +[.small]#<># + +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]#<># + +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. +// TODO: see <> and <>. + +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 more simpler, more +explicit, and less vulnerable to URL path based exploits. + + +[[webflux-ann-requestmapping-pattern-comparison]] +==== Pattern Comparison +[.small]#<># + +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]#<># + +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]#<># + +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]#<># + +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]#<># + +`@GetMapping` -- and also `@RequestMapping(method=HttpMethod.GET)`, are implicitly mapped to +and also support HTTP HEAD. An HTTP HEAD request is processed as if it were HTTP GET except +but instead of writing the body, the number of bytes are counted and the "Content-Length" +header set. + +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]#<># + +`@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]#<># + +The table below shows supported controller method arguments. + +Reactive types (Reactor, RxJava, <>) 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. + +// TODO: not supported +// |`java.util.Locale` +// |The current request locale, determined by the most specific `LocaleResolver` available, in +// effect, the configured `LocaleResolver`/`LocaleContextResolver`. + +// TODO: not supported +//|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. +// TODO: See <>. + +|`@RequestParam` +|For access to Servlet request parameters. Parameter values are converted to the declared +method argument type. +// TODO: See <>. + +|`@RequestHeader` +|For access to request headers. Header values are converted to the declared method argument +type. +// TODO: See <>. + +|`@RequestBody` +|For access to the HTTP request body. Body content is converted to the declared method +argument type using ``HttpMessageReader``'s. Supports reactive types. +// TODO: See <>. + +|`HttpEntity` +|For access to request headers and body. The body is converted with ``HttpMessageReader``'s. +Supports reactive types. +// TODO: See <>. + +|`@RequestPart` +|For access to a part in a "multipart/form-data" request. Supports reactive types. +// TODO: See <> and <>. + +|`java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap` +|For access and updates of the implicit model that is exposed to the web view. + +|Command or form object (with optional `@ModelAttribute`) +|Command object whose properties to bind to request parameters -- via setters or directly to +fields, with customizable type conversion, depending on `@InitBinder` methods and/or the +HandlerAdapter configuration (see the `webBindingInitializer` property on +`RequestMappingHandlerAdapter`). + +Command objects along with their validation results are exposed as model attributes, by +default using the command class name - e.g. model attribute "orderAddress" for a command +object of type "some.package.OrderAddress". `@ModelAttribute` can be used to customize the +model attribute name. + +Supports reactive types. + +|`Errors`, `BindingResult` +|Validation results for the command/form object data binding; this argument must be +declared immediately after the command/form object in the controller method signature. + +|`SessionStatus` +|For marking form processing complete which triggers cleanup of session attributes +declared through a class-level `@SessionAttributes` annotation. + +|`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. + +|`@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. + +|`@RequestAttribute` +|For access to request attributes. +|=== + +[[webflux-ann-return-types]] +==== Return values +[.small]#<># + +The table below shows supported controller method return values. Reactive types -- +Reactor, RxJava, <> 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. +// TODO: See <>. + +|`HttpEntity`, `ResponseEntity` +|The return value specifies the full response including HTTP headers and body be encoded +through ``HttpMessageWriter``s and written to the response. +// TODO: See <>. + +|`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 +from the request path. + +|`Rendering` +|An API for model and view rendering scenarios. + +|`void` +|For use in method that don't write the response body; or methods where the view name is +supposed to be determined implicitly from the request path. + +|`Flux`, `Observable`, 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 type +|A single model attribute to be added to the implicit model with the view name implicitly +determined through a `RequestToViewNameTranslator`; the attribute name may be specified +through a method-level `@ModelAttribute` or otherwise a name is selected based on the +class name of the return type. +|=== + + + +include::webflux-functional.adoc[leveloffset=+1] + + + +[[webflux-config]] +== WebFlux Java Config +[.small]#<># + +The WebFlux Java config provides default configuration suitable for most applications along +with a configuration API to customize it. For more advanced customizations, not available in +the configuration API, see <>. + +You do not need to understand the underlying beans created by the Java config, but it's +easy to seem them in `WebFluxConfigurationSupport`, and if you want to learn more, see +<>. + + +[[webflux-config-enable]] +=== Enable the configuration +[.small]#<># + +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 +<> also adapting to dependencies +available on the classpath -- for JSON, XML, etc. + + +[[webflux-config-customize]] +=== Configuration API +[.small]#<># + +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]#<># + +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 <> +and the `FormattingConversionServiceFactoryBean` for more information on when to use FormatterRegistrars. +==== + + +[[webflux-config-validation]] +=== Validation +[.small]#<># + +By default if <> is present +on the classpath -- e.g. Hibernate Validator, the `LocalValidatorFactoryBean` is registered +as a global <> 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]#<># + +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]#<># + +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]#<># + +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]#<># + +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 <>. + +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[ResourceResolver]'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]#<># + +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]#<># + +`@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-client]] +== WebClient + +The `spring-webflux` module includes a non-blocking, reactive client for HTTP requests +with Reactive Streams back pressure. It shares <> and other +infrastructure with the server <>. + +`WebClient` provides a higher level API over HTTP client libraries. By default +it uses https://github.com/reactor/reactor-netty[Reactor Netty] but that is pluggable +with a different `ClientHttpConnector`. The `WebClient` API returns Reactor `Flux` or +`Mono` for output and accepts Reactive Streams `Publisher` as input (see +<>). + +[TIP] +==== +By comparison to the +<>, the `WebClient` offers a more +functional and fluent API that taking full advantage of Java 8 lambdas. It supports both +sync and async scenarios, including streaming, and brings the efficiency of +non-blocking I/O. +==== + + +[[webflux-client-retrieve]] +=== Retrieve + +The `retrieve()` method is the easiest way to get a decoded response body: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + WebClient client = WebClient.create("http://example.org"); + + Mono result = client.get() + .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Person.class); +---- + +You can also get a stream of decoded objects: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + Flux result = client.get() + .uri("/quotes").accept(TEXT_EVENT_STREAM) + .retrieve() + .bodyToFlux(Quote.class); +---- + +By default, a response with 4xx or 5xx status code results in an error of type +`WebClientResponseException` but you can customize that: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + Mono result = client.get() + .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON) + .retrieve() + .onStatus(HttpStatus::is4xxServerError, response -> ...) + .onStatus(HttpStatus::is5xxServerError, response -> ...) + .bodyToFlux(Person.class); +---- + + + +[[webflux-client-exchange]] +=== Exchange + +The `exchange()` method provides more control. The below example is equivalent +to `retrieve()` but with access to the `ClientResponse`: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + Mono result = client.get() + .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON) + .exchange() + .flatMap(response -> response.bodyToMono(Person.class)); +---- + +At this level you can also create a full `ResponseEntity`: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + Mono> result = client.get() + .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON) + .exchange() + .flatMap(response -> response.bodyToEntity(Person.class)); +---- + +Note that unlike `retrieve()`, with `exchange()` there are no automatic error signals for +4xx and 5xx responses. You have to check the status code and decide how to proceed. + +[CAUTION] +==== +When you use `exchange()`, you must call `response.close()` if you do not intend to read +the response body in order to close the underlying HTTP connection. Not doing so can +result in connection pool inconsistencies or memory leaks. + +You do not have to call `response.close()` if you consume the body because forcing a +connection to be closed negates the benefits of persistent connections and connection +pooling. +==== + + +[[webflux-client-body]] +=== Request body + +The request body can be encoded from an Object: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + Mono personMono = ... ; + + Mono result = client.post() + .uri("/persons/{id}", id) + .contentType(MediaType.APPLICATION_JSON) + .body(personMono, Person.class) + .retrieve() + .bodyToMono(Void.class); +---- + +You can also encode from a stream of objects: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + Flux personFlux = ... ; + + Mono result = client.post() + .uri("/persons/{id}", id) + .contentType(MediaType.APPLICATION_STREAM_JSON) + .body(personFlux, Person.class) + .retrieve() + .bodyToMono(Void.class); +---- + +Or if you have the actual value, use the `syncBody` shortcut: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + Person person = ... ; + + Mono result = client.post() + .uri("/persons/{id}", id) + .contentType(MediaType.APPLICATION_JSON) + .syncBody(person) + .retrieve() + .bodyToMono(Void.class); +---- + + +[[webflux-client-builder]] +=== Builder options + +A simple way to create `WebClient` is through the static factory methods `create()` and +`create(String)` with a base URL for all requests. You can also use `WebClient.builder()` +for further options. + +To customize options of the underlying HTTP client: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + SslContext sslContext = ... + + ClientHttpConnector connector = new ReactorClientHttpConnector( + builder -> builder.sslContext(sslContext)); + + WebClient webClient = WebClient.builder() + .clientConnector(connector) + .build(); +---- + +To customize <> used for encoding and decoding: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + ExchangeStrategies strategies = ExchangeStrategies.builder() + .codecs(configurer -> { + // ... + }) + .build(); + + WebClient webClient = WebClient.builder() + .exchangeStrategies(strategies) + .build(); + +---- + +The builder can be used to insert <>. + +You can also customize URI building, set default headers, cookies, and more. Explore +the `WebClient.Builder` in your IDE. + +Note that you can also obtain a builder from an already existing `WebClient` instance +and create a modified version without affecting the original instance: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + WebClient modifiedClient = client.mutate() + // user builder methods... + .build(); + +---- + + + + + +[[webflux-client-filter]] +=== Filters + +`WebClient` supports interception style request filtering: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + WebClient client = WebClient.builder() + .filter((request, next) -> { + + ClientRequest filtered = ClientRequest.from(request) + .header("foo", "bar") + .build(); + + return next.exchange(filtered); + }) + .build(); +---- + +`ExchangeFilterFunctions` provides a filter for basic authentication: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + +// static import of ExchangeFilterFunctions.basicAuthentication + + WebClient client = WebClient.builder() + .filter(basicAuthentication("user", "pwd")) + .build(); +---- + +You can also mutate an existing `WebClient` instance without affecting the original: + +[source,java,intent=0] +[subs="verbatim,quotes"] +---- + WebClient filteredClient = client.mutate() + .filter(basicAuthentication("user", "pwd") + .build(); +---- + + + + +[[webflux-reactive-libraries]] +== Reactive Libraries + +Reactor is a required dependency for the `spring-webflux` module and is used internally +for composing logic and for Reactive Streams support. An easy rule to remember is that +WebFlux APIs return `Flux` or `Mono` -- since that's what's used internally, and +leniently accept any Reactive Streams `Publisher` implementation. + +The use of `Flux` and `Mono` helps to express cardinality -- e.g. +whether a single or multiple async values are expected. This is important for API design +but also essential in some cases, e.g. when encoding an HTTP message. + +For annotated controllers, WebFlux adapts transparently to the reactive library in use +with proper translation of cardinality. This is done with the help of the +{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] from +`spring-core` which provides pluggable support for reactive and async types. The registry +has built-in support for RxJava and `CompletableFuture` but others can be registered. + +For functional endpoints and other functional APIs such as the `WebClient`: + +* `Flux` or `Mono` for output -- use them to compose logic or pass to any Reactive +Streams library (both are `Publisher` implementations). +* `Publisher` for input -- if a `Publisher` from another reactive library is provided +it can only be treated as a stream with unknown semantics (0..N). If the semantics are +known -- e.g. `io.reactivex.Single`, you can use `Mono.from(Publisher)` and pass that +in instead of the raw `Publisher`. + +[NOTE] +==== +For example, given a `Publisher` that is not a `Mono`, the Jackson JSON message writer +expects multiple values. If the media type implies an infinite stream -- e.g. +`"application/json+stream"`, values are written and flushed individually; otherwise +values are buffered into a list and rendered as a JSON array. +==== -* https://github.com/poutsma/web-function-sample[Functional programming model sample] -* https://github.com/sdeleuze/spring-reactive-playground[Spring Reactive Playground]: playground for most Spring Web reactive features -* https://github.com/reactor/projectreactor.io/tree/spring-functional[Reactor website]: the `spring-functional` branch is a Spring 5 functional, Java 8 lambda-style application -* https://github.com/bclozel/spring-reactive-university[Spring Reactive University session]: live-coded project from https://www.youtube.com/watch?v=Cj4foJzPF80[this Devoxx BE 2106 university talk] -* https://github.com/thymeleaf/thymeleafsandbox-biglist-reactive[Reactive Thymeleaf Sandbox] -* https://github.com/mixitconf/mixit/[MiXiT website]: Boot + Kotlin + Reactive + Functional web registration API application -* https://github.com/sdeleuze/spring-kotlin-functional[Spring Kotlin Functional]: Standalone WebFlux application with functional web and bean DSLs -* https://github.com/simonbasle/reactor-by-example[Reactor by example]: code snippets coming from this https://www.infoq.com/articles/reactor-by-example[InfoQ article] -* https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation[Spring integration tests]: various features tested with Reactor https://projectreactor.io/docs/test/release/api/index.html?reactor/test/StepVerifier.html[`StepVerifier`] diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index 3291294672..b6bd381d0d 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -1,5 +1,5 @@ [[mvc]] -= Spring Web MVC framework += Spring Web MVC :doc-spring-security: {doc-root}/spring-security/site/docs/current/reference [[mvc-introduction]] @@ -13,22 +13,19 @@ but it is more commonly known as "Spring MVC". Parallel to Spring Web MVC, Spring Framework 5.0 introduced a reactive stack, web framework whose name Spring WebFlux is also based on its source module https://github.com/spring-projects/spring-framework/tree/master/spring-webflux[spring-webflux]. - -This section covers Spring Web MVC. The <> +This section covers Spring Web MVC. The <> covers Spring WebFlux. [[mvc-servlet]] == The DispatcherServlet +[.small]#<># Spring MVC, like many other web frameworks, is designed around the front controller -pattern where a central `Servlet`, the `DispatcherServlet`, dispatches incoming -requests to registered handlers for request processing. - -`DispatcherServlet` provides a shared algorithm for request processing while -actual work is performed by configurable, delegate components. This model is very -flexible and supports diverse workflows. +pattern where a central `Servlet`, the `DispatcherServlet`, provides a shared algorithm +for request processing while actual work is performed by configurable, delegate components. +This model is flexible and supports diverse workflows. The `DispatcherServlet`, as any `Servlet`, needs to be declared and mapped according to the Servlet specification using Java configuration or in `web.xml`. @@ -202,6 +199,7 @@ And the `web.xml` equivalent: [[mvc-servlet-special-bean-types]] === Special Bean Types In the WebApplicationContext +[.small]#<># The `DispatcherServlet` delegates to special beans to process requests and render the appropriate responses. By "special beans" we mean Spring-managed Object instances that @@ -273,6 +271,8 @@ provides many extra convenient options on top. [[mvc-servlet-sequence]] === DispatcherServlet Processing Sequence +[.small]#<># + The `DispatcherServlet` processes requests as follows: * The `WebApplicationContext` is searched for and bound in the request as an attribute @@ -337,29 +337,29 @@ initialization parameters ( `init-param` elements) to the Servlet declaration in [[mvc-controller]] == Annotated Controllers -Spring MVC provides an annotation-based programming model where `@Controller` components -use annotations to express request mappings, to bind request input to controller method -arguments, to declare exception handling, and much more. Here is a basic example: +[.small]#<># + +Spring MVC 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. [source,java,indent=0] [subs="verbatim,quotes"] ---- @Controller - public class HelloWorldController { + public class HelloController { - @GetMapping("/helloWorld") - public String helloWorld(Model model) { + @GetMapping("/hello") + public String handle(Model model) { model.addAttribute("message", "Hello World!"); return "index"; } } ---- -Annotated controllers have flexible method signatures and do not have to extend base -classes or implement specific interfaces. They do not need to have direct dependencies -on Servlet APIs either. In this particular example the method accepts a `Model` and -returns a view name as a `String` but many other options exist and are explained further -below in this chapter. +In this particular example the method accepts a `Model` and returns a view name as a `String` +but many other options exist and are explained further below in this chapter. [TIP] ==== @@ -371,6 +371,7 @@ programming model described in this section. [[mvc-ann-controller]] === Defining a controller with @Controller +[.small]#<># You can define controller beans using a standard Spring bean definition in the Servlet's `WebApplicationContext`. The `@Controller` stereotype allows for auto-detection, @@ -415,111 +416,26 @@ The XML configuration equivalent: ---- +[NOTE] +==== +`@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). +==== + [[mvc-ann-requestmapping]] === Mapping Requests With @RequestMapping +[.small]#<># -The `@RequestMapping` annotation is used to map URLs such as `/appointments` onto an -entire class or a particular handler method. A class-level annotation can express -mappings shared across all controller methods such as a URL prefix. Each controller -method then can complete the URL mapping and for example narrow down to a specific -HTTP method such as "GET", "POST", etc. +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. -The following example from the __Petcare__ sample shows a controller in a Spring MVC -application that uses this annotation: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Controller - **@RequestMapping("/appointments")** - public class AppointmentsController { - - private final AppointmentBook appointmentBook; - - @Autowired - public AppointmentsController(AppointmentBook appointmentBook) { - this.appointmentBook = appointmentBook; - } - - **@RequestMapping(method = RequestMethod.GET)** - public Map get() { - return appointmentBook.getAppointmentsForToday(); - } - - **@RequestMapping(path = "/{day}", method = RequestMethod.GET)** - public Map getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) { - return appointmentBook.getAppointmentsForDay(day); - } - - **@RequestMapping(path = "/new", method = RequestMethod.GET)** - public AppointmentForm getNewForm() { - return new AppointmentForm(); - } - - **@RequestMapping(method = RequestMethod.POST)** - public String add(@Valid AppointmentForm appointment, BindingResult result) { - if (result.hasErrors()) { - return "appointments/new"; - } - appointmentBook.addAppointment(appointment); - return "redirect:/appointments"; - } - } ----- - -In the above example, `@RequestMapping` is used in a number of places. The first usage is -on the type (class) level, which indicates that all handler methods in this controller -are relative to the `/appointments` path. The `get()` method has a further -`@GetMapping` refinement: it only accepts `GET` requests, meaning that an HTTP `GET` for -`/appointments` invokes this method. The `add()` has a similar refinement, and the -`getNewForm()` combines the definition of HTTP method and path into one, so that `GET` -requests for `appointments/new` are handled by that method. - -The `getForDay()` method shows another usage of `@RequestMapping`: URI templates. (See -<>). - -A `@RequestMapping` on the class level is not required. Without it, all paths are simply -absolute, and not relative to it. The following example from the __PetClinic__ sample -application shows a multi-action controller using `@RequestMapping`: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Controller - public class ClinicController { - - private final Clinic clinic; - - @Autowired - public ClinicController(Clinic clinic) { - this.clinic = clinic; - } - - **@RequestMapping("/")** - public void welcomeHandler() { - } - - **@RequestMapping("/vets")** - public ModelMap vetsHandler() { - return new ModelMap(this.clinic.getVets()); - } - - } ----- - -Note that the above example does not specify `GET` vs. `PUT`, `POST`, and so forth, because -`@RequestMapping` maps all HTTP methods by default. Use `@GetMapping(method=GET)` or -`@GetMapping` to narrow the mapping. - -[[mvc-ann-requestmapping-composed]] -==== Composed @RequestMapping Variants - -Spring MVC also supports _composed_ shortcut variants of the `@RequestMapping` annotation -that help to simplify mappings for common HTTP methods and better express the semantics of -the annotated handler method. For example, a `@GetMapping` can be read as a `GET` -`@RequestMapping`. +There are also HTTP method specific shortcut variants of `@RequestMapping`: - `@GetMapping` - `@PostMapping` @@ -527,32 +443,32 @@ the annotated handler method. For example, a `@GetMapping` can be read as a `GET - `@DeleteMapping` - `@PatchMapping` -The following example shows a modified version of the `AppointmentsController` from the -previous section that has been simplified with _composed_ `@RequestMapping` annotations. +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"] ---- - @Controller - **@RequestMapping("/appointments")** - public class AppointmentsController { + @RestController + @RequestMapping("/persons") + class PersonController { - // ... + @GetMapping("/{id}") + public Person getPerson(@PathVariable Long id) { + // ... + } - **@GetMapping** - public Map get() {} - - **@GetMapping("/{day}")** - public Map getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {} - - **@GetMapping("/new")** - public AppointmentForm getNewForm() {} - - **@PostMapping** - public String add(@Valid AppointmentForm appointment, BindingResult result) {} + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public void add(@RequestBody Person person) { + // ... + } } ---- + [[mvc-ann-requestmapping-proxying]] ==== @Controller and AOP Proxying @@ -565,239 +481,149 @@ callback (e.g. `InitializingBean`, `*Aware`, etc), you may need to explicitly configure class-based proxying. For example with ``, change to ``. + [[mvc-ann-requestmapping-uri-templates]] -==== URI Template Patterns -__URI templates__ can be used for convenient access to selected parts of a URL in a -`@RequestMapping` method. +==== URI Path Patterns +[.small]#<># -A URI Template is a URI-like string, containing one or more variable names. When you -substitute values for these variables, the template becomes a URI. For example -`http://www.example.com/users/{userId}` contains the variable __userId__. Assigning the -value __fred__ to the variable yields `http://www.example.com/users/fred`. +You can map requests using glob patterns and wildcards: -In Spring MVC you can use the `@PathVariable` annotation on a method argument to bind it -to the value of a URI template variable: +* `?` matches one character +* `*` matches zero or more characters within a path segment +* `**` match zero or more path segments -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @GetMapping("/owners/{ownerId}") - public String findOwner(**@PathVariable** String ownerId, Model model) { - Owner owner = ownerService.findOwner(ownerId); - model.addAttribute("owner", owner); - return "displayOwner"; - } ----- - -The URI Template ++"/owners/{ownerId}"++ specifies the variable name `ownerId`. When the -controller handles this request, the value of `ownerId` is set to the value found in the -appropriate part of the URI. For example, when a request comes in for `/owners/fred`, -the value of `ownerId` is `fred`. - -[TIP] -==== - -To process the @PathVariable annotation, Spring MVC needs to find the matching URI -template variable by name. You can specify it in the annotation: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @GetMapping("/owners/{ownerId}") - public String findOwner(**@PathVariable("ownerId")** String theOwner, Model model) { - // implementation omitted - } ----- - -Or if the URI template variable name matches the method argument name you can omit that -detail. As long as your code is compiled with debugging information or the `-parameters` -compiler flag on Java 8, Spring MVC will match the method argument name to the URI -template variable name: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @GetMapping("/owners/{ownerId}") - public String findOwner(**@PathVariable** String ownerId, Model model) { - // implementation omitted - } ----- -==== - -A method can have any number of `@PathVariable` annotations: +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 String findPet(**@PathVariable** String ownerId, **@PathVariable** String petId, Model model) { - Owner owner = ownerService.findOwner(ownerId); - Pet pet = owner.getPet(petId); - model.addAttribute("pet", pet); - return "displayPet"; - } ----- - -When a `@PathVariable` annotation is used on a `Map` argument, the map -is populated with all URI template variables. - -A URI template can be assembled from type and method level __@RequestMapping__ -annotations. As a result the `findPet()` method can be invoked with a URL such as -`/owners/42/pets/21`. - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @Controller - @RequestMapping(**"/owners/{ownerId}"**) - public class RelativePathUriTemplateController { - - @RequestMapping(**"/pets/{petId}"**) - public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { - // implementation omitted - } - - } ----- - -A `@PathVariable` argument can be of __any simple type__ such as `int`, `long`, `Date`, etc. -Spring automatically converts to the appropriate type or throws a -`TypeMismatchException` if it fails to do so. You can also register support for parsing -additional data types. See <> and <>. - - -[[mvc-ann-requestmapping-uri-templates-regex]] -==== URI Template Patterns with Regular Expressions -Sometimes you need more precision in defining URI template variables. Consider the URL -`"/spring-web/spring-web-3.0.5.jar"`. How do you break it down into multiple parts? - -The `@RequestMapping` annotation supports the use of regular expressions in URI template -variables. The syntax is `{varName:regex}` where the first part defines the variable -name and the second - the regular expression. For example: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}") - public void handle(@PathVariable String version, @PathVariable String extension) { + 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 { -[[mvc-ann-requestmapping-patterns]] -==== Path Patterns -In addition to URI templates, the `@RequestMapping` annotation and all _composed_ -`@RequestMapping` variants also support Ant-style path patterns (for example, -`/myPath/{asterisk}.do`). A combination of URI template variables and Ant-style globs is -also supported (e.g. `/owners/{asterisk}/pets/{petId}`). + @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 <> and <>. + +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: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 MVC uses the `PathMatcher` contract and the `AntPathMatcher` implementation from +`spring-core` for URI path matching. +==== [[mvc-ann-requestmapping-pattern-comparison]] ==== Path Pattern Comparison -When a URL matches multiple patterns, a sort is used to find the most specific match. +[.small]#<># -A pattern with a lower count of URI variables and wild cards is considered more specific. -For example `/hotels/{hotel}/{asterisk}` has 1 URI variable and 1 wild card and is considered -more specific than `/hotels/{hotel}/{asterisk}{asterisk}` which as 1 URI variable and 2 wild cards. +When multiple patterns match a URL, they must be compared to find the best match. This done +via `AntPathMatcher.getPatternComparator(String path)` which looks for patterns that more +specific. -If two patterns have the same count, the one that is longer is considered more specific. -For example `/foo/bar{asterisk}` is longer and considered more specific than `/foo/{asterisk}`. +A pattern is less specific if it has a lower count of URI variables and single wildcards +counted as 1 and double wildcards counted as 2. Given an equal score, the longer pattern is +chosen. Given the same score and length, the pattern with more URI variables than wildcards +is chosen. -When two patterns have the same count and length, the pattern with fewer wild cards is considered more specific. -For example `/hotels/{hotel}` is more specific than `/hotels/{asterisk}`. +The default mapping pattern `/{asterisk}{asterisk}` is excluded from scoring and always +sorted last. Also prefix patterns such as `/public/{asterisk}{asterisk}` are considered less +specific than other pattern that don't have double wildcards. -There are also some additional special rules: - -* The *default mapping pattern* `/{asterisk}{asterisk}` is less specific than any other pattern. -For example `/api/{a}/{b}/{c}` is more specific. -* A *prefix pattern* such as `/public/{asterisk}{asterisk}` is less specific than any other pattern that doesn't contain double wildcards. -For example `/public/path3/{a}/{b}/{c}` is more specific. - -For the full details see `AntPatternComparator` in `AntPathMatcher`. Note that the PathMatcher -can be customized (see <> in the section on configuring Spring MVC). - - -[[mvc-ann-requestmapping-placeholders]] -==== Path Patterns with Placeholders -Patterns in `@RequestMapping` annotations support `${...}` placeholders against local -properties and/or system properties and environment variables. This may be useful in -cases where the path a controller is mapped to may need to be customized through -configuration. For more information on placeholders, see the javadocs of the -`PropertyPlaceholderConfigurer` class. +For the full details see `AntPatternComparator` in `AntPathMatcher` and also keep mind that +the `PathMatcher` implementation used can be customized. See <> +in the configuration section. [[mvc-ann-requestmapping-suffix-pattern-match]] ==== Suffix Pattern Matching + By default Spring MVC performs `".{asterisk}"` suffix pattern matching so that a controller mapped to `/person` is also implicitly mapped to `/person.{asterisk}`. -This makes it easy to request different representations of a resource through the -URL path (e.g. `/person.pdf`, `/person.xml`). +This is used for URL based content negotiation, e.g. `/person.pdf`, `/person.xml`, etc. -Suffix pattern matching can be turned off or restricted to a set of path extensions -explicitly registered for content negotiation purposes. This is generally -recommended to minimize ambiguity with common request mappings such as -`/person/{id}` where a dot might not represent a file extension, e.g. -`/person/joe@email.com` vs `/person/joe@email.com.json`. Furthermore as explained -in the note below suffix pattern matching as well as content negotiation may be -used in some circumstances to attempt malicious attacks and there are good -reasons to restrict them meaningfully. +Suffix pattern matching was quite helpful when browsers used to send Accept headers that +are hard to interpet consistently. In the present, and for REST services, the `Accept` +header should be the preferred choice. -See <> for suffix pattern matching configuration and -also <> for content negotiation configuration. +Suffix patterns can cause ambiguity and complexity in combination with path parameters, +encoded characters, and URI variables. It also makes it harder to reason about URL-based +authorization rules and security (see <>). +Suffix pattern matching can be turned off completely or restricted to a set of explicitly +registered path extensions. We strongly recommend using of one those options. See +<> and <>. If you need URL based +content negotiation consider using query parameters instead. [[mvc-ann-requestmapping-rfd]] ==== Suffix Pattern Matching and RFD -Reflected file download (RFD) attack was first described in a -https://www.trustwave.com/Resources/SpiderLabs-Blog/Reflected-File-Download---A-New-Web-Attack-Vector/[paper by Trustwave] -in 2014. The attack is similar to XSS in that it relies on input -(e.g. query parameter, URI variable) being reflected in the response. -However instead of inserting JavaScript into HTML, an RFD attack relies on the -browser switching to perform a download and treating the response as an executable -script if double-clicked based on the file extension (e.g. .bat, .cmd). +Reflected file download (RFD) attack is similar to XSS in that it relies on request input, +e.g. query parameter, URI variable, being reflected in the response. However instead of +inserting JavaScript into HTML, an RFD attack relies on the browser switching to perform a +download and treating the response as an executable script when double-clicked later. In Spring MVC `@ResponseBody` and `ResponseEntity` methods are at risk because -they can render different content types which clients can request including -via URL path extensions. Note however that neither disabling suffix pattern matching -nor disabling the use of path extensions for content negotiation purposes alone -are effective at preventing RFD attacks. +they can render different content types which clients can request via URL path extensions. +Disabling suffix pattern matching and the use of path extensions for content negotiation +lower the risk but are not sufficient to prevent RFD attacks. -For comprehensive protection against RFD, prior to rendering the response body -Spring MVC adds a `Content-Disposition:inline;filename=f.txt` header to -suggest a fixed and safe download file. This is done only if the URL -path contains a file extension that is neither whitelisted nor explicitly -registered for content negotiation purposes. However it may potentially have +To prevent RFD attacks, prior to rendering the response body Spring MVC adds a +`Content-Disposition:inline;filename=f.txt` header to suggest a fixed and safe download +file. This is done only if the URL path contains a file extension that is neither whitelisted +nor explicitly registered for content negotiation purposes. However it may potentially have side effects when URLs are typed directly into a browser. -Many common path extensions are whitelisted by -default. Furthermore REST API calls are typically not meant to be used as URLs -directly in browsers. Nevertheless applications that use custom -`HttpMessageConverter` implementations can explicitly register file extensions -for content negotiation and the Content-Disposition header will not be added -for such extensions. See <>. - -[NOTE] -==== -This was originally introduced as part of work for -http://pivotal.io/security/cve-2015-5211[CVE-2015-5211]. -Below are additional recommendations from the report: - -* Encode rather than escape JSON responses. This is also an OWASP XSS recommendation. - For an example of how to do that with Spring see https://github.com/rwinch/spring-jackson-owasp[spring-jackson-owasp]. -* Configure suffix pattern matching to be turned off or restricted to explicitly - registered suffixes only. -* Configure content negotiation with the properties "useJaf" and "ignoreUnknownPathExtensions" - set to false which would result in a 406 response for URLs with unknown extensions. - Note however that this may not be an option if URLs are naturally expected to have - a dot towards the end. -* Add `X-Content-Type-Options: nosniff` header to responses. Spring Security 4 does - this by default. -==== +Many common path extensions are whitelisted by default. Applications with custom +`HttpMessageConverter` implementations can explicitly register file extensions for content +negotiation to avoid having a `Content-Disposition` header added for those extensions. +See <>. +Check http://pivotal.io/security/cve-2015-5211[CVE-2015-5211] for additional +recommendations related to RFD. @@ -927,334 +753,331 @@ to `false`. [[mvc-ann-requestmapping-consumes]] ==== Consumable Media Types -You can narrow the primary mapping by specifying a list of consumable media types. The -request will be matched only if the `Content-Type` request header matches the specified -media type. For example: +[.small]#<># + +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, Model model) { - // implementation omitted + public void addPet(@RequestBody Pet pet) { + // ... } ---- -Consumable media type expressions can also be negated as in `!text/plain` to match to -all requests other than those with `Content-Type` of `text/plain`. Also consider -using constants provided in `MediaType` such as `APPLICATION_JSON_VALUE` and -`APPLICATION_JSON_UTF8_VALUE`. +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] ==== - -The __consumes__ condition is supported on the type and on the method level. Unlike most -other conditions, when used at the type level, method-level consumable types override -rather than extend type-level consumable types. +`MediaType` provides constants for commonly used media types -- e.g. +`APPLICATION_JSON_VALUE`, `APPLICATION_JSON_UTF8_VALUE`. ==== [[mvc-ann-requestmapping-produces]] ==== Producible Media Types -You can narrow the primary mapping by specifying a list of producible media types. The -request will be matched only if the `Accept` request header matches one of these -values. Furthermore, use of the __produces__ condition ensures the actual content type -used to generate the response respects the media types specified in the __produces__ -condition. For example: +[.small]#<># + +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 = MediaType.APPLICATION_JSON_UTF8_VALUE**) + @GetMapping(path = "/pets/{petId}", **produces = "application/json;charset=UTF-8"**) @ResponseBody - public Pet getPet(@PathVariable String petId, Model model) { - // implementation omitted + public Pet getPet(@PathVariable String petId) { + // ... } ---- -[NOTE] -==== -Be aware that the media type specified in the __produces__ condition can also optionally -specify a character set. For example, in the code snippet above we specify the same media -type than the default one configured in `MappingJackson2HttpMessageConverter`, including -the `UTF-8` charset. -==== +The media type can specify a character set. Negated expressions are supported -- e.g. +`!text/plain` means any content type other than "text/plain". -Just like with __consumes__, producible media type expressions can be negated as in -`!text/plain` to match to all requests other than those with an `Accept` header -value of `text/plain`. Also consider using constants provided in `MediaType` such -as `APPLICATION_JSON_VALUE` and `APPLICATION_JSON_UTF8_VALUE`. +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] ==== -The __produces__ condition is supported on the type and on the method level. Unlike most -other conditions, when used at the type level, method-level producible types override -rather than extend type-level producible types. +`MediaType` provides constants for commonly used media types -- e.g. +`APPLICATION_JSON_VALUE`, `APPLICATION_JSON_UTF8_VALUE`. ==== [[mvc-ann-requestmapping-params-and-headers]] ==== Request Parameters and Header Values -You can narrow request matching through request parameter conditions such as -`"myParam"`, `"!myParam"`, or `"myParam=myValue"`. The first two test for request -parameter presence/absence and the third for a specific parameter value. Here is an -example with a request parameter value condition: +[.small]#<># + +You can narrow request mappings based on request parameter conditions. You can test for the +presence of a request parameter (`"myParam"`), for the absence (`"!myParam"`), or for a +specific value (`"myParam=myValue"`): [source,java,indent=0] [subs="verbatim,quotes"] ---- - @Controller - @RequestMapping("/owners/{ownerId}") - public class RelativePathUriTemplateController { - - @GetMapping(path = "/pets/{petId}", **params = "myParam=myValue"**) - public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { - // implementation omitted - } - + @GetMapping(path = "/pets/{petId}", **params = "myParam=myValue"**) + public void findPet(@PathVariable String petId) { + // ... } ---- -The same can be done to test for request header presence/absence or to match based on a -specific request header value: +You can also use the same with request header conditions: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @Controller - @RequestMapping("/owners/{ownerId}") - public class RelativePathUriTemplateController { - - @GetMapping(path = "/pets", **headers = "myHeader=myValue"**) - public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { - // implementation omitted - } - + @GetMapping(path = "/pets", **headers = "myHeader=myValue"**) + public void findPet(@PathVariable String petId) { + // ... } ---- [TIP] ==== - -Although you can match to __Content-Type__ and __Accept__ header values using media type -wild cards (for example __"content-type=text/*"__ will match to __"text/plain"__ and -__"text/html"__), it is recommended to use the __consumes__ and __produces__ conditions -respectively instead. They are intended specifically for that purpose. +You can match `Content-Type` and `Accept` with the headers condition but it is better to use +<> and <> +instead. ==== [[mvc-ann-requestmapping-head-options]] -==== HTTP HEAD and HTTP OPTIONS +==== HTTP HEAD and OPTIONS +[.small]#<># -`@RequestMapping` methods mapped to "GET" are also implicitly mapped to "HEAD", -i.e. there is no need to have "HEAD" explicitly declared. An HTTP HEAD request -is processed as if it were an HTTP GET except instead of writing the body only -the number of bytes are counted and the "Content-Length" header set. +`@GetMapping` -- and also `@RequestMapping(method=HttpMethod.GET)`, are implicitly mapped to +and also support HTTP HEAD. An HTTP HEAD request is processed as if it were HTTP GET except +but instead of writing the body, the number of bytes are counted and the "Content-Length" +header set. -`@RequestMapping` methods have built-in support for HTTP OPTIONS. By default an -HTTP OPTIONS request is handled by setting the "Allow" response header to the -HTTP methods explicitly declared on all `@RequestMapping` methods with matching -URL patterns. When no HTTP methods are explicitly declared the "Allow" header -is set to "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS". Ideally always declare the -HTTP method(s) that an `@RequestMapping` method is intended to handle, or alternatively -use one of the dedicated _composed_ `@RequestMapping` variants (see -<>). +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. -Although not necessary an `@RequestMapping` method can be mapped to and handle -either HTTP HEAD or HTTP OPTIONS, or both. +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. [[mvc-ann-methods]] -=== Defining @RequestMapping handler methods - -`@RequestMapping` handler methods can have very flexible signatures. The supported -method arguments and return values are described in the following section. Most -arguments can be used in arbitrary order with the only exception being `BindingResult` -arguments. This is described in the next section. - -[NOTE] -==== -Spring 3.1 introduced a new set of support classes for `@RequestMapping` methods called -`RequestMappingHandlerMapping` and `RequestMappingHandlerAdapter` respectively. They are -recommended for use and even required to take advantage of new features in Spring MVC -3.1 and going forward. The new support classes are enabled by default from the MVC -namespace and with use of the MVC Java config but must be configured explicitly if using -neither. -==== +=== Defining @RequestMapping methods +[.small]#<># +`@RequestMapping` handler methods have a flexible signature and can choose from a range of +supported controller method arguments and return values. [[mvc-ann-arguments]] -==== Supported method argument types -The following are the supported method arguments: +==== Supported Controller Method Arguments +[.small]#<># -* `org.springframework.web.context.request.WebRequest` or - `org.springframework.web.context.request.NativeWebRequest`. Allows for generic - request parameter access as well as request/session attribute access, without ties - to the native Servlet API. -* Request or response objects (Servlet API). Choose any specific request or response - type, for example `ServletRequest` or `HttpServletRequest` or Spring's - `MultipartRequest`/`MultipartHttpServletRequest`. -* Session object (Servlet API) of type `HttpSession`. An argument of this type enforces - the presence of a corresponding session. As a consequence, such an argument is never - `null`. +The table below shows supported controller method arguments. Reactive types are not supported +for any arguments. -[NOTE] -==== -Session access may not be thread-safe, in particular in a Servlet environment. Consider -setting the ``RequestMappingHandlerAdapter``'s "synchronizeOnSession" flag to "true" if -multiple requests are allowed to access a session concurrently. -==== +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`. -* `java.servlet.http.PushBuilder` for the associated Servlet 4.0 push builder API, - allowing for programmatic HTTP/2 resource pushes. -* `java.security.Principal` (or a specific `Principal` implementation class if known), - containing the currently authenticated user. -* `org.springframework.http.HttpMethod` for the HTTP request method, represented as - Spring's `HttpMethod` enum. -* `java.util.Locale` for the current request locale, determined by the most specific - locale resolver available, in effect, the configured `LocaleResolver` / - `LocaleContextResolver` in an MVC environment. -* `java.util.TimeZone` (Java 6+) / `java.time.ZoneId` (Java 8+) for the time zone - associated with the current request, as determined by a `LocaleContextResolver`. -* `java.io.InputStream` / `java.io.Reader` for access to the request's content. - This value is the raw InputStream/Reader as exposed by the Servlet API. -* `java.io.OutputStream` / `java.io.Writer` for generating the response's content. - This value is the raw OutputStream/Writer as exposed by the Servlet API. -* `@PathVariable` annotated parameters for access to URI template variables. See - <>. -* `@MatrixVariable` annotated parameters for access to name-value pairs located in - URI path segments. See <>. -* `@RequestParam` annotated parameters for access to specific Servlet request - parameters. Parameter values are converted to the declared method argument type. - See <>. -* `@RequestHeader` annotated parameters for access to specific Servlet request HTTP - headers. Parameter values are converted to the declared method argument type. - See <>. -* `@RequestBody` annotated parameters for access to the HTTP request body. Parameter - values are converted to the declared method argument type using - ``HttpMessageConverter``s. See <>. -* `@RequestPart` annotated parameters for access to the content of a - "multipart/form-data" request part. See <> and - <>. -* `@SessionAttribute` annotated parameters for access to existing, permanent - session attributes (e.g. user authentication object) as opposed to model - attributes temporarily stored in the session as part of a controller workflow - via `@SessionAttributes`. -* `@RequestAttribute` annotated parameters for access to request attributes. -* `HttpEntity` parameters for access to the Servlet request HTTP headers and - contents. The request stream will be converted to the entity body using - ``HttpMessageConverter``s. See <>. -* `java.util.Map` / `org.springframework.ui.Model` / `org.springframework.ui.ModelMap` - for enriching the implicit model that is exposed to the web view. -* `org.springframework.web.servlet.mvc.support.RedirectAttributes` to specify the exact - set of attributes to use in case of a redirect and also to add flash attributes - (attributes stored temporarily on the server-side to make them available to the - request after the redirect). See <> and - <>. -* Command or form objects to bind request parameters to bean properties (via setters) - or directly to fields, with customizable type conversion, depending on `@InitBinder` - methods and/or the HandlerAdapter configuration. See the `webBindingInitializer` - property on `RequestMappingHandlerAdapter`. Such command objects along with their - validation results will be exposed as model attributes by default, using the command - class name - e.g. model attribute "orderAddress" for a command object of type - "some.package.OrderAddress". The `ModelAttribute` annotation can be used on a method - argument to customize the model attribute name used. -* `org.springframework.validation.Errors` / - `org.springframework.validation.BindingResult` validation results for a preceding - command or form object (the immediately preceding method argument). -* `org.springframework.web.bind.support.SessionStatus` status handle for marking form - processing as complete, which triggers the cleanup of session attributes that have - been indicated by the `@SessionAttributes` annotation at the handler type level. -* `org.springframework.web.util.UriComponentsBuilder` a builder for preparing a URL - relative to the current request's host, port, scheme, context path, and the literal - part of the servlet mapping. +[cols="1,2", options="header"] +|=== +|Controller method argument|Description -The `Errors` or `BindingResult` parameters have to follow the model object that is being -bound immediately as the method signature might have more than one model object and -Spring will create a separate `BindingResult` instance for each of them so the following -sample won't work: +|`WebRequest`, `NativeWebRequest` +|Generic access to request parameters, request & session attributes, without direct +use of the Servlet API. -.Invalid ordering of BindingResult and @ModelAttribute -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @PostMapping - public String processSubmit(**@ModelAttribute("pet") Pet pet**, Model model, **BindingResult result**) { ... } ----- +|`javax.servlet.ServletRequest`, `javax.servlet.ServletResponse` +|Choose any specific request or response type -- e.g. `ServletRequest`, `HttpServletRequest`, +or Spring's `MultipartRequest`, `MultipartHttpServletRequest`. -Note, that there is a `Model` parameter in between `Pet` and `BindingResult`. To get -this working you have to reorder the parameters as follows: +|`javax.servlet.http.HttpSession` +|Enforces the presence of a session. As a consequence, such an argument is never `null`. + +**Note:** Session access is not thread-safe. Consider setting the +``RequestMappingHandlerAdapter``'s "synchronizeOnSession" flag to "true" if multiple +requests are allowed to access a session concurrently. -[source,java,indent=0] -[subs="verbatim,quotes"] ----- - @PostMapping - public String processSubmit(**@ModelAttribute("pet") Pet pet**, **BindingResult result**, Model model) { ... } ----- +|`javax.servlet.http.PushBuilder` +|Servlet 4.0 push builder API for programmatic HTTP/2 resource pushes. -[NOTE] -==== -JDK 1.8's `java.util.Optional` is supported as a method parameter type with annotations -that have a `required` attribute (e.g. `@RequestParam`, `@RequestHeader`, etc). The use -of `java.util.Optional` in those cases is equivalent to having `required=false`. -==== +|`java.security.Principal` +|Currently authenticated user; possibly a specific `Principal` implementation class if known. +|`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`. + +|`java.io.InputStream`, `java.io.Reader` +|For access to the raw request body as exposed by the Servlet API. + +|`java.io.OutputStream`, `java.io.Writer` +|For access to the raw response body as exposed by the Servlet API. + +|`@PathVariable` +|For access to URI template variables. See <>. + +|`@MatrixVariable` +|For access to name-value pairs in URI path segments. See <>. + +|`@RequestParam` +|For access to Servlet request parameters. Parameter values are converted to the declared +method argument type. See <>. + +|`@RequestHeader` +|For access to request headers. Header values are converted to the declared method argument +type. See <>. + +|`@RequestBody` +|For access to the HTTP request body. Body content is converted to the declared method +argument type using ``HttpMessageConverter``s. See <>. + +|`HttpEntity` +|For access to request headers and body. The body is converted with ``HttpMessageConverter``s. +See <>. + +|`@RequestPart` +|For access to a part in a "multipart/form-data" request. +See <> and <>. + +|`java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap` +|For access and updates of the implicit model that is exposed to the web view. + +|`RedirectAttributes` +|Specify attributes to use in case of a redirect -- i.e. to be appended to the query +string, and/or flash attributes to be stored temporarily until the request after redirect. +See <> and <>. + +|Command or form object (with optional `@ModelAttribute`) +|Command object whose properties to bind to request parameters -- via setters or directly to +fields, with customizable type conversion, depending on `@InitBinder` methods and/or the +HandlerAdapter configuration (see the `webBindingInitializer` property on +`RequestMappingHandlerAdapter`). + +Command objects along with their validation results are exposed as model attributes, by +default using the command class name - e.g. model attribute "orderAddress" for a command +object of type "some.package.OrderAddress". `@ModelAttribute` can be used to customize the +model attribute name. + +|`Errors`, `BindingResult` +|Validation results for the command/form object data binding; this argument must be +declared immediately after the command/form object in the controller method signature. + +|`SessionStatus` +|For marking form processing complete which triggers cleanup of session attributes +declared through a class-level `@SessionAttributes` annotation. + +|`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. + +|`@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. + +|`@RequestAttribute` +|For access to request attributes. +|=== [[mvc-ann-return-types]] -==== Supported method return types -The following are the supported return types: +==== Supported Controller Method Return Values +[.small]#<># -* `ModelAndView` object (Spring MVC), providing a view, model attributes, and - optionally a response status. -* `Rendering` object (Spring WebFlux), providing a view, model attributes, and - optionally a response status. -* `Model` object, with the view name implicitly determined through a - `RequestToViewNameTranslator` and the model implicitly enriched with command objects - and the results of `@ModelAttribute` annotated reference data accessor methods. -* `Map` object for exposing a model, with the view name implicitly determined through - a `RequestToViewNameTranslator` and the model implicitly enriched with command objects - and the results of `@ModelAttribute` annotated reference data accessor methods. -* `View` object, with the model implicitly determined through command objects and - `@ModelAttribute` annotated reference data accessor methods. The handler method may - also programmatically enrich the model by declaring a `Model` argument (see above). -* `String` value that is interpreted as the logical view name, with the model - implicitly determined through command objects and `@ModelAttribute` annotated - reference data accessor methods. The handler method may also programmatically enrich - the model by declaring a `Model` argument (see above). -* `void` if the method handles the response itself (by writing the response content - directly, declaring an argument of type `ServletResponse` / `HttpServletResponse` for - that purpose) or if the view name is supposed to be implicitly determined through a - `RequestToViewNameTranslator` (not declaring a response argument in the handler method - signature). -* If the method is annotated with `@ResponseBody`, the return type is written to the - response HTTP body. The return value will be converted to the declared method argument - type using ``HttpMessageConverter``s. See <>. -* `HttpEntity` or `ResponseEntity` object to provide access to the Servlet - response HTTP headers and contents. The entity body will be converted to the response - stream using ``HttpMessageConverter``s. See <>. -* `HttpHeaders` object to return a response with no body. -* `Callable` async computation in a Spring MVC managed thread. -* `DeferredResult` async result produced later from an application managed, or any thread. -* `ListenableFuture` as an alternative equivalent to using `DeferredResult`. -* `CompletableFuture` or `CompletionStage` as an alternative equivalent to `DeferredResult`. -* `ResponseBodyEmitter` can be returned to write multiple objects to the response - asynchronously; also supported as the body within a `ResponseEntity`. -* `SseEmitter` can be returned to write Server-Sent Events to the response - asynchronously; also supported as the body within a `ResponseEntity`. -* `StreamingResponseBody` can be returned to write to the response OutputStream - asynchronously; also supported as the body within a `ResponseEntity`. -* Reactive types from Reactor 3, RxJava 2, RxJava 1 or others registered through - the configured `ReactiveAdapterRegistry` can be returned as an alternative - equivalent to using `DeferredResult` for single-valued types, or - `ResponseBodyEmitter` and `SseEmitter` for multi-valued reactive types where a streaming - media type (e.g. "text/event-stream", "application/json+stream") is requested. -* Any other return type is considered to be a single model attribute to be exposed to - the view, using the attribute name specified through `@ModelAttribute` at the method - level (or the default attribute name based on the return type class name). The model - is implicitly enriched with command objects and the results of `@ModelAttribute` - annotated reference data accessor methods. +The table below shows supported controller method return values. Reactive types are +supported for all return values, see below for more details. + +[cols="1,2", options="header"] +|=== +|Controller method return value|Description + +|`@ResponseBody` +|The return value is converted through ``HttpMessageConverter``s and written to the +response. See <>. + +|`HttpEntity`, `ResponseEntity` +|The return value specifies the full response including HTTP headers and body be converted +through ``HttpMessageConverter``s and written to the response. See <>. + +|`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 +through a `RequestToViewNameTranslator`. + +|`ModelAndView` object +|The view and model attributes to use, and optionally a response status. + +|`void` +|For use in methods that declare a `ServletResponse` or `OutputStream` argument and write +to the response body; or if the view name is supposed to be implicitly determined through a +`RequestToViewNameTranslator`. + +|`Callable` +|Produce any of the above return values asynchronously in a Spring MVC managed thread. + +|`DeferredResult` +|Produce any of the above return values asynchronously from any thread -- e.g. possibly as a +result of some event or callback. + +|`ListenableFuture`, +`java.util.concurrent.CompletionStage`, +`java.util.concurrent.CompletableFuture` +|Alternative to `DeferredResult` as a convenience for example when an underlying service +returns one of those. + +|`ResponseBodyEmitter`, `SseEmitter` +|Emit a stream of objects asynchronously to be written to the response with +``HttpMessageConverter``'s; also supported as the body of a `ResponseEntity`. + +|`StreamingResponseBody` +|Write to the response `OutputStream` asynchronously; also supported as the body of a +`ResponseEntity`. + +|Reactive types -- Reactor, RxJava, or others via `ReactiveAdapterRegistry` +|Alternative to ``DeferredResult` with multi-value streams (e.g. `Flux`, `Observable`) +collected to a `List`. + +For streaming scenarios -- .e.g. `text/event-stream`, `application/json+stream`, +`SseEmitter` and `ResponseBodyEmitter` are used instead, where `ServletOutputStream` blocking +I/O is performed on a Spring MVC managed thread and back pressure applied against the +completion of each write. + +See <>. + +|Any other return type +|A single model attribute to be added to the implicit model with the view name implicitly +determined through a `RequestToViewNameTranslator`; the attribute name may be specified +through a method-level `@ModelAttribute` or otherwise a name is selected based on the +class name of the return type. +|=== [[mvc-ann-requestparam]] @@ -2327,6 +2150,14 @@ If using the reactive `WebClient` from `spring-webflux`, or another client, or a data store with reactive support, you can return reactive types directly from Spring MVC controller methods. +Spring MVC adapts transparently to the reactive library in use with proper translation +of cardinality -- i.e. how many values are expected. This is done with the help of the +{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] from +`spring-core` which provides pluggable support for reactive and async types. The registry +has built-in support for RxJava but others can be registered. + +Return values are handled as follows: + * If the return type has single-value stream semantics such as Reactor `Mono` or RxJava `Single` it is adapted and equivalent to using `DeferredResult`. * If the return type has multi-value stream semantics such as Reactor `Flux` or @@ -4635,29 +4466,25 @@ override the `createDispatcherServlet` method. [[mvc-config]] -== Configuring Spring MVC -<> and <> explained about Spring -MVC's special beans and the default implementations used by the `DispatcherServlet`. In -this section you'll learn about two additional ways of configuring Spring MVC. Namely -the MVC Java config and the MVC XML namespace. +== MVC Java config, XML namespace +[.small]#<># -The MVC Java config and the MVC namespace provide similar default configuration that -overrides the `DispatcherServlet` defaults. The goal is to spare most applications from -having to create the same configuration and also to provide higher-level constructs for -configuring Spring MVC that serve as a simple starting point and require little or no -prior knowledge of the underlying configuration. +The MVC Java config and the MVC namespace provide default configuration suitable for most +applications along with a configuration API to customize it. -You can choose either the MVC Java config or the MVC namespace depending on your -preference. Also as you will see further below, with the MVC Java config it is easier to -see the underlying configuration as well as to make fine-grained customizations directly -to the created Spring MVC beans. But let's start from the beginning. +For more advanced customizations, not available in the configuration API, see +<> and <>. +You do not need to understand the underlying beans created by the MVC Java config and the +MVC namespace but if you want to learn more, see <> and +<>. [[mvc-config-enable]] -=== Enabling the MVC Java Config or the MVC XML Namespace -To enable MVC Java config add the annotation `@EnableWebMvc` to one of your -`@Configuration` classes: +=== Enable the Configuration +[.small]#<># + +In Java config use the `@EnableWebMvc` annotation: [source,java,indent=0] [subs="verbatim,quotes"] @@ -4665,13 +4492,10 @@ To enable MVC Java config add the annotation `@EnableWebMvc` to one of your @Configuration @EnableWebMvc public class WebConfig { - } ---- -To achieve the same in XML use the `mvc:annotation-driven` element in your -DispatcherServlet context (or in your root context if you have no DispatcherServlet -context defined): +In XML use the `` element: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -4691,84 +4515,17 @@ context defined): ---- -The above registers a `RequestMappingHandlerMapping`, a `RequestMappingHandlerAdapter`, -and an `ExceptionHandlerExceptionResolver` (among others) in support of processing -requests with annotated controller methods using annotations such as `@RequestMapping`, -`@ExceptionHandler`, and others. - -It also enables the following: - -. Spring 3 style type conversion through a <> instance -in addition to the JavaBeans PropertyEditors used for Data Binding. -. Support for <> Number fields using the `@NumberFormat` annotation -through the `ConversionService`. -. Support for <> `Date`, `Calendar`, `Long`, and Joda Time fields using the -`@DateTimeFormat` annotation. -. Support for <> `@Controller` inputs with `@Valid`, if -a JSR-303 Provider is present on the classpath. -. `HttpMessageConverter` support for `@RequestBody` method parameters and `@ResponseBody` -method return values from `@RequestMapping` or `@ExceptionHandler` methods. - -+ - -This is the complete list of HttpMessageConverters set up by mvc:annotation-driven: - -+ - -.. `ByteArrayHttpMessageConverter` converts byte arrays. -.. `StringHttpMessageConverter` converts strings. -.. `ResourceHttpMessageConverter` converts to/from -`org.springframework.core.io.Resource` for all media types. -.. `SourceHttpMessageConverter` converts to/from a `javax.xml.transform.Source`. -.. `FormHttpMessageConverter` converts form data to/from a `MultiValueMap`. -.. `Jaxb2RootElementHttpMessageConverter` converts Java objects to/from XML -- added if -JAXB2 is present and Jackson 2 XML extension is not present on the classpath. -.. `MappingJackson2HttpMessageConverter` converts to/from JSON -- added if Jackson 2 -is present on the classpath. -.. `MappingJackson2XmlHttpMessageConverter` converts to/from XML -- added if -https://github.com/FasterXML/jackson-dataformat-xml[Jackson 2 XML extension] is present -on the classpath. -.. `MappingJackson2SmileHttpMessageConverter` converts to/from Smile (binary JSON) -- added if -https://github.com/FasterXML/jackson-dataformats-binary/tree/master/smile[Jackson 2 Smile extension] -is present on the classpath. -.. `MappingJackson2CborHttpMessageConverter` converts to/from CBOR -- added if -https://github.com/FasterXML/jackson-dataformats-binary/tree/master/cbor[Jackson 2 CBOR extension] -is present on the classpath. -.. `AtomFeedHttpMessageConverter` converts Atom feeds -- added if Rome is present on the -classpath. -.. `RssChannelHttpMessageConverter` converts RSS feeds -- added if Rome is present on -the classpath. - -See <> for more information about how to customize these -default converters. - -[NOTE] -==== -Jackson JSON and XML converters are created using `ObjectMapper` instances created by -{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[`Jackson2ObjectMapperBuilder`] -in order to provide a better default configuration. - -This builder 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`. -==== +The above registers a number of Spring MVC +<> also adapting to dependencies +available on the classpath -- for JSON, XML, etc. [[mvc-config-customize]] -=== Customizing the Provided Configuration -To customize the default configuration in Java you simply implement the -`WebMvcConfigurer` interface or more likely extend the class `WebMvcConfigurer` -and override the methods you need: +=== Configuration Mechanism +[.small]#<># + +In Java config implement `WebMvcConfigurer` interface: [source,java,indent=0] [subs="verbatim,quotes"] @@ -4777,13 +4534,12 @@ and override the methods you need: @EnableWebMvc public class WebConfig implements WebMvcConfigurer { - // Override configuration methods... + // Implement configuration methods... } ---- -To customize the default configuration of `` check what -attributes and sub-elements it supports. You can view the +In XML check attributes and sub-elements of ``. You can view the http://schema.spring.io/mvc/spring-mvc.xsd[Spring MVC XML schema] or use the code completion feature of your IDE to discover what attributes and sub-elements are available. @@ -4791,11 +4547,13 @@ available. [[mvc-config-conversion]] === Conversion and Formatting +[.small]#<># 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, override the `addFormatters` method: +formatting library is also installed if Joda Time is present on the classpath. + +In Java config, register custom formatters and converters: [source,java,indent=0] [subs="verbatim,quotes"] @@ -4806,14 +4564,13 @@ register custom formatters and converters, override the `addFormatters` method: @Override public void addFormatters(FormatterRegistry registry) { - // Add formatters and/or converters + // ... } } ---- -In the MVC namespace the same defaults apply when `` is added. -To register custom formatters and converters simply supply a `ConversionService`: +In XML, the same: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -4859,37 +4616,17 @@ See <> and the `FormattingConversionServiceFactoryBean` for more information on when to use FormatterRegistrars. ==== + [[mvc-config-validation]] === Validation +[.small]#<># -Spring provides a <> that can be used for validation in all layers -of an application. In Spring MVC you can configure it for use as a global `Validator` instance, to be used -whenever an `@Valid` or `@Validated` controller method argument is encountered, and/or as a local -`Validator` within a controller through an `@InitBinder` method. Global and local validator -instances can be combined to provide composite validation. +By default if <> is present +on the classpath -- e.g. Hibernate Validator, the `LocalValidatorFactoryBean` is registered +as a global <> for use with `@Valid` and `Validated` on +controller method arguments. -Spring also <> Bean Validation -via `LocalValidatorFactoryBean` which adapts the Spring `org.springframework.validation.Validator` -interface to the Bean Validation `javax.validation.Validator` contract. This class can be -plugged into Spring MVC as a global validator as described next. - -By default use of `@EnableWebMvc` or `` automatically registers Bean -Validation support in Spring MVC through the `LocalValidatorFactoryBean` when a Bean Validation -provider such as Hibernate Validator is detected on the classpath. - -[NOTE] -==== -Sometimes it's convenient to have a `LocalValidatorFactoryBean` injected into a controller -or another class. The easiest way to do that is to declare your own `@Bean` and also mark it -with `@Primary` in order to avoid a conflict with the one provided with the MVC Java config. - -If you prefer to use the one from the MVC Java config, you'll need to override the -`mvcValidator` method from `WebMvcConfigurationSupport` and declare the method to explicitly -return `LocalValidatorFactory` rather than `Validator`. See <> -for information on how to switch to extend the provided configuration. -==== - -Alternatively you can configure your own global `Validator` instance: +In Java config, you can customize the global `Validator` instance: [source,java,indent=0] [subs="verbatim,quotes"] @@ -4900,13 +4637,13 @@ Alternatively you can configure your own global `Validator` instance: @Override public Validator getValidator(); { - // return "global" validator + // ... } } ---- -and in XML: +In XML, the same: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -4926,7 +4663,7 @@ and in XML: ---- -To combine global with local validation, simply add one or more local validator(s): +Note that you can also register ``Validator``'s locally: [source,java,indent=0] [subs="verbatim,quotes"] @@ -4942,19 +4679,18 @@ To combine global with local validation, simply add one or more local validator( } ---- -With this minimal configuration any time an `@Valid` or `@Validated` method argument is encountered, it -will be validated by the configured validators. Any validation violations will automatically -be exposed as errors in the `BindingResult` accessible as a method argument and also renderable -in Spring MVC HTML views. +[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. +==== [[mvc-config-interceptors]] === Interceptors -You can configure `HandlerInterceptors` or `WebRequestInterceptors` to be applied to all -incoming requests or restricted to specific URL path patterns. -An example of registering interceptors in Java: +In Java config, register interceptors to apply to incoming requests: [source,java,indent=0] [subs="verbatim"] @@ -4973,7 +4709,7 @@ An example of registering interceptors in Java: } ---- -And in XML use the `` element: +In XML, the same: [source,xml,indent=0] [subs="verbatim"] @@ -4995,24 +4731,22 @@ And in XML use the `` element: [[mvc-config-content-negotiation]] -=== Content Negotiation -You can configure how Spring MVC determines the requested media types from the request. -The available options are to check a query parameter, the URL path for a file extension, -the "Accept" header, use a fixed list, or a custom strategy. +=== Requested Content Types +[.small]#<># -By default for backwards compatibility the path extension in the request URI is checked -first and the "Accept" header is checked second. However if you must use URL-based content -type resolution, we highly recommend using the query parameter strategy over the path -extension since the latter can cause issues with URI variables, path parameters, and also -in combination with URI decoding. +You can configure how Spring MVC determines the requested media types from the request -- +e.g. `Accept` header, URL path extension, query parameter, etc. -The MVC Java config and the MVC namespace register `json`, `xml`, `rss`, `atom` by -default if corresponding dependencies are on the classpath. Additional -path extension-to-media type mappings may also be registered explicitly and that -also has the effect of whitelisting them as safe extensions for the purpose of RFD -attack detection (see <> for more detail). +By default the URL path extension is checked first -- with `json`, `xml`, `rss`, and `atom` +registered as known extensions depending on classpath dependencies, and the "Accept" header +is checked second. -Below is an example of customizing content negotiation options through the MVC Java config: +Consider changing those defaults to `Accept` header only and if you must use URL-based +content type resolution consider the query parameter strategy over the path extensions. See +<> and <> for +more details. + +In Java config, customize requested content type resolution: [source,java,indent=0] [subs="verbatim,quotes"] @@ -5028,9 +4762,7 @@ Below is an example of customizing content negotiation options through the MVC J } ---- -In the MVC namespace, the `` element has a -`content-negotiation-manager` attribute, which expects a `ContentNegotiationManager` -that in turn can be created with a `ContentNegotiationManagerFactoryBean`: +In XML, the same: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -5047,26 +4779,100 @@ that in turn can be created with a `ContentNegotiationManagerFactoryBean`: ---- -If not using the MVC Java config or the MVC namespace, you'll need to create an instance -of `ContentNegotiationManager` and use it to configure `RequestMappingHandlerMapping` -for request mapping purposes, and `RequestMappingHandlerAdapter` and -`ExceptionHandlerExceptionResolver` for content negotiation purposes. -Note that `ContentNegotiatingViewResolver` now can also be configured with a -`ContentNegotiationManager`, so you can use one shared instance throughout Spring MVC. +[[mvc-config-message-converters]] +=== Message Converters +[.small]#<># -In more advanced cases, it may be useful to configure multiple -`ContentNegotiationManager` instances that in turn may contain custom -`ContentNegotiationStrategy` implementations. For example you could configure -`ExceptionHandlerExceptionResolver` with a `ContentNegotiationManager` that always -resolves the requested media type to `"application/json"`. Or you may want to plug a -custom strategy that has some logic to select a default content type (e.g. either XML or -JSON) if no content types were requested. +Customization of `HttpMessageConverter` can be achieved in Java config by overriding +{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-[`configureMessageConverters()`] +if you want to replace the default converters created by Spring MVC, or by overriding +{api-spring-framework}/web/servlet/config/annotation/WebMvcConfigurer.html#extendMessageConverters-java.util.List-[`extendMessageConverters()`] +if you just want to customize them or add additional converters to the default ones. +Below is an example that adds Jackson JSON and XML converters with a customized +`ObjectMapper` instead of default ones: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Configuration + @EnableWebMvc + public class WebConfiguration implements WebMvcConfigurer { + + @Override + public void configureMessageConverters(List> converters) { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .indentOutput(true) + .dateFormat(new SimpleDateFormat("yyyy-MM-dd")) + .modulesToInstall(new ParameterNamesModule()); + converters.add(new MappingJackson2HttpMessageConverter(builder.build())); + converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build())); + } + + } +---- + +In this example, +{api-spring-framework}/http/converter/json/Jackson2ObjectMapperBuilder.html[Jackson2ObjectMapperBuilder] +is used to create a common configuration for both `MappingJackson2HttpMessageConverter` and +`MappingJackson2XmlHttpMessageConverter` with indentation enabled, a customized date format +and the registration of +https://github.com/FasterXML/jackson-module-parameter-names[jackson-module-parameter-names] +that adds support for accessing parameter names (feature added in Java 8). + +This builder 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`. + +[NOTE] +==== +Enabling indentation with Jackson XML support requires +http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.codehaus.woodstox%22%20AND%20a%3A%22woodstox-core-asl%22[`woodstox-core-asl`] +dependency in addition to http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22jackson-dataformat-xml%22[`jackson-dataformat-xml`] one. +==== + +Other interesting Jackson modules are available: + +. https://github.com/zalando/jackson-datatype-money[jackson-datatype-money]: support for `javax.money` types (unofficial module) +. https://github.com/FasterXML/jackson-datatype-hibernate[jackson-datatype-hibernate]: support for Hibernate specific types and properties (including lazy-loading aspects) + +It is also possible to do the same in XML: + +[source,xml,indent=0] +[subs="verbatim,quotes"] +---- + + + + + + + + + + + + + + +---- [[mvc-config-view-controller]] === View Controllers + This is a shortcut for defining a `ParameterizableViewController` that immediately forwards to a view when invoked. Use it in static cases when there is no Java controller logic to execute before the view generates the response. @@ -5099,6 +4905,8 @@ And the same in XML use the `` element: [[mvc-config-view-resolvers]] === View Resolvers +[.small]#<># + The MVC config simplifies the registration of view resolvers. The following is a Java config example that configures content negotiation view @@ -5188,125 +4996,62 @@ In Java config simply add the respective "Configurer" bean: [[mvc-config-static-resources]] -=== Serving of Resources -This option allows static resource requests following a particular URL pattern to be -served by a `ResourceHttpRequestHandler` from any of a list of `Resource` locations. -This provides a convenient way to serve static resources from locations other than the -web application root, including locations on the classpath. The `cache-period` property -may be used to set far future expiration headers (1 year is the recommendation of -optimization tools such as Page Speed and YSlow) so that they will be more efficiently -utilized by the client. The handler also properly evaluates the `Last-Modified` header -(if present) so that a `304` status code will be returned as appropriate, avoiding -unnecessary overhead for resources that are already cached by the client. For example, -to serve resource requests with a URL pattern of `/resources/{asterisk}{asterisk}` from a -`public-resources` directory within the web application root you would use: +=== Static Resources +[.small]#<># + +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 "/public" under the web application +root or on the classpath under `"/static"`. The resources are 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. + +In Java config: [source,java,indent=0] [subs="verbatim"] ---- @Configuration @EnableWebMvc - public class WebConfig implements WebMvcConfigurer { - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/"); - } - - } ----- - -And the same in XML: - -[source,xml,indent=0] -[subs="verbatim"] ----- - ----- - -To serve these resources with a 1-year future expiration to ensure maximum use of the -browser cache and a reduction in HTTP requests made by the browser: - -[source,java,indent=0] -[subs="verbatim"] ----- - @Configuration - @EnableWebMvc - public class WebConfig implements WebMvcConfigurer { - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926); - } - - } ----- - -And in XML: - -[source,xml,indent=0] -[subs="verbatim"] ----- - ----- - -For more details, see <>. - - -The `mapping` attribute must be an Ant pattern that can be used by -`SimpleUrlHandlerMapping`, and the `location` attribute must specify one or more valid -resource directory locations. Multiple resource locations may be specified using a -comma-separated list of values. The locations specified will be checked in the specified -order for the presence of the resource for any given request. For example, to enable the -serving of resources from both the web application root and from a known path of -`/META-INF/public-web-resources/` in any jar on the classpath use: - -[source,java,indent=0] -[subs="verbatim"] ----- - @EnableWebMvc - @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") - .addResourceLocations("/", "classpath:/META-INF/public-web-resources/"); + .addResourceLocations("/public", "classpath:/static/") + .setCachePeriod(31556926); } } ---- -And in XML: +In XML: [source,xml,indent=0] [subs="verbatim,quotes"] ---- - + ---- -When serving resources that may change when a new version of the application is -deployed it is recommended that you incorporate a version string into the mapping -pattern used to request the resources so that you may force clients to request the -newly deployed version of your application's resources. Support for versioned URLs is -built into the framework and can be enabled by configuring a resource chain -on the resource handler. The chain consists of one more `ResourceResolver` -instances followed by one or more `ResourceTransformer` instances. Together they -can provide arbitrary resolution and transformation of resources. +See also +<>. -The built-in `VersionResourceResolver` can be configured with different strategies. -For example a `FixedVersionStrategy` can use a property, a date, or other as the version. -A `ContentVersionStrategy` uses an MD5 hash computed from the content of the resource -(known as "fingerprinting" URLs). Note that the `VersionResourceResolver` will automatically -use the resolved version strings as HTTP ETag header values when serving resources. +The resource handler also supports a chain of +{api-spring-framework}/web/servlet/resource/ResourceResolver.html[ResourceResolver]'s and +{api-spring-framework}/web/servlet/resource/ResourceTransformer.html[ResourceResolver]'s. +which can be used to create a toolchain for working with optimized resources. -`ContentVersionStrategy` is a good default choice to use except in cases where -it cannot be used (e.g. with JavaScript module loaders). You can configure -different version strategies against different patterns as shown below. Keep in mind -also that computing content-based versions is expensive and therefore resource chain -caching should be enabled in production. +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. -Java config example; +For example in Java config; [source,java,indent=0] [subs="verbatim"] @@ -5318,20 +5063,20 @@ Java config example; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") - .addResourceLocations("/public-resources/") - .resourceChain(true).addResolver( - new VersionResourceResolver().addContentVersionStrategy("/**")); + .addResourceLocations("/public/") + .resourceChain(true) + .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); } } ---- -XML example: +In XML, the same: [source,xml,indent=0] [subs="verbatim"] ---- - + @@ -5343,24 +5088,22 @@ XML example: ---- -In order for the above to work the application must also -render URLs with versions. The easiest way to do that is to configure the -`ResourceUrlEncodingFilter` which wraps the response and overrides its `encodeURL` method. -This will work in JSPs, FreeMarker, and any other view technology that calls the response -`encodeURL` method. Alternatively, an application can also inject and use directly the -`ResourceUrlProvider` bean, which is automatically declared with the MVC Java config and -the MVC namespace. +You can use `ResourceUrlProvider` to rewrite URLs and apply the full chain of resolvers and +transformers -- e.g. to insert versions. The MVC config provides a `ResourceUrlProvider` +bean so it can be injected into others. You can also make the rewrite transparent with the +`ResourceUrlEncodingFilter` for Thymeleaf, JSPs, FreeMarker, and others with URL tags that +rely on `HttpServletResponse#encodeURL`. -Webjars are also supported with `WebJarsResourceResolver`, which is automatically registered -when the `"org.webjars:webjars-locator"` library is on classpath. This resolver allows -the resource chain to resolve version agnostic libraries from HTTP GET requests -`"GET /jquery/jquery.min.js"` will return resource `"/jquery/1.2.0/jquery.min.js"`. -It also works by rewriting resource URLs in templates -`