服务端接口参数校验之一 —— 简单实用版

我们在开发接口时,请求方需要按照我们接口的要求传送参数,如果不符合要求,我们要第一时间告诉对方参数格式错误,这就是参数校验。
根据参数校验所在的位置,可以分为前端/客户端参数校验 和 服务端参数校验。

站在系统整体角度来看,一端实现即可保证错误参数无法直达系统内部。
但实际上,一来前端有可能使用某种方式被绕过,二来复杂的校验逻辑由服务端来做更合适。

如果调用是来自web端或者app端,倒是可以让前端或者web端进行校验,但是一旦有遗漏,服务端就会面临各种系统异常或者是脏数据落库;
而如果调用是来自其他服务,那么就没有谁可以帮助服务端来做接口参数校验了。
如此说来,服务端定的规则,无论如何还是自己校验比较保险。

我们分三篇来说参数校验,
一来文章短,利于阅读;二来,确实是各有所用,优劣不一而论。

本文说的参数校验方式,最适用于为其他服务提供的接口,也是最简单实用的方法。
这类接口的特点是,功能专一,不会太多,相互隔离。

比如下面这个接口,实现了折扣配置功能:

@PostMapping("/controller/configure")
    public ServiceResult configure(@RequestBody @Valid ConfigRequestDto reqDto) {
		Long configId = service.configure(reqDto);
    	return ServiceResult.success(String.valueOf(configId));        
    }

其中请求参数 ConfigRequestDto 包括如下属性:

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class ConfigRequestDto {
	//折扣金额
    @NotNull(message = "金额必填")
    @Pattern(regexp = "^[0-9]*$", message = "金额格式错误")
    @Range(min = 1, max = 100000, message = "金额格式错误")
    private String amount;

    //次数
    @NotNull(message = "次数必填")
    @Pattern(regexp = "^[0-9]*$", message = "次数格式错误")
    @Range(min = 0, max = 1000000000, message = "次数格式错误")
    private String limit;

	//开始时间
    @NotEmpty(message = "开始日期必填")
    @Pattern(regexp = "^[1-9]\\d{3}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$", message = "开始日期格式错误")
    private String startTime;       

    //说明
    @Length(max = 10, message = "说明长度错误")
    private String desc;

可以看到,入参中既包括数字格式,也包括日期格式,还有字符格式。
我们建议统一使用String格式来定义参数,这样便于使用正则,对于一些格式方面的异常更好捕获。
接口入参上标记 @Valid 来实现参数的校验,如此就完成了参数校验的功能。
(注:对于@valid 以及@validation的具体用法本文就不做具体说明了。)
值得一提的是,这种方式定义的参数类比较多,比如一个请求参往往会对应一个响应参数;如果是有增删改查参数,也会分别对应4个请求-响应DTO,初时比较麻烦,但是如果涉及到后续接口的修改或变动,倒是互不影响。

再举个分页查询的例子:
这是一个列表查询接口,但是对于请求参数做了一些限制,除了当前页和每页条数必填之外,查询条件中状态也是必填的,另外对于非必填字段也有格式限制。如下 QueryRequestDto所示:

@JsonIgnoreProperties(ignoreUnknown = true)
public class QueryRequestDto  {    
    @Length(max = 20, message = "标识超长")
    private String id;
    
    @NotNull(message = "名称必填")
    private String name;

这里定义了一个名为QueryPager的分页DTO,其中,使用泛型作为列表的实体类型。注意要在实体类型上标记@valid 来实现对列表实体类的校验。

public class QueryPager<T> implements Serializable {
    @Valid
    private T example;//查询条件
    private List<T> result;//返回列表结果
    private String total;//总页数
    
    @NotNull(message = "当前页必填")
    @Pattern(regexp = "^[0-9]*$", message = "当前页格式错误")
    @Range(min = 1, max = 100000, message = "当前页格式错误")
    private String page;//当前页
    
    @NotNull(message = "每页条数必填")
    @Pattern(regexp = "^[0-9]*$", message = "每页条数格式错误")
    @Range(min = 1, max = 100000, message = "每页记录数格式错误")
    private String pageCount;//每页条数

下方是接口:

@PostMapping("/controller/query")
    public ServiceResult query(@RequestBody @valid QueryPager<QueryRequestDto> queryDto){                 
            QueryPager<QueryResponseDto> rtnQuery  = service.queryList(queryDto);
            return ServiceResult.success(rtnQuery);        
    }

QueryResponseDto为分页查询的响应结果DTO

public class QueryResponseDto implements Serializable{
    private static final long serialVersionUID = 1L;
    private String    id;
    private Integer amount;
    private String limit;
    private String startTime;
    private String  desc;

综上所示,
① 将不同接口的入参、出参分别定义成一个个独立的DTO;
② 针对要校验的字段,使用valid/validation 以及正则来实现;
③ 可以使用泛型实现嵌套校验。
以上三点是本文想要说的第一种参数校验。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值