• 接口参数校验(不使用hibernate-validator,规避大量if else)


    引言

    编写接口时,常用的参数校验使用hibernate-validator注解+@Validated注解进行参数校验。当遇到一些特殊场景或需求,需要自己对参数进行手动校验时,会出现以下问题:
    不可避免的需要对接受的参数进行判断,此时便会出现大量if…else…影响代码可读性,且校验不够优雅。
    本文给出一个参数校验方案,给大家一个思路。如果只是想使用,基本校验的已足够。

    使用举例

    具体项目中是如何使用的,可以参考博客中我正在开发的U-Learning后端开发日志,其中有GitHub项目地址

    Before1

    @PostMapping("/save")
    public ResponseEntity<JsonResult> save(TeacherDto teacher) {
        if(StringUtil.isEmpty(teacher.getTeaName())){
            return ...;
        }
        if(StringUtil.isEmpty(teacher.getTeaNumber())){
            return ...;
        }
        if(StringUtil.isEmpty(teacher.getTeaEmail())){
            return ...;
        }
    
        //接口处理业务代码
        ...
    }
    

    After1

    @PostMapping("/save")
    public ResponseEntity<JsonResult> save(TeacherDto teacher) {
        ValidatorBuilder.build()
            .on(StringUtil.isEmpty(teacher.getTeaName()), SystemErrorCodeEnum.NAME_CANNOT_BE_NULL)
            .on(StringUtil.isEmpty(teacher.getTeaNumber()), SystemErrorCodeEnum.TEA_NUMBER_CANNOT_BE_NULL)
            .on(StringUtil.isEmpty(teacher.getTeaEmail()), SystemErrorCodeEnum.EMAIL_CANNOT_BE_NULL)
            .doValidate().checkResult();
    
        //接口处理业务代码
        ...
    }
    

    Beafore2

    当你只有一个需要校验的参数时,可能会觉得这有些啰嗦

    @GetMapping("/delete")
    public ResponseEntity<JsonResult> delete(Long id) {
        if(StringUtil.isEmpty(id)){
            return ...;
        }
        //接口处理业务代码
        ...
    }
    

    After2

    @GetMapping("/delete")
    public ResponseEntity<JsonResult> delete(Long id) {
        ValidateHandler.checkParameter(StringUtil.isEmpty(id), SystemErrorCodeEnum.ID_CANNOT_BE_NULL);
        //接口处理业务代码
        ...
    }
    

    知识点

    涉及到的知识点

    1. 建造者模式
    2. 链式调用
    3. 枚举
    4. 异常
    5. 继承与多态

    拓展所需知识点

    1. 委托模式

    使用前准备

    自定义异常

    public class BaseException extends RuntimeException {
        private Integer status = HttpStatus.BAD_REQUEST.value();
    
        public BaseException(Integer status) {
            this.status = status;
        }
    
        public BaseException(BaseEnum baseEnum) {
            super(baseEnum.getMessage());
            this.status = baseEnum.getCode();
        }
    
        public BaseException(String message) {
            super(message);
        }
    
        public BaseException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public BaseException(Throwable cause) {
            super(cause);
        }
    
        public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    
        public Integer getStatus() {
            return status;
        }
    }
    
    

    举个栗子

    @Getter
    public class BadRequestException extends BaseException {
    
        public BadRequestException(String msg) {
            super(msg);
        }
    
        public BadRequestException(BaseEnum baseEnum) {
            super(baseEnum);
        }
    }
    
    

    统一异常管理

    @Slf4j
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        /**
         * 处理系统自定义异常
         */
        @ExceptionHandler(BaseException.class)
        public ResponseEntity handleBaseException(BaseException e) {
            String message = StringUtil.isContainChinese(e.getMessage()) ? e.getMessage() : null;
            return ResponseEntityUtil.badRequest(JsonResult.buildErrorMsg(e.getStatus(),
                    Optional.ofNullable(message).orElse(MicroErrorCodeEnum.OPERATE_ERROR.getMessage())));
        }
    
        /**
         * Throwable
         * 接收非系统预测内的异常
         */
        @ExceptionHandler(Throwable.class)
        public ResponseEntity handleThrowable(Throwable e) {
            log.error("系统捕捉Throwable异常并处理 ==> " + e.getMessage(), e);
            String message = StringUtil.isContainChinese(e.getMessage()) ? e.getMessage() : null;
            return ResponseEntityUtil.internalServerError(JsonResult.buildErrorMsg(HttpStatus.INTERNAL_SERVER_ERROR.value(),
                    Optional.ofNullable(message).orElse(MicroErrorCodeEnum.SYSTEM_ERROR.getMessage())));
        }
    }
    

    自定义枚举

    基础枚举接口

    public interface BaseEnum {
    
        Integer getCode();
    
        String getMessage();
    }
    
    

    举个栗子(根据自己需求创建)

    public enum SystemErrorCodeEnum implements BaseEnum {
    
        /**
         * 后台管理系统错误状态码
         */
        PARAMETER_EMPTY(HttpStatus.BAD_REQUEST, "参数不可为空!"),
        TEACHER_NOT_EXISTS(HttpStatus.BAD_REQUEST, "教师不存在!"),
        ID_CANNOT_BE_NULL(HttpStatus.BAD_REQUEST, "ID不可为空!"),
        EMAIL_CANNOT_BE_NULL(HttpStatus.BAD_REQUEST, "邮箱不可为空!"),
        ;
    
        private Integer code;
        private String message;
    
        SystemErrorCodeEnum(HttpStatus httpStatus, String message) {
            this.code = httpStatus.value();
            this.message = message;
        }
    
        @Override
        public Integer getCode() {
            return code;
        }
    
        @Override
        public String getMessage() {
            return message;
        }
    }
    
    

    Validate核心代码

    校验类context

    @Data
    @AllArgsConstructor
    public class ValidatorContext {
    
        /** 校验结果 */
        private Boolean checkResult;
    
        /** 错误信息枚举 */
        private BaseEnum baseEnum;
    
    }
    

    校验类上下文

    public class ValidatorHolder {
    
        /** context集合 */
        private List<ValidatorContext> validatorContexts;
    
        /** 校验结果 */
        private Boolean result;
    
        /** 若result = true,保存错误信息 */
        private BaseEnum baseEnum;
    
        /** 初始化 */
        public ValidatorHolder() {
            this.validatorContexts = new ArrayList<>();
            this.result = true;
            this.baseEnum = null;
        }
    
        /** 校验链 */
        public ValidatorHolder on(Boolean checkResult, BaseEnum baseEnum){
            if(checkResult != null && baseEnum != null){
                addContext(checkResult, baseEnum);
            }
            return this;
        }
    
        /** 添加待校验属性和错误信息 */
        private void addContext(Boolean checkResult, BaseEnum baseEnum){
            validatorContexts.add(new ValidatorContext(checkResult, baseEnum));
        }
    
        /** 校验 */
        public ValidatorHolder doValidate(){
            for (ValidatorContext validatorContext : validatorContexts) {
                if(validatorContext.getCheckResult()){
                    result = false;
                    baseEnum = validatorContext.getBaseEnum();
                    break;
                }
            }
            return this;
        }
    
        public void checkResult(){
            ValidateHandler.checkValidator(this);
        }
    
        public Boolean getResult() {
            return result;
        }
    
        public BaseEnum getBaseEnum() {
            return baseEnum;
        }
    }
    

    校验builder类

    public class ValidatorBuilder {
    
        public static ValidatorHolder build(){
            return new ValidatorHolder();
        }
    }
    

    校验处理器

    参考Guava类库中提供的一个作参数检查的工具类--Preconditions类

    public class ValidateHandler {
    
        /**
         * 判断校验是否成功,若存在错误,抛出异常
         * 针对ValidatorHolder
         */
        public static void checkValidator(ValidatorHolder validator){
            if(! validator.getResult()){
                throw new BadRequestException(validator.getBaseEnum());
            }
        }
    
        /**
         * 针对单参数校验
         *
         * @param checkResult true:参数错误; false:参数正确
         * @param baseEnum 错误码
         */
        public static void checkParameter(Boolean checkResult, BaseEnum baseEnum){
            if(checkResult){
                throw new BadRequestException(baseEnum);
            }
        }
    }
    
  • 相关阅读:
    高效管理,经理人须熟练运用的几个工具
    投资感情 收获人心
    忽如一夜入冬来
    贺嫦娥奔月
    正确处理人际关系,给自己做无形的投资
    观南溪豆干有感
    身在职场,请善待你的每一张白纸
    游一品天下有感
    增强影响力,如何提升你的“领袖气质”?
    oracle 创建表空间
  • 原文地址:https://www.cnblogs.com/darren1112/p/12181716.html
Copyright © 2020-2023  润新知