服务端接口参数校验

在我们进行接口开发时,在对参数的接收,我们需要冗余复杂的校验规则 。那么如何优雅的实现参数的校验呢?

什么是不优雅的参数校验?

如果在控制层对前端传来的参数进行直接校验,那么避免不了许多的if-else的嵌套
如下(以添加用户的接口为例):

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("add")
    public ResponseEntity<String> add(User user) {
        if(user.getName()==null) {
            return ResponseResult.fail("user name should not be empty");
        } else if(user.getName().length()<5 || user.getName().length()>50){
            return ResponseResult.fail("user name length should between 5-50");
        }
        if(user.getAge()< 1 || user.getAge()> 150) {
            return ResponseResult.fail("invalid age");
        }
        // ...
        return ResponseEntity.ok("success");
    }
}

如何优雅的实现参数校验呢?

这里推荐一个好用的校验工具Spring Validation。
:::tips
Java API规范 (JSR303) 定义了Bean校验的标准validation-api,但没有提供实现。hibernate validation是对这个规范的实现,并增加了校验注解如@Email、@Length等。
:::

validator内置注解:

注解详细信息
@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(value)被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的 constraint:

注解详细信息
@Email被注释的元素必须是电子邮箱地址
@Length被注释的字符串的大小必须在指定的范围内
@NotEmpty被注释的字符串的必须非空
@Range被注释的元素必须在合适的范围内
@NotBlank验证字符串非null,且长度必须大于0

实现步骤:

1.添加依赖

<!--参数校验相关-->
<dependency>
  <groupId>javax.validation</groupId>
  <artifactId>validation-api</artifactId>
  <version>2.0.1.Final</version>
</dependency>

<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.0.12.Final</version>
</dependency>
<!--参数校验相关-->

2.接口参数校验

对于web服务来说,为防止非法参数对业务造成影响,在Controller层一定要做参数校验的!大部分情况下,请求参数分为如下两种形式:

  • POST、PUT请求,使用requestBody传递参数;
  • GET请求,使用requestParam/PathVariable传递参数。
2.1 requestBody参数校验

POSTPUT请求一般会使用requestBody传递参数,这种情况下,后端使用DTO对象进行接收。只要给 DTO 对象加上@Validated注解就能实现自动参数校验。
:::tips
DTO 表示数据传输对象(Data Transfer Object),用于服务器和客户端之间交互传输使用的。
:::

在DTO对象类中加入注解实现约束:
@Data
public class UserDTO  implements Serializable {

    private Long userId;

    /**
     * 用户名
     */
    @NotNull(message = "用户名不能为空")
    @Length(min = 2, max = 10,message = "用户名必须为2-10个字符")
    private String username;

    /**
     * 邮箱
     */
    @Email(message = "邮箱格式不正确")
    @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
    private String email;

    /**
     * 电话
     */
    @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
    @Pattern(regexp ="^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误")
    private String phone;

    /**
     * 密码
     */
    private String password;
}
在接口方法参数上声明校验注解:
 @PutMapping("/api/save")
    public Result add(@RequestBody @Validated UserDTO userDTO){
        if(userDTO != null){
            User user = new User();
            BeanUtils.copyProperties(userDTO,user);
            userSerive.insertUser(user);
            return Result.success("保存成功");
        }
        throw new GlobalException("用户新增失败");
    }
在全局异常处理类中加入参数异常的处理方法:
    /**
     * 控制器参数校验异常
     * @param ex
     * @return
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseBody
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        Map<String, Object> result = new HashMap<>();
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("参数校验异常  ");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(";");
        }
        String msg = sb.toString();
        return Result.error(ApiCodeConstant.PARAMETER_VALIDATION_ERROR,msg);
    }

    /**
     * Bean约束异常
     * @param ex
     * @return
     */
    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseBody
    public Result handleConstraintViolationException(ConstraintViolationException ex) {

        return Result.error(ApiCodeConstant.PARAMETER_VALIDATION_ERROR,ex.getMessage());
    }
测试结果:

image.png

image.png
image.png

2.2 requestParam/PathVariable参数校验
在控制器类上添加注解(@Validated):

image.png

@RequestParam: 通过键值对添加在路径中 通过这个注解可以映射至方法参数 (id=1)映射等号前面

:::tips

  • 用于获取请求中的查询参数。
  • 查询参数通常是以?key=value的形式出现在URL中,例如/api/user?id=1。
  • 在方法参数中使用@RequestParam注解可以获取特定名称的查询参数的值。
  • 使用该注解时,可以指定查询参数的名称和是否必需。如果不指定名称,则默认与方法参数名相同。
    :::
 @GetMapping("/api/get")
    public Result getUserById( @Length(min = 6, max = 20)  @NotNull @RequestParam("username") String username){
        return Result.success("请求成功");
    }

image.png

@PathVariable: 参数值直接作为路径的一部分 映射{ id} 的内容

:::tips

  • 用于获取请求中的路径参数。
  • 路径参数是将变量值直接包含在URL路径中的一部分,例如/api/user/{id}。
  • 在方法参数中使用@PathVariable注解可以获取URL路径中的具体变量值。
  • 使用该注解时,需要指定路径参数的名称,并且不能设置为可选。
    :::
    在定义 Restful 风格的接口时,通常会采用 PathVariable 指定关键业务参数,如下:
@GetMapping("/api/get/{username}")
    public Result getUserById( @Length(min = 5, max = 20)  @PathVariable("username") String username){
        return Result.success("请求成功");
}

image.png

  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值