美文网首页
SpringBoot 自定义表单校验工具

SpringBoot 自定义表单校验工具

作者: 东方不喵 | 来源:发表于2019-01-08 20:23 被阅读40次

    在项目开发的时候,对于表单的校验非常必要,而SpringBoot 集成的 Hibernate-validator 大部分被用来校验表单传参,对于Json映射就不能使用了。而且有时候需要校验 Collection<Entity> 时也不能使用。所以为了简便开发,可以编写一套自己的校验工具类。
    GitHub https://github.com/oldguys/MultipleDataSourceDemo

    Step1:引文Maven依赖

    <!-- 自动构建 Getter & Setter 插件 (如果选择自己手动创建Getter & Setter 方法可以不引用) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
        <scope>provided</scope>
    </dependency>
    

    Step2: 编写统一异常处理模板类。用于统一表单异常处理。

    1. 自定义异常类
    package com.oldguy.example.modules.common.exceptions;
    /**
     * @Date: 2019/1/8 0008
     * @Author: ren
     * @Description: 自定义异常类
     */
    public class FormValidException extends RuntimeException {
    
        public FormValidException(String message){
            super(message);
        }
    }
    
    
    1. 基于 SpringBoot 的自定义异常处理工具
    package com.oldguy.example.modules.common.handles;
    
    import com.oldguy.example.modules.common.dto.HttpResult;
    import com.oldguy.example.modules.common.exceptions.FormValidException;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    /**
     * @Date: 2019/1/8 0008
     * @Author: ren
     * @Description: 自定义异常处理工具
     */
    @RestControllerAdvice
    public class FormExceptionHandle {
    
        @ResponseStatus(value = HttpStatus.BAD_REQUEST)
        @ExceptionHandler(FormValidException.class)
        public Object FormException(FormValidException e){
            return new HttpResult(HttpStatus.BAD_REQUEST.value(),e.getMessage(),null);
        }
    }
    
    1. 统一结果类。
    package com.oldguy.example.modules.common.dto;
    
    import com.fasterxml.jackson.annotation.JsonInclude;
    import lombok.Data;
    
    /**
     * @Date: 2019/1/8 0008
     * @Author: ren
     * @Description: 统一结果类
     */
    @Data
    public class HttpResult {
    
        private Integer code;
    
        private String message;
    
        @JsonInclude(JsonInclude.Include.NON_EMPTY)
        private Object data;
    
        public HttpResult(Integer code, String message, Object data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }
        
    }
    
    

    Step3: 编写异常处理工具类

    package com.oldguy.example.modules.common.utils;
    
    
    import com.oldguy.example.modules.common.exceptions.FormValidException;
    import org.hibernate.validator.constraints.NotBlank;
    import org.hibernate.validator.constraints.NotEmpty;
    import org.springframework.util.ObjectUtils;
    import org.springframework.util.StringUtils;
    
    import javax.validation.constraints.NotNull;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.*;
    
    /**
     * @author ren
     * @date 2019/1/8
     */
    public class FormValidateUtils {
    
        /**
         * 校验入口
         * Map<校验注解,异常信息>
         */
        private static Map<Class, String> annotationsType = new HashMap<>();
    
        /**
         * 基本数据类型
         */
        private static Set<Class> baseType = new HashSet<>();
    
        static {
            annotationsType.put(NotBlank.class, "message");
            annotationsType.put(NotNull.class, "message");
            annotationsType.put(NotEmpty.class, "message");
    
            baseType.add(Integer.class);
            baseType.add(Long.class);
            baseType.add(Boolean.class);
            baseType.add(Float.class);
            baseType.add(Double.class);
            baseType.add(String.class);
            baseType.add(Character.class);
            baseType.add(Byte.class);
        }
    
        /**
         * 校验入口
         *
         * @param object
         */
        public static void validate(Object object) {
            validate(object.getClass(), object, false);
        }
    
        /**
         * @param object
         * @param cascade 级联校验 关联关系字段
         */
        public static void validate(Object object, boolean cascade) {
            validate(object.getClass(), object, cascade);
        }
    
        /**
         *  校验集合
         * @param obj
         */
        public static void validateCollection(Object obj) {
            validateCollection(obj.getClass(), obj, false);
        }
    
        /**
         *  校验集合
         * @param obj
         * @param cascade
         */
        public static void validateCollection(Object obj, boolean cascade) {
            validateCollection(obj.getClass(), obj, cascade);
        }
    
    
        /**
         * 校验集合对象
         *
         * @param clazz
         * @param obj
         * @param cascade
         */
        private static void validateCollection(Class clazz, Object obj, boolean cascade) {
    
            // 校验集合对象
            if (obj instanceof Collection) {
    
                Log4jUtils.getInstance(FormValidateUtils.class).debug("validate Array");
                Collection collection = Collection.class.cast(obj);
    
                if (collection.isEmpty()) {
                    String errorMessage = "集合不能为空!";
                    Log4jUtils.getInstance(FormValidateUtils.class).debug(errorMessage);
                    throw new FormValidException(errorMessage);
                }
    
                for (Object item : collection) {
                    if (!ObjectUtils.isEmpty(item)) {
                        validate(item, true);
                    }
                }
                return;
            }
        }
    
        private static void validate(Class clazz, Object obj, boolean cascade) {
            if (clazz.equals(Object.class)) {
                return;
            }
    
            Log4jUtils.getInstance(FormValidateUtils.class).debug("validate Object");
    
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                validateField(obj.getClass(), field, obj);
                if (cascade) {
                    validateObjectField(field, obj);
                }
            }
    
            Class superClass = clazz.getSuperclass();
            if (!superClass.equals(Object.class)) {
                validate(superClass, obj, cascade);
            }
    
        }
    
        /**
         * 校验对象数据类型
         * 只用于校验 级联的 关联对象数据
         *
         * @param field
         * @param obj
         */
        private static void validateObjectField(Field field, Object obj) {
    
            String getMethodName = ReflectUtils.tranFieldToGetterMethodName(field.getName());
            Object value = null;
            try {
                // 不需要校验
                Method method = obj.getClass().getMethod(getMethodName);
                if (null == method) {
                    return;
                }
                value = method.invoke(obj);
                if (null == value) {
                    return;
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            // 剔除基本数据类型
            if (baseType.contains(value.getClass())) {
                Log4jUtils.getInstance(FormValidateUtils.class).debug("Base Type");
                return;
            }
    
            // 级联校验
            if (value instanceof Collection) {
    
                Log4jUtils.getInstance(FormValidateUtils.class).debug("validate Collection");
                Collection collection = Collection.class.cast(value);
                for (Object item : collection) {
                    if (!ObjectUtils.isEmpty(item)) {
                        validate(item, true);
                    }
                }
            } else if (value instanceof Map) {
    
                // Map 一般不做表单参数,需要可以自行扩展
                Log4jUtils.getInstance(FormValidateUtils.class).debug("validate Map");
    
            } else if (value instanceof Object) {
    
                // 递归校验 对象参数
                Log4jUtils.getInstance(FormValidateUtils.class).debug("validate field [ " + value.getClass().getName() + " ]");
                validate(value, true);
            }
    
    
        }
    
        /**
         * 检验基本数据类型
         *
         * @param clazz
         * @param field
         * @param object
         */
        private static void validateField(Class clazz, Field field, Object object) {
    
            String getMethod = ReflectUtils.tranFieldToGetterMethodName(field.getName());
    
            String errorMessage = null;
            try {
    
                Method method = clazz.getMethod(getMethod);
                // 不具备Get方法,不做校验
                if (null == method) {
                    return;
                }
                Object value = method.invoke(object);
    
                for (Class key : annotationsType.keySet()) {
                    Annotation annotation = field.getAnnotation(key);
                    if (null != annotation) {
                        Object obj = key.cast(annotation);
    
                        Log4jUtils.getInstance(FormValidateUtils.class).debug(field.getClass().getSimpleName() + ":" + obj);
    
                        // 非空有效值,不做校验
                        if (!ObjectUtils.isEmpty(value)) {
                            return;
                        }
    
                        Method messageMethod = key.getMethod(annotationsType.get(key));
                        if (null != messageMethod) {
                            errorMessage = (String) messageMethod.invoke(obj);
                            if (StringUtils.isEmpty(errorMessage)) {
                                errorMessage = field.getName() + " 出现异常";
                            }
                        }
                    }
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            if (!StringUtils.isEmpty(errorMessage)) {
                Log4jUtils.getInstance(FormValidateUtils.class).warn("表单校验异常:" + errorMessage);
                // 抛出自定义异常,用于统一的异常捕获
                throw new FormValidException(errorMessage);
            }
        }
    
    
    }
    

    其中Log4jUtils为自定义日志处理类,可以忽略。另外具有一个转换Get Method 的方法

        public static String tranFieldToGetterMethodName(String field) {
            field = "get" + field.substring(0, 1).toUpperCase() + field.substring(1, field.length());
            return field;
        }
    

    以上就完成了自定义校验工具类的编写,可以进行测试了。

    1. 测试类
    package com.oldguy.example.common;
    
    import com.oldguy.example.modules.common.utils.FormValidateUtils;
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Date: 2019/1/8 0008
     * @Author: ren
     * @Description:
     */
    public class FormValidateTest {
    
        @Test
        public void testList(){
    
            List<FormEntity> list = new ArrayList<>();
            list.add(new FormEntity());
            FormValidateUtils.validateCollection(list);
    
        }
    
        @Test
        public void testNoCascade(){
    
            FormEntity entity = new FormEntity();
    
            FormEntity.FormItem item = new FormEntity.FormItem();
            entity.setItem1(item);
    
            List<FormEntity.FormItem> list = new ArrayList<>();
            list.add(item);
            entity.setItemList(list);
    
            FormValidateUtils.validate(entity);
    
        }
    
        @Test
        public void testCascade(){
    
            FormEntity entity = new FormEntity();
    
            FormEntity.FormItem item = new FormEntity.FormItem();
            entity.setItem1(item);
    
            List<FormEntity.FormItem> list = new ArrayList<>();
            list.add(item);
            entity.setItemList(list);
    
            FormValidateUtils.validate(entity,true);
    
        }
    }
    
    
    1. 测试参数类
    package com.oldguy.example.common;
    
    import javax.validation.constraints.NotNull;
    
    /**
     * @Date: 2019/1/8 0008
     * @Author: ren
     * @Description:
     */
    public class AbstractForm {
    
    
        @NotNull(message = "测试继承 id")
        private Long id;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    }
    
    
    1. 参数类
    package com.oldguy.example.common;
    
    import lombok.Data;
    import org.hibernate.validator.constraints.NotBlank;
    import org.hibernate.validator.constraints.NotEmpty;
    
    import javax.validation.constraints.NotNull;
    import java.util.List;
    
    /**
     * @Date: 2019/1/8 0008
     * @Author: ren
     * @Description:
     */
    @Data
    public class FormEntity extends AbstractForm{
    
        @NotBlank(message = "测试 message")
        private String message;
    
        @NotBlank(message = "测试 comment")
        private String comment;
    
        @NotEmpty(message = "测试 List")
        public List<FormItem> itemList;
    
        @NotNull(message = "item1 不为空")
        public FormItem item1;
    
        public FormItem item2;
    
        @Data
        public static class FormItem{
    
            @NotNull(message = "测试级联 code")
            private Integer code;
    
            @NotBlank(message = "测试级联 message")
            private String message;
    
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public void setComment(String comment) {
            this.comment = comment;
        }
    
        public void setItemList(List<FormItem> itemList) {
            this.itemList = itemList;
        }
    
        public void setItem1(FormItem item1) {
            this.item1 = item1;
        }
    
        public void setItem2(FormItem item2) {
            this.item2 = item2;
        }
    }
    

    结果:
    [图片上传中...(QQ截图20190108202259.png-539101-1546950193064-0)]

    到此完成了 SpringBoot 自定义参数校验模板工具类的编写
    代码可以参考 GitHub https://github.com/oldguys/MultipleDataSourceDemo

    相关文章

      网友评论

          本文标题:SpringBoot 自定义表单校验工具

          本文链接:https://www.haomeiwen.com/subject/xygbrqtx.html