路由器函数被用来 将请求路由到相应的处理程序函数。通常情况下,你不会自己编写路由器函数,而是使用 RouterFunctions 实用类中的一个方法来创建一个。RouterFunctions.route() (no parameters)
为你提供了一个创建路由器函数的流畅构建器,而RouterFunctions.route(RequestPredicate, HandlerFunction)
提供了一个创建路由器的直接方法。
一般来说,我们推荐使用 route()
构建器,因为它为典型的映射场景提供了方便的捷径,而不需要难以发现的静态导入。例如,路由器函数生成器提供了 GET(String, HandlerFunction)
方法来创建 GET 请求的映射;以及 POST(String, HandlerFunction)
方法来创建 POST 请求的映射。
除了基于 HTTP 方法的映射,路由生成器还提供了一种方法,在映射到请求时引入额外的谓词(Gateway 中的断言)。每个 HTTP 方法都有一个重载的变体,它以 RequestPredicate 为参数,通过它可以表达额外的约束。
Predicates
你可以编写你自己的 RequestPredicate,但是 RequestPredicates 实用类提供了常用的实现,基于请求路径、HTTP 方法、内容类型等等。下面的例子使用一个请求谓词来创建一个基于 Accept 头的约束:
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN), // 这里断言了
request -> ServerResponse.ok().body("Hello World")).build();
你可以通过使用以下方式将多个请求谓词组合在一起:
RequestPredicate.and(RequestPredicate)
- 两个都必须匹配。RequestPredicate.or(RequestPredicate)
- 任何一个都可以匹配。
许多来自 RequestPredicates 的谓词都是组成的。例如,RequestPredicates.GET(String)
是由 RequestPredicates.method(HttpMethod)
和 RequestPredicates.path(String)
组成。上面的例子也使用了两个请求谓词,因为构建器在内部使用了 RequestPredicates.GET,并将其与 accept 谓词组合。
Routes
路由函数是按顺序评估的:如果第一个路由不匹配,就评估第二个,以此类推。因此,在一般路由之前声明更具体的路由是有意义的。在将路由器函数注册为 Spring Bean 时,这一点也很重要,后面会介绍。请注意,这种行为与基于注解的编程模型不同,在后者中,「最具体的」控制器方法会被自动选中。
当使用路由函数构建器时,所有定义的路由都被组成一个 RouterFunction,并从 build()
返回。也有其他方法可以将多个路由函数组成在一起:
RouterFunctions.route()
后面使用add(RouterFunction)
最后 builderRouterFunction.and(RouterFunction)
RouterFunction.andRoute(RequestPredicate, HandlerFunction)
— 是RouterFunction.and()
和嵌套RouterFunctions.route()
的快捷方式.
下面的例子显示了四条路线的组成:
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()
// 带有与 JSON 匹配的 accept 的 GET/person/{id} 被路由到 PersonHandler.getPerson
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
// 带有与 JSON 匹配的 accept 的 GET/person 被路由到 PersonHandler.getPerson
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
// 没有附加断言的 /person 被路由到 PersonHandler.createPerson
.POST("/person", handler::createPerson)
// 是一个在其他地方创建的路由函数,并添加到构建的路由中。
.add(otherRoute)
.build();
Nested Routes/ 嵌套路由
一组路由函数有一个共享谓词是很常见的,例如共享路径。在上面的例子中,共享谓词是一个匹配 /person
的路径谓词,被三个路由使用。当使用注解时,你可以通过使用一个类型级别的 @RequestMapping
注解来消除这种重复,该注解映射到 /person
。在 WebMvc.fn 中,路径谓词可以通过路由器函数生成器的路径方法来共享。例如,上面例子的最后几行可以通过使用嵌套的路由而得到如下改进:
RouterFunction<ServerResponse> route = route()
// 请注意,path 的第二个参数是一个消费者,它接受路由的构建者。
.path("/person", builder -> builder
.GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET(accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson))
.build();
尽管基于路径的嵌套是最常见的,但你可以通过使用构建器上的嵌套方法在任何种类的谓词上嵌套。上面的内容仍然包含一些共享的 Accept-header 谓词形式的重复。我们可以通过与 accept 一起使用 nest 方法来进一步改进:
RouterFunction<ServerResponse> route = route()
.path("/person", b1 -> b1
.nest(accept(APPLICATION_JSON), b2 -> b2
.GET("/{id}", handler::getPerson)
.GET(handler::listPeople))
.POST("/person", handler::createPerson))
.build();