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就没有决定请求是否继续的能力.