1

WebFlux 中的请求地址路由怎么玩?

 2 years ago
source link: http://www.javaboy.org/2021/0618/webflux-router-function.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

[TOC]

在 SpringMVC 中,我们可以通过如下一些注解来控制请求 URL 和处理器之间的映射关系:

  • @RequestMapping
  • @GetMapping
  • @PostMapping
  • @DeleteMapping
  • @PutMapping

这些注解我们在 WebFlux 中依然还可以继续使用,不过 WebFlux 也提供了自己的方案–Router。

今天我们就一起来学习下 Router 的用法。

1.项目创建

首先我们还是先来新建一个 WebFlux 项目,选择的依赖也和之前的一样,如下图:

这里我们还是基于 MongoDB 来做。

创建完成后,在 application.properties 中配置一下 MongoDB,如下:

spring.data.mongodb.port=27017
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.username=madmin
spring.data.mongodb.password=m123
spring.data.mongodb.database=test
spring.data.mongodb.authentication-database=admin

如此,我们的准备工作就算完成了。

2.MongoDB 的准备工作

接下来我们再为 MongoDB 的使用做一些准备工作。

首先我们需要一个实体类:

@Document
public class Person {
@Id
private Long id;
private String name;
private String address;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}
}

然后我们需要一个 Repository,如下:

@EnableMongoRepositories
public interface PersonRepository extends ReactiveMongoRepository<Person,Long> {
}

这个 PersonRepository 的内容很简单,就继承自 ReactiveMongoRepository 即可,和上篇文章中介绍的也基本一致,松哥不再赘述。

3.开发处理器

接下来我们来开个一个 Person 的处理器,如下:

@Component
public class PersonHandler {
@Autowired
PersonRepository personRepository;

public Mono<ServerResponse> addPerson(ServerRequest serverRequest) {
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(personRepository.saveAll(serverRequest.bodyToMono(Person.class)), Person.class);
}

public Mono<ServerResponse> deletePerson(ServerRequest serverRequest) {
return personRepository.findById(Long.parseLong(serverRequest.pathVariable("id")))
.flatMap(p -> personRepository.delete(p).then(ServerResponse.ok().build()))
.switchIfEmpty(ServerResponse.notFound().build());
}
public Mono<ServerResponse> getAllPerson(ServerRequest serverRequest) {
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(personRepository.findAll(), Person.class);
}
}

关于这个处理器,松哥有话说:

  1. 首先这个处理器我们需要将其注入到 Spring 容器中,注入时作为一个普通组件注入即可。
  2. 所有方法的返回值类型都是 Mono<ServerResponse>,参数类型都是 ServerRequest,因为一会配置 Router 时涉及到的 HandlerFunction 里边就是这样定义的,换句话说,我们这里定义的每一个方法都满足 HandlerFunction 函数式接口。
  3. addPerson 方法中,首先调用 ServerResponse.ok() 方法设置响应状态码为 200,然后调用 contentType 设置响应的 MediaType,最后调用 body 方法设置响应内容。
  4. deletePerson 中,先查询要删除的数据,然后再删除即可。

上面的写法有点臃肿,一些静态方法我们可以直接使用静态导入,这样代码看起来更清爽一些,修改后的代码如下:

import static java.lang.Long.parseLong;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.ServerResponse.notFound;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;

@Component
public class PersonHandler {
@Autowired
PersonRepository personRepository;

public Mono<ServerResponse> addPerson(ServerRequest serverRequest) {
return ok().contentType(APPLICATION_JSON)
.body(personRepository.saveAll(serverRequest.bodyToMono(Person.class)), Person.class);
}

public Mono<ServerResponse> deletePerson(ServerRequest serverRequest) {
return personRepository.findById(parseLong(serverRequest.pathVariable("id")))
.flatMap(p -> personRepository.delete(p).then(ok().build()))
.switchIfEmpty(notFound().build());
}
public Mono<ServerResponse> getAllPerson(ServerRequest serverRequest) {
return ok().contentType(APPLICATION_JSON)
.body(personRepository.findAll(), Person.class);
}
}

修改后的代码看起来简洁一些了,上面多了一些静态导入。我们在网上看别人写的响应式代码,大部分也都是上来就 ok()...

4.配置路由

接下来我们来配置路由,就是将请求的 URL 地址和这些处理器之间关联起来,配置类如下:

@Configuration
public class RouterConfiguration {
@Bean
RouterFunction<ServerResponse> personRouter(PersonHandler personHandler) {
return RouterFunctions
.nest(RequestPredicates.path("/person"),
RouterFunctions.route(RequestPredicates.POST("/"), personHandler::addPerson)
.andRoute(RequestPredicates.GET("/"), personHandler::getAllPerson)
.andRoute(RequestPredicates.DELETE("/{id}"), personHandler::deletePerson));
}
}

这个配置类是这样:

  1. 这个配置类的作用有点像 SpringMVC 中的 DispatcherServlet,负责请求的分发,根据不同的请求 URL,找到对应的处理器去处理(如果 DispatcherServlet 不熟悉的话,可以在公众号后台回复 springmvc源码 查看松哥之前关于 SpringMVC 源码分析的系列文章)。
  2. 通过 RouterFunctions 这样一个工具类来创建 RouterFunction 实例。
  3. 首先调用 nest 方法,第一个参数配置的相当于是接下来配置的地址的一个前缀,这有点类似于我们在 Controller 类上直接写 @RequestMapping 注解去配置地址。
  4. nest 方法的第二个参数就是 RouterFunction 实例了,每一个 RouterFunction 实例通过 RouterFunctions.route 方法来构建,它的第一个参数就是请求的 URL 地址(注意这个时候配置的地址都是有一个共同的前缀),第二个参数我们通过方法引用的方式配置了一个 HandlerFunction,这个就是当前请求的处理器了。
  5. 通过 addRoute 方法可以配置多个路由策略。

OK,如此,我们的配置工作就算完成了。

接下来我们来进行简单的测试。

我们一共有三个接口可以测试,先来看添加数据:

这个添加接口其实也可以实现修改,如果添加的数据已经存在就会自动修改,不存在就添加(根据 id 判断数据是否存在)。

再来看删除数据:

如果删除的数据不存在,就会响应 404,如下:

如果删除的数据存在,则会响应 200:

最后再来看查询的接口:

如此,我们的 CURD 都 OK 啦。

好啦,今天通过一篇简单的文章和大家分享了 WebFlux 中的 RouterFunction,不知道小伙伴们学废没?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK