美文网首页
Spring Validation框架+AOP实现Control

Spring Validation框架+AOP实现Control

作者: gigglesoso | 来源:发表于2018-12-24 11:39 被阅读0次

    项目开发过程中,通常都涉及到表单提交时候前台传递的表单数据的数据合法性校验,这里说的合法性指的是数据合法性,不涉及业务逻辑上的合法性。为了避免在每个方法中都去写重复的校验逻辑,基于这个目的,使用了spring自带的validation校验框架+aop实现了一个通用的的入参校验处理方法。
    这里实现基于springboot1.5.18.RELEASE,java 1.8,项目结构如下:

    pom文件引入的依赖:

    创建实体类:

    
    import javax.validation.constraints.Max;
    
    import org.hibernate.validator.constraints.Email;
    import org.hibernate.validator.constraints.Length;
    
    public class User {
        
        private String id;
        
        //校验字符串是否在指定的范围内
        @Length(min=0, max=20,message = "名字最大长度20")
        private String name;
        
        @Length(min=0, max=100,message = "地址最大长度100")
        private String address;
        
        //email校验,不符合格式则直接返回,为空不校验
        @Email(message = "邮箱格式不正确")
        private String email;
        
        //年龄最大值
        @Max(value = 150,message = "年龄超过最大值150")
        private Integer age;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + ", address=" + address + ", email=" + email + ", age=" + age + "]";
        }
    }
    

    自定义注解,用来创建切面时使用:

    
    import java.lang.annotation.*;
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME) 
    @Inherited
    @Target(ElementType.METHOD)
    public @interface ParamValidate {
    
    }
    

    创建切面类:

    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.List;
    
    import javax.servlet.http.HttpServletResponse;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.MediaType;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.ObjectError;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import cst.constant.ResponseConstant;
    
    
    @Aspect
    @Component
    public class ParamValidateAspect {
        
        //使用java简单日志门面slf4j,springboot默认使用logback
        private static final Logger log = LoggerFactory.getLogger(ParamValidateAspect.class);
        
        //定义切面要切的方法为所有的带这个注解的方法
        @Pointcut("@annotation(cst.annotation.ParamValidate)")
        public void paramValidate() {}
        
        //切面逻辑
        @Before("paramValidate()")
        public void paramValidateBefore(JoinPoint point) {
            //获取方法入参的数据
            Object[] paramObjs = point.getArgs();
            StringBuffer buffer = new StringBuffer();
            //如果入参个数不为0
            if (paramObjs.length > 0) {
                for (Object object : paramObjs) {
                    //如果是BindingResult类型的参数
                    if (object instanceof BindingResult) {
                        BindingResult result = (BindingResult)object;
                        //如果有校验失败的信息
                        if (result.hasErrors()) {
                            //循环拼接所有的错误信息
                            List<ObjectError> allErrors = result.getAllErrors();
                            for (ObjectError error : allErrors) {
                                buffer.append(error.getDefaultMessage()+";");
                            }
                        }
                        
                    }
                }
            }
            //如果校验信息不为空,则直接返回请求
            String checkResult = buffer.toString();
            if (!StringUtils.isEmpty(checkResult)) {
                //获取到HttpServletResponse对象
                ServletRequestAttributes res = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletResponse response = res.getResponse();
                //设置编码格式
                response.setCharacterEncoding("UTF-8");
                //设置应答头类型
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                //根据实际情况拼接要返回的json字符串,这里因为返回使用了自定义的ReturnData实体,所以拼接成这种实体的json格式
                String returnData = "{" + 
                                        "\"code\":" + "\""+ ResponseConstant.FAILURE_CODE+ "\"" + "," +
                                        "\"msg\":" + "\""+ checkResult + "\"" + "," +
                                        "\"data\":" + "\""+ checkResult + "\""+"," +
                                        "\"pages\": null" + 
                                     "}";
                //将请求返回
                OutputStream output = null;
                try {
                    output = response.getOutputStream();
                    output.write(returnData.getBytes("UTF-8"));
                } catch (Exception e) {
                    log.error(e.getMessage());
                } finally {
                    try {
                        if (output != null) {
                            output.close();
                        }
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
    }
    

    要使用的返回值的统一实体和返回值常量类:

    /**
     * 返回到前台的信息实体
     * @author Administrator
     *
     * @param <T>
     */
    public class ReturnData<T> {
        //状态码,0为请求成功,其他均失败
        private Integer code;
        //返回的附加信息
        private String msg;
        //要返回的数据
        private T data;
        
        private Integer pages;
        //无参构造
        public ReturnData() {}
        
        //有参构造
        public ReturnData(int code,String msg,T t){
            this.code=code;
            this.msg=msg;
            this.data=t;
        }
        public ReturnData(int code,String msg,T t,int pages){
            this.code=code;
            this.msg=msg;
            this.data=t;
            this.pages=pages;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
        public Integer getPages() {
            return pages;
        }
    
        public void setPages(Integer pages) {
            this.pages = pages;
        }
    
        @Override
        public String toString() {
            return "ReturnData [code=" + code + ", msg=" + msg + ", data=" + data + ", pages=" + pages + "]";
        }
    }
    
    /**
     * 返回到前台信息的实体常量类
     */
    public class ResponseConstant {
        //请求成功的code
        public static final Integer SUCCESS_CODE=0;
        //请求失败的code
        public static final Integer FAILURE_CODE=1;
        //请求成功的msg
        public static final String SUCCESS_MESSAGE="操作成功!";
        //请求失败的msg
        public static final String FAILURE_MESSAGE="操作失败!";
    }
    

    创建的测试controller:

    
    
    import javax.validation.Valid;
    
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import cst.annotation.ParamValidate;
    import cst.constant.ResponseConstant;
    import cst.entity.ReturnData;
    import cst.entity.User;
    
    
    @RestController
    @RequestMapping(value = "/user")
    public class UserController {
        
        @ParamValidate//这个方法上定义这个注解,切面注解
        @RequestMapping(value = "/saveUser")
        //这里注意使用@Valid 和 BindingResult,这有对应关系
        public ReturnData<String> saveUser(@Valid @RequestBody User user,BindingResult result) {
            return new ReturnData<String>(ResponseConstant.SUCCESS_CODE,ResponseConstant.SUCCESS_MESSAGE,ResponseConstant.SUCCESS_MESSAGE);
        }
    }
    

    使用postman模拟表单提交进行测试,请求头设置:

    请求返回结果:

    常见的注解如下,基本满足需求:
    JSR提供的校验注解:
    @Null 被注释的元素必须为 null
    @NotNull 被注释的元素必须不为 null
    @AssertTrue 被注释的元素必须为 true
    @AssertFalse 被注释的元素必须为 false
    @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    @Size(max=, min=) 被注释的元素的大小必须在指定的范围内
    @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
    @Past 被注释的元素必须是一个过去的日期
    @Future 被注释的元素必须是一个将来的日期
    @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
    Hibernate Validator提供的校验注解:
    @NotBlank(message =) 验证字符串非null,且长度必须大于0
    @Email 被注释的元素必须是电子邮箱地址
    @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
    @NotEmpty 被注释的字符串的必须非空
    @Range(min=,max=,message=) 被注释的元素必须在合适的范围内

    以上是一种最简单的实现思路,具体可以根据自己需求进一步优化。

    相关文章

      网友评论

          本文标题:Spring Validation框架+AOP实现Control

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