今天看啥  ›  专栏  ›  襄垣

Spring Boot 自定义注解进行参数校验(一)

襄垣  · 掘金  ·  · 2019-08-02 01:44
阅读 86

Spring Boot 自定义注解进行参数校验(一)

JSR 303 - Bean Validation

在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的。

在通常的情况下,应用程序是分层的,不同的层由不同的开发人员来完成。

很多时候同样的数据验证逻辑会出现在不同的层,这样就会导致代码冗余和一些管理的问题,比如说语义的一致性等。

为了避免这样的情况发生,最好是将验证逻辑与相应的域模型进行绑定。

于是 JSR 303 - Bean Validation 应运而生。

在Maven中使用

在Maven的pom.xml中引入validation-api

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
复制代码

validation-api的一个很好的补充来源于hibernate,虽然hibernate已经远去,但是其留存下来了很多精髓。

        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
复制代码

注解列表

Bean Validation 中内置的 constraint

@Null	被注释的元素必须为 null
@NotNull	被注释的元素必须不为 null
@AssertTrue	被注释的元素必须为 true
@AssertFalse	被注释的元素必须为 false
@Min(value)	被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)	被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)	被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)	被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)	被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)	被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past	被注释的元素必须是一个过去的日期
@Future	被注释的元素必须是一个将来的日期
@Pattern(value)	被注释的元素必须符合指定的正则表达式
复制代码

Hibernate Validator 附加的 constraint

@Email	被注释的元素必须是电子邮箱地址
@Length	被注释的字符串的大小必须在指定的范围内
@NotEmpty	被注释的字符串的必须非空
@Range	被注释的元素必须在合适的范围内
复制代码

自定义注解

通过实现ConstraintValidator可以自定义参数注解。

Mobile.java
@Target( {
             METHOD,
             FIELD,
             ANNOTATION_TYPE,
             CONSTRUCTOR,
             PARAMETER
         })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {MobileValidator.class})
public @interface Mobile {

    String regexp() default "";

    String message() default "手机号码格式不正确";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
复制代码
MobileValidator.java
public class MobileValidator implements ConstraintValidator<Mobile, String> {
    /**
     * 手机号的正则表达式.
     */
    private static Pattern pattern = Pattern.compile(
        "^0?(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$");

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
        Matcher m = pattern.matcher(value);
        return m.matches();
    }

    @Override
    public void initialize(Mobile constraintAnnotation) {}
}
复制代码

如此,我们就可以在Bean中使用注解@Mobile了。

User.java

@Data
public class User {
    @NotBlank
    private String userName;

    @Mobile
    private String mobile;
}
复制代码

Spring Boot 中使用

方法级别的校验配置

@Configuration
public class ValidateConfig {
	@Bean
	public MethodValidationPostProcessor methodValidationPostProcessor(){
		return new MethodValidationPostProcessor();
	}
}
复制代码
1. 使用@Valid进行参数校验
    @RequestMapping(value = "/book", method = RequestMethod.POST)
    public void addBook(@RequestBody @Valid Book book) {
        System.out.println(book.toString());
    }
复制代码
2. 在类上使用@Validated进行参数校验
    @RestController
    @Validated
    public class ValidateController {
        // ...
    }
复制代码

异常拦截

针对参数异常BindException进行全局异常拦截。

GlobalWebExceptionHandler.java
@ControllerAdvice
public class GlobalWebExceptionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalWebExceptionHandler.class);
    
    /**
     * 定义参数异常处理器.
     *
     * @param e 当前平台异常参数对象.
     * @return org.springframework.http.ResponseEntity
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<Map<String, Object>> validateErrorHandler(BindException e) {
        Map<String, Object> errorMap = new HashMap<>(2);
        BindingResult bindingResult = e.getBindingResult();
        if (bindingResult.hasErrors()) {
            List<FieldError> errorList = bindingResult.getFieldErrors();
            String errorMsg = "[字段:" + errorList.get(0).getField() + "]错误,原因:" + errorList.get(0).getDefaultMessage();
            errorMap.put("message", errorMsg);
            errorMap.put("code", EnumError.PARAMS_ERROR.getCode());
            return new ResponseEntity<>(errorMap, HttpStatus.BAD_REQUEST);
        }
        LOGGER.error("[服务] - [捕获参数异常。异常信息:{}]", JSON.toJSONString(bindingResult));
        return new ResponseEntity<>(errorMap, HttpStatus.BAD_REQUEST);
    }
}
复制代码



原文地址:访问原文地址
快照地址: 访问文章快照