Spring Boot系列十四 Spring boot使用spring validation实现对Restful请求的数据进行校验

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hry2015/article/details/79572713

1. 概述

本文介绍在Spring Boot中实现对请求的数据进行校验。数据校验常用到概念:

  • JSR303/JSR-349: JSR303是一项标准,只提供规范不提供实现,规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,位于javax.validation.constraints包下。JSR-349是其的升级版本,添加了一些新特性。
  • hibernate validation:hibernate validation是对这个规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等
  • spring validation:spring validation对hibernate validation进行了二次封装,在springmvc模块中添加了自动校验,并将校验信息封装进了特定的类中

本文主要包括如下内容:

  1. 演示spring boot validation + 异常捕获机制实现数据自动校验功能
  2. 自定义校验注解,并演示这个用法

2. 演示spring boot的数据自动校验功能

功能:向服务发送请求,这个请求带上参数,服务需要对参数进行校验。

2.1. Result

封装返回处理结果,如果code=200,则表示添加成功,code=400,则表示输入的数据异常

public class Result {
    private int code;
    private String message;

    // set/get方法略
    …
}

2.2. CustomerDto:

客户端的请求封装到这个dto中。使用校验注解注解此类的成员属性。这些注解的功能看名称就可以看出来。其中 @PhoneValidation就我们自定义的注解,这个后面会说明。每个注解里有个属性message,如果我们不想使用系统默认提供的报错信息,我们可以修改这个值

public class CustomerDto {
    @Size(min=2, max=30)
    private String name;

    // 自定义错误信息
    @NotEmpty(message = "自定义错误信息,Email不能为空")
    @Email
    private String email;

    @NotNull
    @Min(18) @Max(100)
    private Integer age;

    @NotNull
    private Gender gender;

    @DateTimeFormat(pattern="MM/dd/yyyy")
    @NotNull @Past
    private Date birthday;

    // 自定义规则注解
    @PhoneValidation
    private String phone;

    public enum Gender {
        MALE, FEMALE
    }

   // set/get方法略
    …
}

2.3. 异常捕获类:BindExceptionHanlder

如果数据校验不通过,则Spring boot会抛出BindException异常,我们可以捕获这个异常并使用Result封装返回结果。通过@RestControllerAdvice定义异常捕获类

@RestControllerAdvice
public class BindExceptionHanlder {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(BindException.class)
    public Result handleBindException(BindException ex) {
        // ex.getFieldError():随机返回一个对象属性的异常信息。如果要一次性返回所有对象属性异常信息,则调用ex.getAllErrors()
        FieldError fieldError = ex.getFieldError();
                    StringBuilder sb = new StringBuilder();
            sb.append(fieldError.getField()).append("=[").append(fieldError.getRejectedValue()).append("]")
                    .append(fieldError.getDefaultMessage());
        // 生成返回结果
        Result errorResult = new Result();
        errorResult.setCode(400);
        errorResult.setMessage(sb.toString());
        return errorResult;
    }
}

2.4. ValidationCtrl :

提供Control层的对外接口

@RestController
public class ValidationCtrl {
    private static final Logger logger = LoggerFactory
            .getLogger(ValidationCtrl.class);

    @RequestMapping(value = "/validation/save", method = RequestMethod.GET)
    public Result saveCustomerPage(@Validated CustomerDto model) {
        logger.info("Good" + model.getBirthday());
        Result okResult = new Result();
        okResult.setCode(200);
        okResult.setMessage(JSONObject.toJSON(model).toString());
        return okResult;
    }
}

2.5. 测试:

{ "code":200, "message":"{\"birthday\":477849600000,\"gender\":\"MALE\",\"phone\":\"13589567201\",\"name\":\"name\",\"age\":18,\"email\":\"[email protected]\"}" }
{ "code":400, "message":"name=[n]个数必须在2和30之间" }
{ "code":400, "message":"email=[]自定义错误信息,Email不能为空" }

3. 自定义校验注解,并演示这个用法

功能:自定义校验注解,对输入手机进行合法性检查。并演示这个用法

3.1. @PhoneValidation:

定义自己的校验注解
头注解@Constraint属性validatedBy 指定真正执行校验的类PhoneValidationValidator

@Documented
// 指定真正实现校验规则的类
@Constraint(validatedBy = PhoneValidationValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface PhoneValidation {
    String message() default "不是正确的手机号码";

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

    Class<? extends Payload>[] payload() default { };

    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        PhoneValidation[] value();
    }
}

3.2. PhoneValidationValidator

真正执行校验的类,对输入的手机执行校验

public class PhoneValidationValidator implements ConstraintValidator<PhoneValidation, String> {

    private static final Pattern PHONE_PATTERN = Pattern.compile(
            "^((13[0-9])|(15[^4])|(18[0,2,3,5-9])|(17[0-8])|(147))\\d{8}$"
    );

    @Override
    public void initialize(PhoneValidation constraintAnnotation) {

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if ( value == null || value.length() == 0 ) {
            return true;
        }
        Matcher m = PHONE_PATTERN.matcher(value);
        return m.matches();
    }
}

3.3. 在Customer上使用新的注解

public class CustomerDto {

    // 自定义规则注解
    @PhoneValidation
    private String phone;

   // set/get方法略
    …
}

3.4. 测试

{ "code":200, "message":"{\"birthday\":477849600000,\"gender\":\"MALE\",\"phone\":\"13589567201\",\"name\":\"name\",\"age\":18,\"email\":\"[email protected]\"}" }
{ "code":400, "message":"phone=[13589567]不是正确的手机号码" }

3.5. 代码

所有的详细代码见github代码,请尽量使用tag v0.18,不要使用master,因为master一直在变,不能保证文章中代码和github上的代码一直相同

猜你喜欢

转载自blog.csdn.net/hry2015/article/details/79572713