springboot项目创建笔记15 之《参数校验功能》

Validator框架可以帮助开发人员少写代码,提高开发效率

一、校验框架
spring的validator校验框架遵循了JSR-303验证规范
在默认情况下springboot会引入hibernate validator机制来支持JSR-303验证规范
springboot的validator校验框架有3个特性:
1、JSR-303特性:JSR-303是一项标准,只提供规范不提供实现,规定了一些校验注解,如@Null,@NotNull,@Pattern
2、hibernate validation特性:hibernate validation是对JSR-303规范的实现,并增加了一些其他校验注解,如@Email,@Length,@Range等等
3、spring validation:spring validateion是对hibernate validation的二次封装,在springmvc中添加了自动校验

二、pom文件
springboot本身就包含,不需要额外的依赖包

三、注解
1、@Valid注解:javax提供的(一般用这个)
2、@Validated注解:spring提供的
3、Validation常用注解
1)@Null:限制只能为null
2)@NotNull:限制必须不为null
3)@AssertFalse:限制必须为false
4)@AssertTrue:限制必须为true
5)@DecimalMax(value):限制必须为一个不大于指定值的数字
6)@DecimalMin(value):限制必须为一个不小于指定值的数字
7)@Digits(integer, fraction):限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
8)@Future:限制必须是一个将来的日期
9)@Max(value):限制必须为一个不大于指定值的数字
10)@Min(value):限制必须为一个不小于指定值的数字
11)@Past:限制必须是一个过去的日期
12)@Pattern(value):限制必须符合指定的正则表达式
13)@Size(max, min):限制字符长度必须在min到max之间
14)@Past:限制必须是一个过去的日期
15)@NotEmpty:验证注解的元素值不为null且不为空(字符串长度不为0,集合大小不为0)
16)@NotBlank:验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
17)@Email:验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
4、修改Teacher.java

package com.example.domain;

import javax.validation.constraints.NotEmpty;

import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel("实体对象")
@Data
public class Teacher {
	
	@ApiModelProperty(value = "姓名")
	@NotEmpty(message = "用户名不能为空")
	@Length(min = 2, max = 12, message = "长度必须位于2到12之间")
	private String name;
	
	@ApiModelProperty(value = "年龄")
	@NotEmpty(message = "年龄不能为空")
	@Range(min = 20, max = 65, message = "年龄范围必须在20到65之间")
	private String age;
	
	@ApiModelProperty(value = "起始时间")
	private String beginTime;
	
	@ApiModelProperty(value = "结束时间")
	private String endTime;
}

5、修改TeacherController.java测试方法

@ApiOperation("测试方法二")
@PostMapping("/update")
public boolean update(@Valid Teacher request) {
	//do something
	return true;
}

6、测试下
age填100

{
  "data": null,
  "sign": null,
  "repCode": "999999",
  "repMsg": "org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object 'teacher' on field 'age': rejected value [100]; codes [Range.teacher.age,Range.age,Range.java.lang.String,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [teacher.age,age]; arguments []; default message [age],65,20]; default message [年龄范围必须在20到65之间]"
}

四、自定义注解
因为validator框架支持的注解有限,不可能方方面面都支持,故需要我们自定义注解
1、自定义验证手机号码的注解
建立包com.example.annotation
在包下建立自定义注解Phone.java

package com.example.annotation;

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;

@Documented
@Constraint(validatedBy = PhoneValidator.class) //指定注解的实现类
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {
	
	//校验失败的提示信息
	String message() default "请输入正确的手机号码";
	
	Class<?>[] groups() default {};
	
	Class <? extends Payload>[] payload() default {};
	
	//定义List,为了让Bean的一个属性上可以添加多套规则
	@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, 
		ElementType.CONSTRUCTOR, ElementType.PARAMETER})
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	@interface List{
		Phone[] value();
	}
}

2、在包com.example下建立注解处理类PhoneValidator.java

package com.example.annotation;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PhoneValidator implements ConstraintValidator<Phone, String>{

	private static final Pattern PHONE_PATTERN = Pattern.compile(
			"^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$");
	
	@Override
	public void initialize(Phone 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、修改Teacher.java添加手机号字段

package com.example.domain;

import javax.validation.constraints.NotEmpty;

import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;

import com.example.annotation.Phone;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@ApiModel("实体对象")
@Data
public class Teacher {
	
	@ApiModelProperty(value = "姓名")
	@NotEmpty(message = "用户名不能为空")
	@Length(min = 2, max = 12, message = "长度必须位于2到12之间")
	private String name;
	
	@ApiModelProperty(value = "年龄")
	@NotEmpty(message = "年龄不能为空")
	@Range(min = 20, max = 65, message = "年龄范围必须在20到65之间")
	private String age;
	
	@ApiModelProperty(value = "起始时间")
	private String beginTime;
	
	@ApiModelProperty(value = "结束时间")
	private String endTime;
	
	@ApiModelProperty(value = "手机号")
	@Phone
	private String phone;
}

4、测试下
手机号输错

{
  "data": null,
  "sign": null,
  "repCode": "999999",
  "repMsg": "org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object 'teacher' on field 'phone': rejected value [12000000000]; codes [Phone.teacher.phone,Phone.phone,Phone.java.lang.String,Phone]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [teacher.phone,phone]; arguments []; default message [phone]]; default message [请输入正确的手机号码]"
}

五、添加验证异常的全局异常捕获
@Valid注解校验抛的异常是org.springframework.validation.BindException
1、修改GlobalExceptionHandler.java
添加BindException异常的处理

package com.example.utils;

import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.example.exception.BizException;
import com.example.message.CommonResponse;

@ControllerAdvice(basePackages = "com.example.web")
@ResponseBody
public class GlobalExceptionHandler {

	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(RuntimeException.class)
	public CommonResponse runtimeException(RuntimeException e) {
		return CommonResponse.fail("999999", e.getMessage());
	}
	
	@ExceptionHandler(Exception.class)
	public CommonResponse exception(Exception e) {
		return CommonResponse.fail("999999", e.getMessage());
	}
	
	@ExceptionHandler(BizException.class)
	public CommonResponse bizException(BizException e) {
		return CommonResponse.fail(e.getCode(), e.getMessage());
	}
	
	@ExceptionHandler(BindException.class)
	public CommonResponse bindException(BindException  e) {
		BindingResult bindingResult = e.getBindingResult();
        StringBuffer sb = new StringBuffer();
        for(FieldError fieldError : bindingResult.getFieldErrors()){
            sb.append(fieldError.getDefaultMessage());
        }
		return CommonResponse.fail("999999", sb.toString());
	}
}

2、再次测试,输错手机号

{
  "data": null,
  "sign": null,
  "repCode": "999999",
  "repMsg": "请输入正确的手机号码"
}

六、断言
断言是spring提供的一个工具类:org.springframework.util.Assert
可以用来简化,if判断,然后抛异常的场景。使代码更优雅
1、在TeacherController.java添加测试方法

@ApiOperation("测试断言")
@GetMapping("/testAssert")
public void testAssert() {
	Teacher teacher = null;
	Assert.notNull(teacher, "对象不能为空");
}

2、断言抛的异常是:java.lang.IllegalArgumentException
修改GlobalExceptionHandler.java
添加IllegalArgumentException异常的处理

@ExceptionHandler(IllegalArgumentException.class)
public CommonResponse illegalArgumentException(IllegalArgumentException e) {
	return CommonResponse.fail("999999", e.getMessage());
}

3、测试

{
  "data": null,
  "sign": null,
  "repCode": "999999",
  "repMsg": "对象不能为空"
}

 七、参考资料
https://www.liaoxuefeng.com/wiki/1252599548343744/1265102026065728

注:最新代码上传至https://github.com/csj50/myboot
 

猜你喜欢

转载自blog.csdn.net/csj50/article/details/108331628