왜 검증
-
javax.validation
주석의 시리즈는 성가신 시리얼 체크를 교체, 우리에게 완전한 매개 변수 검사를 할 수 있습니다그렇지 않으면이 같은 우리의 코드 :
// http://localhost:8080/api/user/save/serial
/**
* 走串行校验
*
* @param userVO
* @return
*/
@PostMapping("/save/serial")
public Object save(@RequestBody UserVO userVO) {
String mobile = userVO.getMobile();
//手动逐个 参数校验~ 写法
if (StringUtils.isBlank(mobile)) {
return RspDTO.paramFail("mobile:手机号码不能为空");
} else if (!Pattern.matches("^[1][3,4,5,6,7,8,9][0-9]{9}$", mobile)) {
return RspDTO.paramFail("mobile:手机号码格式不对");
}
//抛出自定义异常等~写法
if (StringUtils.isBlank(userVO.getUsername())) {
throw new BizException(Constant.PARAM_FAIL_CODE, "用户名不能为空");
}
// 比如写一个map返回
if (StringUtils.isBlank(userVO.getSex())) {
Map<String, Object> result = new HashMap<>(5);
result.put("code", Constant.PARAM_FAIL_CODE);
result.put("msg", "性别异常");
return result;
}
//.........各种写法 ...
userService.save(userVO);
return RspDTO.success();
}
复制代码
이것은 볼 족장, 어떤 말, 여전히 9102을 작성하고 그를 제지하려고하고 있습니다 .....
- 무엇
javax.validation
JSR303, 우리는 그것을 확인 시간에 확인하고, SpringBoot에서 할 수있다, 바로 위에 우리의 자바 빈즈 속성에서 이러한 주석을 추가 할 수 있습니다 많은 일반적인 점검 사항을 정의하는 표준 자바 빈즈 매개 변수 검사의 집합입니다 스타터 - 웹에 포함하고 참조하고 자신의 버전을 조정할 수 있습니다 다른 프로젝트에 의존하고있다 :
<!--jsr 303-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
复制代码
-
해설
1.@NotNull:不能为null,但可以为empty(""," "," ") 2.@NotEmpty:不能为null,而且长度必须大于0 (" "," ") 3.@NotBlank:只能作用在String上,不能为null,而且调用trim()后,长度必须大于0("test") 即:必须有实际字符 复制代码
코멘트를 확인 | 데이터 유형의 검증 | 설명 |
---|---|---|
@AssertFalse | 부울, 부울 | 값이 false 주석 요소 확인 |
@AssertTrue | 부울, 부울 | 값이 true 주석 요소 확인 |
@NotNull | 모든 유형 | |
@없는 | 모든 유형 | 주석 요소가 null인지 확인 |
@min (값 = 값) | BigDecimal의 등의 BigInteger 바이트 짧고, INT, 길이, 또는 임의의 CharSequence 번호 (저장된 디지털) 아형 | 확인 주석 요소 값이 지정된 값 @min 동일한 값보다 큰 |
@Max (값 = 값) | 같은 요구 사항 및 @min | 지정된 값을 값 이하인 주석 요소 값을 확인 @Max |
@DecimalMin (값 = 值) | 같은 요구 사항 및 @min | 확인 주석 요소 값은 값 @의 DecimalMin 동일한 규정 값보다 큰 |
@DecimalMax (값 = 值) | 같은 요구 사항 및 @min | DecimalMax @ 지정된 값의 값보다 작거나 같은 주석 요소의 값을 확인 |
@Digits (정수 = 정수 비트 분획물 = 소수점 수) | 같은 요구 사항 및 @min | 정수 숫자 확인 주석 요소 값 소수점의 상한 |
@Size (최소 = 하한 최대 = 상한값) | 문자열, 컬렉션,지도, 배열, 등 | 이러한 글자 검증 주석 요소 값 설정 크기 (포함) 최소 및 최대 지정된 구역 내 |
@과거 | java.util.Date, java.util.Calendar의, Joda 시간 날짜 형식 라이브러리 | 현재 시간보다 일찍 주석 요소 값 (날짜 형식)를 확인 |
@미래 | @Past 요구 사항처럼 | 현재 시간보다 늦게 주석 요소 값 (날짜 형식)를 확인 |
@NotBlank | CharSequence를 하위 유형 | 확인 주석 요소의 값이 (이 아니라 0의 첫 번째 길이의 제거 이후의 공간 널)하지 빈 비교 문자열에만 적용 @는 NotEmpty @ NotBlank 달리하고 첫 번째 빈 문자열을 제거 |
@Length (최소 = 하한 최대 = 상한값) | CharSequence를 하위 유형 | 범위 최소 최대 값의 요소 길이 검증 및 주석 |
@NotEmpty | CharSequence를 하위 유형, 컬렉션,지도, 배열 | 주석 요소의 값이 null가 아니고, 비어 있지 않은 확인합니다 (문자열의 길이가 0이 아닌, 설정 크기는 0이 아닌) |
@range (최소 = 최소, 최대 = 최대 값) | 인 BigDecimal BigInteger를, CharSequence를 바이트 짧고, INT 긴 원자 유형 및 패키지 형 등 | 최소값과 최대 값 사이의 주석 요소 값을 확인 |
@ 이메일 (정규 표현식 = 정규 표현식, 플래그 = 모드 플래그) | CharSequence를 하위 유형 (예를 들어, 문자열) | 요소 값은 사용자 정의 이메일 형식 플래그와 정규 표현식으로 지정할 수 있습니다, 주석 이메일을 확인 |
@Pattern (정규 표현식 = 정규 표현식, 플래그 = 모드 플래그) | 문자열, CharSequence를 부속 유형 | 주석 요소 값 지정된 정규 표현식 매칭을 확인 |
@유효한 | 비 원자의 모든 유형 | 당신이 사용자 개체, 개체에 대해 캐스케이드 주소 확인 @Valid 주석을 추가 할 수 있습니다 때 주소 확인 개체를 확인하려는 경우 객체가 주소를 속성과 같은 사용자 개체와 관련된 재귀 지정된 인증 대상 |
여기에만 최대 절전 모드 검사기 제약 검증 노트의 대부분을 제공, 추가 확인 제약 검증 제약 주석의 최대 절전 모드 검사기 공식 문서를 참조하여 정의 된 주석을 사용자 정의하십시오.
실제 운동
话不多说,直接走实践路线,同样使用的是SpringBoot的快速框架,详细代码见:github.com/leaJone/myb…
1. @Validated 声明要检查的参数
这里我们在控制器层进行注解声明
/**
* 走参数校验注解
*
* @param userDTO
* @return
*/
@PostMapping("/save/valid")
public RspDTO save(@RequestBody @Validated UserDTO userDTO) {
userService.save(userDTO);
return RspDTO.success();
}
复制代码
2. 对参数的字段进行注解标注
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.*;
import java.io.Serializable;
import java.util.Date;
/**
* @author LiJing
* @ClassName: UserVO
* @Description: 用户传输对象
* @date 2019/7/30 13:55
*/
@Data
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
/*** 用户ID*/
@NotNull(message = "用户id不能为空")
private Long userId;
/** 用户名*/
@NotBlank(message = "用户名不能为空")
@Length(max = 20, message = "用户名不能超过20个字符")
@Pattern(regexp = "^[\\u4E00-\\u9FA5A-Za-z0-9\\*]*$", message = "用户昵称限制:最多20字符,包含文字、字母和数字")
private String username;
/** 手机号*/
@NotBlank(message = "用户名不能为空")
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
private String mobile;
/**性别*/
private String sex;
/** 邮箱*/
@NotBlank(message = "联系邮箱不能为空")
@Email(message = "邮箱格式不对")
private String email;
/** 密码*/
private String password;
/*** 创建时间 */
@Future(message = "时间必须是将来时间")
private Date createTime;
}
复制代码
3. 在全局校验中增加校验异常
MethodArgumentNotValidException
是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理,其他需要 处理ConstraintViolationException异常进行处理.
- 为了优雅一点,我们将参数异常,业务异常,同一做了一个全局异常,将控制层的异常包装到我们自定义的异常中
- 为了优雅一点,我们还做了一个同一的结构体,将前端请求的code,和msg,data一起统一封装到结构体中,增加了代码的复用性
import com.boot.lea.mybot.dto.RspDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
/**
* @author LiJing
* @ClassName: BizException
* @Description: 全局异常处理器
* @date 2019/7/30 13:57
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
private static int DUPLICATE_KEY_CODE = 1001;
private static int PARAM_FAIL_CODE = 1002;
private static int VALIDATION_CODE = 1003;
/**
* 处理自定义异常
*/
@ExceptionHandler(BizException.class)
public RspDTO handleRRException(BizException e) {
logger.error(e.getMessage(), e);
return new RspDTO(e.getCode(), e.getMessage());
}
/**
* 方法参数校验
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public RspDTO handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
logger.error(e.getMessage(), e);
return new RspDTO(PARAM_FAIL_CODE, e.getBindingResult().getFieldError().getDefaultMessage());
}
/**
* ValidationException
*/
@ExceptionHandler(ValidationException.class)
public RspDTO handleValidationException(ValidationException e) {
logger.error(e.getMessage(), e);
return new RspDTO(VALIDATION_CODE, e.getCause().getMessage());
}
/**
* ConstraintViolationException
*/
@ExceptionHandler(ConstraintViolationException.class)
public RspDTO handleConstraintViolationException(ConstraintViolationException e) {
logger.error(e.getMessage(), e);
return new RspDTO(PARAM_FAIL_CODE, e.getMessage());
}
@ExceptionHandler(NoHandlerFoundException.class)
public RspDTO handlerNoFoundException(Exception e) {
logger.error(e.getMessage(), e);
return new RspDTO(404, "路径不存在,请检查路径是否正确");
}
@ExceptionHandler(DuplicateKeyException.class)
public RspDTO handleDuplicateKeyException(DuplicateKeyException e) {
logger.error(e.getMessage(), e);
return new RspDTO(DUPLICATE_KEY_CODE, "数据重复,请检查后提交");
}
@ExceptionHandler(Exception.class)
public RspDTO handleException(Exception e) {
logger.error(e.getMessage(), e);
return new RspDTO(500, "系统繁忙,请稍后再试");
}
}
复制代码
4. 测试
如下文:确实做到了参数校验时返回异常信息和对应的code,方便了我们不再繁琐的处理参数校验
在ValidationMessages.properties 就是校验的message,有着已经写好的默认的message,且是支持i18n的,大家可以阅读源码赏析
自定义参数注解
1. 比如我们来个 自定义身份证校验 注解
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdentityCardNumberValidator.class)
public @interface IdentityCardNumber {
String message() default "身份证号码不合法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
复制代码
这个注解是作用在Field字段上,运行时生效,触发的是IdentityCardNumber这个验证类。
- message 定制化的提示信息,主要是从ValidationMessages.properties里提取,也可以依据实际情况进行定制
- groups 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
- payload 主要是针对bean的,使用不多。
2. 然后自定义Validator
这个是真正进行验证的逻辑代码:
public class IdentityCardNumberValidator implements ConstraintValidator<IdentityCardNumber, Object> {
@Override
public void initialize(IdentityCardNumber identityCardNumber) {
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
return IdCardValidatorUtils.isValidate18Idcard(o.toString());
}
}
复制代码
IdCardValidatorUtils在项目源码中,可自行查看
3. 使用自定义的注解
@NotBlank(message = "身份证号不能为空")
@IdentityCardNumber(message = "身份证信息有误,请核对后提交")
private String clientCardNo;
复制代码
总结
用起来很简单,soEasy,重点参与的统一结构体返回,统一参数校验,是减少我们代码大量的try catch 的法宝,我觉得在项目中,将异常处理好,并将异常做好日志管理,才是很好的升华,文章浅显,只是一个菜鸟的进阶笔记....
这里只是个人见解,技术菜,欢迎大佬不吝赐教... 我是一个小白,技术在不断的更新迭代,我只有不断的填充自己的空白才能....跟上大佬们的步伐...