Weflux的完整应用:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springwebfluxtest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springwebfluxtest</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-web</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.example.springwebfluxtest.domain;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "user")
@Data
public class User {
@Id
private String id;
private String name;
private int age;
}
package com.example.springwebfluxtest.repository;
import com.example.springwebfluxtest.domain.User;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
public interface UserRepository extends ReactiveMongoRepository<User,String> {
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRepository repository;
public UserController(UserRepository userRepository){
this.repository = userRepository;
}
以数组形式一次性返回数据
@GetMapping("/")
public Flux<User> findAll(){
return repository.findAll();
}
以SSE形式流一样返回数据
@GetMapping(value = "/stream/all",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamfindAll(){
return repository.findAll();
}
}
@SpringBootApplication
@EnableReactiveMongoRepositories
public class SpringwebfluxtestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringwebfluxtestApplication.class, args);
}
}
spring.data.mongodb.uri=mongodb://localhost:27017/webflux
启动MongoDB-postman访问
@PostMapping("/")
public Mono<User> add(@RequestBody User user){
return repository.save(user);
}
全部的增删改查
package com.example.springwebfluxtest.controller;
import com.example.springwebfluxtest.domain.User;
import com.example.springwebfluxtest.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRepository repository;
public UserController(UserRepository userRepository){
this.repository = userRepository;
}
@GetMapping("/")
public Flux<User> findAll(){
return repository.findAll();
}
@PostMapping("/")
public Mono<User> add(@RequestBody User user){
return repository.save(user);
}
@GetMapping(value = "/stream/all",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamfindAll(){
return repository.findAll();
}
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Void>> deleteUser(@PathVariable("id") String id){
return this.repository.findById(id).flatMap(user -> this.repository.delete(user)
.then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
@PutMapping("/{id}")
public Mono<ResponseEntity<User>> uodate(
@PathVariable("id") String id,
@RequestBody User user
){
//flatMap操作数据
return this.repository.findById(id).flatMap(u -> {
u.setName(user.getName());
u.setAge(user.getAge());
return this.repository.save(u);
})//map转换数据
.map(u -> new ResponseEntity<User>(u,HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
@GetMapping("/{id}")
public Mono<ResponseEntity<User>> find(@PathVariable("id") String id){
return this.repository.findById(id)
.map(u -> new ResponseEntity<User>(u,HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
}
public interface UserRepository extends ReactiveMongoRepository<User,String> {
//根据年龄查找用户
Flux<User> findByAgeBetween(int start, int end);
}
@GetMapping("/age/{start}/{end}")
public Flux<User> findByAge(@PathVariable("start") int start,
@PathVariable("end") int end){
return this.repository.findByAgeBetween(start,end);
}
@GetMapping(value = "stream/age/{start}/{end}",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> findByAgeStream(@PathVariable("start") int start,
@PathVariable("end") int end){
return this.repository.findByAgeBetween(start,end);
}
使用MongoDB的Query语法
@Repository
public interface UserRepository extends ReactiveMongoRepository<User,String> {
//根据年龄查找用户
Flux<User> findByAgeBetween(int start, int end);
@Query("{'age':{'$gte':10,'$lte':30}}")
Flux<User> oldUser();
}
//得到10-30的用户
@GetMapping("/old")
public Flux<User> oldUser(){
return this.repository.oldUser();
}
@GetMapping(value = "stream/old",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> oldUserStream(){
return this.repository.oldUser();
}
参数校验:
1、使用Hibernate的注解校验
@Document(collection = "user")
@Data
public class User {
@Id
private String id;
@NotBlank
private String name;
@Range(min=10,max = 100)
private int age;
}
@ControllerAdvice
public class CheckAdvice {
@ExceptionHandler(WebExchangeBindException.class)
public ResponseEntity<String> handleBindException(WebExchangeBindException e){
//HttpStatus.BAD_REQUEST返回给前台是400
return new ResponseEntity<String>(toStr(e),HttpStatus.BAD_REQUEST);
}
//把校验异常转换成字符串
private String toStr(WebExchangeBindException ex) {
return ex.getFieldErrors().stream()
.map(e -> e.getField()+":"+e.getDefaultMessage())//把异常转换成一个字符串
.reduce("",(s1,s2) -> s1+"\n"+s2); //把数组转换成字符串
}
}
校验名字
package com.example.springwebfluxtest.util;
public class CheckUtil {
public static void checkName(String name) {
}
}
package com.example.springwebfluxtest.exception;
import lombok.Data;
@Data
public class CheckExceptiom extends RuntimeException {
private static final long serialVersionUID = 1L;
//出错字段的名字
private String filedName;
//出错字段的值
private String filedValue;
public CheckExceptiom(String filedName,String filedValue){
super();
this.filedName = filedName;
this.filedValue = filedValue;
}
public CheckExceptiom() {
}
public CheckExceptiom(String message) {
super(message);
}
public CheckExceptiom(String message, Throwable cause) {
super(message, cause);
}
public CheckExceptiom(Throwable cause) {
super(cause);
}
public CheckExceptiom(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
绿色为覆盖构造方法
package com.example.springwebfluxtest.util;
import com.example.springwebfluxtest.exception.CheckExceptiom;
import java.util.stream.Stream;
public class CheckUtil {
private static final String[] INVALID_NAMES = {"admin","guanliyuan"};
public static void checkName(String value) {
Stream.of(INVALID_NAMES).filter(name -> name.equalsIgnoreCase(value))
.findAny().ifPresent(name -> {
throw new CheckExceptiom("name",value);
});
}
}
@ControllerAdvice
public class CheckAdvice {
@ExceptionHandler(WebExchangeBindException.class)
public ResponseEntity<String> handleBindException(WebExchangeBindException e){
//HttpStatus.BAD_REQUEST返回给前台是400
return new ResponseEntity<String>(toStr(e),HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(CheckExceptiom.class)
public ResponseEntity<String> handlecheckException(CheckExceptiom e){
//HttpStatus.BAD_REQUEST返回给前台是400
return new ResponseEntity<String>(toStr(e),HttpStatus.BAD_REQUEST);
}
private String toStr(CheckExceptiom e) {
return e.getFiledName()+":错误的值"+e.getFiledValue();
}
//把校验异常转换成字符串
private String toStr(WebExchangeBindException ex) {
return ex.getFieldErrors().stream()
.map(e -> e.getField()+":"+e.getDefaultMessage())//把异常转换成一个字符串
.reduce("",(s1,s2) -> s1+"\n"+s2); //把数组转换成字符串
}
}
webFlux的第二种开发方式——基于Rounter Function
package com.example.springwebfluxtest.handler;
import com.example.springwebfluxtest.domain.User;
import com.example.springwebfluxtest.repository.UserRepository;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
@Component
public class UserHandler {
private UserRepository repository;
public UserHandler(UserRepository rep){
this.repository = rep;
}
/**
* 得到所有用户
* @param request
* @return
*/
public Mono<ServerResponse> getAllUser(ServerRequest request){
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
.body(this.repository.findAll(),User.class);
}
/**
* 创建用户
* @param request
* @return
*/
public Mono<ServerResponse> createUser(ServerRequest request){
Mono<User> user = request.bodyToMono(User.class);
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
.body(this.repository.saveAll(user),User.class);
}
/**
* 根据id删除用户
* @param request
* @return
*/
public Mono<ServerResponse> deleteUserById(ServerRequest request){
String id = request.pathVariable("id");
return this.repository.findById(id)
.flatMap(user -> this.repository.delete(user))
.then(ServerResponse.ok().build())
.switchIfEmpty(ServerResponse.notFound().build());
}
}
package com.example.springwebfluxtest.router;
import com.example.springwebfluxtest.handler.UserHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.*;
import static org.springframework.web.reactive.function.server.RequestPredicates.DELETE;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
@Configuration
public class AllRouters {
@Bean
RouterFunction<ServerResponse> userRouter(UserHandler handler){
return RouterFunctions.nest(
//相当于@RequestMapping("/user")
RequestPredicates.path("/user"),
//得到所有用户
RouterFunctions.route(RequestPredicates.GET("/"),
handler::getAllUser)
//创建用户
.andRoute(POST("/").and(accept(MediaType.APPLICATION_JSON_UTF8)),
handler::createUser)
//删除用户
.andRoute(DELETE("/{id}"),handler::deleteUserById)
);
}
}
参数校验:
Exception.util包拷贝
package com.example.springwebfluxtest.handler;
import com.example.springwebfluxtest.exception.CheckExceptiom;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Mono;
@Component
@Order(-2)//S数值越小优先级越高
public class ExceptionHandler implements WebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
//设置响应头
response.setStatusCode(HttpStatus.BAD_REQUEST);
//设置返回值类型
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
//异常信息
String errorMsg = toStr(ex);
DataBuffer body= response.bufferFactory().wrap(errorMsg.getBytes());
return response.writeWith(Mono.just(body));
}
private String toStr(Throwable ex) {
if(ex instanceof CheckExceptiom){
CheckExceptiom e = (CheckExceptiom) ex;
return e.getFiledName()+":value"+e.getFiledValue();
}else{
ex.printStackTrace();
return ex.toString();
}
}
}