您好,登錄后才能下訂單哦!
今天小編給大家分享一下怎么通過自定義spring invalidator注解校驗數(shù)據(jù)合法性的相關知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
在項目中經(jīng)常會對用戶輸入的數(shù)據(jù),或者外部導入到系統(tǒng)的數(shù)據(jù)做合法性檢查。在spring boot框架的微服務中可以使用invalidator注解對數(shù)據(jù)做合法性,安全性校驗。
下面給一個樣例說明如何自定義注解實現(xiàn)校驗邏輯。
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ù),變量就使用默認值。
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; } }
public class User { private int userId = -1; @FieldLengthInvalidator(maxLength=10) private String name = ""; }
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)一的校驗調(diào)用接口。 * @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(); } }
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提供了強大的基于注解的、開箱即用的驗證功能,這種基于bean validation的實現(xiàn)和 hibernate validator類似
創(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.h3database</groupId> <artifactId>h3</artifactId> <version>1.4.197</version> <scope>runtime</scope> </dependency>
測試項目為了方便,直接用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> {}
@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
直接拋出異常顯然是不合理的,大部分情況需要經(jīng)過處理返回給前端更友好的提示信息,通過@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ā)時會調(diào)用此實現(xiàn),該方法將每個無效字段的名稱和驗證后錯誤消息存儲在映射中,然后它將映射作為JSON表示形式發(fā)送回客戶端進行進一步處理。
使用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。
@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" }
以上就是“怎么通過自定義spring invalidator注解校驗數(shù)據(jù)合法性”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。