我们选择 MongoDB 作为 Repository 来实现 CRUD 操作
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
配置 application.properties
spring.data.mongodb.uri=mongodb://localhost:27017/webflux
开启 Reactive MongoDB 注解
@SpringBootApplication
@EnableReactiveMongoRepositories
public class FluxApplication {
public static void main(String[] args) {
SpringApplication.run(FluxApplication.class, args);
}
}
定义 MongoDB 对应的实体类
@Data
@Document(collation = "user")
public class User {
@Id
private String id;
private String name;
private int age;
}
定义仓库
@Repository
public interface UserRepository extends ReactiveMongoRepository<User, String> {
}
编写 Controller
@RestController
@RequestMapping("/user")
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
/**
* 以数组形式,一次返回数据
*/
@GetMapping(value = "/all")
public Flux<User> getAll() {
return userRepository.findAll();
}
/**
* 以 SSE 的形式,多次返回数据
*/
@GetMapping(value = "/stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamGetAll() {
return userRepository.findAll();
}
/**
* 添加用户
*
* @param user user
*/
@PostMapping("/add")
public Mono<User> createUser(@RequestBody User user) {
user.setId(null);
return this.userRepository.save(user);
}
/**
* 根据 ID 删除用户
* 存在的时候返回 200
* 不存在返回 404
*
* @param id id
*/
@DeleteMapping("/delete/{id}")
public Mono<ResponseEntity<Void>> deleteUser(@PathVariable("id") String id) {
return this.userRepository.findById(id)
// 当需要操作数据,并返回 Mono,这个时候需要使用 flatMap
// 如果不需要操作数据,只是对数据进行转换,就用 map
.flatMap(user -> this.userRepository.delete(user).then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
/**
* 更新用户
* 存在的时候返回 200 和 用户数据
* 不存在返回 404
*
* @param id id
* @param user user
*/
@PutMapping("/update/{id}")
public Mono<ResponseEntity<User>> updateUser(@PathVariable("id") String id, @RequestBody User user) {
return this.userRepository.findById(id)
// flatMap 操作数据
.flatMap(u -> {
u.setName(user.getName());
u.setAge(user.getAge());
return this.userRepository.save(u);
})
// map 转换数据
.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
/**
* 查找数据
*
* @param id id
*/
@GetMapping("/get/{id}")
public Mono<ResponseEntity<User>> getUser(@PathVariable("id") String id) {
return this.userRepository.findById(id)
// map 转换数据
.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}
参数校验
POM 依赖
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
实体类
使用 Hibernat validator 进行参数校验
@Data
@Document(collection = "user")
public class User {
@Id
private String id;
@NotBlank(message = "姓名不能为空")
private String name;
@Range(min = 10, max = 120, message = "超出年龄限定值")
private int age;
}
Controller
使用 @Valid 注解,标记需要校验的实体类
public Mono<User> createUser(@Valid @RequestBody User user){
CheckUtils.checkName(user.getName()); // 自定义校验异常
// ...
}
public Mono<ResponseEntity<User>> updateUser(@PathVariable("id") String id, @Valid @RequestBody User user) {
CheckUtils.checkName(user.getName()); // 自定义校验异常
// ...
}
// 校验工具类
public class CheckUtils {
private static final String[] INVALID_NAMES = {"admin", "root", "super"};
public static void checkName(String name) {
Stream.of(INVALID_NAMES).filter(value -> value.equalsIgnoreCase(name))
.findAny().ifPresent(value -> {
throw new CheckException("name", value);
});
}
}
自定义异常
@Getter
@Setter
public class CheckException extends RuntimeException {
private String filedName;
private String filedValue;
public CheckException(String filedName, String filedValue) {
this.filedName = filedName;
this.filedValue = filedValue;
}
}
切面
@ControllerAdvice
public class CheckAdvice {
// 全局异常捕获
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(Exception exception) {
System.out.println(exception.getClass());
return new ResponseEntity<>(exception.getMessage(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(WebExchangeBindException.class)
public ResponseEntity<String> handleBindException(WebExchangeBindException exception) {
return new ResponseEntity<>(toStr(exception), HttpStatus.BAD_REQUEST);
}
// 自定义异常捕获
@ExceptionHandler(CheckException.class)
public ResponseEntity<String> handleBindException(CheckException exception) {
return new ResponseEntity<>(toStr(exception), HttpStatus.BAD_REQUEST);
}
/**
* 把校验异常转换为字符串
*
* @param exception 异常
*/
private String toStr(WebExchangeBindException exception) {
return exception.getFieldErrors().stream().map(e -> e.getField() + ":" + e.getDefaultMessage())
.reduce("", (s1, s2) -> s1 + "\n" + s2);
}
private String toStr(CheckException exception) {
return exception.getFiledName() + ": 错误的值 " + exception.getFiledValue();
}
}