Anaconda Nginx环境搭建 xcode powershell debugging download electron gdb webkit ACE vue插件 python程序界面 移动端上传图片插件 js教程文档 oracle增加主键 js数组截取前5个 js控制台打印 python例子 python实例教程 python中import python平台 java编程基础 java的接口 java获取当前月 java正则表达式详解 java中文文档 java连接sql java获取文件 linux教学 图解深度学习 无限弹窗bat 魔兽改图工具 bz2解压命令 模拟按键 抖音代码 jarsigner 子节点 电脑待机费电吗 脚本大师 android下载文件
当前位置: 首页 > 学习教程  > 编程语言

集成验证框架实现统一参数校验

2020/11/24 10:54:21 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

前言 在我们实际项目开发过程中,避免不了的就是参数的校验,一般参数的校验,分为如下几种情况;1.前端直接验证;2. 在Controller层单独验证;3. 通过集成验证框架验证;显然3种里面,我们…

前言

在我们实际项目开发过程中,避免不了的就是参数的校验,一般参数的校验,分为如下几种情况;1.前端直接验证;2. 在Controller层单独验证;3. 通过集成验证框架验证;显然3种里面,我们一般建议1+3结合的方式进行参数的校验比较合理和安全。在本章我们将围绕HTTP请求中参数校验的应用

Controller的几种接收参数的方式

在介绍验证框架之前,我们来介绍下通过Controller接收参数的方式;现在大部分都是通过注解的方式进行参数的接收,主流的有如下几种

请求路径参数

  • @PathVariable 获取路径参数。即url/{id}这种形式。
  • @RequestParam 获取查询参数。即url?name=这种形式

代码形式如下:

GET http://localhost:8080/demo/SN20201011021211?name=hank

对应的接收代码如下:

@GetMapping("/demo/{sn}")
public void demo(@PathVariable(name = "sn") String sn, @RequestParam(name = "name") String name) {
    System.out.println("sn="+sn);
    System.out.println("name="+name);
}

Body参数

Body参数一般是POST请求,主要有两种方式

  • 以JSON格式接收可通过@RequestBody获取对应的参数
  • 以form表单形式提交的,暂无注解适配,可直接对象接收

JSON参数接收

例如:添加用户的接口,PostMan 请求信息如下

image.png

对应后端代码1,通过Map接收

@PostMapping(value = "/user/map")
public ResultVO createUser(@RequestBody Map<String,Object> user){
    String name=user.get("name").toString();
    return RV.success(user);
}

对应后代代码2 通过定义的对象接收

@PostMapping(value = "/user")
public ResultVO createUser2(@RequestBody User user){
    System.out.println("User Info:"+user.toString());
    return RV.success(user);
}

FORM 参数接收

form方式提交,在PostMan中对应参数如下

image.png

对应后端代码如下:

@PostMapping(value = "/user/form")
public ResultVO createUser3(User user){
    System.out.println("User Info:"+user.toString());
    return RV.success(user);
}

请求头和Cookie参数

  • @RequestHeader,是直接获取请求头HttpServletRequest对象里面Header中的参数
  • @CookieValue 可以直接获取HttpServletRequest对象里面Cookies中的参数

通过注解的方式获取,分别如下

@GetMapping("demo3")
public void demo3(@RequestHeader(name = "myHeader") String myHeader,
        @CookieValue(name = "myCookie") String myCookie) {
    System.out.println("myHeader=" + myHeader);
    System.out.println("myCookie=" + myCookie);
}

通过硬编码的获取方式为

@GetMapping("/demo3")
public void demo3(HttpServletRequest request) {
    System.out.println(request.getHeader("myHeader"));
    for (Cookie cookie : request.getCookies()) {
        if ("myCookie".equals(cookie.getName())) {
            System.out.println(cookie.getValue());
        }
    }
}

由上可以看出,通过注解的方式可以极大简化编码,使我们的代码变得美观大方,如果通过request对象直接获取,也是可以的,这个主要是看什么样的需求,一般情况下,通过注解的方式基本上能满足我们的绝大部分需求,所以在项目中我们比较推荐直接通过注解的方式获取参数。

文件上传

文件上传主要是基于@RequestParam 结合MultipartFile对象;如下示例;

这里需要注意,前端的传入需要用表单方式提交,并且在Head的Content-Type里面加入,如下信息

Content-Type: application/x-www-form-urlencoded

image.png

对应后端代码如下

@Value("${file.upload.url}")
private String filePath;

@RequestMapping("/upload")
public ResultVO httpUpload(@RequestParam("files") MultipartFile files[]){
    for(int i=0;i<files.length;i++){
        String fileName = files[i].getOriginalFilename();  // 文件名
        File dest = new File(filePath+'/'+fileName);
        if (!dest.getParentFile().exists()) {
            dest.getParentFile().mkdirs();
        }
        try {
            files[i].transferTo(dest);
        } catch (Exception e) {
            log.error("{}",e);
            return RV.result(ErrorCode.UPLOAD_FILE_ERROR,e);
        }
    }
    return RV.success(null);
}

参数校验

在Controller层的参数校验可以分为两种场景,单个参数校验和实体参数校验

单个参数校验

后端代码如下

@RestController
public class ValidateController {

    @GetMapping("/getUser")
    @Validated
    public ResultVO getUserStr(@NotNull(message = "name 不能为空") String name,
                               @Max(value = 99, message = "不能大于99岁") Integer age) {
        return RV.success("name: " + name + " ,age:" + age);
    }
}

在Postman请求如下,可以看到请求后,返回了系统异常,应该是被全局异常拦截了

image.png

我们再看后台打印的日志

image.png

如果我们系统中有很多地方都用到了参数校验,那么我们最好是能够在全局异常拦截处直接拦截ConstraintViolationException异常,并进行统一处理,可以在我们前面章节讲到的,在加了@RestControllerAdvice注解的GlobalException类中加入如下拦截的新方法;由于ConstraintViolationException继承了ValidationException异常类,所以我们可以直接拦截父类异常,直接进行多个不同的校验异常进行处理

@RestControllerAdvice
public class GlobalException {

    /**
     * 描述:表单异常拦截
     * @param [e]
     * @date 2020/11/22
     * @Author Hank
     **/
    @ExceptionHandler(value = ValidationException.class)
    public  ResultVO validationException(ValidationException e){
        Map map=new HashMap();
        if(e instanceof ConstraintViolationException){
            ConstraintViolationException exs = (ConstraintViolationException) e;

            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                //打印验证不通过的信息
                System.out.println(item.getMessage());
                map.put(item.getPropertyPath(),item.getMessage());
            }
        }
        return RV.result(ErrorCode.FORM_VALIDATION_ERROR,map) ;
    }
}

同样,再通过Postman请求后,得到如下结果

image.png

实体参数校验

一般我们在传入参数比较少的情况下,就直接用上面的方法进行了验证了,但是,如果我们传入的是一个对象,直接在方法上一个一个属性设置,就显得有点不美观了,因此我们一般都是封装一个接收参数的对象。如下面实例提供的,添加用户接口,我们对用户对象的变量设置了校验规则,如下

@Data
class Person {
    @NotNull(message = "名称不能为空")
    @Max(value = 30, message = "名称不能超过30个字符")
    String name;
    @Max(value = 200, message = "年龄不能超过200岁吧")
    @NotNull(message = "年龄不能为空")
    Integer age;
    @NotNull(message = "地址信息不能为空")
    String address;
}

在Controller类中我们增加添加用户的方法,这里我们需要注意,我们只需要在方法对应的参数Person对象前面加上@Valid注解即可

    /**
     * 描述:添加一个 Person
     * @param [person]
     * @date 2020/11/22
     * @Author Hank
     **/
    @PostMapping(value = "/person")
    public ResultVO<Person> addPerson(@RequestBody @Valid Person person) {
        //此处略过处理业务的代码...
        return RV.success(person);
    }

其实大家可能就会问,那要是参数校验不同过,这里应该是抛出什么异常呢,当我们执行如上代码,设定超出范围的参数后,在后端可以看到异常如下

image.png

可以看到,这里抛出的MethodArgumentNotValidException与上面的异常截然不同;相同道理,我依然在GlobalException类中加入对MethodArgumentNotValidException异常的全局拦截即可

    /**
     * 描述: 方法参数验证异常拦截
     * @param [e]
     * @date 2020/11/22
     * @Author Hank
     **/
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public  ResultVO methodArgumentNotValidException(MethodArgumentNotValidException e){
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        String[] errMessage=new String[errors.size()];
        for (int i = 0; i < errors.size(); i++) {
            ObjectError error=errors.get(i);
            errMessage[i]=error.getDefaultMessage();
            System.out.println(error.getCode()+":"+error.getDefaultMessage());
        }
        return RV.result(ErrorCode.METHOD_ARGS_VALID_ERROR,errMessage) ;
    }

Validated与Valid区别

  • @Validated: 用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证
  • @Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

如上Person对象,如果里面再有一个对象,比如还是Person,在提交的过程中,需要这种嵌套验证,就需要通过使用Valid的嵌套验证功能,即可将代码修改如下;

@Data
class Person {
    @NotNull(message = "名称不能为空")
    @Size(min = 2,max = 30, message = "名称长度限定在2-30之间的长度")
    String name;
    @Max(value = 200, message = "年龄不能超过200岁吧")
    @NotNull(message = "年龄不能为空")
    Integer age;
    @NotNull(message = "地址信息不能为空")
    String address;
    @Valid
    Person p;
}

常用参数校验的注解

这里我们主要介绍在springboot中的几种参数校验方式。常用的用于参数校验的注解如下:

  • @AssertFalse 所注解的元素必须是Boolean类型,且值为false
  • @AssertTrue 所注解的元素必须是Boolean类型,且值为true
  • @DecimalMax 所注解的元素必须是数字,且值小于等于给定的值
  • @DecimalMin 所注解的元素必须是数字,且值大于等于给定的值
  • @Digits 所注解的元素必须是数字,且值必须是指定的位数
  • @Future 所注解的元素必须是将来某个日期
  • @Max 所注解的元素必须是数字,且值小于等于给定的值
  • @Min 所注解的元素必须是数字,且值小于等于给定的值
  • @Range 所注解的元素需在指定范围区间内
  • @NotNull 所注解的元素值不能为null
  • @NotBlank 所注解的元素值有内容
  • @Null 所注解的元素值为null
  • @Past 所注解的元素必须是某个过去的日期
  • @PastOrPresent 所注解的元素必须是过去某个或现在日期
  • @Pattern 所注解的元素必须满足给定的正则表达式
  • @Size 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内
  • @Email 所注解的元素需满足Email格式

 

小结

本章主要围绕两方面内容作了详细的介绍

  • Controller的参数接收方式
    • 请求路径参数
    • Body参数(JSON\FORM\请求头和Cookie参数)
    • 文件上传
  • 参数的校验
    • 单个参数校验
    • 实体参数校验
    • 嵌套参数校验以及Validated与Valid的区别

以上我们分别用示例进行了详细阐述,希望对你后期的开发工作有所帮助;如果你在这方面有不同的实战经验,也欢迎在评论区进行留言。


想要了解更多信息,可关注本公众号(一起学开源);或请长按以下二维码添加助手。将拉你加入社区进行更多交流

在这里插入图片描述


本文链接: http://www.dtmao.cc/news_show_400388.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?