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

[TOC]

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

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

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

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

1.项目创建

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

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

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

1
2
3
4
5
6
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 的使用做一些准备工作。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@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,如下:

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

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

3.开发处理器

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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 中,先查询要删除的数据,然后再删除即可。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 地址和这些处理器之间关联起来,配置类如下:

1
2
3
4
5
6
7
8
9
10
11
@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,如此,我们的配置工作就算完成了。

5.测试

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

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

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

再来看删除数据:

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

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

最后再来看查询的接口:

如此,我们的 CURD 都 OK 啦。

6.小结

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

喜欢这篇文章吗?扫码关注公众号【江南一点雨】【江南一点雨】专注于 SPRING BOOT+微服务以及前后端分离技术,每天推送原创技术干货,关注后回复 JAVA,领取松哥为你精心准备的 JAVA 干货!

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×