精华内容
下载资源
问答
  • 故事的开始: 前端进行校验,这个概念应该是没有问题的吧?比如:前端输入手机号的、链接的、邮箱地址的,我们是不是web端传入参数的时候,要对用户填入的格式进行校验。...2、“后端进行参数校验,...

    故事的开始:

    前端进行校验,这个概念应该是没有问题的吧?比如:前端输入手机号的、链接的、邮箱地址的,我们是不是web端传入参数的时候,要对用户填入的格式进行校验。OK,这个概念大家都懂。但是,一般情况下,后端我们也要进行校验。

    为什么?后端为什么校验?

    我目前听到的两种说法:1、“客户端传来的参数都是不可信的”我大boss说的,具体原因不知道,就是说是行业的一种说法;2、“后端进行参数校验,是防止别人通过接口乱刷服务”。反正我是信第二种说法的,如果一些不安好心的人,通过接口刷我们的服务,随便哪个参数我们都允许填入,会导致数据库中导致大量的脏数据、风险。如果注入的是病毒怎么办?所以,后端是要进行参数校验的。

    怎么进行后端参数校验?

    我目前也是了解到了两种做法。

    1、通过注解的方法,在实体上加注解。通过工具,当客户端传入参数的时候,自动进行校验。

    大概的方法:pom中引入工具类;然后实体中添加注解;controller层传入数据时添加@validator进行参数校验。参考下面这篇文章:

    后端参数校验:通过对实体添加注解——Hibernate validator

    2、通过工具类,进行参数校验。

    思路:将常用的校验方法放在一个类中,然后哪个参数需要校验,就调用工具类中的方法。我自认为很好用。

    下面提供一个工具类,正则表达式工具类:RegexUtils.java

    
    
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * @author Meng
     * @Date 2018/12/14 14:00
     */
    public class RegexUtils {
    
            /**
             * 验证Email
             * @param email email地址,格式:zhangsan@zuidaima.com,zhangsan@xxx.com.cn,xxx代表邮件服务商
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkEmail(String email) {
                String regex = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?";
                return Pattern.matches(regex, email);
            }
    
            /**
             * 验证身份证号码
             * @param idCard 居民身份证号码15位或18位,最后一位可能是数字或字母
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkIdCard(String idCard) {
                String regex = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}";
                return Pattern.matches(regex,idCard);
            }
    
            /**
             * 验证手机号码(支持国际格式,+86135xxxx...(中国内地),+00852137xxxx...(中国香港))
             * @param mobile 移动、联通、电信运营商的号码段
             *<p>移动的号段:134(0-8)、135、136、137、138、139、147(预计用于TD上网卡)
             *、150、151、152、157(TD专用)、158、159、187(未启用)、188(TD专用)</p>
             *<p>联通的号段:130、131、132、155、156(世界风专用)、185(未启用)、186(3g)</p>
             *<p>电信的号段:133、153、180(未启用)、189</p>
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkMobile(String mobile) {
                String regex = "(\\+\\d+)?1[34578]\\d{9}$";
                return Pattern.matches(regex,mobile);
            }
    
            /**
             * 验证固定电话号码
             * @param phone 电话号码,格式:国家(地区)电话代码 + 区号(城市代码) + 电话号码,如:+8602085588447
             * <p><b>国家(地区) 代码 :</b>标识电话号码的国家(地区)的标准国家(地区)代码。它包含从 0 到 9 的一位或多位数字,
             *  数字之后是空格分隔的国家(地区)代码。</p>
             * <p><b>区号(城市代码):</b>这可能包含一个或多个从 0 到 9 的数字,地区或城市代码放在圆括号——
             * 对不使用地区或城市代码的国家(地区),则省略该组件。</p>
             * <p><b>电话号码:</b>这包含从 0 到 9 的一个或多个数字 </p>
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkPhone(String phone) {
                String regex = "(\\+\\d+)?(\\d{3,4}\\-?)?\\d{7,8}$";
                return Pattern.matches(regex, phone);
            }
    
            /**
             * 验证整数(正整数和负整数)
             * @param digit 一位或多位0-9之间的整数
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkDigit(String digit) {
                String regex = "\\-?[1-9]\\d+";
                return Pattern.matches(regex,digit);
            }
    
            /**
             * 验证整数和浮点数(正负整数和正负浮点数)
             * @param decimals 一位或多位0-9之间的浮点数,如:1.23,233.30
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkDecimals(String decimals) {
                String regex = "\\-?[1-9]\\d+(\\.\\d+)?";
                return Pattern.matches(regex,decimals);
            }
    
            /**
             * 验证空白字符
             * @param blankSpace 空白字符,包括:空格、\t、\n、\r、\f、\x0B
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkBlankSpace(String blankSpace) {
                String regex = "\\s+";
                return Pattern.matches(regex,blankSpace);
            }
    
            /**
             * 验证中文
             * @param chinese 中文字符
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkChinese(String chinese) {
                String regex = "^[\u4E00-\u9FA5]+$";
                return Pattern.matches(regex,chinese);
            }
    
            /**
             * 验证日期(年月日)
             * @param birthday 日期,格式:1992-09-03,或1992.09.03
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkBirthday(String birthday) {
                String regex = "[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}";
                return Pattern.matches(regex,birthday);
            }
    
            /**
             * 验证URL地址
             * @param url 格式:http://blog.csdn.net:80/xyang81/article/details/7705960? 或 http://www.csdn.net:80
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkURL(String url) {
                String regex = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?";
                return Pattern.matches(regex, url);
            }
    
            /**
             * <pre>
             * 获取网址 URL 的一级域
             * </pre>
             *
             * @param url
             * @return
             */
            public static String getDomain(String url) {
                Pattern p = Pattern.compile("(?<=http://|\\.)[^.]*?\\.(com|cn|net|org|biz|info|cc|tv)", Pattern.CASE_INSENSITIVE);
                // 获取完整的域名
                // Pattern p=Pattern.compile("[^//]*?\\.(com|cn|net|org|biz|info|cc|tv)", Pattern.CASE_INSENSITIVE);
                Matcher matcher = p.matcher(url);
                matcher.find();
                return matcher.group();
            }
            /**
             * 匹配中国邮政编码
             * @param postcode 邮政编码
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkPostcode(String postcode) {
                String regex = "[1-9]\\d{5}";
                return Pattern.matches(regex, postcode);
            }
    
            /**
             * 匹配IP地址(简单匹配,格式,如:192.168.1.1,127.0.0.1,没有匹配IP段的大小)
             * @param ipAddress IPv4标准地址
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkIpAddress(String ipAddress) {
                String regex = "[1-9](\\d{1,2})?\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))";
                return Pattern.matches(regex, ipAddress);
            }
    
    
    }

     怎么用?我是直接在controller层进行调用的。!RegexUtils.checkEmail(obj.getContactsEmail())

    
        @RequestMapping(value = "/submit", method = RequestMethod.POST)
        @ResponseBody
        public BaseResponse submitTenantInfo(@RequestBody WebObjRequest<TenantInfo> request) {
            TenantInfo obj = request.getObj();
            if (obj == null || !request.isCorrectParams()|| !RegexUtils.checkEmail(obj.getContactsEmail())) {
                return new BaseResponse(RespConstants.GLOBAL_PARAM_ERROR);
            }
           ```````
            return new BaseResponse(RespConstants.GLOBAL_SUCCESS);
        }

    最后,我想说的是。客户端校验和服务端校验的不同:我的理解是,客户端校验的提示,是提示用户正确输入,加强用户体验。服务端校验是为了接口安全,防止非法用户乱刷。所以,服务端进行参数校验的时候,返回的错误码不要具体到具体的错误,返回“参数异常”相关异常的错误即可。

    相关拓展:常用的正则表达式

    你是技术宅么?那就加入我们吧!点击下方链接 或 扫描二维码 即可。

    欢迎加入 CSDN技术交流群2:(点击即可加群)QQ群:456543087

                   CSDN技术交流群(已满员):(点击即可加群)QQ群:681223095。  

    商务合作@群主,谢谢!

    因经常有人留言,未能及时查看到和回复,所以特建此群,以方便交流。方便问题讨论,有问题和没有问题的小伙伴均可加入,用作自我学习和共同进步。本博主不一定长期在线,但是qq群里会有很多热心的小伙伴,大家一起讨论解决问题。

     

     

     

    展开全文
  • public Response addSubItem(@RequestBody AddSubRequest addSubRequest) { govAffairService.addSubItem(addSubRequest.getId(),...以上述接口为例,增加参数校验的方法为,为AddSubRequest类打上校验注解 @Data pub.
     public Response addSubItem(@RequestBody AddSubRequest addSubRequest) {
            govAffairService.addSubItem(addSubRequest.getId(),addSubRequest.getSubItemIds());
            return createResponse(null);
        }
    

    以上述接口为例,增加参数校验的方法为,为AddSubRequest类打上校验注解

    @Data
    public class AddSubRequest {
    	@NotNull
        private int id;
        @NotEmpty
        private List<Integer> SubItemIds;
    }
    
    

    其中,对于int,double,long等基本数据类型使用@NotNull;
    对于String字符串使用@NotBlank;
    对于集合类,比如数组,List,Map等使用@NotEmpty;
    然后修改接口参数,增加校验函数

     public Response addSubItem(@RequestBody @Validated AddSubRequest addSubRequest,BindingResult bindingResult) {
            
                 
    validate(bindingResult);
    govAffairService.addSubItem(addSubRequest.getId(),addSubRequest.getSubItemIds());
            return createResponse(null);
        }
    

    重新部署运行项目,在Swagger页面中如果参数为空页面会报错。

    展开全文
  • 之前一直心存疑惑,对于同一表单的参数,是前端校验好还是后端。今天翻看了网上一些文章,发现这不是一个技术性问题,是我概念没弄清。先抛出结论: 前端校验是辅助,后端校验是核心。后端校验必不可少。 前端校验...

    之前一直心存疑惑,对于同一表单的参数,是前端校验好还是后端。今天翻看了网上一些文章,发现这不是一个技术性问题,是我概念没弄清。先抛出结论:

    前端校验是辅助,后端校验是核心。后端校验必不可少。

    前端校验

    前端校验主要是针对用户输入时,一些基础的错误进行提示,提升用户体验。比如:参数为空,密码邮箱的组合规则等。归根到底是对所有请求的一种过滤操作,如果前端都校验不通过当然就没必要请求后端了,这样可以减轻服务器负担。

    但是:对于某些不走寻常路的用户,前端校验其实形同虚设。

    后端校验

    后端校验是针对整个系统的业务逻辑进行校验,比如用户权限,token验证等。校验范围比前端更大。更关键的是,对于某些避开前端校验的请求,后端要是不做校验的隐患相当大。比如:前端校验手机号,用户请求http://example.com/?tel=12345678901后,直接复制该链接,用浏览器或其他工具(如postman)请求http://example.com/?tel=abc,假如这是个插入操作,后端就会尝试插入。要是连数据库设计都不规范的话,这么一串字符就成功混入了我们的系统。

    总结

    前端校验是辅助,后端校验是核心。后端校验必不可少。

    展开全文
  • Spring MVC后端参数校验

    2021-10-18 00:50:37
    Spring MVC后端参数校验一、基本概念1.为什么要进行统一参数校验2. Spring的统一参数校验框架3. 统一参数校验是如何进行的4. @Validated 和 @Valid 的区别二、基本校验操作1. 简单参数校验2. 嵌套校验3. 分组校验4. ...


    一、相关概念


    1.为什么要进行统一参数校验

      通常为保证接口的安全性,后端接口需要对入参进行合法性校验,如所填参数是否为空、参数长度是否在规定范围内、手机号或邮箱是否为正确格式等。因此,不可避免在业务代码中进行手动校验,但这种方式使得代码臃肿,可读性差。在Spring中提供了统一参数校验框架,将校验逻辑与业务逻辑进行隔离,进而优雅处理这个问题,简化开发人员负担


    2. 统一参数校验框架

      在Spring中,其自身不仅拥有独立的参数校验框架,还支持符合JSR303标准的框架。JSR303是java为Bean数据合法性校验提供的标准框架,通过在Bean属性上标注注解,来设置对应的校验规则,下表展示JSR303部分注解

    注解说明
    @Null被注释元素必须为null
    @NotNull被注释元素必须不为null
    @Max(value)被注释元素必须是一个数字,且值必须小于等于指定最大值
    @Size(max,min)被注释元素的大小在指定范围内
    @Pattern(value)被注释元素必须符合指定的正则表达式

      Spring本身并没有提供JSR303标准的实现,在使用JSR303参数校验时,需要引入具体的第三方实现。 Hibernate Validator是一个符合JSR303标准的官方参考实现,除支持以上所有标准注解外,它还支持以下的扩展注解

    注解说明
    @Email被注释元素必须是电子邮箱地址
    @Length被注释字符串大小必须在指定范围内
    @NotEmpty被注释字符串必须非空
    @Range被注释元素必须在合适范围内

      当然,若以上校验注解均无法满足实际校验需求,可以通过实现ConstraintValidator接口,来自定义注解和校验规则。


    3. 统一参数校验基本流程

    • 首先需要在Spring容器中创建一个LocalValidatorFactoryBean(SpringBoot会自动装配好一个LocalValidateFactoryBean)
    • 然后在已标注校验注解的入参bean对象前加上一个@Valid,表明需要被校验,当Spring MVC框架将请求参数绑定到该bean对象后,交由spring校验框架进行处理
    • 根据注解声明的校验规则进行校验,若不合法抛出javax.validation.ConstraintViolationException异常。另外也可将校验的结果保存到随后的入参中,入参必须是BindingResult或Errors类型,并通过对应方法获取到校验失败的字段及信息(需要被校验的入参bean对象和接收校验结果的对象是成对出现,它们之间不允许声明其它入参)


    4. @Validated 和 @Valid 的区别

       在Spring参数校验框架中增加了一个@Validated注解(它是JSR303的一个变种,对JSR303进行一定程度的封装和扩展),@Validated和@Valid在进行基本验证功能上没有太多区别,但它们主要区别在于注解范围、分组校验、嵌套校验上有所不同

    • @Validatd注解范围可以用在类、方法和方法参数上,但是不能用在成员属性(字段)上,而@Valid可以用在类、方法、方法参数和成员属性(字段)上

    • @Validatd提供了分组校验功能,而@Valid未提供分组校验功能

    • @Validated和@Valid单独加在方法参数前,都不会自动对参数进行嵌套检验,因为@Valid可应用在成员属性上,通过添加该注解,配合@Validated或者@Valid来进行嵌套校验


    二、不同类型参数校验举例


    1. 简单参数校验

    1. 定义一个简单的入参类,作为用户注册时入参所绑定的bean对象

      @Data
      public class AdminVo {
      
          /**
           * id
           */
          private Long id;
      
          /**
           * 用户名
           */
          @NotBlank(message = "用户名不能为空")
          private String username;
      
          /**
           * 密码
           */
          @NotBlank(message = "密码不能为空")
          private String password;
      
          /**
           * 创建日期
           */
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")  //入参格式化
          private Date createTime;
      
          /**
           * 更新日期
           */
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          private Date updateTime;
      }
      
    2. 定义controller类,创建一个新增用户接口

      @RestController
      public class AdminController {
        @Autowired
        AdminService adminService;
              
        @PostMapping("/add")
        public Long add(@Validated @RequestBody AdminVo adminVo) {
            Admin admin = new Admin();
            BeanUtils.copyProperties(adminVo, admin);
            Date date = new Date();
            admin.setCreateTime(date);
            admin.setUpdateTime(date);
            adminService.save(admin);
            return admin.getId();
        }
      }
      
    3. 定义一个全局异常处理类,用来接收参数校验异常的结果

      @Slf4j
      @RestControllerAdvice
      public class GlobalExceptionHandler {
            
             @ExceptionHandler(value = Exception.class)
             public R<Object> handleBadRequest(Exception e) {
                    /*
                     * 参数校验异常
                     */
                  if (e instanceof BindException) {
                      BindingResult bindingResult = ((BindException) e).getBindingResult();
                        if (null != bindingResult && bindingResult.hasErrors()) {
                            //校验结果集
                            List<String> errorList = new ArrayList<>();
                            List<FieldError> errors = bindingResult.getFieldErrors();
                            if (CollectionUtil.isNotEmpty(errors)) {
                                errors.forEach(error->{
                                    errorList.add(error.getField()+" "+error.getDefaultMessage());
                                });
                            } else {
                                String msg = bindingResult.getAllErrors().get(0).getDefaultMessage();
                                errorList.add(msg);
                            }
                            return R.restResult(errorList, ApiErrorCode.FAILED);
                        }
                    }
            
                    /**
                     * 系统内部异常,打印异常栈
                     */
                    log.error("Error: handleBadRequest StackTrace : {}", e);
                    return R.failed(ApiErrorCode.FAILED);
                }
            }
      
    4. 启动服务,若请求内容均为空如{},返回如下结果

      {
        "code": -1,
        "data": [
           "username 用户名不能为空""password 密码不能为空"
        ],
        "msg": "操作失败"
      }
      


    2. 嵌套校验

    1. 当入参对象包含复杂属性时需要进行嵌套校验。如用户包含多个角色,不仅需要对用户基本信息校验,还要对所填角色进行校验,修改入参对象并添加角色列表,@Valid作用于成员属性roleVoList上

      @Data
      public class AdminVo {
      
         /**
          * id
          */
         private Long id;
      
         /**
          * 用户名
          */
         @NotBlank(message = "用户名不能为空")
         private String username;
      
         /**
          * 密码
          */
         @NotBlank(message = "密码不能为空")
         private String password;
      
         /**
          * 角色
          */
         @Valid
         private List<RoleVo> roleVoList;
      
         /**
          * 创建日期
          */
         @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")  //入参格式化
         private Date createTime;
      
         /**
          * 更新日期
          */
         @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
         private Date updateTime;
      }
      
      @Data
      public class RoleVo {
         /**
          *角色id
          */
         private Long id;
      
         /**
          *角色名称
          */
         @NotBlank(message = "角色名不能为空")
         private String RoleName;
      
      }
      
    2. 启动服务,请求内容中添加角色信息roleVoList为空

      {
         "username": "wsp",
         "password": "123456",
         "roleVoList":[{}]
      }
      
      
    3. 返回如下结果

      {
          "code": -1,
          "data": [
              "roleVoList[0].RoleName 角色名不能为空"
          ],
          "msg": "操作失败"
      }
      


    3. 分组校验

    1. 当多个接口的入参对象为同一个,但是校验规则不同,此时需要使用分组校验。对用户进行修改时,保证传入用户id不能为空,并指定分组类型为Update.class(不设置分组类型时,会给定一个默认分组类型为Default.class)

      @Data
      public class AdminVo {
      
          /**
           * id
           */
          @NotNull(groups = {Update.class})
          private Long id;
      
          /**
           * 用户名
           */
          @NotBlank(message = "用户名不能为空")
          private String username;
      
          /**
           * 密码
           */
          @NotBlank(message = "密码不能为空")
          private String password;
      
          /**
           * 角色
           */
          @Valid
          private List<RoleVo> roleVoList;
      
          /**
           * 创建日期
           */
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")  //入参格式化
          private Date createTime;
      
          /**
           * 更新日期
           */
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          private Date updateTime;
      }
      
    2. 创建一个修改用户接口,定义校验分组类型为Update.class,只会找入参对象中相同分组类型的属性进行校验,若需要对其它未设置分组的属性进行校验,因为默认的分组类型为Default.class,可以定义校验的分组类型为@Validated({Update.class,Default.class}

      @PostMapping("/update")
      public Long update(@Validated({Update.class}) @RequestBody AdminVo adminVo) {
              Admin admin = new Admin();
              BeanUtils.copyProperties(adminVo, admin);
              admin.setUpdateTime(new Date());
              adminService.updateById(admin);
              return admin.getId();
          }
      
    3. 启动服务,请求内容中不填用户id

      {
          "username":"wsp",
          "password":"123456",
          "roleVoList":[{"roleName":"管理员"}]
      }
      
    4. 返回结果如下

      {
          "code": -1,
          "data": [
              "id 不能为null"
          ],
          "msg": "操作失败"
      }
      


    4. 自定义校验

    1. 当基本注解无法满足实际需求时,需要自定义注解校验。如根据用户名批量删除用户,所提供@NotEmpty只能保证入参集合不能为空,无法保证集合中元素为空即用户名为空字符串的情况,首先定义一个自定义注解

      @Documented
      @Constraint(validatedBy = {NotEmptyValidatorForCollection.class})
      @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
      @Retention(RUNTIME)
      @Repeatable(List.class)
      public @interface NotEmptyForCharSequence {
      
      	String message() default "{javax.validation.constraints.NotEmpty.message}";
      
      	Class<?>[] groups() default { };
      
      	Class<? extends Payload>[] payload() default { };
      
      	/**
      	 * Defines several {@code @NotEmpty} constraints on the same element.
      	 *
      	 * @see NotEmptyForCharSequence
      	 */
      	@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
      	@Retention(RUNTIME)
      	@Documented
      	public @interface List {
      		NotEmptyForCharSequence[] value();
      	}
      }
      
    2. 具体的校验规则由NotEmptyValidatorForCollection.class实现

      public class NotEmptyValidatorForCollection implements ConstraintValidator<NotEmptyForCharSequence, Collection> {
         public NotEmptyValidatorForCollection() {
         }
      
         public boolean isValid(Collection collection, ConstraintValidatorContext constraintValidatorContext) {
             //集合不能为空,且集合中不能为空字符串
             if (collection == null || checkEmptyElement(collection)) {
                 return false;
             } else {
                 return collection.size() > 0;
             }
         }
      
         private boolean checkEmptyElement(Collection<String> collection) {
             for (String s : collection) {
                 if(StringUtils.isBlank(s)){
                     return true;
                 }
             }
             return false;
         }
      }
      
    3. 创建一个根据用户名批量删除用户的接口,并添加自定义注解@NotEmptyForCharSequence (为保证注解生效,此接口的Contoller类上需要添加@Validated注解)

      @RestController
      @Validated
      public class AdminController {
          @Autowired
          AdminService adminService;
          
          @DeleteMapping("/batchDelete")
          public boolean batchDeleteByUserName(@RequestBody @NotEmptyForCharSequence List<String> userNameList) throws Exception {
              return adminService.remove(Wrappers.<Admin>lambdaQuery().in(Admin::getUsername,userNameList));
          }
      
      }
      
    4. 在接口方法中指定具体校验注解,校验异常默认为ConstraintViolationException.class,需要在全局异常类中捕获该异常,在全局异常类GlobalExceptionHandler.class的handleBadRequest(Exception e)方法中添加如下方法

      if (e instanceof ConstraintViolationException){
                  //校验结果
                  List<String> errorList = new ArrayList<>();
                  Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) e).getConstraintViolations();
                  if (CollUtil.isNotEmpty(constraintViolations)){
                      constraintViolations.forEach(constraintViolation -> {
                          Path propertyPath = constraintViolation.getPropertyPath();
                          String message = constraintViolation.getMessage();
                          errorList.add(propertyPath.toString()+" "+message);
                      });
                  }
                  return R.restResult(errorList, ApiErrorCode.FAILED);
              }
      
    5. 启动服务,请求内容中部分用户为空字符串

      [
          "1"," "
      ]
      
    6. 返回结果如下

      {
          "code": -1,
          "data": [
              "batchDeleteByUserName.userNameList 不能为空"
          ],
          "msg": "操作失败"
      }
      


    5. 多属性交叉校验

    1. 以上都是单个属性校验,当入参对象中多个不同属性需要进行交叉校验,可以通过脚本执行器@ScriptAssert执行脚本或者自定义注解实现。如要查询指定时间范围内所创建用户列表时,要保证入参的开始时间小于等于结束时间,定义一个条件查询对象

      @Data
      @ScriptAssert(lang = "javascript", script = "com.wsp.validate.model.AdminCondition.checkTime(_this.startTime,_this.endTime)", message = "开始日期不能大于结束日期")
      public class AdminCondition {
          /**
           * 开始日期
           */
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          private Date startTime;
      
          /**
           * 结束日期
           */
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          private Date endTime;
      
          //脚本校验(为静态方法)
          public static boolean checkTime(String startTime,String endTime){
             if(startTime.compareTo(endTime) > 0){
                 return false;
             }
             return true;
          }
      }
      
    2. 创建一个根据用户注册日期查询用户列表的接口

      @PostMapping("/search")
      public List<Admin> searchByRangeOfDate(@Validated @RequestBody AdminCondition adminCondition){
          return adminService
                 .list(Wrappers<Admin>lambdaQuery().between(Admin::getCreateTime,
                  adminCondition.getStartTime(),adminCondition.getEndTime()));
      }
      
    3. 启动服务,请求内容中开始时间大于结束时间

      {
          "startTime": "2021-10-10 22:00:00",
          "endTime":"2021-10-10 21:37:00"
      }
      
    4. 返回结果如下

      {
          "code": -1,
          "data": [
              "开始日期不能大于结束日期"
          ],
          "msg": "操作失败"
      }
      
    5. 另外,还可以使用自定义注解方式来实现,首先自定义一个注解

      @Documented
      @Constraint(validatedBy = {CheckTimeIntervalValidator.class})
      @Target({TYPE, METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
      @Retention(RUNTIME)
      @Repeatable(CheckTimeInterval.List.class)
      public @interface CheckTimeInterval {
      
          String startTime() default "startTime"; //所需校验的属性为startTime
      
          String endTime() default "endTime"; //所需校验的属性为endTime
      
          String message() default "开始时间不能大于结束时间";
      
          Class<?>[] groups() default { };
      
          Class<? extends Payload>[] payload() default { };
      
          @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
          @Retention(RUNTIME)
          @Documented
          public @interface List {
              CheckTimeInterval[] value();
          }
      }
      
    6. 具体校验规则由CheckTimeIntervalValidator.class实现

      public class CheckTimeIntervalValidator implements ConstraintValidator<CheckTimeInterval, Object> {
      
          private String startTime;
      
          private String endTime;
      
          @Override
          public void initialize(CheckTimeInterval constraintAnnotation) {
               this.startTime = constraintAnnotation.startTime();
               this.endTime = constraintAnnotation.endTime();
          }
      
          @Override
          public boolean isValid(Object value, ConstraintValidatorContext context) {
              if(null == value){
                  return false;
              }
              //取对象属性值
              BeanWrapperImpl beanWrapper = new BeanWrapperImpl(value);
              Object start = beanWrapper.getPropertyValue(startTime);
              Object end = beanWrapper.getPropertyValue(endTime);
              if(((Date)start).compareTo((Date) end) > 0){
                  return false;
              }
              return true;
          }
      }
      
    7. 创建一个根据用户注册日期查询用户列表接口,并添加@CheckTimeInterval校验注解(为保证该注解生效,Controller类上需要添加@Validated)

      @PostMapping("/search")
      public List<Admin> searchByRangeOfDate(@CheckTimeInterval @RequestBody AdminCondition adminCondition){
              return adminService.list(Wrappers.<Admin>lambdaQuery().between(Admin::getCreateTime,
                      adminCondition.getStartTime(),adminCondition.getEndTime()));
          }
      
    8. 启动服务,若请求内容中开始时间大于结束时间

      {
          "startTime": "2021-10-10 23:00:00",
          "endTime":"2021-10-10 21:37:00"
      }
      
    9. 返回结果如下

      {
          "code": -1,
          "data": [
              "searchByRangeOfDate.adminCondition 开始时间不能大于结束时间"
          ],
          "msg": "操作失败"
      }
      
    展开全文
  • 查看mysql最大并大数量(线上最好连接数大于1000...Springboot-后端统一返回接口+统一异常处理+后端参数校验 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifac.
  • 后端参数校验工具类

    千次阅读 2019-01-11 17:10:37
    后端参数校验工具类 1、应用场景 1、Controller层判断传入参数是否符合规则,不符合规则直接返回错误状态码。 2、其他需要校验参数的地方。 2、工具类 public class CheckUtil { /** * 中文,字母,数字 _ - . ...
  • java后端参数校验--Hibernate validator

    千次阅读 2018-06-03 21:15:39
    后端开发过程中,对参数校验成为开发环境不可缺少的一个环节。一般情况下,我们会再前端和后端都对数据进行双重校验,以保证数据的准确性。如果参数较少,且规格简单的情况下,我们是可以还用简单的if判断即可...
  • 后端请求参数校验

    2021-03-03 08:42:06
    后端请求参数校验校验异常处理valid注解解释参考文章 校验异常处理 valid注解解释 空检查 @Null 验证对象是否为null @NotNull 验证对象是否不为null, 无法查检长度为0的字符串 @NotBlank 检查约束字符串是不是Null...
  • Java后端参数校验SpringBoot+Hibernate Validator,以及对结果的处理
  • java后端优雅进行参数校验

    千次阅读 2020-01-16 16:39:09
    java后端优雅进行参数校验 利用注解的方式进行验证前端传入参数: public class UavAddDto { // import javax.validation.constraints.*; @NotNull(message = "姓名不允许为null") @NotEmpty(message = "姓名...
  • SpringBoot项目配置文件pom.xml引入依赖 <!--web依赖包,web应用必备--> <dependency> <groupId>org.springframework.boot<...接口参数校验 public String getList(Term ter
  • 后端数据校验最详细Demo

    千次阅读 2019-01-08 16:43:36
    但是为了避免用户绕过浏览器,使用http工具直接向后端请求一些违法数据,服务端的数据校验也是必要的,可以防止脏数据落到数据库中等等。  一、数据校验的两种方式 1、基于注解形式的数据校验 JSR提供的校验...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,809
精华内容 16,323
关键字:

后端参数校验