我们选择 MongoDB 作为 Repository 来实现 CRUD 操作

依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-webflux</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
  8. </dependency>

配置 application.properties

  1. spring.data.mongodb.uri=mongodb://localhost:27017/webflux

开启 Reactive MongoDB 注解

  1. @SpringBootApplication
  2. @EnableReactiveMongoRepositories
  3. public class FluxApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(FluxApplication.class, args);
  6. }
  7. }

定义 MongoDB 对应的实体类

  1. @Data
  2. @Document(collation = "user")
  3. public class User {
  4. @Id
  5. private String id;
  6. private String name;
  7. private int age;
  8. }

定义仓库

  1. @Repository
  2. public interface UserRepository extends ReactiveMongoRepository<User, String> {
  3. }

编写 Controller

  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4. private final UserRepository userRepository;
  5. public UserController(UserRepository userRepository) {
  6. this.userRepository = userRepository;
  7. }
  8. /**
  9. * 以数组形式,一次返回数据
  10. */
  11. @GetMapping(value = "/all")
  12. public Flux<User> getAll() {
  13. return userRepository.findAll();
  14. }
  15. /**
  16. * 以 SSE 的形式,多次返回数据
  17. */
  18. @GetMapping(value = "/stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
  19. public Flux<User> streamGetAll() {
  20. return userRepository.findAll();
  21. }
  22. /**
  23. * 添加用户
  24. *
  25. * @param user user
  26. */
  27. @PostMapping("/add")
  28. public Mono<User> createUser(@RequestBody User user) {
  29. user.setId(null);
  30. return this.userRepository.save(user);
  31. }
  32. /**
  33. * 根据 ID 删除用户
  34. * 存在的时候返回 200
  35. * 不存在返回 404
  36. *
  37. * @param id id
  38. */
  39. @DeleteMapping("/delete/{id}")
  40. public Mono<ResponseEntity<Void>> deleteUser(@PathVariable("id") String id) {
  41. return this.userRepository.findById(id)
  42. // 当需要操作数据,并返回 Mono,这个时候需要使用 flatMap
  43. // 如果不需要操作数据,只是对数据进行转换,就用 map
  44. .flatMap(user -> this.userRepository.delete(user).then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))))
  45. .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
  46. }
  47. /**
  48. * 更新用户
  49. * 存在的时候返回 200 和 用户数据
  50. * 不存在返回 404
  51. *
  52. * @param id id
  53. * @param user user
  54. */
  55. @PutMapping("/update/{id}")
  56. public Mono<ResponseEntity<User>> updateUser(@PathVariable("id") String id, @RequestBody User user) {
  57. return this.userRepository.findById(id)
  58. // flatMap 操作数据
  59. .flatMap(u -> {
  60. u.setName(user.getName());
  61. u.setAge(user.getAge());
  62. return this.userRepository.save(u);
  63. })
  64. // map 转换数据
  65. .map(u -> new ResponseEntity<>(u, HttpStatus.OK))
  66. .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
  67. }
  68. /**
  69. * 查找数据
  70. *
  71. * @param id id
  72. */
  73. @GetMapping("/get/{id}")
  74. public Mono<ResponseEntity<User>> getUser(@PathVariable("id") String id) {
  75. return this.userRepository.findById(id)
  76. // map 转换数据
  77. .map(u -> new ResponseEntity<>(u, HttpStatus.OK))
  78. .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
  79. }
  80. }

参数校验

POM 依赖

  1. <dependency>
  2. <groupId>javax.validation</groupId>
  3. <artifactId>validation-api</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.hibernate.validator</groupId>
  7. <artifactId>hibernate-validator</artifactId>
  8. <version>6.1.5.Final</version>
  9. </dependency>

实体类

使用 Hibernat validator 进行参数校验

  1. @Data
  2. @Document(collection = "user")
  3. public class User {
  4. @Id
  5. private String id;
  6. @NotBlank(message = "姓名不能为空")
  7. private String name;
  8. @Range(min = 10, max = 120, message = "超出年龄限定值")
  9. private int age;
  10. }

Controller

使用 @Valid 注解,标记需要校验的实体类

  1. public Mono<User> createUser(@Valid @RequestBody User user){
  2. CheckUtils.checkName(user.getName()); // 自定义校验异常
  3. // ...
  4. }
  5. public Mono<ResponseEntity<User>> updateUser(@PathVariable("id") String id, @Valid @RequestBody User user) {
  6. CheckUtils.checkName(user.getName()); // 自定义校验异常
  7. // ...
  8. }
  9. // 校验工具类
  10. public class CheckUtils {
  11. private static final String[] INVALID_NAMES = {"admin", "root", "super"};
  12. public static void checkName(String name) {
  13. Stream.of(INVALID_NAMES).filter(value -> value.equalsIgnoreCase(name))
  14. .findAny().ifPresent(value -> {
  15. throw new CheckException("name", value);
  16. });
  17. }
  18. }

自定义异常

  1. @Getter
  2. @Setter
  3. public class CheckException extends RuntimeException {
  4. private String filedName;
  5. private String filedValue;
  6. public CheckException(String filedName, String filedValue) {
  7. this.filedName = filedName;
  8. this.filedValue = filedValue;
  9. }
  10. }

切面

  1. @ControllerAdvice
  2. public class CheckAdvice {
  3. // 全局异常捕获
  4. @ExceptionHandler(Exception.class)
  5. public ResponseEntity<String> handleException(Exception exception) {
  6. System.out.println(exception.getClass());
  7. return new ResponseEntity<>(exception.getMessage(), HttpStatus.BAD_REQUEST);
  8. }
  9. @ExceptionHandler(WebExchangeBindException.class)
  10. public ResponseEntity<String> handleBindException(WebExchangeBindException exception) {
  11. return new ResponseEntity<>(toStr(exception), HttpStatus.BAD_REQUEST);
  12. }
  13. // 自定义异常捕获
  14. @ExceptionHandler(CheckException.class)
  15. public ResponseEntity<String> handleBindException(CheckException exception) {
  16. return new ResponseEntity<>(toStr(exception), HttpStatus.BAD_REQUEST);
  17. }
  18. /**
  19. * 把校验异常转换为字符串
  20. *
  21. * @param exception 异常
  22. */
  23. private String toStr(WebExchangeBindException exception) {
  24. return exception.getFieldErrors().stream().map(e -> e.getField() + ":" + e.getDefaultMessage())
  25. .reduce("", (s1, s2) -> s1 + "\n" + s2);
  26. }
  27. private String toStr(CheckException exception) {
  28. return exception.getFiledName() + ": 错误的值 " + exception.getFiledValue();
  29. }
  30. }