Spring WebFlux学习笔记:3 请求的处理(WebFlux.Fn编程模型)

2,513 阅读5分钟

3.1 dispatherhandler

这些都是和spring mvc相同的, 略。 


3.2 annoatedcontroller

这些都是和spring mvc相同的, 略。

3.3 functional endpoint

Spring WebFLux包含了WebFlux.Fn. 这是一套用于路由和请求处理的轻量级的函数式模型。 这套东西提供了Annoated Controller一致的功能, 可以作为一个选择。
WebFlux.Fn和Annoated Controller都基于同一套Reactive Core之上.

1 概述

从客户端传来的请求通过一个RouterFunction 路由到一个处理器函数上. RouterFunction是一个接受ServerRequest返回一个延迟的HandlerFunction(即Mono<HandlerFunction> 的函数, 并且是不可变的. 当请求有匹配的处理器函数时, 则返回相应的处理器函数, 否则返回一个空的Mono.

实例代码:

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) {
        // ...
    }

    public Mono<ServerResponse> createPerson(ServerRequest request) {
        // ...
    }

    public Mono<ServerResponse> getPerson(ServerRequest request) {
        // ...
    }
}

要使RouterFunction生效, 其中的一种方式是将RouterFunction绑定到HttpServer上, 如下:
  • RouterFunctions.toHttpHandler(RouterFunction)
  • RouterFunctions.toHttpHandler(RouterFunction, HandlerStrategies)



2 HandlerFunction

ServerRequest和ServerReponse是两个不可变的接口, 是http请求和响应的两个抽象, 在jdk8上使用这两个接口更加容易. 这两个类都提供一个背压的Reactive Stream相对于请求体. 请求体和响应体都被抽象为Mono或者Flux.


ServerRequest
这是HTTP请求的一个抽象.

从请求体转化为Mono和FLux:

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

下面是等同的简写方法:
从请求体转化为Mono:

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

从请求体转化为Flux示例:

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



接受FormData:

Mono<MultiValueMap<String, String> map = request.body(BodyExtractors.toFormData());


接受Multipart:

Mono<MultiValueMap<String, String> map = request.body(BodyExtractors.toFormData());
以流的风格访问Multipart:

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




ServerResponse
是http响应的抽象, 它创建之后就不可变.
创建一个200状态码的响应:

Mono<Person> person = ...
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(person, Person.class);

创建一个201的响应:

URI location = ...
ServerResponse.created(location).build();


对报文体的序列化和反序列化进行自定义:

ServerResponse.ok().hint(Jackson2CodecSupport.JSON_VIEW_HINT, MyJacksonView.class).body(...);








Handler Classes
类似于Controller一样的角色. 虽然也可以直接传递lambda表达式, 但是当处理器太多还是使用Handler Class的方式传递处理器函数比较整洁.

如下:

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.ServerResponse.ok;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;

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).body(fromObject(person)))
            .switchIfEmpty(ServerResponse.notFound().build());
    }
}


参数校检
使用Spring自己提供的验证工具来定义验证贵则的例子:

public class PersonHandler {

    private final Validator validator = new PersonValidator(); 

    // ...

    public Mono<ServerResponse> createPerson(ServerRequest request) {// doOnNext指定了这个验证器什么时候被执行
        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(body, "person");
        validator.validate(body, errors);
        if (errors.hasErrors) {
            throw new ServerWebInputException(errors.toString()); 
        }
    }


3 RouterFunction

通常没有必要创建自己的RouterFunction, 而是通过RouterFunctions.rule()构造器来定义路由的规则.


设置匹配条件:
可以用HTTP方法, url, http头部作为匹配的条件. 如下:

RouterFunction<ServerResponse> route = RouterFunctions.route()
    .GET("/hello-world", accept(MediaType.TEXT_PLAIN),
        request -> Response.ok().body(fromObject("Hello World")));
  • RequestPredicate.and(RequestPredicate) —两个条件都需要匹配
  • RequestPredicate.or(RequestPredicate) — 只需要一个条件匹配就可以了



路由:
RouterFunction对于请求的匹配是从第一个匹配条件开始, 逐条进行匹配的. 因此匹配条件的声明顺序是需要注意的. 它不想Controller风格的路由, 是按照最具体的路径的进行匹配.

另外, RouterFunction和RouterFunction之间还可以组合, 有:
  • 构建RouterFunction时, 有add(RouterFunction)方法
  • 有RouterFunction.and(RouterFunction)方法
  • 还有RouterFunction.andRoute(RequestPredicate, HandlerFunction)是第一种方式的简写

组合两个RouterFunction的例子:

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) 
    .GET("/person", accept(APPLICATION_JSON), handler::listPeople) 
    .POST("/person", handler::createPerson) 
    .add(otherRoute) 
    .build();


嵌套路由
主要用来对公共的路由路径, 统一在一个地方定义. 相当于在Controller类几倍的RequestMapping注解.
在WebFlux.Fn中, 可以用嵌套路由的方式的解决. 示例如下:

RouterFunction<ServerResponse> route = route()
    .path("/person", builder -> builder
        .GET("/{id}", accept(APPLICATION_JSON), handler::getPerson)
        .GET("", accept(APPLICATION_JSON), handler::listPeople)
        .POST("/person", handler::createPerson))
    .build();


不只是统一在一个地方定义公共路径, 还可以在一个地方定义匹配的请求头部等. 示例如下:

RouterFunction<ServerResponse> route = route()
    .path("/person", b1 -> b1
        .nest(accept(APPLICATION_JSON), b2 -> b2
            .GET("/{id}", handler::getPerson)
            .GET("", handler::listPeople))
        .POST("/person", handler::createPerson))
    .build();




4 Filter

可以通过RouterFunction的before, after或者filter方法指定过滤器. 过滤器的指定对于RouterFunction下的所有EndPoint都将生效, 但是内嵌的RouterFunction中指定的过滤器并不在更上层的RouterFunction中生效, 如下:

RouterFunction<ServerResponse> route = route()
    .path("/person", b1 -> b1
        .nest(accept(APPLICATION_JSON), b2 -> b2
            .GET("/{id}", handler::getPerson)
            .GET("", handler::listPeople)
            .before(request -> ServerRequest.from(request) 
                .header("X-RequestHeader", "Value")
                .build()))
        .POST("/person", handler::createPerson))
    .after((request, response) -> logResponse(response)) 
    .build();


路由构建器中的filter方法, 接受一个函数, 这个函数的入参为ServerRequest和HandlerFunction, 返回一个ServerReponse. 这个方法可以决定一个请求是否继续被处理. 示例如下:

SecurityManager securityManager = ...

RouterFunction<ServerResponse> route = route()
    .path("/person", b1 -> b1
        .nest(accept(APPLICATION_JSON), b2 -> b2
            .GET("/{id}", handler::getPerson)
            .GET("", handler::listPeople))
        .POST("/person", handler::createPerson))
    .filter((request, next) -> {
        if (securityManager.allowAccessTo(request.path())) {
            return next.handle(request);
        }
        else {
            return ServerResponse.status(UNAUTHORIZED).build();
        }
    })
    .build();

如果filter函数中, 返回了next.handler(request), 那么这个请求将会被传递到下一层进行处理.
但是像before和after就没有决定请求是否继续的能力.