Spring WebFlux使用函數(shù)式編程之HandlerFunction
本篇主要內(nèi)容:
- RouterFunction的使用
概述
路由器函數(shù)用于將請(qǐng)求路由到相應(yīng)的HandlerFunction。通常情況下,你不需要自己編寫路由器函數(shù),而是使用RouterFunctions類中的方法來(lái)創(chuàng)建一個(gè)。RouterFunctions.route()(無(wú)參數(shù))為創(chuàng)建路由器函數(shù)提供了一個(gè)流暢的構(gòu)建器,而RouterFunctions.route(RequestPredicate, HandlerFunction)提供了創(chuàng)建路由器的直接方法。
通常,建議使用route()構(gòu)建器,因?yàn)樗鼮榈湫偷挠成鋱?chǎng)景提供了方便的快捷方式,而不需要難以發(fā)現(xiàn)的靜態(tài)導(dǎo)入。例如,路由器函數(shù)構(gòu)建器提供了方法GET(String, HandlerFunction)來(lái)為GET請(qǐng)求創(chuàng)建映射;和POST(String, HandlerFunction)用于POST。
除了基于HTTP方法的映射之外,路由構(gòu)建器還提供了一種在映射到請(qǐng)求時(shí)引入附加謂詞的方法。對(duì)于每個(gè)HTTP方法,都有一個(gè)以RequestPredicate作為參數(shù)的重載變體,盡管可以表示附加的約束。
謂詞Predicates
你可以編寫自己的RequestPredicate,但RequestPredites類提供了常用的實(shí)現(xiàn),基于請(qǐng)求路徑、HTTP方法、內(nèi)容類型等。以下示例使用請(qǐng)求謂詞根據(jù)Accept標(biāo)頭創(chuàng)建約束:
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/hello-world", accept(MediaType.TEXT_PLAIN),
request -> ServerResponse.ok().bodyValue("Hello World")).build();
你可以使用以下命令將多個(gè)請(qǐng)求謂詞組合在一起:
- RequestPredicate.and(RequestPredicate)? // 兩者必須匹配。
- RequestPredicate.or(RequestPredicate) // 兩者都可以匹配。
RequestPredicates中的許多謂詞都是組合的。例如,RequestPredicates.GET(String)由RequestPredicates.method(HttpMethod)和RequestPredicates.path(String)組合而成。上面顯示的示例還使用了兩個(gè)請(qǐng)求謂詞,因?yàn)闃?gòu)建器使用RequestPredicates.GET內(nèi)部,并用accept謂詞組合它。
路由Routes
路由器函數(shù)按順序計(jì)算:如果第一個(gè)路由不匹配,則計(jì)算第二個(gè)路由,依此類推。因此,在一般路由之前聲明更具體的路由是有意義的。這在將路由器函數(shù)注冊(cè)為Spring bean時(shí)也很重要,稍后將對(duì)此進(jìn)行描述。注意,此行為不同于基于注釋的編程模型,后者自動(dòng)選擇“最特定的”控制器方法。
當(dāng)使用路由器函數(shù)構(gòu)建器時(shí),所有定義的路由被組合到一個(gè)RouterFunction中,該RouterFunction從build()返回。還有其他方法可以將多個(gè)路由器功能組合在一起:
- 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是一個(gè)在別處創(chuàng)建的路由器函數(shù),并添加到構(gòu)建的路由中。
嵌套路由Nested Routes
一組路由器函數(shù)通常具有一個(gè)共享謂詞,例如共享路徑。在上面的示例中,共享謂詞將是一個(gè)與/person匹配的路徑謂詞,由三條路由使用。在使用注釋時(shí),可以通過(guò)使用映射到/person的類型級(jí)@RequestMapping注釋來(lái)消除這種重復(fù)。在WebFlux.Fn,路徑謂詞可以通過(guò)路由器函數(shù)構(gòu)建器上的路徑方法共享。例如,上面例子的最后幾行可以通過(guò)使用嵌套路由通過(guò)以下方式進(jìn)行改進(jìn):
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的第二個(gè)參數(shù)是接受路由器構(gòu)建器的消費(fèi)者。
盡管基于路徑的嵌套是最常見的,但是你可以通過(guò)在構(gòu)建器上使用嵌套方法來(lái)嵌套任何類型的謂詞。上面的內(nèi)容仍然以共享的Accept-header謂詞的形式包含了一些重復(fù)。我們可以通過(guò)使用nest方法和accept來(lái)進(jìn)一步改進(jìn):
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();
總結(jié):
- 路由函數(shù)中RouteFunction的使用。
- 嵌套路由的使用。