SpringBoot2.0不容错过的新特性 ——WebFlux响应式编程(3)

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();
        }
    }


}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值