Spring WebFlux使用函數式編程之HandlerFunction
本篇主要內容:
- RouterFunction的使用
概述
路由器函數用于將請求路由到相應的HandlerFunction。通常情況下,你不需要自己編寫路由器函數,而是使用RouterFunctions類中的方法來創建一個。RouterFunctions.route()(無參數)為創建路由器函數提供了一個流暢的構建器,而RouterFunctions.route(RequestPredicate, HandlerFunction)提供了創建路由器的直接方法。
通常,建議使用route()構建器,因為它為典型的映射場景提供了方便的快捷方式,而不需要難以發現的靜態導入。例如,路由器函數構建器提供了方法GET(String, HandlerFunction)來為GET請求創建映射;和POST(String, HandlerFunction)用于POST。
除了基于HTTP方法的映射之外,路由構建器還提供了一種在映射到請求時引入附加謂詞的方法。對于每個HTTP方法,都有一個以RequestPredicate作為參數的重載變體,盡管可以表示附加的約束。
謂詞Predicates
你可以編寫自己的RequestPredicate,但RequestPredites類提供了常用的實現,基于請求路徑、HTTP方法、內容類型等。以下示例使用請求謂詞根據Accept標頭創建約束:
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().bodyValue("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中,該RouterFunction從build()返回。還有其他方法可以將多個路由器功能組合在一起:
- add(RouterFunction) on the RouterFunctions.route() builder。
- RouterFunction.and(RouterFunction)。
- RouterFunction.andRoute(RequestPredicate, HandlerFunction)?—?shortcut for RouterFunction.and() with nested RouterFunctions.route()。
下面的例子展示了四條路由的組合:
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.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();
- 帶有匹配JSON的Accept頭的GET /person/{id}被路由到PersonHandler.getPerson。
- 帶有與JSON匹配的Accept頭的GET /person被路由到PersonHandler.listPeople。
- 沒有附加謂詞的POST /person被映射到PersonHandler.createPerson。
- otherRoute是一個在別處創建的路由器函數,并添加到構建的路由中。
嵌套路由Nested Routes
一組路由器函數通常具有一個共享謂詞,例如共享路徑。在上面的示例中,共享謂詞將是一個與/person匹配的路徑謂詞,由三條路由使用。在使用注釋時,可以通過使用映射到/person的類型級@RequestMapping注釋來消除這種重復。在WebFlux.Fn,路徑謂詞可以通過路由器函數構建器上的路徑方法共享。例如,上面例子的最后幾行可以通過使用嵌套路由通過以下方式進行改進:
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();
注意:path的第二個參數是接受路由器構建器的消費者。
盡管基于路徑的嵌套是最常見的,但是你可以通過在構建器上使用嵌套方法來嵌套任何類型的謂詞。上面的內容仍然以共享的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();
總結:
- 路由函數中RouteFunction的使用。
- 嵌套路由的使用。