Spring中的@Valid 和 @Validated註解你用對了嗎

1.概述

本文我們將重點介紹Spring中 @Valid@Validated註解的區別 。

驗證用戶輸入是否正確是我們應用程序中的常見功能。Spring提供了@Valid和@Validated兩個註解來實現驗證功能,下面我們來詳細介紹它們。

2. @Valid和@Validate註解

在Spring中,我們使用@Valid 註解進行方法級別驗證,同時還能用它來標記成員屬性以進行驗證。

但是,此注釋不支持分組驗證。@Validated則支持分組驗證。

3.例子

讓我們考慮一個使用Spring Boot開發的簡單用戶註冊表單。首先,我們只有名稱密碼屬性:

public class UserAccount {

    @NotNull
    @Size(min = 4, max = 15)
    private String password;

    @NotBlank
    private String name;

    // standard constructors / setters / getters / toString

}

接下來,讓我們看一下控制器。在這裡,我們將使用帶有@Valid批註的saveBasicInfo方法來驗證用戶輸入:

@RequestMapping(value = "/saveBasicInfo", method = RequestMethod.POST)
public String saveBasicInfo(
  @Valid @ModelAttribute("useraccount") UserAccount useraccount, 
  BindingResult result, 
  ModelMap model) {
    if (result.hasErrors()) {
        return "error";
    }
    return "success";
}

現在讓我們測試一下這個方法:

@Test
public void givenSaveBasicInfo_whenCorrectInput`thenSuccess() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfo")
      .accept(MediaType.TEXT_HTML)
      .param("name", "test123")
      .param("password", "pass"))
      .andExpect(view().name("success"))
      .andExpect(status().isOk())
      .andDo(print());
}

在確認測試成功運行之後,現在讓我們擴展功能。下一步的邏輯步驟是將其轉換為多步驟註冊表格,就像大多數嚮導一樣。第一步,名稱密碼保持不變。在第二步中,我們將獲取其他信息,例如age 和 phone。因此,我們將使用以下其他字段更新域對象:

public class UserAccount {

    @NotNull
    @Size(min = 4, max = 15)
    private String password;

    @NotBlank
    private String name;

    @Min(value = 18, message = "Age should not be less than 18")
    private int age;

    @NotBlank
    private String phone;

    // standard constructors / setters / getters / toString   

}

但是,這一次,我們將注意到先前的測試失敗。這是因為我們沒有傳遞年齡電話字段。

為了支持此行為,我們引入支持分組驗證的@Validated批註。

分組驗證,就是將字段分組,分別驗證,比如我們將用戶信息分為兩組:BasicInfoAdvanceInfo

可以建立兩個空接口:

public interface BasicInfo {
}
public interface AdvanceInfo {
}

第一步將具有BasicInfo接口,第二步 將具有AdvanceInfo  。此外,我們將更新UserAccount類以使用這些標記接口,如下所示:

public class UserAccount {

    @NotNull(groups = BasicInfo.class)
    @Size(min = 4, max = 15, groups = BasicInfo.class)
    private String password;

    @NotBlank(groups = BasicInfo.class)
    private String name;

    @Min(value = 18, message = "Age should not be less than 18", groups = AdvanceInfo.class)
    private int age;

    @NotBlank(groups = AdvanceInfo.class)
    private String phone;

    // standard constructors / setters / getters / toString   

}

另外,我們現在將更新控制器以使用@Validated注釋而不是@Valid

@RequestMapping(value = "/saveBasicInfoStep1", method = RequestMethod.POST)
public String saveBasicInfoStep1(
  @Validated(BasicInfo.class) 
  @ModelAttribute("useraccount") UserAccount useraccount, 
  BindingResult result, ModelMap model) {
    if (result.hasErrors()) {
        return "error";
    }
    return "success";
}

更新後,再次執行測試,現在可以成功運行。現在,我們還要測試這個新方法:

@Test
public void givenSaveBasicInfoStep1`whenCorrectInput`thenSuccess() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders.post("/saveBasicInfoStep1")
      .accept(MediaType.TEXT_HTML)
      .param("name", "test123")
      .param("password", "pass"))
      .andExpect(view().name("success"))
      .andExpect(status().isOk())
      .andDo(print());
}

也成功運行!

接下來,讓我們看看@Valid對於觸發嵌套屬性驗證是必不可少的。

4.使用@Valid批註標記嵌套對象

@Valid 可以用於嵌套對象。例如,在我們當前的場景中,讓我們創建一個 UserAddress 對象:

public class UserAddress {

    @NotBlank
    private String countryCode;

    // standard constructors / setters / getters / toString
}

為了確保驗證此嵌套對象,我們將使用@Valid批註裝飾屬性:

public class UserAccount {

    //...

    @Valid
    @NotNull(groups = AdvanceInfo.class)
    private UserAddress useraddress;

    // standard constructors / setters / getters / toString 
}

5. 總結

@Valid保證了整個對象的驗證, 但是它是對整個對象進行驗證,當僅需要部分驗證的時候就會出現問題。 這時候,可以使用@Validated 進行分組驗證。

參考


作者:Jadepeng
出處:jqpeng的技術記事本–//www.cnblogs.com/xiaoqi
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。