如何通過自定義spring?invalidator注解校驗數(shù)據(jù)合法性
自定義spring invalidator注解校驗數(shù)據(jù)合法性
在項目中經常會對用戶輸入的數(shù)據(jù),或者外部導入到系統(tǒng)的數(shù)據(jù)做合法性檢查。在spring boot框架的微服務中可以使用invalidator注解對數(shù)據(jù)做合法性,安全性校驗。
下面給一個樣例說明如何自定義注解實現(xiàn)校驗邏輯。
1、定義校驗屬性字符串長度的注解
package com.elon.springbootdemo.manager.invalidator; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; /** ?* 屬性字段長度校驗注解定義。 ?*? ?* @author elon ?* @version 2018年9月19日 ?*/ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = FieldLengthInvalidatorImpl.class) @Documented public @interface FieldLengthInvalidator { ? ? // 字段支持的最大長度(字符數(shù)) ? ? int maxLength() default 50; ? ? // 校驗失敗后返回的錯誤信息 ? ? String message() default ""; ? ? // 分組 ? ? Class<?>[] groups() default {}; ? ? // 負載 ? ? Class<? extends Payload>[] payload() default {}; }
在定義注解時可聲明變量用于輔助校驗。上面的注解中定義了maxLength變量用于指定最大長度限制。變量可以設置默認值,使用注解時不傳參數(shù),變量就使用默認值。
2、實現(xiàn)校驗邏輯,校驗失敗后返回錯誤提示
package com.elon.springbootdemo.manager.invalidator; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** ?* 字段長度校驗實現(xiàn)類。 ?*? ?* @author elon ?* @version 2018年9月19日 ?*/ public class FieldLengthInvalidatorImpl implements ConstraintValidator<FieldLengthInvalidator, String> { ? ? private int maxLength = 0; ? ? @Override ? ? public void initialize(FieldLengthInvalidator invalidator) { ? ? ? ? maxLength = invalidator.maxLength(); ? ? } ? ? @Override ? ? public boolean isValid(String fieldValue, ConstraintValidatorContext context) { ? ? ? ? if (fieldValue.length() > maxLength) { ? ? ? ? ? ? context.disableDefaultConstraintViolation(); ? ? ? ? ? ? context.buildConstraintViolationWithTemplate("對象屬性長度超過限制。").addConstraintViolation(); ? ? ? ? ? ? // 校驗失敗返回false。返回true上游收集不到錯誤信息。 ? ? ? ? ? ? return false; ? ? ? ? } ? ? ? ? return true; ? ? } }
3、在模型字段屬性上增加校驗的注解
public class User { ?? ?private int userId = -1; ?? ?@FieldLengthInvalidator(maxLength=10) ?? ?private String name = ""; }
4、提供統(tǒng)一的校驗方法
package com.elon.springbootdemo.manager; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; /** ?* 有效性校驗管理類。對外提供統(tǒng)一的校驗調用接口。 ?* @author elon ?* @version 2018年9月19日 ?*/ public class InvalidatorMgr { ? ? private InvalidatorMgr() { ? ? ? ?? ? ? } ? ?? ? ? /** ? ? ?* 獲取單例對象。 ? ? ?*? ? ? ?* @return 單例對象 ? ? ?*/ ? ? public static InvalidatorMgr instance() { ? ? ? ? return InvalidatorMgrBuilder.instance; ? ? } ? ?? ? ? /** ? ? ?* 校驗模型所有屬性的有效性。 ? ? ?*? ? ? ?* @param model 待校驗模型 ? ? ?* @return 錯誤信息列表 ? ? ?*/ ? ? public <T> List<String> validate(T model) { ? ? ? ?? ? ? ? ? ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); ? ? ? ? Validator validator = validatorFactory.getValidator(); ? ? ? ? Set<ConstraintViolation<T>> resultSet = validator.validate(model); ? ? ? ?? ? ? ? ? List<String> messageList = new ArrayList<>(); ? ? ? ? resultSet.forEach((r)->messageList.add(r.getMessage()));? ? ? ?? ? ? ? ? return messageList; ? ? } ? ?? ? ? /** ? ? ?* 單例構建器。 ? ? ?* @author elon ? ? ?* @version 2018年9月19日 ? ? ?*/ ? ? private static class InvalidatorMgrBuilder{ ? ? ? ? private static InvalidatorMgr instance = new InvalidatorMgr(); ? ? } }
5、業(yè)務層調用校驗方法
? ? ? ? User user = new User(); ? ? ? ? user.setName("ahskahskhqlwjqlwqlwhqlhwlqjwlqhwlhqwhqlwjjqlwl"); ? ? ? ? List<String> messageList = InvalidatorMgr.instance().validate(user); ? ? ? ? System.out.println(messageList);
invalidator注解主要用于實現(xiàn)長度,范圍,非法字符等通用的規(guī)則校驗。不適合用于做業(yè)務邏輯的校驗,特定的業(yè)務校驗寫在業(yè)務層。
springboot 參數(shù)驗證 validation
1、綜述
springboot提供了強大的基于注解的、開箱即用的驗證功能,這種基于bean validation的實現(xiàn)和 hibernate validator類似
2、依賴
創(chuàng)建springboot項目,包含以下依賴
<dependency> ? ? <groupId>org.springframework.boot</groupId> ? ? <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency>? ? ? <groupId>org.springframework.boot</groupId> ? ? <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>? <dependency>? ? ? <groupId>com.h2database</groupId>? ? ? <artifactId>h2</artifactId> ? ? <version>1.4.197</version>? ? ? <scope>runtime</scope> </dependency>
3、定義實體類
測試項目為了方便,直接用JPA,使用@NotBlank指定非空字段,message是驗證觸發(fā)后返回的信息,還有@Null、@NotNull、@NotBlank、@Email、@Max、@Min、@Size、@Negative、@DecimalMax、@DecimalMin、@Positive、@PositiveOrZero、@NegativeOrZero、@AssertTrue、@AssertFalse、@Future、@FutureOrPresent、@Past、@PastOrPresent、@Pattern
@Entity public class User { ? ?? ? ? @Id ? ? @GeneratedValue(strategy = GenerationType.AUTO) ? ? private long id; ? ? ? ? ? @NotBlank(message = "Name is mandatory") ? ? private String name; ? ? ? ? ? @NotBlank(message = "Email is mandatory") ? ? private String email; ? ?? ? ? // standard constructors / setters / getters / toString ? ? ? ?? }
創(chuàng)建JPA的repository定義增刪改查接口
@Repository public interface UserRepository extends CrudRepository<User, Long> {}
4、創(chuàng)建rest controller
@RestController public class UserController { ? ? ? @PostMapping("/users") ? ? ResponseEntity<String> addUser(@Valid @RequestBody User user) { ? ? ? ? // persisting the user ? ? ? ? return ResponseEntity.ok("User is valid"); ? ? } ? ? ? ? ? // standard constructors / other methods ? ? ? }
接收到的user對象添加了@Valid,當Spring Boot發(fā)現(xiàn)帶有@Valid注解的參數(shù)時,會自動引導默認的JSR 380驗證器驗證參數(shù)。當目標參數(shù)未能通過驗證時,Spring Boot將拋出一個MethodArgumentNotValidException
5、實現(xiàn)ExceptionHandler
直接拋出異常顯然是不合理的,大部分情況需要經過處理返回給前端更友好的提示信息,通過@ExceptionHandler來處理拋出的異常實現(xiàn)該功能
@ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) public Map<String, String> handleValidationExceptions( ? MethodArgumentNotValidException ex) { ? ? Map<String, String> errors = new HashMap<>(); ? ? ex.getBindingResult().getAllErrors().forEach((error) -> { ? ? ? ? String fieldName = ((FieldError) error).getField(); ? ? ? ? String errorMessage = error.getDefaultMessage(); ? ? ? ? errors.put(fieldName, errorMessage); ? ? }); ? ? return errors; }
MethodArgumentNotValidException作為上一步拋出的異常,當springboot執(zhí)行validition觸發(fā)時會調用此實現(xiàn),該方法將每個無效字段的名稱和驗證后錯誤消息存儲在映射中,然后它將映射作為JSON表示形式發(fā)送回客戶端進行進一步處理。
6、寫測試代碼
使用springboot自帶的插件進行測試rest controller,
@RunWith(SpringRunner.class)? @WebMvcTest @AutoConfigureMockMvc public class UserControllerIntegrationTest { ? ? ? @MockBean ? ? private UserRepository userRepository; ? ? ? ? ? @Autowired ? ? UserController userController; ? ? ? @Autowired ? ? private MockMvc mockMvc; ? ? ? //... ? ?? }
@WebMvcTest允許我們使用MockMvcRequestBuilders和MockMvcResultMatchers實現(xiàn)的一組靜態(tài)方法測試請求和響應。測試addUser()方法,在請求體中傳遞一個有效的User對象和一個無效的User對象。
@Test public void whenPostRequestToUsersAndValidUser_thenCorrectResponse() throws Exception { ? ? MediaType textPlainUtf8 = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8")); ? ? String user = "{\"name\": \"bob\", \"email\" : \"bob@domain.com\"}"; ? ? mockMvc.perform(MockMvcRequestBuilders.post("/users") ? ? ? .content(user) ? ? ? .contentType(MediaType.APPLICATION_JSON_UTF8)) ? ? ? .andExpect(MockMvcResultMatchers.status().isOk()) ? ? ? .andExpect(MockMvcResultMatchers.content() ? ? ? ? .contentType(textPlainUtf8)); } ? @Test public void whenPostRequestToUsersAndInValidUser_thenCorrectResponse() throws Exception { ? ? String user = "{\"name\": \"\", \"email\" : \"bob@domain.com\"}"; ? ? mockMvc.perform(MockMvcRequestBuilders.post("/users") ? ? ? .content(user) ? ? ? .contentType(MediaType.APPLICATION_JSON_UTF8)) ? ? ? .andExpect(MockMvcResultMatchers.status().isBadRequest()) ? ? ? .andExpect(MockMvcResultMatchers.jsonPath("$.name", Is.is("Name is mandatory"))) ? ? ? .andExpect(MockMvcResultMatchers.content() ? ? ? ? .contentType(MediaType.APPLICATION_JSON_UTF8)); ? ? } }
也可以使用postman或fiddler來測試REST controller API。
7、跑測試
@SpringBootApplication public class Application { ? ? ? ? ? public static void main(String[] args) { ? ? ? ? SpringApplication.run(Application.class, args); ? ? } ? ? ? ? ? @Bean ? ? public CommandLineRunner run(UserRepository userRepository) throws Exception { ? ? ? ? return (String[] args) -> { ? ? ? ? ? ? User user1 = new User("Bob", "bob@domain.com"); ? ? ? ? ? ? User user2 = new User("Jenny", "jenny@domain.com"); ? ? ? ? ? ? userRepository.save(user1); ? ? ? ? ? ? userRepository.save(user2); ? ? ? ? ? ? userRepository.findAll().forEach(System.out::println); ? ? ? ? }; ? ? } }
如果用沒有用戶名或郵箱的數(shù)據(jù)發(fā)送請求會收到返回的提示信息
{ ? "name":"Name is mandatory", ? "email":"Email is mandatory" }
8、自定義注解
在進行參數(shù)驗證的時候,往往存在現(xiàn)有的約束注解不能滿足的情況,此時就需要我們自己定義validation注解了,下次介紹如何自己定義一個驗證注解。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring?Boot對接Oracle數(shù)據(jù)庫具體流程
這篇文章主要給大家介紹了關于Spring?Boot對接Oracle數(shù)據(jù)庫的具體流程,本文將介紹如何在Spring Boot中連接Oracle數(shù)據(jù)庫的基本配置,包括添加依賴、配置數(shù)據(jù)源、配置JPA等,需要的朋友可以參考下2023-11-11SpringBoot集成Access?DB實現(xiàn)數(shù)據(jù)導入和解析
microsoft?office?access是由微軟發(fā)布的關聯(lián)式數(shù)據(jù)庫管理系統(tǒng),它結合了?microsoft?jet?database?engine?和?圖形用戶界面兩項特點,是一種關系數(shù)據(jù)庫工具,本文給大家介紹了SpringBoot集成Access?DB實現(xiàn)數(shù)據(jù)導入和解析,需要的朋友可以參考下2024-11-11