深圳幻海软件技术有限公司 欢迎您!

WebFlux使用函数式编程

2023-02-28

本篇主要内容:HandlerFunction的使用概述SpringWebFlux包括WebFlux.Fn是一种轻量级函数式编程模型,其中函数用于路由和处理请求,契约设计为不可变。它是基于注释的编程模型的另一种选择,但在其他方面运行在相同的ReactiveCore基础上。在WebFlux.Fn,HTT

本篇主要内容:

    QQRmUSWQ" style="text-align: justify;">
  • HandlerFunction的使用

概述

Spring WebFlux包括WebFlux.Fn是一种轻量级函数式编程模型,其中函数用于路由和处理请求,契约设计为不可变。它是基于注释的编程模型的另一种选择,但在其他方面运行在相同的Reactive Core基础上。

在WebFlux.Fn,HTTP请求由HandlerFunction处理:该函数接受ServerRequest并返回延迟的ServerResponse(即Mono<ServerResponce>)。请求和响应对象都有不可变的契约,提供对HTTP请求和响应的JDK 8友好访问。HandlerFunction相当于基于注释的编程模型中@RequestMapping方法的主体。

传入的请求被路由到一个带有RouterFunction的处理函数:一个接受ServerRequest并返回延迟HandlerFunction(即Mono<HandlerFunction>)的函数。当路由器函数匹配时,返回处理函数;否则为空Mono。RouterFunction相当于@RequestMapping注释,但主要区别在于router函数不仅提供数据,还提供行为。

RouterFunctions.route()提供了一个路由器生成器,可以方便创建路由器,如下例所示:

示例:

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 业务处理逻辑
  }
  public Mono<ServerResponse> createPerson(ServerRequest request) {
    // todo 业务处理逻辑
  }
  public Mono<ServerResponse> getPerson(ServerRequest request) {
    // todo 业务处理逻辑
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

运行RouterFunction的一种方法是将其转换为HttpHandler,并通过内置服务器适配器之一进行安装:

RouterFunctions.toHttpHandler(RouterFunction)。

RouterFunctions.toHttpHandler(RouterFunction,HandlerStrategies)。

大多数应用程序都可以通过WebFlux Java配置运行。

HandlerFunction

ServerRequest和ServerResponse是不可变的接口,提供对HTTP请求和响应的JDK 8友好访问。请求和响应都提供了针对体流的反应流背压。请求主体用反应器Flux或Mono表示。响应主体由任何反应流Publisher表示,包括Flux和Mono。

  • ServerRequest​

ServerRequest提供对HTTP方法、URI、头和查询参数的访问,而对正文的访问是通过正文方法提供的。

下面的例子将请求体提取为Mono:

Mono<String> string = request.bodyToMono(String.class);
  • 1.

以下示例将正文提取为Flux(或Kotlin中的Flow),其中Person对象从某种序列化形式(如JSON或XML)解码:

Flux<Person> people = request.bodyToFlux(Person.class);
  • 1.

前面的例子是使用更通用的ServerRequest.body(BodyExtractor)的快捷方式,它接受BodyExtractor函数策略接口。实用工具类BodyExtractors提供了对多个实例的访问。例如,前面的例子也可以写成这样:

Mono<String> string = request.body(BodyExtractors.toMono(String.class));
Flux<Person> people = request.body(BodyExtractors.toFlux(Person.class));
  • 1.
  • 2.

下面的例子展示了如何访问表单数据:

Mono<MultiValueMap<String, String>> map = request.formData();
  • 1.

下面的例子展示了如何将多部分数据作为映射访问:

Mono<MultiValueMap<String, Part>> map = request.multipartData();
  • 1.

下面的例子展示了如何以流方式一次访问多个部分:

Flux<Part> parts = request.body(BodyExtractors.toParts());
  • 1.
  • ServerResponse

ServerResponse提供对HTTP响应的访问,因为它是不可变的,所以你可以使用构建方法来创建它。你可以使用构造器来设置响应状态、添加响应头或提供响应正文。下面的例子创建了一个包含JSON内容的200 (OK)响应:

Mono<Person> person = Mono.just(new Person("张三", 12))
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);
  • 1.
  • 2.

下面的例子展示了如何构建一个201 (CREATED)响应,它有一个Location头,没有正文:

URI location = ...
ServerResponse.created(location).build();
  • 1.
  • 2.

根据所使用的编解码器,可以通过传递提示参数来定制体的序列化或反序列化方式。例如,要指定一个Jackson JSON视图:

ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...);
  • 1.
  • Handler Classes

我们可以将处理函数编写为lambda,如下示例所示。

HandlerFunction<ServerResponse> helloWorld = request -> ServerResponse.ok().bodyValue("Hello World");
  • 1.

这很方便,但在应用程序中,我们需要多个函数,而多个内联lambda会变得很混乱。因此,将相关的处理程序函数组合到一个处理程序类中是很有用的,这个处理程序类在基于注释的应用程序中具有类似于@Controller的角色。例如,下面的类公开了一个响应式Person存储库:

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());
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • Validation

一个functional endpoint可以使用Spring的验证工具对请求体应用验证。例如,给定一个Person的自定义Spring Validator实现:

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()); 
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

总结:

  1. 路由函数中HandlerFunction的使用。
  2. ServerRequest,ServerResponse使用示例。