zoukankan      html  css  js  c++  java
  • java参数校验validation-api

    一、参数校验的由来

    ​ 校验参数在项目中是很常见的,在java中,几乎每个有入参的方法,在执行下一步操作之前,都要验证参数的合法性,比如是入参否为空,数据格式是否正确等等,往常的写法就是一大推的if-else,既不美观也不优雅,这个时候JCP组织站出来了,并且制定了一个标准来规范校验的操作,这个标准就是Java Validation API(JSR 303)

    但是这个仅仅是一个标准而是,并没有具体的实现,下面介绍两种常用实现。

    二、Java Validation API 的实现者

    2.1、hibernate-validator

    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.0.20.Final</version>
    </dependency>
    

    这个实现是有hibernate实现的,如果入参是一个对象,配合@Valid注解即可,但是无法对单个参数应用@NotNull@Min这类的注解

    2.2、spring-boot-starter-validation

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    或者

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    

    ​ 这两个pomspringhibernate-validator的一个封装和扩展,同时提供了一个@Validated的注解,该注解即可标记在类上,也可以跟@Valid注解用法一样,标记一个入参对象,更强大的是,该注解支持了单个参数的校验,但是需要在上加@Validated这个注解,可见该注解完全可以替代@Valid注解来使用。

    ​ 从依赖依赖关系图,可以看出starter-webstarter-validation都依赖于hibernate-validatorhibernate-validator依赖于validation-api,而且项目中经用到的@NotBlank、@NotNull、@Min、@Valid 等注解都出自validation-api包中,hibernate-validator 中的注解已不推荐使用,validation-api的包路径为 javax.validation.constraints

    image-20200825001442768

    三、使用

    3.1、需求

    用户注册接口,名称,年龄,邮箱、不能为空

    用户修改接口,名称,年龄,邮箱,主键id,不能为空

    用户信息接口,入参为单个参数

    3.2、代码实现

    注册接口group

    /**
     * 添加时的验证规则
     * @author DUCHONG
     * @since 2020-08-24 23:35:46
     */
    public interface ValidAddRules {
    }
    

    修改接口group

    /**
     * 修改时的验证规则
     * @author DUCHONG
     * @since 2020-08-24 23:35:46
     **/
    public interface ValidUpdateRules {
    }
    

    入参对象UserRequest

    /**
     * 入参对象
     *
     * @author DUCHONG
     * @since 2020-08-24 23:33
     **/
    @Data
    @Builder
    public class UserRequest implements java.io.Serializable {
    
        private static final long serialVersionUID = -2655536314774756670L;
        /**
         * 主键
         */
        @NotNull(message = "id不能为空",groups = {ValidUpdateRules.class})
        @Min(1)
        private Long userId;
        /**
         * 年龄
         */
        @NotNull(message = "年龄不能为空",groups = {ValidUpdateRules.class,ValidAddRules.class})
        @Min(1)
        private Integer age;
    
        /**
         * 企业类型名称
         */
        @NotBlank(message = "名称不能为空",groups = {ValidUpdateRules.class,ValidAddRules.class})
        private String name;
    
        /**
         * 邮箱
         */
        @NotBlank(message = "邮箱不能为空",groups = {ValidUpdateRules.class,ValidAddRules.class})
        @Email(message = "邮箱格式不正确")
        private String email;
    
        /**
         * 昵称
         */
        private String nickName;
    
    }
    
    

    controller

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotNull;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /**
     * 用户控制器
     *
     * @author DUCHONG
     * @since 2020-08-24 23:41
     **/
    @RestController
    @Slf4j
    @Validated
    public class UserController {
    
        /**
         * 用户注册
         * @param userRequest
         * @param bindingResult
         * @return
         */
        @PostMapping("/user/register")
        public String registerUser(@Validated(ValidAddRules.class) @RequestBody UserRequest userRequest, BindingResult bindingResult){
    
            List<String> list=new ArrayList<>();
            bindingResult.getFieldErrors().forEach(fieldError -> {
                list.add(fieldError.getDefaultMessage());
            });
            return list.stream().collect(Collectors.joining(",")) ;
        }
    
        /**
         * 用户注册
         * @param userId
         * @param bindingResult
         * @return
         */
        @PostMapping("/user/get")
        public String getUser(@NotNull(message = "用户id不能为空") @Min(1) Long userId, BindingResult bindingResult){
    
    
            List<String> list=new ArrayList<>();
            bindingResult.getFieldErrors().forEach(fieldError -> {
                list.add(fieldError.getDefaultMessage());
            });
            return list.stream().collect(Collectors.joining(",")) ;
        }
    
        /**
         * 用户修改
         * @param userRequest
         * @param bindingResult
         * @return
         */
        @PostMapping("/user/update")
        public String updateUser(@Validated(ValidUpdateRules.class) @RequestBody UserRequest userRequest, BindingResult bindingResult){
    
            List<String> list=new ArrayList<>();
            bindingResult.getFieldErrors().forEach(fieldError -> {
                list.add(fieldError.getDefaultMessage());
            });
            return list.stream().collect(Collectors.joining(",")) ;
        }
    }
    
    

    搞定!!!

    ​ 但是你有木有发现,每个方法上有一个BindingResult,存储这报错的信息,那是不是可以提供一个公用的方法,当校验规则触发时,能捕获到异常信息,然后返回,它来了,它就是统一的异常处理入口,话不多少上代码

    四、校验统一异常处理

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.validation.BindException;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
    
    import javax.validation.ConstraintViolation;
    import javax.validation.ConstraintViolationException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /**
     * 统一的异常处理器
     * @author DUCHONG
     * @since 2020-08-25 00:57:40
     */
    @Slf4j
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        /**
         * 参数合法性校验异常
         * @param exception
         * @return
         */
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public BaseResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException exception){
    
            BaseResponse exceptionInfo = getErrorInfo(exception);
            log.error("参数校验异常---{}",exceptionInfo.getMsg());
            return exceptionInfo;
    
        }
    
        /**
         * 参数合法性校验异常-类型不匹配
         * @param exception
         * @return
         */
        @ExceptionHandler(MethodArgumentTypeMismatchException.class)
        public BaseResponse handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException exception){
    
            BaseResponse exceptionInfo = getErrorInfo(exception);
            log.error("参数校验异常---{}",exceptionInfo.getMsg());
            return exceptionInfo;
    
        }
        /**
         * 参数绑定异常
         * @param exception
         * @return
         */
        @ExceptionHandler(value = BindException.class)
        public BaseResponse handleBindException(BindException exception) {
    
    
            BaseResponse exceptionInfo = getErrorInfo(exception);
            log.error("参数校验异常---{}",exceptionInfo.getMsg());
            return exceptionInfo;
        }
    
        /**
         * 违反约束异常 单个参数使用
         * @param exception
         * @return
         */
        @ExceptionHandler(value = ConstraintViolationException.class)
        public BaseResponse handleConstraintViolationException(ConstraintViolationException exception) {
    
            BaseResponse exceptionInfo = getErrorInfo(exception);
            log.error("参数校验异常---{}", exceptionInfo.getMsg());
            return exceptionInfo;
        }
    
        /**
         * 将List结果转换成json格式
         * @param exception
         * @return
         */
        public  BaseResponse getErrorInfo(Exception exception) {
    
            if(exception instanceof BindException){
                return convertBindingResultToJson(((BindException) exception).getBindingResult());
            }
            if(exception instanceof MethodArgumentNotValidException){
                return convertBindingResultToJson(((MethodArgumentNotValidException) exception).getBindingResult());
            }
            if(exception instanceof ConstraintViolationException){
                return convertSetToJson(((ConstraintViolationException) exception).getConstraintViolations());
            }
            if(exception instanceof MethodArgumentTypeMismatchException){
                String msg= exception.getMessage();
                return new BaseResponse(500, msg, null);
            }
            //未定义的异常
            return new BaseResponse(500, "未知错误", null);
    
        }
    
        /**
         * 将单个参数实体校验结果封装
         * @param constraintViolations
         * @return
         */
        public  BaseResponse convertSetToJson(Set<? extends ConstraintViolation> constraintViolations) {
            List<String> list=new ArrayList<>();
            for (ConstraintViolation violation : constraintViolations) {
                list.add(violation.getMessage());
            }
            return new BaseResponse(500,list.stream().collect(Collectors.joining(",")), null);
        }
    
        /**
         * 将实体对象的校验结果封装
         * @param result
         * @return
         */
        public BaseResponse convertBindingResultToJson(BindingResult result){
    
            List<String> list=new ArrayList<>();
            result.getFieldErrors().forEach(fieldError -> {
                list.add(fieldError.getDefaultMessage());
            });
    
            return new BaseResponse(500,list.stream().collect(Collectors.joining(",")),null);
        }
    }
    

    到此可以跟BindingResult说拜拜了。

  • 相关阅读:
    c# proto文件导入报错 File not found. Import "common.proto" was not found or had errors.
    拓端tecdat|Python用稀疏、高斯随机投影和主成分分析PCA对MNIST手写数字数据进行降维可视化
    拓端tecdat|上海无印良品地理空间分布特征与选址策略可视化研究
    拓端tecdat|Python用Keras神经网络序列模型回归拟合预测、准确度检查和结果可视化
    拓端tecdat|R语言分布滞后非线性模型(DLNM)空气污染研究温度对死亡率影响建模应用
    拓端tecdat|R语言用AR,MA,ARIMA 模型进行时间序列预测
    拓端tecdat|R语言广义二次跳跃、非线性跳跃扩散过程转移函数密度的估计及其应用
    拓端tecdat|Python用LSTM长短期记忆神经网络对不稳定降雨量时间序列进行预测分析
    拓端tecdat|基于机器学习的印度肝脏病诊断分析
    拓端tecdat|R语言Metropolis Hastings采样和贝叶斯泊松回归Poisson模型
  • 原文地址:https://www.cnblogs.com/geekdc/p/13557459.html
Copyright © 2011-2022 走看看