1.4. 函数端点

预计阅读时间: 23 分钟

Spring Web MVC includes WebMvc.fn, a lightweight functional programming model in which functions are used to route and handle requests and contracts are designed for immutability. It is an alternative to the annotation-based programming model but otherwise runs on the same DispatcherServlet.
Spring Web MVC 包括 WebMvc.fn,这是一个轻量级的函数式编程模型,其中使用函数来路由和处理请求,并设计合同以实现不可变性。它是基于注解的编程模型的替代品,但除此之外,它运行在同一个 DispatcherServlet 上。

(#webmvc-fn-overview)1.4.1. Overview 1.4.1. 概述

In WebMvc.fn, an HTTP request is handled with a HandlerFunction: a function that takes ServerRequest and returns a ServerResponse. Both the request and the response object have immutable contracts that offer JDK 8-friendly access to the HTTP request and response. HandlerFunction is the equivalent of the body of a @RequestMapping method in the annotation-based programming model.
在 WebMvc.fn 中,一个 HTTP 请求通过一个 HandlerFunction 处理:一个接收 ServerRequest 并返回 ServerResponse 的函数。请求和响应对象都有不可变合约,提供了对 HTTP 请求和响应的 JDK 8 友好访问。 HandlerFunction 是注解编程模型中 @RequestMapping 方法体的等效。

Incoming requests are routed to a handler function with a RouterFunction: a function that takes ServerRequest and returns an optional HandlerFunction (i.e. Optional<HandlerFunction>). When the router function matches, a handler function is returned; otherwise an empty Optional. RouterFunction is the equivalent of a @RequestMapping annotation, but with the major difference that router functions provide not just data, but also behavior.
进入的请求被路由到一个带有 RouterFunction 的处理函数:一个接收 ServerRequest 并返回一个可选的 HandlerFunction (即 Optional<HandlerFunction> )的函数。当路由函数匹配时,返回一个处理函数;否则返回一个空的 Optional。 RouterFunction 相当于一个 @RequestMapping 注解,但主要区别在于路由函数不仅提供数据,还提供行为。

RouterFunctions.route() provides a router builder that facilitates the creation of routers, as the following example shows:
RouterFunctions.route() 提供了一个路由构建器,它有助于创建路由,如下例所示:

import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.servlet.function.RequestPredicates.*; import static org.springframework.web.servlet.function.RouterFunctions.route; PersonRepository repository = ... PersonHandler handler = new PersonHandler(repository); RouterFunction<ServerResponse> route = route() .GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) .GET("/person", accept(APPLICATION_JSON), handler::listPeople) .POST("/person", handler::createPerson) .build(); public class PersonHandler { // ... public ServerResponse listPeople(ServerRequest request) { // ... } public ServerResponse createPerson(ServerRequest request) { // ... } public ServerResponse getPerson(ServerRequest request) { // ... } }

1

Create router using the router DSL.
使用路由 DSL 创建路由器。

import org.springframework.web.servlet.function.router val repository: PersonRepository = ... val handler = PersonHandler(repository) val route = router { (1) accept(APPLICATION_JSON).nest { GET("/person/{id}", handler::getPerson) GET("/person", handler::listPeople) } POST("/person", handler::createPerson) } class PersonHandler(private val repository: PersonRepository) { // ... fun listPeople(request: ServerRequest): ServerResponse { // ... } fun createPerson(request: ServerRequest): ServerResponse { // ... } fun getPerson(request: ServerRequest): ServerResponse { // ... } }

If you register the RouterFunction as a bean, for instance by exposing it in a @Configuration class, it will be auto-detected by the servlet, as explained in Running a Server.
如果您将 RouterFunction 注册为一个 bean,例如通过在一个 @Configuration 类中公开它,Servlet 将自动检测到它,如《运行服务器》中所述。

(#webmvc-fn-handler-functions)1.4.2. HandlerFunction 1.4.2. 处理函数

ServerRequest and ServerResponse are immutable interfaces that offer JDK 8-friendly access to the HTTP request and response, including headers, body, method, and status code.
ServerRequestServerResponse 是不可变接口,提供了对 HTTP 请求和响应(包括头部、正文、方法和状态码)的 JDK 8 兼容访问。

(#webmvc-fn-request)ServerRequest 服务器请求

ServerRequest provides access to the HTTP method, URI, headers, and query parameters, while access to the body is provided through the body methods.
ServerRequest 提供了对 HTTP 方法、URI、头部和查询参数的访问,而通过 body 方法提供对主体的访问。

The following example extracts the request body to a String:
以下示例将请求体提取到 String

String string = request.body(String.class);
val string = request.body<String>()

The following example extracts the body to a List<Person>, where Person objects are decoded from a serialized form, such as JSON or XML:
以下示例将体提取到 List<Person> ,其中 Person 对象是从序列化形式(如 JSON 或 XML)解码得到的:

List<Person> people = request.body(new ParameterizedTypeReference<List<Person>>() {});
val people = request.body<Person>()

The following example shows how to access parameters:
以下示例展示了如何访问参数:

MultiValueMap<String, String> params = request.params();
val map = request.params()

(#webmvc-fn-response)ServerResponse 服务器响应

ServerResponse provides access to the HTTP response and, since it is immutable, you can use a build method to create it. You can use the builder to set the response status, to add response headers, or to provide a body. The following example creates a 200 (OK) response with JSON content:
ServerResponse 提供对 HTTP 响应的访问,由于它是不可变的,您可以使用 build 方法来创建它。您可以使用构建器来设置响应状态、添加响应头或提供正文。以下示例创建了一个包含 JSON 内容的 200(OK)响应:

Person person = ... ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
val person: Person = ... ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person)

The following example shows how to build a 201 (CREATED) response with a Location header and no body:
以下示例展示了如何使用 Location 头部和没有正文构建一个 201(已创建)响应:

URI location = ... ServerResponse.created(location).build();
val location: URI = ... ServerResponse.created(location).build()

You can also use an asynchronous result as the body, in the form of a CompletableFuture, Publisher, or any other type supported by the ReactiveAdapterRegistry. For instance:
您还可以使用异步结果作为正文,形式为 CompletableFuturePublisher 或任何由 ReactiveAdapterRegistry 支持的其它类型。例如:

Mono<Person> person = webClient.get().retrieve().bodyToMono(Person.class); ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person);
val person = webClient.get().retrieve().awaitBody<Person>() ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person)

If not just the body, but also the status or headers are based on an asynchronous type, you can use the static async method on ServerResponse, which accepts CompletableFuture<ServerResponse>, Publisher<ServerResponse>, or any other asynchronous type supported by the ReactiveAdapterRegistry. For instance:
如果不只是正文,状态或头部也是基于异步类型,您可以使用 ServerResponse 上的静态 async 方法,该方法接受 CompletableFuture<ServerResponse>Publisher<ServerResponse> 或任何由 ReactiveAdapterRegistry 支持的异步类型。例如:

Mono<ServerResponse> asyncResponse = webClient.get().retrieve().bodyToMono(Person.class) .map(p -> ServerResponse.ok().header("Name", p.name()).body(p)); ServerResponse.async(asyncResponse);

Server-Sent Events can be provided via the static sse method on ServerResponse. The builder provided by that method allows you to send Strings, or other objects as JSON. For example:
服务器端发送事件可以通过 ServerResponse 上的静态 sse 方法提供。该方法提供的构建器允许您发送字符串或其他作为 JSON 的对象。例如:

public RouterFunction<ServerResponse> sse() { return route(GET("/sse"), request -> ServerResponse.sse(sseBuilder -> { // Save the sseBuilder object somewhere.. })); } // In some other thread, sending a String sseBuilder.send("Hello world"); // Or an object, which will be transformed into JSON Person person = ... sseBuilder.send(person); // Customize the event by using the other methods sseBuilder.id("42") .event("sse event") .data(person); // and done at some point sseBuilder.complete();
fun sse(): RouterFunction<ServerResponse> = router { GET("/sse") { request -> ServerResponse.sse { sseBuilder -> // Save the sseBuilder object somewhere.. } } // In some other thread, sending a String sseBuilder.send("Hello world") // Or an object, which will be transformed into JSON val person = ... sseBuilder.send(person) // Customize the event by using the other methods sseBuilder.id("42") .event("sse event") .data(person) // and done at some point sseBuilder.complete()

(#webmvc-fn-handler-classes)Handler Classes 处理器类

We can write a handler function as a lambda, as the following example shows:
我们可以将处理函数写为一个 lambda 函数,如下例所示:

HandlerFunction<ServerResponse> helloWorld = request -> ServerResponse.ok().body("Hello World");

1

listPeople is a handler function that returns all Person objects found in the repository as JSON.
listPeople 是一个返回在存储库中找到的所有 Person 对象的处理函数 JSON.

2

createPerson is a handler function that stores a new Person contained in the request body.
createPerson 是一个存储请求体中包含的新 Person 的处理函数。

3

getPerson is a handler function that returns a single person, identified by the id path variable. We retrieve that Person from the repository and create a JSON response, if it is found. If it is not found, we return a 404 Not Found response.
getPerson 是一个返回单个人员的处理函数,通过 id 路径进行识别 变量。我们从仓库中检索 Person ,如果它 找到。如果未找到,我们返回 404 未找到响应。

val helloWorld: (ServerRequest) -> ServerResponse = { ServerResponse.ok().body("Hello World") }

That is convenient, but in an application we need multiple functions, and multiple inline lambda’s can get messy. Therefore, it is useful to group related handler functions together into a handler class, which has a similar role as @Controller in an annotation-based application. For example, the following class exposes a reactive Person repository:
这是方便的,但在一个应用程序中我们需要多个功能,多个内联 lambda 表达式可能会变得混乱。因此,将相关的处理函数分组到一个处理类中是有用的,这个类在基于注解的应用程序中类似于 @Controller 的作用。例如,以下类公开了一个反应式 Person 仓库:

import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.reactive.function.server.ServerResponse.ok; public class PersonHandler { private final PersonRepository repository; public PersonHandler(PersonRepository repository) { this.repository = repository; } public ServerResponse listPeople(ServerRequest request) { (1) List<Person> people = repository.allPeople(); return ok().contentType(APPLICATION_JSON).body(people); } public ServerResponse createPerson(ServerRequest request) throws Exception { (2) Person person = request.body(Person.class); repository.savePerson(person); return ok().build(); } public ServerResponse getPerson(ServerRequest request) { (3) int personId = Integer.parseInt(request.pathVariable("id")); Person person = repository.getPerson(personId); if (person != null) { return ok().contentType(APPLICATION_JSON).body(person); } else { return ServerResponse.notFound().build(); } } }

1

listPeople is a handler function that returns all Person objects found in the repository as JSON.
listPeople 是一个返回在存储库中找到的所有 Person 对象的处理函数 JSON.

2

createPerson is a handler function that stores a new Person contained in the request body.
createPerson 是一个存储请求体中包含的新 Person 的处理函数。

3

getPerson is a handler function that returns a single person, identified by the id path variable. We retrieve that Person from the repository and create a JSON response, if it is found. If it is not found, we return a 404 Not Found response.
getPerson 是一个返回单个人员的处理函数,通过 id 路径进行识别 变量。我们从仓库中检索 Person ,如果它 找到。如果未找到,我们返回 404 未找到响应。

class PersonHandler(private val repository: PersonRepository) { fun listPeople(request: ServerRequest): ServerResponse { (1) val people: List<Person> = repository.allPeople() return ok().contentType(APPLICATION_JSON).body(people); } fun createPerson(request: ServerRequest): ServerResponse { (2) val person = request.body<Person>() repository.savePerson(person) return ok().build() } fun getPerson(request: ServerRequest): ServerResponse { (3) val personId = request.pathVariable("id").toInt() return repository.getPerson(personId)?.let { ok().contentType(APPLICATION_JSON).body(it) } ?: ServerResponse.notFound().build() } }

(#webmvc-fn-handler-validation)Validation 验证

A functional endpoint can use Spring’s validation facilities to apply validation to the request body. For example, given a custom Spring Validator implementation for a Person:
一个功能端点可以使用 Spring 的验证功能来对请求体进行验证。例如,给定一个针对 Person 的自定义 Spring 验证器实现。

public class PersonHandler { private final Validator validator = new PersonValidator(); (1) // ... public ServerResponse createPerson(ServerRequest request) { Person person = request.body(Person.class); validate(person); (2) repository.savePerson(person); return ok().build(); } private void validate(Person person) { Errors errors = new BeanPropertyBindingResult(person, "person"); validator.validate(person, errors); if (errors.hasErrors()) { throw new ServerWebInputException(errors.toString()); (3) } } }

1

Create Validator instance. 创建 Validator 实例。

2

Apply validation. 应用验证。

3

Raise exception for a 400 response.
抛出 400 响应的异常。

class PersonHandler(private val repository: PersonRepository) { private val validator = PersonValidator() (1) // ... fun createPerson(request: ServerRequest): ServerResponse { val person = request.body<Person>() validate(person) (2) repository.savePerson(person) return ok().build() } private fun validate(person: Person) { val errors: Errors = BeanPropertyBindingResult(person, "person") validator.validate(person, errors) if (errors.hasErrors()) { throw ServerWebInputException(errors.toString()) (3) } } }

1

Create Validator instance.

2

Apply validation.

3

Raise exception for a 400 response.

Handlers can also use the standard bean validation API (JSR-303) by creating and injecting a global Validator instance based on LocalValidatorFactoryBean. See Spring Validation.
处理器还可以通过创建和注入基于 LocalValidatorFactoryBean 的全局 Validator 实例来使用标准的 bean 验证 API(JSR-303)。请参阅 Spring Validation。

(#webmvc-fn-router-functions)1.4.3.RouterFunction

Router functions are used to route the requests to the corresponding HandlerFunction. Typically, you do not write router functions yourself, but rather use a method on the RouterFunctions utility class to create one. RouterFunctions.route() (no parameters) provides you with a fluent builder for creating a router function, whereas RouterFunctions.route(RequestPredicate, HandlerFunction) offers a direct way to create a router.
路由功能用于将请求路由到相应的 HandlerFunction 。通常,您不会自己编写路由功能,而是使用 RouterFunctions 工具类上的方法来创建一个。 RouterFunctions.route() (无参数)为您提供创建路由功能的流畅构建器,而 RouterFunctions.route(RequestPredicate, HandlerFunction) 提供了一种直接创建路由的方式。

Generally, it is recommended to use the route() builder, as it provides convenient short-cuts for typical mapping scenarios without requiring hard-to-discover static imports. For instance, the router function builder offers the method GET(String, HandlerFunction) to create a mapping for GET requests; and POST(String, HandlerFunction) for POSTs.
通常建议使用 route() 构建器,因为它为典型的映射场景提供了方便的快捷方式,而不需要发现静态导入。例如,路由函数构建器提供了 GET(String, HandlerFunction) 方法来创建 GET 请求的映射;以及 POST(String, HandlerFunction) 用于 POST 请求。

Besides HTTP method-based mapping, the route builder offers a way to introduce additional predicates when mapping to requests. For each HTTP method there is an overloaded variant that takes a RequestPredicate as a parameter, through which additional constraints can be expressed.
除了基于 HTTP 方法的映射之外,路由构建器提供了一种在映射到请求时引入额外谓词的方法。对于每种 HTTP 方法,都有一个重载变体,它接受一个 RequestPredicate 作为参数,通过它可以表达额外的约束。

(#webmvc-fn-predicates)Predicates 谓词

You can write your own RequestPredicate, but the RequestPredicates utility class offers commonly used implementations, based on the request path, HTTP method, content-type, and so on. The following example uses a request predicate to create a constraint based on the Accept header:
您可以编写自己的 RequestPredicate ,但 RequestPredicates 实用工具类提供了基于请求路径、HTTP 方法、内容类型等常用的实现。以下示例使用请求谓词根据 Accept 头创建约束:

RouterFunction<ServerResponse> route = RouterFunctions.route() .GET("/hello-world", accept(MediaType.TEXT_PLAIN), request -> ServerResponse.ok().body("Hello World")).build();
import org.springframework.web.servlet.function.router val route = router { GET("/hello-world", accept(TEXT_PLAIN)) { ServerResponse.ok().body("Hello World") } }

You can compose multiple request predicates together by using:
您可以通过以下方式组合多个请求谓词:

  • RequestPredicate.and(RequestPredicate)— both must match.
    RequestPredicate.and(RequestPredicate) — 两者必须匹配。

  • RequestPredicate.or(RequestPredicate)— either can match.
    RequestPredicate.or(RequestPredicate) — 两者均可匹配。

Many of the predicates from RequestPredicates are composed. For example, RequestPredicates.GET(String) is composed from RequestPredicates.method(HttpMethod) and RequestPredicates.path(String). The example shown above also uses two request predicates, as the builder uses RequestPredicates.GET internally, and composes that with the accept predicate.
许多来自 RequestPredicates 的谓词是组合的。例如, RequestPredicates.GET(String)RequestPredicates.method(HttpMethod)RequestPredicates.path(String) 组合而成。上面显示的示例还使用了两个请求谓词,因为构建器内部使用 RequestPredicates.GET ,并将其与 accept 谓词组合。

(#webmvc-fn-routes)Routes 路由

Router functions are evaluated in order: if the first route does not match, the second is evaluated, and so on. Therefore, it makes sense to declare more specific routes before general ones.
路由功能按顺序评估:如果第一个路由不匹配,则评估第二个,依此类推。因此,在通用路由之前声明更具体的路由是有意义的。
This is also important when registering router functions as Spring beans, as will be described later. Note that this behavior is different from the annotation-based programming model, where the "most specific" controller method is picked automatically.
这同样重要,当将路由器功能注册为 Spring Bean 时,这一点将在后面进行说明。请注意,这种行为与基于注解的编程模型不同,在注解编程模型中,“最具体的”控制器方法会被自动选择。

When using the router function builder, all defined routes are composed into one RouterFunction that is returned from build(). There are also other ways to compose multiple router functions together:
当使用路由功能构建器时,所有定义的路由都被组合成一个由 build() 返回的 RouterFunction 。还有其他方法可以将多个路由功能组合在一起:

  • add(RouterFunction) on the RouterFunctions.route() builder
    add(RouterFunction)RouterFunctions.route() 构建器上

  • RouterFunction.and(RouterFunction)

  • RouterFunction.andRoute(RequestPredicate, HandlerFunction)— shortcut for RouterFunction.and() with nested RouterFunctions.route().
    RouterFunction.andRoute(RequestPredicate, HandlerFunction)RouterFunction.and() 的快捷方式,包含嵌套的 RouterFunctions.route()

The following example shows the composition of four routes:
以下示例展示了四条路由的组成:

import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.web.servlet.function.RequestPredicates.*; PersonRepository repository = ... PersonHandler handler = new PersonHandler(repository); RouterFunction<ServerResponse> otherRoute = ... RouterFunction<ServerResponse> route = route() .GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) (1) .GET("/person", accept(APPLICATION_JSON), handler::listPeople) (2) .POST("/person", handler::createPerson) (3) .add(otherRoute) (4) .build();

1

GET /person/{id} with an Accept header that matches JSON is routed to PersonHandler.getPerson
GET /person/{id} 与匹配 JSON 的 Accept 标题头进行路由 PersonHandler.getPerson

2

GET /person with an Accept header that matches JSON is routed to PersonHandler.listPeople
GET /person 与匹配 JSON 的 Accept 标题头进行路由 PersonHandler.listPeople

3

POST /person with no additional predicates is mapped to PersonHandler.createPerson, and
POST /person 没有其他谓词映射到 PersonHandler.createPerson ,和

4

otherRoute is a router function that is created elsewhere, and added to the route built.
otherRoute 是在别处创建并添加到构建的路由中的路由器功能。

import org.springframework.http.MediaType.APPLICATION_JSON import org.springframework.web.servlet.function.router val repository: PersonRepository = ... val handler = PersonHandler(repository); val otherRoute = router { } val route = router { GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson) (1) GET("/person", accept(APPLICATION_JSON), handler::listPeople) (2) POST("/person", handler::createPerson) (3) }.and(otherRoute) (4)

1

GET /person/{id} with an Accept header that matches JSON is routed to PersonHandler.getPerson

2

GET /person with an Accept header that matches JSON is routed to PersonHandler.listPeople

3

POST /person with no additional predicates is mapped to PersonHandler.createPerson, and

4

otherRoute is a router function that is created elsewhere, and added to the route built.

(#nested-routes)Nested Routes 嵌套路由

It is common for a group of router functions to have a shared predicate, for instance a shared path. In the example above, the shared predicate would be a path predicate that matches /person, used by three of the routes. When using annotations, you would remove this duplication by using a type-level @RequestMapping annotation that maps to /person. In WebMvc.fn, path predicates can be shared through the path method on the router function builder. For instance, the last few lines of the example above can be improved in the following way by using nested routes:
一组路由功能通常具有共享谓词,例如共享路径。在上面的示例中,共享谓词将是一个匹配 /person 的路径谓词,由三条路由使用。在使用注解时,您可以通过使用映射到 /person 的类型级 @RequestMapping 注解来消除这种重复。在 WebMvc.fn 中,可以通过路由功能构建器的 path 方法共享路径谓词。例如,可以通过使用嵌套路由以下方式改进上面示例的最后几行:

RouterFunction<ServerResponse> route = route() .path("/person", builder -> builder (1) .GET("/{id}", accept(APPLICATION_JSON), handler::getPerson) .GET(accept(APPLICATION_JSON), handler::listPeople) .POST(handler::createPerson)) .build();

1

Note that second parameter of path is a consumer that takes the router builder.
请注意, path 的第二个参数是一个接受路由构建器的消费者。

import org.springframework.web.servlet.function.router val route = router { "/person".nest { GET("/{id}", accept(APPLICATION_JSON), handler::getPerson) GET(accept(APPLICATION_JSON), handler::listPeople) POST(handler::createPerson) } }

Though path-based nesting is the most common, you can nest on any kind of predicate by using the nest method on the builder. The above still contains some duplication in the form of the shared Accept-header predicate. We can further improve by using the nest method together with accept:
尽管基于路径的嵌套是最常见的,您可以通过在构建器上使用 nest 方法在任何类型的谓词上进行嵌套。上面仍然包含一些重复,形式为共享的 Accept -header 谓词。我们可以通过结合使用 nest 方法和 accept :进一步改进。

RouterFunction<ServerResponse> route = route() .path("/person", b1 -> b1 .nest(accept(APPLICATION_JSON), b2 -> b2 .GET("/{id}", handler::getPerson) .GET(handler::listPeople)) .POST(handler::createPerson)) .build();
import org.springframework.web.servlet.function.router val route = router { "/person".nest { accept(APPLICATION_JSON).nest { GET("/{id}", handler::getPerson) GET("", handler::listPeople) POST(handler::createPerson) } } }

(#webmvc-fn-running)1.4.4. Running a Server

1.4.4. 运行服务器

You typically run router functions in a DispatcherHandler-based setup through the MVC Config, which uses Spring configuration to declare the components required to process requests. The MVC Java configuration declares the following infrastructure components to support functional endpoints:
您通常通过 MVC 配置在基于 DispatcherHandler 的设置中运行路由功能,该配置使用 Spring 配置来声明处理请求所需的组件。MVC Java 配置声明以下基础设施组件以支持功能端点:

  • RouterFunctionMapping: Detects one or more RouterFunction<?> beans in the Spring configuration, orders them, combines them through RouterFunction.andOther, and routes requests to the resulting composed RouterFunction.
    RouterFunctionMapping :检测 Spring 配置中的一个或多个 RouterFunction<?> 豆,对它们进行排序,通过 RouterFunction.andOther 进行组合,并将请求路由到生成的组合 RouterFunction

  • HandlerFunctionAdapter: Simple adapter that lets DispatcherHandler invoke a HandlerFunction that was mapped to a request.
    HandlerFunctionAdapter :一个简单的适配器,允许 DispatcherHandler 调用映射到请求的 HandlerFunction

The preceding components let functional endpoints fit within the DispatcherServlet request processing lifecycle and also (potentially) run side by side with annotated controllers, if any are declared. It is also how functional endpoints are enabled by the Spring Boot Web starter.
前述组件使功能端点能够适应 DispatcherServlet 请求处理生命周期,并且(可能)与声明的任何注解控制器并行运行。这也是 Spring Boot Web 启动器启用功能端点的方式。

The following example shows a WebFlux Java configuration:
以下示例展示了 WebFlux Java 配置:

@Configuration @EnableMvc public class WebConfig implements WebMvcConfigurer { @Bean public RouterFunction<?> routerFunctionA() { // ... } @Bean public RouterFunction<?> routerFunctionB() { // ... } // ... @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // configure message conversion... } @Override public void addCorsMappings(CorsRegistry registry) { // configure CORS... } @Override public void configureViewResolvers(ViewResolverRegistry registry) { // configure view resolution for HTML rendering... } }
@Configuration @EnableMvc class WebConfig : WebMvcConfigurer { @Bean fun routerFunctionA(): RouterFunction<*> { // ... } @Bean fun routerFunctionB(): RouterFunction<*> { // ... } // ... override fun configureMessageConverters(converters: List<HttpMessageConverter<*>>) { // configure message conversion... } override fun addCorsMappings(registry: CorsRegistry) { // configure CORS... } override fun configureViewResolvers(registry: ViewResolverRegistry) { // configure view resolution for HTML rendering... } }

(#webmvc-fn-handler-filter-function)1.4.5. Filtering Handler Functions

1.4.5. 过滤处理函数

You can filter handler functions by using the before, after, or filter methods on the routing function builder. With annotations, you can achieve similar functionality by using @ControllerAdvice, a ServletFilter, or both. The filter will apply to all routes that are built by the builder. This means that filters defined in nested routes do not apply to "top-level" routes. For instance, consider the following example:
您可以通过在路由函数构建器上使用 beforeafterfilter 方法来过滤处理函数。使用注解,您可以通过使用 @ControllerAdvice 、一个 ServletFilter 或两者结合来实现类似的功能。该过滤器将应用于由构建器构建的所有路由。这意味着嵌套路由中定义的过滤器不适用于“顶级”路由。例如,考虑以下示例:

RouterFunction<ServerResponse> route = route() .path("/person", b1 -> b1 .nest(accept(APPLICATION_JSON), b2 -> b2 .GET("/{id}", handler::getPerson) .GET(handler::listPeople) .before(request -> ServerRequest.from(request) (1) .header("X-RequestHeader", "Value") .build())) .POST(handler::createPerson)) .after((request, response) -> logResponse(response)) (2) .build();

1

The before filter that adds a custom request header is only applied to the two GET routes.
before 添加自定义请求头的过滤器仅应用于两个 GET 路由。

2

The after filter that logs the response is applied to all routes, including the nested ones.
after 记录响应的过滤器应用于所有路由,包括嵌套的路由。

import org.springframework.web.servlet.function.router val route = router { "/person".nest { GET("/{id}", handler::getPerson) GET(handler::listPeople) before { (1) ServerRequest.from(it) .header("X-RequestHeader", "Value").build() } } POST(handler::createPerson) after { _, response -> (2) logResponse(response) } }

1

The before filter that adds a custom request header is only applied to the two GET routes.

2

The after filter that logs the response is applied to all routes, including the nested ones.

The filter method on the router builder takes a HandlerFilterFunction: a function that takes a ServerRequest and HandlerFunction and returns a ServerResponse. The handler function parameter represents the next element in the chain. This is typically the handler that is routed to, but it can also be another filter if multiple are applied.
路由构建器上的 filter 方法接受一个 HandlerFilterFunction :一个接受 ServerRequestHandlerFunction 并返回 ServerResponse 的函数。处理函数参数表示链中的下一个元素。这通常是路由到的处理程序,但也可以是另一个过滤器,如果应用了多个过滤器。

Now we can add a simple security filter to our route, assuming that we have a SecurityManager that can determine whether a particular path is allowed. The following example shows how to do so:
现在我们可以在我们的路由中添加一个简单的安全过滤器,假设我们有一个 SecurityManager 可以确定特定的路径是否允许。以下示例展示了如何操作:

SecurityManager securityManager = ... RouterFunction<ServerResponse> route = route() .path("/person", b1 -> b1 .nest(accept(APPLICATION_JSON), b2 -> b2 .GET("/{id}", handler::getPerson) .GET(handler::listPeople)) .POST(handler::createPerson)) .filter((request, next) -> { if (securityManager.allowAccessTo(request.path())) { return next.handle(request); } else { return ServerResponse.status(UNAUTHORIZED).build(); } }) .build();
import org.springframework.web.servlet.function.router val securityManager: SecurityManager = ... val route = router { ("/person" and accept(APPLICATION_JSON)).nest { GET("/{id}", handler::getPerson) GET("", handler::listPeople) POST(handler::createPerson) filter { request, next -> if (securityManager.allowAccessTo(request.path())) { next(request) } else { status(UNAUTHORIZED).build(); } } } }

The preceding example demonstrates that invoking the next.handle(ServerRequest) is optional. We only let the handler function be run when access is allowed.
前一个示例演示了调用 next.handle(ServerRequest) 是可选的。我们只允许在访问被允许时运行处理器函数。

Besides using the filter method on the router function builder, it is possible to apply a filter to an existing router function via RouterFunction.filter(HandlerFilterFunction).
除了在路由器功能构建器上使用 filter 方法外,还可以通过 RouterFunction.filter(HandlerFilterFunction) 将过滤器应用于现有的路由器功能。

CORS support for functional endpoints is provided through a dedicated CorsFilter.
CORS 支持功能端点通过专用 CorsFilter 提供。