Spring WebFlux使用函數語言程式設計之HandlerFunction

語言: CN / TW / HK

本篇主要內容:

  • 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();
  1. 帶有匹配JSON的Accept頭的GET /person/{id}被路由到 PersonHandler.getPerson。
  2. 帶有與JSON匹配的Accept頭的GET /person被路由到 PersonHandler.listPeople。
  3. 沒有附加謂詞的POST /person被對映到 PersonHandler.createPerson。
  4. 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();

總結:

  1. 路由函式中 RouteFunction 的使用。
  2. 巢狀路由的使用。