WebFlux使用函數(shù)式編程
本篇主要內(nèi)容:
- HandlerFunction的使用
概述
Spring WebFlux包括WebFlux.Fn是一種輕量級(jí)函數(shù)式編程模型,其中函數(shù)用于路由和處理請(qǐng)求,契約設(shè)計(jì)為不可變。它是基于注釋的編程模型的另一種選擇,但在其他方面運(yùn)行在相同的Reactive Core基礎(chǔ)上。
在WebFlux.Fn,HTTP請(qǐng)求由HandlerFunction處理:該函數(shù)接受ServerRequest并返回延遲的ServerResponse(即Mono<ServerResponce>)。請(qǐng)求和響應(yīng)對(duì)象都有不可變的契約,提供對(duì)HTTP請(qǐng)求和響應(yīng)的JDK 8友好訪問。HandlerFunction相當(dāng)于基于注釋的編程模型中@RequestMapping方法的主體。
傳入的請(qǐng)求被路由到一個(gè)帶有RouterFunction的處理函數(shù):一個(gè)接受ServerRequest并返回延遲HandlerFunction(即Mono<HandlerFunction>)的函數(shù)。當(dāng)路由器函數(shù)匹配時(shí),返回處理函數(shù);否則為空Mono。RouterFunction相當(dāng)于@RequestMapping注釋,但主要區(qū)別在于router函數(shù)不僅提供數(shù)據(jù),還提供行為。
RouterFunctions.route()提供了一個(gè)路由器生成器,可以方便創(chuàng)建路由器,如下例所示:
示例:
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
PersonRepository repository = ...
PersonHandler handler = new PersonHandler(repository);
RouterFunction<ServerResponse> route = route()
.GET("/person/{id}", accept(APPLICATION_JSON), handler::getPerson)
.GET("/person", accept(APPLICATION_JSON), handler::listPeople)
.POST("/person", handler::createPerson)
.build();
public class PersonHandler {
public Mono<ServerResponse> listPeople(ServerRequest request) {
// todo 業(yè)務(wù)處理邏輯
}
public Mono<ServerResponse> createPerson(ServerRequest request) {
// todo 業(yè)務(wù)處理邏輯
}
public Mono<ServerResponse> getPerson(ServerRequest request) {
// todo 業(yè)務(wù)處理邏輯
}
}
運(yùn)行RouterFunction的一種方法是將其轉(zhuǎn)換為HttpHandler,并通過內(nèi)置服務(wù)器適配器之一進(jìn)行安裝:
RouterFunctions.toHttpHandler(RouterFunction)。
RouterFunctions.toHttpHandler(RouterFunction,HandlerStrategies)。
大多數(shù)應(yīng)用程序都可以通過WebFlux Java配置運(yùn)行。
HandlerFunction
ServerRequest和ServerResponse是不可變的接口,提供對(duì)HTTP請(qǐng)求和響應(yīng)的JDK 8友好訪問。請(qǐng)求和響應(yīng)都提供了針對(duì)體流的反應(yīng)流背壓。請(qǐng)求主體用反應(yīng)器Flux或Mono表示。響應(yīng)主體由任何反應(yīng)流Publisher表示,包括Flux和Mono。
- ServerRequest?
ServerRequest提供對(duì)HTTP方法、URI、頭和查詢參數(shù)的訪問,而對(duì)正文的訪問是通過正文方法提供的。
下面的例子將請(qǐng)求體提取為Mono:
Mono<String> string = request.bodyToMono(String.class);
以下示例將正文提取為Flux(或Kotlin中的Flow),其中Person對(duì)象從某種序列化形式(如JSON或XML)解碼:
Flux<Person> people = request.bodyToFlux(Person.class);
前面的例子是使用更通用的ServerRequest.body(BodyExtractor)的快捷方式,它接受BodyExtractor函數(shù)策略接口。實(shí)用工具類BodyExtractors提供了對(duì)多個(gè)實(shí)例的訪問。例如,前面的例子也可以寫成這樣:
Mono<String> string = request.body(BodyExtractors.toMono(String.class));
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
下面的例子展示了如何訪問表單數(shù)據(jù):
Mono<MultiValueMap<String, String>> map = request.formData();
下面的例子展示了如何將多部分?jǐn)?shù)據(jù)作為映射訪問:
Mono<MultiValueMap<String, Part>> map = request.multipartData();
下面的例子展示了如何以流方式一次訪問多個(gè)部分:
Flux<Part> parts = request.body(BodyExtractors.toParts());
- ServerResponse
ServerResponse提供對(duì)HTTP響應(yīng)的訪問,因?yàn)樗遣豢勺兊模阅憧梢允褂脴?gòu)建方法來創(chuàng)建它。你可以使用構(gòu)造器來設(shè)置響應(yīng)狀態(tài)、添加響應(yīng)頭或提供響應(yīng)正文。下面的例子創(chuàng)建了一個(gè)包含JSON內(nèi)容的200 (OK)響應(yīng):
Mono<Person> person = Mono.just(new Person("張三", 12))
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
下面的例子展示了如何構(gòu)建一個(gè)201 (CREATED)響應(yīng),它有一個(gè)Location頭,沒有正文:
URI location = ...
ServerResponse.created(location).build();
根據(jù)所使用的編解碼器,可以通過傳遞提示參數(shù)來定制體的序列化或反序列化方式。例如,要指定一個(gè)Jackson JSON視圖:
ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...);
- Handler Classes
我們可以將處理函數(shù)編寫為lambda,如下示例所示。
HandlerFunction<ServerResponse> helloWorld = request -> ServerResponse.ok().bodyValue("Hello World");
這很方便,但在應(yīng)用程序中,我們需要多個(gè)函數(shù),而多個(gè)內(nèi)聯(lián)lambda會(huì)變得很混亂。因此,將相關(guān)的處理程序函數(shù)組合到一個(gè)處理程序類中是很有用的,這個(gè)處理程序類在基于注釋的應(yīng)用程序中具有類似于@Controller的角色。例如,下面的類公開了一個(gè)響應(yīng)式Person存儲(chǔ)庫:
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
public class PersonHandler {
private final PersonRepository repository;
public PersonHandler(PersonRepository repository) {
this.repository = repository;
}
public Mono<ServerResponse> listPeople(ServerRequest request) {
Flux<Person> people = repository.allPeople();
return ok().contentType(APPLICATION_JSON).body(people, Person.class);
}
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class);
return ok().build(repository.savePerson(person));
}
public Mono<ServerResponse> getPerson(ServerRequest request) {
int personId = Integer.valueOf(request.pathVariable("id"));
return repository.getPerson(personId)
.flatMap(person -> ok().contentType(APPLICATION_JSON).bodyValue(person))
.switchIfEmpty(ServerResponse.notFound().build());
}
}
- Validation
一個(gè)functional endpoint可以使用Spring的驗(yàn)證工具對(duì)請(qǐng)求體應(yīng)用驗(yàn)證。例如,給定一個(gè)Person的自定義Spring Validator實(shí)現(xiàn):
public class PersonHandler {
private final Validator validator = new PersonValidator();
// ...
public Mono<ServerResponse> createPerson(ServerRequest request) {
Mono<Person> person = request.bodyToMono(Person.class).doOnNext(this::validate);
return ok().build(repository.savePerson(person));
}
private void validate(Person person) {
Errors errors = new BeanPropertyBindingResult(person, "person");
validator.validate(person, errors);
if (errors.hasErrors()) {
throw new ServerWebInputException(errors.toString());
}
}
}
總結(jié):
- 路由函數(shù)中HandlerFunction的使用。
- ServerRequest,ServerResponse使用示例。