路由器函数被用来 将请求路由到相应的处理程序函数。通常情况下,你不会自己编写路由器函数,而是使用 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 头的约束:

  1. RouterFunction<ServerResponse> route = RouterFunctions.route()
  2. .GET("/hello-world", accept(MediaType.TEXT_PLAIN), // 这里断言了
  3. 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) 最后 builder
  • RouterFunction.and(RouterFunction)
  • RouterFunction.andRoute(RequestPredicate, HandlerFunction) —  是RouterFunction.and() 和嵌套 RouterFunctions.route() 的快捷方式.

下面的例子显示了四条路线的组成:

  1. import static org.springframework.http.MediaType.APPLICATION_JSON;
  2. import static org.springframework.web.servlet.function.RequestPredicates.*;
  3. PersonRepository repository = ...
  4. PersonHandler handler = new PersonHandler(repository);
  5. RouterFunction<ServerResponse> otherRoute = ...
  6. RouterFunction<ServerResponse> route = route()
  7. // 带有与 JSON 匹配的 accept 的 GET/person/{id} 被路由到 PersonHandler.getPerson
  8. .GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
  9. // 带有与 JSON 匹配的 accept 的 GET/person 被路由到 PersonHandler.getPerson
  10. .GET("/person", accept(APPLICATION_JSON), handler::listPeople)
  11. // 没有附加断言的 /person 被路由到 PersonHandler.createPerson
  12. .POST("/person", handler::createPerson)
  13. // 是一个在其他地方创建的路由函数,并添加到构建的路由中。
  14. .add(otherRoute)
  15. .build();

Nested Routes/ 嵌套路由

一组路由函数有一个共享谓词是很常见的,例如共享路径。在上面的例子中,共享谓词是一个匹配 /person的路径谓词,被三个路由使用。当使用注解时,你可以通过使用一个类型级别的 @RequestMapping注解来消除这种重复,该注解映射到 /person。在 WebMvc.fn 中,路径谓词可以通过路由器函数生成器的路径方法来共享。例如,上面例子的最后几行可以通过使用嵌套的路由而得到如下改进:

  1. RouterFunction<ServerResponse> route = route()
  2. // 请注意,path 的第二个参数是一个消费者,它接受路由的构建者。
  3. .path("/person", builder -> builder
  4. .GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
  5. .GET(accept(APPLICATION_JSON), handler::listPeople)
  6. .POST("/person", handler::createPerson))
  7. .build();

尽管基于路径的嵌套是最常见的,但你可以通过使用构建器上的嵌套方法在任何种类的谓词上嵌套。上面的内容仍然包含一些共享的 Accept-header 谓词形式的重复。我们可以通过与 accept 一起使用 nest 方法来进一步改进:

  1. RouterFunction<ServerResponse> route = route()
  2. .path("/person", b1 -> b1
  3. .nest(accept(APPLICATION_JSON), b2 -> b2
  4. .GET("/{id}", handler::getPerson)
  5. .GET(handler::listPeople))
  6. .POST("/person", handler::createPerson))
  7. .build();