springboot整合响应编程

WebFlux

在响应式编程中主要是基于spring5

img

与spring mvc相同

Spring WebFlux提供了一个基于注解的编程模型,@Controller和@RestController组件使用注解来表示请求映射、请求输入、处理异常等等。

带注释的控制器具有灵活的方法签名,无需扩展基类或实现特定的接口。

定义

特性Spring Web MVCSpringWebFlux
应用控制器注解声明@Controller相同
应用REST控制器注解声明@RestController相同

映射

特性Spring Web MVCSpringWebFlux
请求映射注解声明@RequestMapping相同
GET方法映射@GetMapping相同
POST方法映射@PostMapping相同
PUT方法映射@PutMapping相同
DELETE方法映射@DeleteMapping相同
PATCH方法映射@PatchMapping相同

请求

特性Spring Web MVCSpringWebFlux
获取请求参数@RequestParam相同
获取请求头@RequestHeader相同
获取Cookie值@CookieValue相同
获取完整请求主体内容@RequestBody相同
获取请求路径变量@PathVariable相同
获取请求内容(包括请求主体和请求头)RequestEntity相同

响应

特性Spring Web MVCSpringWebFlux
响应主体注解声明@ResponseBody相同
响应内容(包括响应主体和响应头)@ResponseEntity相同
响应Cookie内容@ResponseCookie相同

拦截

特性Spring Web MVCSpringWebFlux
@Controller注解切面通知@ControllerAdvice相同
@ResetController注解切面通知@RestControlerAdvice相同

初识WebFlux

@GetMapping("/1")
public String get1(){
    log.info("get1 start");
    String string = createStr();
    log.info("get1 end");
    return string+"1";
}

private String createStr() {
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "some string";
}
//Mono 0-1个
@GetMapping("/2")
public Mono<String> get2(){
    log.info("get2 start");
    Mono<String> result = Mono.fromSupplier(()->createStr()+"2");
    log.info("get2 end");
    return result;
}
//Flux 0-N个
@GetMapping(value = "/3",produces = "text/event-stream")
public Flux<String> get3(){
    log.info("get3 start");
    Flux<String> result = Flux.fromStream(IntStream.range(1,5).mapToObj(i->{
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "flux data--" + i;
            }
    ));
    log.info("get3 end");
    return result;
}

@GetMapping(value = "/33", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
private Flux<String> flux() {
    Flux<String> result = Flux
            .fromStream(IntStream.range(1, 5).mapToObj(i -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                }
                return "flux data--" + i;
            }));
    return result;
}

这是结果

2022-02-09 21:58:10.955 INFO 6524 — [nio-8080-exec-7] c.e.d.Servlet.RestController : get1 start
2022-02-09 21:58:15.963 INFO 6524 — [nio-8080-exec-7] c.e.d.Servlet.RestController : get1 end
2022-02-09 21:58:19.800 INFO 6524 — [nio-8080-exec-9] c.e.d.Servlet.RestController : get2 start
2022-02-09 21:58:19.806 INFO 6524 — [nio-8080-exec-9] c.e.d.Servlet.RestController : get2 end
2022-02-09 21:58:33.030 INFO 6524 — [nio-8080-exec-2] c.e.d.Servlet.RestController : get3 start
2022-02-09 21:58:33.034 INFO 6524 — [nio-8080-exec-2] c.e.d.Servlet.RestController : get3 end

可以很清楚的发现webFlux使用了非阻塞的方式

应用

webFlux与webMvc最大的区别就是他无法使用关系型数据库

所以在应用中我们都是连接MongoDb

引入依赖和配置文件
        <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>
spring:
  data:
    mongodb:
#      uri: mongodb://8.142.10.1:27017/webFlux
      username: root
      password: "123456"
      host: 8.142.109.15
      port: 27017
      database: webFlux
实体类
@Document(collection = "user")
@Data
public class User {

    @Id
    private String id;

    @NotBlank
    private String name;

    @Range(min=10, max=100)
    private int age;
}
数据层类

data-mongodb-reactive的操作方式类似于mybatis-plus

封装了基础的增删改查

@Repository
public interface UserRepository extends ReactiveMongoRepository<User,String> {

    /**
     * 根据年龄查询
     * @param start
     * @param end
     * @return
     */
    Flux<User> findByAgeBetween(int start,int end);

    @Query("{'age':{ '$gte': 20, '$lte' : 30}}")
    Flux<User> oldUser();

}
web类
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    UserRepository userRepository;

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

    /**
     * 以SSE形式多次返回数据
     * @return
     */
    @GetMapping(value = "get",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<User> streamGetAll() {
        return userRepository.findAll();
    }

    /**
     * 保持
     * @param user
     * @return
     */
    @PostMapping("save")
    public Mono<User> createUser(@Valid @RequestBody User user) {
        user.setId(null);
        CheckUtil.checkName(user.getName());
        return userRepository.save(user);
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public Mono<ResponseEntity<Void>> deleteUser(@PathVariable("id") String id) {
        return userRepository.findById(id)
        //当要操作数据并返回一个Mono这个时候使用flatMap
        //如果不操作数据,只传转换数据,使用map
                .flatMap(user -> userRepository.delete(user))
                .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    /**
     * 更改
     * @param id
     * @param user
     * @return
     */
    @PutMapping("{id}")
    public Mono<ResponseEntity<User>> updateUser(@PathVariable String id,
                                                 @Valid @RequestBody User user){
        return userRepository.findById(id)
                //flatMap操作数据
            .flatMap(u->{
            u.setAge(user.getAge());
            u.setName(user.getName());
            return userRepository.save(u);})
                //转换数据
                .map(user1 -> new ResponseEntity<>(user1, HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

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

    /**
     * 根据年龄查询
     * @param start
     * @param end
     * @return
     */
    @GetMapping(value = "/age/{start}/{end}",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<User> findByAge(@PathVariable("start") int start,
                                @PathVariable("end") int end){
        return userRepository.findByAgeBetween(start, end);
    }

    @GetMapping("old")
    public Flux<User> findByAge(){
        return userRepository.oldUser();
    }
}
异常处理切面
@ControllerAdvice
public class CheckAdvice {


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

    private String toString(WebExchangeBindException e) {
        return e.getFieldErrors().stream()
                .map(ex -> ex.getField() + ":" + ex.getDefaultMessage())
                .reduce("", (s1, s2) -> s1 + "\n" + s2);
    }

}

尝试一下自定义参数校验

异常类
@Data
public class checkException extends RuntimeException{

    private String fieldName;

    private String value;

    public checkException(String fieldName, String value) {
        super();
        this.fieldName = fieldName;
        this.value = value;
    }
}
Util类
/**
 * 校验名字是否有敏感词
 */
public class CheckUtil {

    private static final String[] INVALID_NAME = {"admin","root"};

    public static void checkName(String value) {
        Stream.of(INVALID_NAME).filter(name -> name.equalsIgnoreCase(value))
                .findAny().ifPresent(name->{throw new checkException(name,value);
                });
    }
}
切面类

类似于刚才的操作把我们自定义的参数校验添加到里面

@ExceptionHandler(checkException.class)
public ResponseEntity<String> errorCheckException(checkException e){
    return new ResponseEntity<String>(toString(e),HttpStatus.BAD_REQUEST);
}
private String toString(checkException e){
    return e.getFieldName() + ":错误的值是" + e.getValue();
}

踩雷

1.首先mongodb的密码是字符串需要添加双引号

2.mongodb的用户操作数据库需要有readwrite的权限

3.mongodb的实体类id的类型必须是String

4.mongodb不能直接配置uri那种是无用户登录

同时分享一个嘎嘎好用的API工具

Restlet Client

eckException(checkException e){
return new ResponseEntity(toString(e),HttpStatus.BAD_REQUEST);
}
private String toString(checkException e){
return e.getFieldName() + “:错误的值是” + e.getValue();
}


## 踩雷

1.首先mongodb的密码是字符串需要添加双引号

2.mongodb的用户操作数据库需要有readwrite的权限

3.mongodb的实体类id的类型必须是String

4.mongodb不能直接配置uri那种是无用户登录

同时分享一个嘎嘎好用的API工具

Restlet Client

![请添加图片描述](https://img-blog.csdnimg.cn/97af0dbc9a5d40a9a0c628a3848ff8b2.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP6LW15ZGi,size_20,color_FFFFFF,t_70,g_se,x_16)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值