Spring Validation和Hibernate Validator结合国际化代码实例
作者:蜡笔小久
这篇文章主要介绍了Spring Validation和Hibernate Validator结合国际化代码实例,我们需要对请求参数进行非空、长度、正确性进行校验, 本文主要讲解Spring Validation 和 Hibernate Validator, 同时整合i18n(国际化)实现参数校验自动,需要的朋友可以参考下
一、Spring Validation是什么?
日常开发中, 我们需要对请求参数进行非空、长度、正确性进行校验, 如果不使用Spring Validation,那我们会在代码中写很多的if来校验, 这样做也可以,但不优雅, 为了优雅的进行入参校验, 此时我们就需要Spring Validation.
二、项目说明
1.引入依赖
依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--lombok插件--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--验证包--> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <!--hibernate validator依赖--> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.6.Final</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.74</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.0-jre</version> </dependency>
2.代码介绍
多语言和Spring Validation Bean初始化:
package com.zzb.valid.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.StandardCharsets;
/**
 * @author create by zhouzongbo on 2020/10/27.
 */
@Configuration
public class MessageConfig implements WebMvcConfigurer {
    /**
     * 初始化多语言
     * @return MessageSource
     */
    @Bean
    public MessageSource resourceBundleMessageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setDefaultEncoding(StandardCharsets.UTF_8.toString());
        // 多语言文件地址.
        messageSource.addBasenames("i18n/valid");
        return messageSource;
    }
    /**
     * 本地校验工厂bean
     * @return LocalValidatorFactoryBean
     */
    @Bean
    public LocalValidatorFactoryBean localValidatorFactoryBean() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        // 设置消息源
        bean.setValidationMessageSource(resourceBundleMessageSource());
        return bean;
    }
    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        processor.setValidator(localValidatorFactoryBean().getValidator());
        return processor;
    }
    @Override
    public Validator getValidator() {
        return localValidatorFactoryBean();
    }
}全局异常处理代码片段: Spring Validation 如果在校验时出现了异常, 会抛出org.springframework.web.bind.MethodArgumentNotValidException, 因此这里对MethodArgumentNotValidException进行拦截, 然后重新组装返回信息.
@ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultEntity<?> test(MethodArgumentNotValidException ex) {
        String errorMessage = buildErrorMessage(ex.getBindingResult());
        return ResultEntity.failure(errorMessage);
    }重新组装异常信息
private String buildErrorMessage(BindingResult result) {
        StringBuilder builder = new StringBuilder();
        for (FieldError error : result.getFieldErrors()) {
            final String defaultMessage = error.getDefaultMessage();
            if (hasDefaultMessage(error)) {
                builder.append(defaultMessage).append("\n");
            } else {
                // 提取错误属性值. 比如@Range 那么错误属性值包含min 和max
                final Object[] arguments = error.getArguments();
                Object[] args;
                if (arguments == null || 1 == arguments.length) {
                    args = new Object[0];
                } else {
                    args = new Object[arguments.length - 1];
                    System.arraycopy(arguments, 1, args, 0, args.length);
                }
                // 获取错误信息.
                final String code = I18nUtil.getMessage(error.getCode(), args);
                final String field = I18nUtil.getMessage(error.getField(), "", args);
                if (StringUtils.isEmpty(field)) {
                    builder.append("[").append(error.getField()).append("]: ");
                } else {
                    builder.append(field);
                }
                builder.append(StringUtils.isEmpty(code) ? defaultMessage : code).append("\n");
            }
        }
        for (ObjectError globalError : result.getGlobalErrors()) {
            builder.append(globalError.getDefaultMessage()).append("\n");
        }
        return builder.toString();
    }3.自定义约束注解
用户名长度校验注解:
@Target(value = { ElementType.FIELD, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@Repeatable(UsernameLength.List.class)
@Constraint(validatedBy = UsernameLengthValidator.class) //
public @interface UsernameLength {
    String message() default "{com.zzb.valid.util.validator.UsernameLength}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    /**
     * 允许添加多个重复注解.
     */
    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        UsernameLength[] value();
    }
}用户名长度校验Validator: 这里需要实现javax.validation.ConstraintValidator用于定义具体的校验规则,这里只校验了字符串的长度.
public class UsernameLengthValidator implements ConstraintValidator<UsernameLength, String> {
    public static final int MAX_LENGTH = 20;
    public static final int MIN_LENGTH = 1;
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return !StringUtils.isEmpty(value) && value.length() >= MIN_LENGTH && value.length() <= MAX_LENGTH;
    }
}4.测试用例
从valid.properties中读取异常提示信息,从而适配多语言.

4.1中文环境异常提示信息

4.2 英文环境异常提示信息.
此处与中文唯一不同的地方是把Accept-Language 设置为英文环境.

5.调用链
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#validateIfApplicable org.springframework.validation.DataBinder#validate(java.lang.Object...) org.springframework.validation.DataBinder#getInternalBindingResult org.springframework.validation.beanvalidation.LocalValidatorFactoryBean # 使用个factoryBean进行校验. org.springframework.boot.autoconfigure.validation.ValidatorAdapter#validate(java.lang.Object, org.springframework.validation.Errors) org.springframework.validation.beanvalidation.SpringValidatorAdapter#validate(java.lang.Object, org.springframework.validation.Errors) org.springframework.validation.beanvalidation.SpringValidatorAdapter#determineField
到此这篇关于Spring Validation和Hibernate Validator结合国际化代码实例的文章就介绍到这了,更多相关Validation和Validator结合国际化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
