사용 사용자 정의 매개 변수에서 통합 검증 매개 변수 검사 확인

패리티 정보는 매개 변수의 많은 수를 확인하기 위해 매우 고통스러운 만남이지만, 또한 사업에 던져 또는 코드에서 예외가있는 경우 - 다른 사람이 체크 코드, 오늘날 우리가 가진의 전체 꽤 긴 것입니다 반환 계속 봄의 javax.validation 주석 형식 매개 변수 보정을 배운다.


왜 검증

  1. 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을 작성하고 그를 제지하려고하고 있습니다 .....

  1. 무엇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. 해설

       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 的法宝,我觉得在项目中,将异常处理好,并将异常做好日志管理,才是很好的升华,文章浅显,只是一个菜鸟的进阶笔记....

这里只是个人见解,技术菜,欢迎大佬不吝赐教... 我是一个小白,技术在不断的更新迭代,我只有不断的填充自己的空白才能....跟上大佬们的步伐...

추천

출처juejin.im/post/5d3fbeb46fb9a06b317b3c48