Packet guide and configuration
Import JSR 303 packages and hibernate valid packages
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.5.Final</version></dependency><dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.0.Final</version></dependency>
springboot configuration
resources/application.yml message resource file international processing configuration
spring:
messages:
basename: base,todo # Resource files base.properties and todo.properties, separated by commas
encoding: UTF-8 # parsing encoding must be specified, otherwise Chinese garbled code
Configure in springboot startup class
@SpringBootApplicationpublic class Application extends WebMvcConfigurerAdapter { @Value("${spring.messages.basename}") private String basename; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean @Primary public MessageSource messageSource() { ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource(); resourceBundleMessageSource.setUseCodeAsDefaultMessage(false); resourceBundleMessageSource.setDefaultEncoding("UTF-8"); // Repeat definition resourceBundleMessageSource.setBasenames(basename.split(",")); return resourceBundleMessageSource; } @Bean @Primary public LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean(); validatorFactoryBean.setProviderClass(HibernateValidator.class); validatorFactoryBean.setValidationMessageSource(messageSource()); return validatorFactoryBean; } @Override public Validator getValidator() { return validator(); } /** * Single parameter verification at the method level is enabled*/ @Bean public MethodValidationPostProcessor methodValidationPostProcessor() { return new MethodValidationPostProcessor(); }}We process exceptions that cannot pass the verification parameters and cannot pass the thrown, which is through unified exception capture.
@ControllerAdvice@Componentpublic class BindValidExceptionHandler { @ResponseStatus(value = HttpStatus.OK) @ExceptionHandler(ConstraintViolationException.class) public @ResponseBody Msg handleConstraintViolationException(ConstraintViolationException e) { String messageTemplate = e.getConstraintViolations().iterator().next().getMessageTemplate(); return Msg.error(messageTemplate); } @ResponseStatus(value = HttpStatus.OK) @ExceptionHandler(BindException.class) public @ResponseBody Msg handleBindException(BindException e) { BindingResult bindingResult = e.getBindingResult(); String className = bindingResult.getTarget().getClass().getName(); FieldError next = bindingResult.getFieldErrors().iterator().next(); String fieldName = next.getField(); String defaultMessage = next.getDefaultMessage(); if (Pattern.compile("IllegalArgumentException: No enum").matcher(defaultMessage).find()) { Matcher matcher = Pattern.compile("for value '(.*?)'").matcher(defaultMessage); if (matcher.find()) { defaultMessage = "The enum type cannot be found [" + matcher.group(1) + "]"; } } return Msg.error(defaultMessage); } @ResponseStatus(value = HttpStatus.OK) @ExceptionHandler(ValidError.class) public @ResponseBody Msg handleValidError(ValidError e) { return Msg.error(e.getMessage()); }}resources/base.propertie
creatorId=creator id cannot be less than {value}.
modifierId=Modifier id cannot be less than {value}.
resources/todo.properties
todo.privateId.min=Private id cannot be less than {value}.
Use annotations on the bean field, where the C and S interfaces in the group refer to the abbreviation of the Controller and Service, which include the Insert interface, Update interface, etc., which are all custom conventions.
/** * Private id is the foreign key representing multiple tables such as project tasks/non-project tasks/risks/problems/review to-do questions*/@Min(value = 1, message = "{todo.privateId.min}", groups = {C.Insert.class, C.Update.class, S.Insert.class, S.Update.class})private long privateId;/** * Creator id */@Min(value = 1, message = "{creatorId}", groups = {S.Insert.class})private long creatorId;Controller Control layer verification @Validated@RestController@RequestMapping("todo")public class TodoController { @Autowired private TodoService todoService; @GetMapping("getVo") public Msg getVo( @Min(value = 1, message = "To do id cannot be less than 1.") @RequestParam(required = false, defaultValue = "0") long id ) { return this.todoService.getVo(id); } @PostMapping("add") public Msg add(@Validated({C.Insert.class}) Todo todo) { return this.todoService.add(todo); }}@Validated({C.Insert.class}) Declares to enable verification groups on bean annotation, and other verification groups will not perform verification, which can be distinguished for separate verification.
For example, if there is no entity and only one basic data type, it can be verified, but three conditions need to be met:
Verify by yourself.
Service service layer AOP verification
ValidUtil tool class
Need to be scanned and registered as a single case by springboot
@Componentpublic class ValidUtil { @Autowired private Validator validator; public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) { return validator.validate(object, groups); } public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) { return validator.validateValue(beanType, propertyName, value, groups); } /** * Verify the parameter and return the first error prompt* @param t Verified object* @param groups Verified group* @param <T> Original type before object erasing* @return First error prompt*/ public <T> void validAndReturnFirstErrorTips(T t, Class<?>... groups) { Set<ConstraintViolation<T>> validate = validator.validate(t, groups); if (validate.size() > 0) { ConstraintViolation<T> next = validate.iterator().next(); String message = next.getRootBeanClass().getName() + "-" + next.getPropertyPath() + "-" + next.getMessage(); throw new ValidError(message); } } /** * Check the parameter and return the first error prompt* @param targetClass The class type of the object that is validated* @param fieldName Name that needs to be validated* @param obj property value required* @param groups group verification group* @param <T> Original type before object erasing* @return First error prompt*/ public <T> void validAndReturnFirstErrorTips(Class targetClass, String fieldName, Object obj, Class<?>... groups) { Set<ConstraintViolation<T>> validate = validator.validateValue(targetClass, fieldName, obj, groups); if (validate.size() > 0) { String message = targetClass.getName() + "-" + fieldName + "-" + validate.iterator().next().getMessage(); throw new ValidError(message); } }}AOP configuration
The main principle is to use the aop intercept method to execute parameters and obtain annotations for parameters. Then use the tool class to verify the parameters. If the verification fails, a custom error will be directly thrown, and the custom error has been processed globally.
@Aspect@Componentpublic class ValidatorAOP { @Autowired private ValidUtil validUtil; /** * Define interception rules: Intercept all classes below the com.service package, there are methods with @Service annotation. */ @Pointcut("execution(* com.service..*(..)) and @annotation(org.springframework.stereotype.Service)") public void controllerMethodPointcut() { } /** * Specific implementation of interceptor*/ @Around("controllerMethodPointcut()") // Specify the interceptor rules; you can also directly write "execution(* com.xjj.........)" into this public Object Interceptor(ProceedingJoinPoint pjp) { MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); Method method = methodSignature.getMethod(); Annotation[][] argAnnotations = method.getParameterAnnotations(); Object[] args = pjp.getArgs(); for (int i = 0; i < args.length; i++) { for (Annotation annotation : argAnnotations[i]) { if (Validated.class.isInstance(annotation)) { Validated validated = (Validated) annotation; Class<?>[] groups = validated.value(); validUtil.validAndReturnFirstErrorTips(args[i], groups); } } try { return pjp.proceed(args); } catch (Throwable throwable) { throwable.printStackTrace(); } return true; }}Verify annotation @Min @NotNull usage method
Can't be written on the implementation class, only annotations can be used in the interface
Basically the same way as Controller
@Validatedpublic interface TodoService { /** * Query single to-do* @param id Serial number* @return Single to-do*/ Msg getVo(@Min(value = 1, message = "To-do id cannot be less than 1.") long id); /** * Add data* @param todo object*/ Msg add(@Validated({S.Insert.class}) Todo todo);}Share a few custom verification annotations
String null verification
package javax.validation.constraints;import javax.validation.Constraint;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;import javax.validation.Payload;import java.lang.annotation.*;/** * String null verification, there may be problems with hibernate. It cannot be used and needs to be rewrited. Package cannot be changed. */@Documented@Constraint( validatedBy = {NotBlank.NotBlankValidator.class})@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)public @interface NotBlank { Class<?>[] groups() default {}; String message() default "{notBlank}"; Class<? extends Payload>[] payload() default {}; class NotBlankValidator implements ConstraintValidator<NotBlank, Object> { public NotBlankValidator() { } @Override public void initialize(NotBlank constraintAnnotation) { } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { return value != null && !value.toString().isEmpty(); } }}Type judgment, to determine whether type is one of the values, you can customize the judgment based on the verification group
resources/todo.propertiestodo.todoType.insert= When newly added, the to-do type can only be one of non-project tasks, project tasks, or problems. todo.todoType.update=When modifying, the to-do type can only be one of the risks and review the to-do issues. bean/** * TodoType0Non-project Task1Project Task2Problem3Risk4ReviewTodoTypeValid(value = {"0", "1", "2"}, message = "{todo.todoType.insert}", groups = {C.Insert.class, S.Insert.class})@TodoTypeValid(value = {"3", "4"}, message = "{todo.todoType.update}", groups = {C.Update.class, S.Update.class})private String todoType;Custom annotations
@Documented@Constraint(validatedBy = {TodoTypeValid.TodoTypeValidFactory.class})@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Repeatable(TodoTypeValid.List.class)public @interface TodoTypeValid { String message() default "Please enter the correct type"; String[] value() default {}; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; class TodoTypeValidFactory implements ConstraintValidator<TodoTypeValid, String> { private String[] annotationValue; @Override public void initialize(TodoTypeValid todoStatusValid) { this.annotationValue = todoStatusValid.value(); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (Arrays.asList(annotationValue).contains(value)) return true; return false; } } @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { TodoTypeValid[] value(); }}@Repeatable(TodoTypeValid.List.class) is the same annotation multiple-time feature supported by JDK8.
According to the above, it can also be used on enumeration classes.
When resources/todo.propertiestodo.todoStatus.insert=Add to new, the status can only be not started. When todo.todoStatus.update= is modified, the status can only be in progress or completed. bean/** * Todo Status 0 Not started 1 In progress 2 Completed*/@TodoStatusValid(enums = {TodoStatus.NOT_STARTED}, message = "{todo.todoStatus.insert}", groups = {C.Insert.class, S.Insert.class})@TodoStatusValid(enums = {TodoStatus.PROCESSING, TodoStatus.COMPLETED}, message = "{todo.todoStatus.update}", groups = {C.Update.class, S.Update.class})private TodoStatus todoStatus;Custom annotations
@Documented@Constraint(validatedBy = {TodoStatusValid.TodoStatusValidFactory.class})@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Repeatable(TodoStatusValid.List.class)public @interface TodoStatusValid { String message() default "Please enter the correct status"; TodoStatus[] enums() default {}; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; class TodoStatusValidFactory implements ConstraintValidator<TodoStatusValid, TodoStatus> { private TodoStatus[] enums; @Override public void initialize(TodoStatusValid todoStatusValid) { this.enums = todoStatusValid.enums(); } @Override public boolean isValid(TodoStatus value, ConstraintValidatorContext context) { TodoStatus[] values = TodoStatus.values(); if (enums != null && enums.length != 0) { values = enums; } if (Arrays.asList(values).contains(value)) return true; return false; } } @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { TodoStatusValid[] value(); }}Summarize
The above is what the editor introduced to Springboot uses JSR 303 to verify the Controller Control layer and the Service Service layer AOP verification. Use message resource files to internationalize messages. I hope it will be helpful to everyone. If you have any questions, please leave me a message and the editor will reply to everyone in time. Thank you very much for your support to Wulin.com website!