Hibernate Validator 校驗(yàn) (二)

還想看更多文章的朋友可以訪問(wèn)我的個(gè)人博客


Hibernate Validator 校驗(yàn) (一)中,談了如何利用 Hibernate Validator 提供的約束注解完成較為簡(jiǎn)單的對(duì)象校驗(yàn),但這也不能滿足開發(fā)的需求。這時(shí),我們需要自己實(shí)現(xiàn)校驗(yàn)邏輯。

自定義校驗(yàn)邏輯

自實(shí)現(xiàn)校驗(yàn)有很多優(yōu)點(diǎn),一個(gè)項(xiàng)目下,肯定存在多個(gè)服務(wù)對(duì)同一類型的實(shí)例操作,而其校驗(yàn)邏輯大多類似。通過(guò)自實(shí)現(xiàn)校驗(yàn)將該邏輯抽象出來(lái),一方面減少代碼重復(fù),又可以滿足當(dāng)下模塊開發(fā)的思想。

先來(lái)看看測(cè)試類與 Controller 層實(shí)現(xiàn),其實(shí)與上一篇中的實(shí)現(xiàn)無(wú)太大差別,只是此次我們將使用正則表達(dá)式校驗(yàn)username,規(guī)則是匹配任何字類字符,包括下劃線,因此此處我們模擬發(fā)起增添用戶的POST請(qǐng)求。代碼如下:

Controller層實(shí)現(xiàn):

@RestController
@RequestMapping("/user")
public class UserController {
  @PostMapping
  public User updateUser(@PathVariable Integer id, @RequestBody @Valid User user, BindingResult result) {
      // 輸出錯(cuò)誤信息
      if (result.hasErrors()) {
          result.getAllErrors().forEach(err -> System.out.println(err.getDefaultMessage()));
      }

      user.setId(id);

     return userService.update(user);
  }
}

測(cè)試類:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

  @Autowired
  private WebApplicationContext wac;

  private MockMvc mockMvc;

  @Before
  public void setUp() {
      this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
  }

  @Test
  public void testValidUser() throws Exception {
      Date date = new Date();
      String content = "{\"username\": \"@ksjkd\",\"password\": \"password\",\"birthday\":"
           - date.getTime() + "}";
      mockMvc.perform(post("/user")
                      .contentType(MediaType.APPLICATION_JSON_UTF8)
                      .content(content))
                      .andExpect(status().isOk());
  }

}

上一篇中也有提到,JPA Validation 規(guī)范對(duì)約束的定義包括兩部分,一是約束注解,如上篇中用到的@NotBlank、NotNull等就是約束注解;二是約束校驗(yàn)器,每一個(gè)約束注解都存在對(duì)應(yīng)的約束校驗(yàn)器,約束校驗(yàn)器實(shí)現(xiàn)具體的校驗(yàn)邏輯。我們自己實(shí)現(xiàn) validator 也需要滿足這兩點(diǎn)。

一、創(chuàng)建約束注解與其對(duì)應(yīng)的約束校驗(yàn)器

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { TestValidator.class })
public @interface TestConstraint {

    String message() default ""; // 約束注解校驗(yàn)時(shí)的輸出消息

    Class<?>[] groups() default { }; // 約束注解在校驗(yàn)時(shí)所屬的組別

    Class<? extends Payload>[] payload() default { }; // 約束注解的有效負(fù)載
}

如上,在該注解上@Target@Retention是 Java 中元注解中的兩個(gè),分別用于指明該注解的作用目標(biāo)與保留位置,此處不多做贅述。而Constraint注解特用于指明 JPA 約束注解與約束校驗(yàn)器的對(duì)應(yīng)關(guān)系。

另外,JPA Validation 規(guī)范要求自實(shí)現(xiàn)的約束注解必須聲明以上三個(gè)參數(shù),分別是:message, groups, payload。其中message屬性便是當(dāng)校驗(yàn)器校驗(yàn)失敗時(shí)的輸出消息,另兩個(gè)屬性的大致用途已在代碼中注釋,不再詳述。

如下,為該校驗(yàn)器的實(shí)現(xiàn):

public class TestValidator implements ConstraintValidator<TestConstraint, String> {

    @Autowired
    private TestService testService;

    @Override
    public void initialize(TestConstraint constraintAnnotation) {
        // 添加約束注解在初始化時(shí)需要做的動(dòng)作
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        System.out.println(testService.test(value));

        String pattern = "^\\w+$"; // 匹配任何字類字符,包括下劃線。與"[A-Za-z0-9_]"等效

        Pattern r = Pattern.compile(pattern);
        Matcher m = r.matcher(value);

        return m.matches();
    }
}

自實(shí)現(xiàn)的校驗(yàn)器必須實(shí)現(xiàn)ConstraintValidator<A, T>接口,該接口有兩個(gè)泛型,分別是該校驗(yàn)器所“對(duì)應(yīng)的約束注解的類類型”和“被校驗(yàn)對(duì)象的類類型”。這里是針對(duì)String類型的username字段進(jìn)行校驗(yàn),所以兩泛型類型分別為TestConstraintString。

如果是 Spring 項(xiàng)目,當(dāng)實(shí)現(xiàn)該接口則會(huì)自動(dòng)被 Bean 容器收集為 Bean。因此,我們可以按需求Autowired需要的Bean。TestService實(shí)現(xiàn)如下:

@Service
public class TestService {
    public String test(String o) {
        return "test:[" + o + "]";
    }
}

本例中,該校驗(yàn)器采用“正則規(guī)則”對(duì)字符串進(jìn)行匹配。

二、在實(shí)體中聲明需要校驗(yàn)的字段

@Entity
public class User {

    @TestConstraint(message = "測(cè)試校驗(yàn):用戶名必須是數(shù)字、字母或_的組合")
    private String username;

    ...
}

利用我們方才定義的約束注解@TestConstraint對(duì) username 字段進(jìn)行校驗(yàn),并自定義校驗(yàn)錯(cuò)誤消息。

測(cè)試結(jié)果

在測(cè)試請(qǐng)求中,我們模擬的請(qǐng)求數(shù)據(jù)中username字段中添加了非法字符@,因此校驗(yàn)肯定是無(wú)法通過(guò)的:

image

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容