webFlux 学习(三)

入门crud

这次我们来学习webflux入门CRUD

首先我们需要安装一个mondodb 在这里我就不说怎么安装了.

配置文件如下

spring.data.mongodb.uri=mongodb://xxxxx:27017/webflux

领域对象

/**
 * @Created by xiaodao
 */
@Document(collection = "user")
@Data
public class User {


    @Id
    private String id;

    @NotBlank
    private String name;
    @Range(min = 10,max = 100)
    private Integer age;
}

repository

/**
 * @Created by xiaodao
 */
@Repository
public interface UserRepository extends ReactiveMongoRepository<User,String> {

    Flux<User> findByAgeBetween(int start, int end );


    @Query("{'age':{$gte:?0,$lte:?1}}")
    Flux<User> oldUser(int start, int end );
}

 Controller

package com.xiaodao.webflux.controller;

import com.xiaodao.webflux.pojo.User;
import com.xiaodao.webflux.repository.UserRepository;
import com.xiaodao.webflux.utils.CheckUtil;
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;

import javax.validation.Valid;
import java.awt.*;

/**
 * @Created by xiaodao
 */
@RestController
@RequestMapping("/user")
public class UserController {

    private final UserRepository userRepository;

    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    /**
     * 以数组形式一次性返回数据
     * @return
     */
    @GetMapping("/")
    public Flux<User> getAll(){
        return userRepository.findAll();
    }

    /**
     * sse 形式多次返回方式
     * @return
     */
    @GetMapping(value = "/stream/all",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<User> getStreamAll(){
        return userRepository.findAll();
    }

    /**
     * 新增
     * @param user
     * @return
     */
    @PostMapping("/")
    public Mono<User> createUser(@Valid @RequestBody User user){
        CheckUtil.checkName(user.getName());
        return this.userRepository.save(user);

    }

    /**
     * 返回状态码 存在返回200 不存在返回404
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public Mono<ResponseEntity<Void>> delete(@PathVariable String id){

        //没有返回值.不知道数据有没有
//        this.userRepository.deleteById(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
     * @param user
     * @return
     */
    @PutMapping("/{id}")
    public Mono<ResponseEntity<User>> updateUser( @PathVariable String id ,@RequestBody @Valid User user){
       return this.userRepository.findById(id).flatMap(u->{
            user.setId(id);
          return   this.userRepository.save(user);
        }).map(u->new ResponseEntity<User>(u,HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }


    /**
     * 根据id查找一个值
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Mono<ResponseEntity<User>> findById(@PathVariable String id ){
        return  this.userRepository.findById(id).map(u->new ResponseEntity<User>(u,HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<User>(HttpStatus.NOT_FOUND));
    }


    /**
     * 根据年龄段查找
     * @param start
     * @param end
     * @return
     */
    @GetMapping("/age/{start}/{end}")
    public Flux<User> findByage(@PathVariable int start , @PathVariable int end){
        return this.userRepository.findByAgeBetween( start,end  );
    }

    /**
     * 流式
     * @param start
     * @param end
     * @return
     */
    @GetMapping(value = "/ageStream/{start}/{end}",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<User> findByageStream(@PathVariable int start , @PathVariable int end){
        return this.userRepository.findByAgeBetween( start,end  );
    }


    @GetMapping("/findoldUser/{start}/{end}")
    public Flux<User> findoldUser(@PathVariable int start , @PathVariable int end){
        return this.userRepository.oldUser( start,end  );
    }
}

上面的代码加了一点hibernate的校验和自己的校验

controllerAdvice

/**
 * 异常处理
 * @Created by xiaodao
 */
@ControllerAdvice
public class CheckAdvice {



    @ExceptionHandler(WebExchangeBindException.class)
    public ResponseEntity<String> handleBindException(WebExchangeBindException e ){
        return new ResponseEntity<String>(tostr(e), HttpStatus.BAD_REQUEST);
    }

    /**
     * 校验异常转换为字符串
     * @param e
     * @return
     */
    private String tostr(WebExchangeBindException e) {

     return    e.getFieldErrors().stream()
                .map(a->a.getField()+" : "+ a.getDefaultMessage())
                .reduce("",(s1,s2)-> s1+ "\n"+s2);

    }

    @ExceptionHandler(CheckException.class)
    public ResponseEntity<String> handleCheckException(
            CheckException e) {
        return new ResponseEntity<String>(toStr(e), HttpStatus.BAD_REQUEST);
    }

    private String toStr(CheckException e) {
        return e.getFieldName() + ": 错误的值 " + e.getFieldValue();
    }

}

这里注意的一点就是@valid需要加载具体的类上,不然不会生效

这里还有我们的自己定义的校验名字的方法,我也贴到这里

/**
 * @Created by xiaodao
 */
public class CheckUtil {

    private static final String[] INVALID_NAMES={"admin","role"};


    public static void checkName(String value){

        Stream.of(INVALID_NAMES).filter(name -> name.equalsIgnoreCase(value))
                .findAny().ifPresent(name->{throw new CheckException("name",value);});
    }


}

 RouterFucntion

routerFunction 是webflux的另一种开始模式

webflux 可以运行在servlet3.1 和netty上 需要把

httpServletRequest - >serverRequest

httpServletResponse - >serverResponse 

使用routerFunction开发过程需要  

1.HandlerFunction  就是输入servletRequest 返回的是 serverResponse

2.routerFunction (将请求url和HandleFunction对应起来)

3.然后才是你自己定义的userhandle处理

4.server 处理

代码如下

package com.xiaodao.webflux.handle;

import com.xiaodao.webflux.pojo.User;
import com.xiaodao.webflux.repository.UserRepository;
import com.xiaodao.webflux.utils.CheckUtil;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * @Created by xiaodao
 */

@Component
public class UserHandle {

    @Autowired
    private UserRepository userRepository;

    /***
     * 获取所有用户
     * @param request
     * @return
     */
    public Mono<ServerResponse> getAllUser(ServerRequest request){
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(userRepository.findAll(), User.class);
    }


    /**
     * 创建用户
     * @param request
     * @return
     */
    public Mono<ServerResponse> createUser(ServerRequest request) {
        Mono<User> userMono = request.bodyToMono(User.class);

        return userMono.flatMap(u -> {
                    CheckUtil.checkName(u.getName());
                    return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8)
                            .body(userRepository.save(u), User.class);
                }

        );
    }
    /**
     * 删除
     * @param request
     * @return
     */
    public Mono<ServerResponse> deleteUser(ServerRequest request){
        String id = request.pathVariable("id");
        return userRepository.findById(id).flatMap(user->userRepository.delete(user))
                .then(ServerResponse.ok().build())
                .switchIfEmpty(ServerResponse.notFound().build());
    }

}

routerFunction 

/**
 * @Created by xiaodao
 */
@Configuration
public class AllRouter {

    @Bean
    RouterFunction<ServerResponse> userRouter(UserHandle userHandle){

        return RouterFunctions.nest(
                RequestPredicates.path("/user"),
                //查找所有用户
                RouterFunctions.route(RequestPredicates.GET("/"),userHandle::getAllUser)
                        //创建用户
                .andRoute(RequestPredicates.POST("/")
                        .and(RequestPredicates.accept(MediaType.APPLICATION_JSON_UTF8)
               ) ,userHandle::createUser)
                        //删除用户
                .andRoute(RequestPredicates.DELETE("/{id}"),userHandle::deleteUser)
        );
    }
}
/**
 * @Created by xiaodao
 */
@Component
@Order(-2)
public class ExceptionHandle implements WebExceptionHandler {
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
        ServerHttpResponse response = exchange.getResponse();
        //设置响应头400
        response.setStatusCode(HttpStatus.BAD_REQUEST);
        //设置返回类型
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);

        //异常信息
        String errorMsg = tostr(throwable);


        DataBuffer wrap = response.bufferFactory().wrap(errorMsg.getBytes());
        return response.writeWith(Mono.just(wrap));
    }

    private String tostr(Throwable ex) {
        //已知异常
        if(ex instanceof CheckException){

            CheckException check = (CheckException)ex;
            return check.getFieldName() +"  invalid value  "+ check.getFieldValue();

            //未知异常需要打印堆栈方便定位
        }else{
            ex.printStackTrace();
            return ex.toString();
        }
    }
}

 

这个时候我来就可以去访问.

localhost:8080/user

 

posted @ 2019-06-19 16:39  北京de小刀  阅读(1019)  评论(0编辑  收藏  举报