參數校驗與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