美文网首页
RestController详解(下)

RestController详解(下)

作者: 陆小愤 | 来源:发表于2018-11-10 20:16 被阅读0次

    最近在网易云课堂上看一些视频,给大家推荐一个讲Spring Boot的视频https://study.163.com/course/courseMain.htm?courseId=1005213034,老师讲的很不错。在学习的时候我也会做一些笔记,方便日后巩固。
    对这个系列感兴趣的可以看我之前写的博客:

    开始一个最简单的RESTful API项目

    RestController详解(上)

    对客户端传入数据的校验

    原则

    • 不要相信前端传过来的数据
    • 不能相信前端传过来的任何数据
    • 绝不要相信前端传过来的任何数据
    • 尽量要前端少传递数据

    一般来说,我们要对传进来的数据做校验,有一种写法是:

    @PostMapping
        public TvSeriesDto insertOne(@RequestBody TvSeriesDto tvSeriesDto) {
            if (tvSeriesDto == null) {
                throw new RuntimeException("参数tvSeriesDto不可为空");
            }
            if (tvSeriesDto.getName() == null) {
                throw new RuntimeException("电视剧名字不可为空");
            }
            …
        }
    

    这样写是没有什么问题的,但是这样写占了很多行代码,大部分判断的条件也十分单一。这时候就需要用到Bean Vlidation。

    Bean Vlidation有两种规范

    • JSR:定义了注解和接口等一些标准,具体实现需要靠其他的厂家来做一些框架
    • Hibernate Validator:这就是上面所说的其中一种框架,在Spring Boot中可以直接用

    Bean Validation注解

    • @Null 验证对象是否为空
    • @NotNull 验证对象是否为非空
    • @Min 验证 Number 和 String 对象是否大等于指定的值
    • @Max 验证 Number 和 String 对象是否小等于指定的值
    • @Size 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
    • @Past 验证 Date 和 Calendar 对象是否在当前时间之前
    • @Future 验证 Date 和 Calendar 对象是否在当前时间之后
    • @AssertTrue 验证 Boolean 对象是否为 true
    • @AssertFalse 验证 Boolean 对象是否为 false
    • @Valid 级联验证注解,即这个bean里面的值也需要验证

    讲了这么多注解,现在就来实际操作一下!

    1. 我们要给电视连续剧(TvSeries)这个类加一个属性,就是它的人物。这是一个新的类,代码如下:

      package cn.luxiaofen.test;
      
      import javax.validation.constraints.NotNull;
      
      public class TvCharacterDto {
      private int id;
      private int tvSeriesId;
      @NotNull private String name;//这里规定name不能为null
      
      //setter,getter方法
      
      public int getId() {
          return id;
      }
      
      public void setId(int id) {
          this.id = id;
      }
      
      public int getTvSeriesId() {
          return tvSeriesId;
      }
      
      public void setTvSeriesId(int tvSeriesId) {
          this.tvSeriesId = tvSeriesId;
      }
      
      public String getName() {
          return name;
      }
      
      public void setName(String name) {
          this.name = name;
      }
      }
      
      
    2. 对TvSeries的field做一些修改,这里不要忘了添加对应的setter和getter方法,不然Request Body里面的参数是传不进来的:

      @Null private Integer id;
      @NotNull private String name;
      @DecimalMin("1") private int seasonCount;
      @JsonFormat(timezone="GMT+8", pattern="yyyy-MM-dd")
      @Past private Date originalRelease; //首发日期
      //@Valid表示要级联校验;@Size(min=2)表示这个列表至少要有2项内容,否则通不过校验
      @Valid @Size(min=2) @NotNull
      private List<TvCharacterDto> tvCharacters;
      

      注意:在学习十一课时的时候遇到了一些小问题,TvCharacterDto这个类的id一开始被设置为了Integer型,这是因为之后可能需要加上@Null注解,而int是基本数据类型,不会为null。然而这样设置以后,如果在上传的JSON格式中省略了对id这个参数的声明,系统会抛出JsonMappingException。这是因为Integer类型默认为null,在JSON格式中无法转换,需要加@JSONignore注解。但是加了这个注解,以后在JSON中声明这个field也无效了。将类型改为int后问题可以解决。

    3. 下面这一步很重要!为了在使用方法的时候可以让我们的注解起作用,一定要在传入的参数上加上@Valid注解:

      /**
       *
       * @param tvSeriesDto @RequestBody这个注解表明上传数据储存在RequestBody中,
       *                    用JSON格式表示了一个TvSeriesDto对象。@Valid注解在这里
       *                    很重要,它代表了要去校验TvSeriesDto这个类里面的field
       *
       *
       */
      @PostMapping
      public TvSeriesDto insertOne(@Valid @RequestBody TvSeriesDto tvSeriesDto) {
          if (log.isTraceEnabled()) {
              log.trace("传进来的参数是:"+tvSeriesDto);
          }
          tvSeriesDto.setId(9999);
          return tvSeriesDto;
      }
      
    4. 去POSTMAN里面测试一下这个方法,这里要注意JSON的格式

      课时11.1

    深入学习Bean Validation

    这里对上文提到的一些注解做了分类:


    课时12.1

    注解的位置

    • Field level 成员变量,之前我们加的注解都是加在成员变量上
    • Property level get(is) 方法上,这用于验证方法的返回值
    • Class level 类上,JSR标准里没有,可以自定义

    下面就来看几个例子:

    //images不可为null,每个元素(Image类型)需要级联验证
    @NotNull
    private List<@Valid Image> images;
    
    
    //images长度至少为1,不可为null,每个元素(String类型)不可为null,最短长度10
    @NotNull
    @Size(min=1)
    private List<@Size(min=10)  @NotNull String> images;   
    
    @AssertTrue
    public boolean isValid() {
        //这里验证类的各种成员变量组合
        return true;
    }
    

    注意:

    约束规则对子类依旧有效。验证的框架实际上是利用了java的反射把父类的反射出来进行验证来实现这一点。

    GROUPS参数

    假设现在我们有以下新的需求:

    • 操作分步进行       第一步,输入名字等基本信息。第二步,验证手机号码
    • 分角色,不同修改权限       管理员可修改的内容比一般用户多

    解决方案是使用GROUPS参数:

    • 每个约束用注解都有一个groups参数
    • 可接收多个class类型(必须是接口)
    • 不声明groups参数是默认组javax.validation.groups.Default
    • 声明了groups参数的会从Default组移除,如需加入Default组需要显示声明,例如:
    @Null(groups={Default.class, Step1.class})
    

    @Valid vs. @Validated

    • @Valid是JSR标准定义的注解,只验证Default组的约束
    • @Validated是spring定义的注解,可以通过参数来指定验证的组,例如:@Validated({Step1.class, Default.class})表示验证Step1和Default两个组的约束
    • @Valid可用在成员变量上,进行级联验证;@Validated只能用在参数上,表示这个参数需要验证

    BindingResult

    到目前我们讲的都是不符合规则,spring就会把请求给退回去,返回一个400的Bad Request,不会调用我们的方法。如:

    public Object doSomething(@Validated @RequestBody OneDto oneDto) {
         // 通不过校验的参数,不会执行这个方法
    }
    

    如果我们希望验证完不论什么结果,都不给客户端返回了,而是交给程序进行处理,我们就可以在方法的参数上加一个BindingResult,校验结果会通过result参数传递进来,然后由程序根据校验结果选择执行。

    public Object doSomething(@Validated @RequestBody OneDto oneDto, 
                              BindingResult result) {
        // 参数通不过校验也会进入方法执行,校验结果会通过result参数传递进来
        if (result.hasErrors()){
             // 没通过校验
        }
    }
    

    Bean Validation 2.0中的约束用注解

    13.1
    13.2
    13.3

    相关文章

      网友评论

          本文标题:RestController详解(下)

          本文链接:https://www.haomeiwen.com/subject/yfrtfqtx.html