チェックするために使用するカスタムパラメータから統一されたバリデータのパラメータチェック

パリティ情報は、我々が持っている今日このチェックコードのif-elseのフルチェックするために、多数のパラメータが非常に痛いの出会いですが、また、ビジネスでスローまたはコードで例外を返し続け、非常に長いです春の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は、我々はそれを検証する時間に検証することができ、上記の私たちのJavaBeanのプロパティに直接これらのアノテーションを追加することができ、多くの一般的なチェックノートを定義する標準のJavaBeanパラメータチェック、のセットです、と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の、バイト、ショート、整数、長い、など、または任意のCharSequence番号(記憶されたデジタル)亜型 注釈要素値が@Minを指定された値に等しい値よりも大きいベリファイ
@max(値=値) 同じ要件と@Min 指定された値の値以下の注釈要素の値を確認し@Max
@DecimalMin(値=值) 同じ要件と@Min 検証注釈要素の値は、値の@ DecimalMinに等しい指定した値よりも大きいです
@DecimalMax(値=值) 同じ要件と@Min DecimalMax @指定された値の値以下の注釈要素の値を検証します
@Digits(整数=整数ビット、小数=小数点以下の桁数) 同じ要件と@Min 整数桁検証注釈要素値と小数点以下の上限
@size(MIN =下限、最大=上限値) 文字列、コレクション、地図、配列など minとmax(含む)、文字長検証アノテーション要素値、セットサイズとして指定区間内
@過去 java.util.Date、java.util.Calendarの;ジョダ時刻、日付タイプライブラリ 現在の時刻より前の注釈付き要素値(日付型)ベリファイ
@未来 @Past要件と同様に 現在の時刻より後の注釈要素の値(日付型)ベリファイ
@NotBlank CharSequenceのサブタイプ @ NotEmpty、@ NotBlank比較文字列にのみ適用され、最初の空の文字列を削除し異なり、アノテーション要素の値が(0の最初の長さの除去後空間、nullでない)空でない検証
@Length(MIN =下限、最大=上限値) CharSequenceのサブタイプ 範囲の最小最大の要素値の長さの検証および注釈
@空ではない たCharSequenceのサブタイプ、コレクション、地図、アレイ (文字列長が0でない、セット・サイズが0でない)注釈要素値がnullでなく、空でない検証
@Range(MIN = MIN、MAX =最大値) BigDecimalを、BigIntegerの、のCharSequence、バイト、ショート、int型、長い原子タイプとパッケージタイプなど 最小値と最大値との間の注釈要素の値を検証します
@email(正規表現=正規表現、フラグ=モードフラグ) たCharSequenceのサブタイプ(例えば、文字列) 要素の値は、カスタムの電子メール形式フラグと正規表現で指定することができ、注釈の電子メールを確認しています
@Pattern(正規表現=正規表現、フラグ=モードフラグ) 文字列、任意のサブタイプのCharSequence 正規表現のマッチングを指定された注釈要素の値を確認します。
@Valid 非原子の任意のタイプ ユーザーオブジェクトは、それがオブジェクトにカスケードアドレス確認@Valid注釈を追加することができたときにアドレス検証対象と検証したい場合は、オブジェクトは、アドレス属性などのユーザーオブジェクトに関連付けられた再帰指定された認証対象

ここだけHibernateバリ制約の検証ノートのほとんどを提供され、追加の検証制約の検証制約注釈の休止バリ公式ドキュメントを参照し、定義された注釈をカスタマイズしてください。

実践的な演習

话不多说,直接走实践路线,同样使用的是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