参数校验与LomBok的使用


1、参数校验机制

在 SpringBoot 中如何接收前端发送过来的参数,并对其进行验证是否符合要求,是否合法是非常非常重要的;对于 Web 开发来讲,参数合法的验证之所以如此重要有两个原因,一是对于服务端开发者,如果参数校验写的足够规范,是可以大大提高前后端开发的效率;二是保护 Web 里面的机密数据是非常重要的,因此对参数校验一定要有深刻的认知。

在日常工作中,我们经常会为了省事儿直接在控制器中写大量的校验代码,这是不对的,因为控制器主要是承接是视图层和Module或者服务层之间的一道桥梁,它不是用来编写主要的业务逻辑也不是写大量的校验代码的;针对于复杂的业务系统来讲,校验代码是非常复杂,甚至可能写到上百行,因此不建议在控制器中写校验代码,此外一定不要再控制器中写业务逻辑。

我们要进行参数校验,首先要能在控制器中获取到前台传过来的参数,参数主要分为两大类,一类是通过 URL 传递过来的参数,一类是通过 POST 请求的 body 传递过来的参数。通过 URL 传递的参数又分为两类,一种是写在 URL 路径中,例如@GetMapping("/test/param"),另一种是查询查询,例如@GetMapping("/test?param=xxx");

2、获取URL路径中的参数和查询参数

2.1 接收URL的路径参数

路径参数就是直接在URL中,使用 "/" 后面拼接起来的参数变量,使用 @PathVariable 注解来进行接收

 @GetMapping("/test1/{id}")
    public String test1(@PathVariable Integer id) {
        System.out.println("路径参数" + id);

        return "Gabriel";
    }

接收结果:

注意:这里的{id} 必须要和形参中的 Integer id 保持一致,如果 @GetMapping("/test1/id") 这里是 id, 但是在形参中是 Integer id1,也是无法接收到的;如果这两个地方的名称不一样,还需要接收的话可以使用下面的写法:

    @GetMapping("/test1/{id1}")
    public String test1(@PathVariable(name = "id1") Integer id) {
        System.out.println("路径参数" + id);

        return "Gabriel";
    }

2.2 接收URL的查询参数

查询参数,也就是在 URL 中直接使用 ? 拼接后的变量,使用 @RequestParam 注解来接收查询参数

   @GetMapping("/test1/{id1}")
    public String test1(@PathVariable(name = "id1") Integer id, @RequestParam String name) {
        System.out.println("路径参数" + id + ": name: " + name);

        return "Gabriel";
    }

 

接收结果

 

2.3 接收数据传输对象DTO

我们发送一些简单的参数的时候,可以通过使用这些 key-value 形式的 GET 方式发送,但是如果发送大量的比较复杂的数据的时候,例如下订单的情况,那么通常这种使用 POST 请求;POST是不能直接使用浏览器发送的,需要借助 Postman 发送(勾选 body -- raw -- JSON)

    @PostMapping("/test1/{id1}")
    public String test1(
            @PathVariable(name = "id1") Integer id,
            @RequestParam String name,
            @RequestBody Map<String, Object> person) {
        System.out.println("路径参数: " + id + ": name: " + name);

        return "Gabriel";
    }

接收结果

这里需要注意的是,在接收的时候参数的时候需要使用的是 @RequestBody 注解;接收的类型可以使用 Map类型,但是由于这里建议使用的方式是定义一个类,专门用来接收这个对象。这就是我们说的数据传输对象 DTO

创建DTO

@Setter
@Getter
public
class PersonDTO { private String name; private Integer age; }

接收参数

    @PostMapping("/test1/{id1}")
    public String test1(
            @PathVariable(name = "id1") Integer id,
            @RequestParam String name,
            @RequestBody PersonDTO personDTO) {
        System.out.println("路径参数: " + id + ": name: " + name + " person: " + personDTO);

        return "Gabriel";
    }

 

3、Lombok 的使用

3.1 引入 Lombok 依赖

    <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

3.2 通过 Lombok 可以快速生成 Getter 和 Setter 方法

@Getter
@Setter
@ToString
public class PersonDTO {
    private String name;
    private Integer age;
}

如果有成员变量是被 final 修饰的话,是不会生成 Setter 方法,只会生成 Getter 

3.2 Lombok 中构造函数的注解

1.  @AllArgsConstructor 通过该注解可以生成一个全部变量的构造函数

2.  @NoArgsConstructor 通过该注解可以生成一个无参的构造函数

3. @RequiredArgsConstructor 通过该注解可以生成一个成员不能为空的所有的构造函数

  @NonNull 用于声明该变量不能为空

@Getter
@Setter
//@AllArgsConstructor   所有成员变量的构造方法
@RequiredArgsConstructor    // 不为空的变量的构造方法
@NoArgsConstructor  // 空构造方法
public class PersonDTO {
    @NonNull    // name 不能为空
    private String name;
    @NonNull    // age 不能为空
    private Integer age;
}

 

3.2 @Builder 构造器模式

通过 @Builder 构造对象

@Builder
public class PersonDTO {
    private String name;
    private Integer age;
}

  @PostMapping("/test1/{id1}")
    public String test1(
            @PathVariable(name = "id1") Integer id,
            @RequestParam String name,
            @RequestBody PersonDTO personDTO) {

        PersonDTO gabriel = PersonDTO.builder().name("Gabriel").age(18).build();
        System.out.println(gabriel);

        return "Gabriel";
    }  

注意:

1. 如果使用了 @Builder 注解,就不能使用无参构造函数了去构建对象,也不能使用 Setter 方法赋值,只能使用 Builder 方式去构建对象

原因:@Builder 会为Bean生成一个 private 的无参构造函数

2. 如果使用了 @Builder 注解,还想使用无参构造函数,则必须显示声明一个无参构造函数

@Builder
@Setter
@NoArgsConstructor
public class PersonDTO {
    private String name;
    private Integer age;
}

3.  如果使用 Builder 模式构建了对象,并且直接返回前端是会报错的,如下示例:

    @PostMapping("/test1/{id1}")
    public PersonDTO test1(
            @PathVariable(name = "id1") Integer id,
            @RequestParam String name,
            @RequestBody PersonDTO personDTO) {

        PersonDTO gabriel = PersonDTO.builder().name("gabriel").age(18).build();

        return gabriel;
    }

调用后报错:

常原因:org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.gx.missyou.dto.PersonDTO
2020-08-04 20:29:33.037  WARN 10576 --- [nio-8080-exec-3] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.gx.missyou.dto.PersonDTO]

原因:因为使用 @Builder 注解后,Bean是没有 Getter 方法的,但是序列化一定是需要有 Getter 方法才可以,因此要解决该问题,需要添加 Getter 方法

@Builder
@Getter
public class PersonDTO {
    private String name;
    private Integer age;
}

4. 使用 @Validated 注解进行参数校验

使用 Hibernate-Validated 进行校验,要使用则必须在类上添加 @Validated 注解,否则是不会生效的

@Max(10) 最大为10

@RestController
@Validated
@RequestMapping("/banner")
public class BannerController {
    @Autowired
    private ISkill iSkill;

    @PostMapping("/test1/{id1}")
    public PersonDTO test1(
//            @PathVariable(name = "id1") @Max(10) Integer id,
//            @PathVariable(name = "id1") @Max(value = 10, message = "最大不能超过10哦") Integer id,
            @PathVariable(name = "id1") @Range(min = 1, max = 8, message = "范围超过了") Integer id,
            @RequestParam @NotBlank String name,
            @RequestBody PersonDTO personDTO) {

        PersonDTO gabriel = PersonDTO.builder().name("gabriel").age(18).build();

        return gabriel;
    }

 

4.2 如何在自定义的 DTO 进行验证

在 DTO 类上添加校验

@Builder
@Getter
public class PersonDTO {
    @Length(min = 3, max = 8, message = "姓名长度必须在3-8之间")
    private String name;
    private Integer age;
}

在 Controller 中的形参中的 HTTP body 的参数中添加 @Validated, 如果不添加该注解,则是不会生效的

 

4.2 如何Http body 中的参数进行级联校验

如果在一个 DTO 还嵌套一个 DTO, 那么要如何进行校验,需要在嵌套的 DTO上添加一个 @Valid 的注解

@Builder
@Getter
public class PersonDTO {
    @Length(min = 3, max = 8, message = "姓名长度必须在3-8之间")
    private String name;
    @Max(120)
    private Integer age;

    @Valid
    private SchooleDTO schooleDTO;
}

SchoolDTO

@Getter
@Setter
public class SchoolDTO {
    @Min(4)
    private String schoolName;
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM