- 入参合法性校验
- 接口的版本控制
- 接口考虑幂等性
- 接口考虑防止重复请求
- 提高接口的响应时间
- 接口限流控制
- 黑白IP白名单
- 敏感数据脱敏
- 请求接口的先决条件-token
- 记录接口请求日志
- 调用第三方接口要考虑异常、超时、重试
- 统一响应数据格式
- 接口单一职责
- 接口文档的可读性
在我们日常开发中设计一个接口的步骤大致如下:
Controller层定义接口url、请求方式、入参、统一格式的出参。然后在Service层去定义接口和实现类,这样一个接口就算完成了。
当然我们还需要考虑很多东西,比如接口的规范性、安全性,可扩展性、稳定性等等。
这篇文就来整理下 设计一个良好的接口需要考虑哪些点呢?
1、入参合法性校验
入参的合法性校验是一个良好的接口必备的前提条件,通过入参的校验我们可以过滤掉许多无效的请求,提高系统的稳定性。
我们可以将入参合法性校验分为: 常规性校验 和 业务校验;
所谓的常规性校验包括:token校验、必填校验、长度校验、类型校验等等;
业务校验也就是特定业务场景下的校验:比如用户商品下单接口,那么下单金额一定要大于0;
有关参数校验之前写过一篇文章:Spring Boot 实现各种参数校验
2、接口的版本控制
我们在开发小程序或者app的时候,都需要对版本进行升级,而提前设计好版本,可以避免因为升级导致旧的服务无法正常工作,我们要保证在升级的时候,新旧版本的服务都能正常运转。
多版本控制现在比较常见的方式有:url标识版本
, header标识版本
, params标识版本
3种方式。
这里举url为例。接口定义:
http://localhost:8888/test/v{d}/hello,其中d表示版本号,如v1.0,v2.0
后台接口:
@RequestMapping("test/v{d}/hello")
public String hello(@PathVariable("id") Integer id){
//根据不同版本,执行不同逻辑
return "成功";
}
基于url标识版本不友好的点在于,对于前端工程师来说会比较麻烦,必须在每个请求的URL上标记一个版本号来使用对应的版本的API。
更友好的方式就是,前端只需要传一个当前APP的版本号,后端根据前端传过来的版本号,自动匹配到对应的版本API接口。
3、接口考虑幂等性
所谓幂等: 多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。
我们在开发中主要操作也就是CURD,其中读取
操作和删除
操作是天然幂等的,我们所关心的就是创建
操作、更新
操作。
创建操作
一定是非幂等的因为要涉及到新数据的产生,而更新操作
有可能幂等有可能非幂等,这个要看具体业务场景。
有关接口如何保证幂等之前有写过6种常见解决办法:
接口的幂等性如何设计?
4、接口考虑防止重复请求
防重和幂等的概念其实很像,上面保证幂等的解决方案同样适用于防止重复请求。
它们的区别在于:
防重
防重的目的是防止重复数据的产生,比如新增接口,用户快速点击两次,如果没做防重,就会产生重复数据。
幂等
比如请求多次,只有第一次请求才会做数据处理,后面的请求不会产生数据改变,例如退款接口,第一次退款成功后,后面的请求,不会再次退款成功。
5、提高接口的响应时间
1)、数据库查询尽量走索引,优化查询速度。
2)、是否考虑需要添加缓存:本地缓存、redis缓存、es存储等等。
3)、是否考虑走异步操作、多线程、mq消息队列等一些方式,提高接口的响应速度。
6、接口限流控制
限流是为了更好的维护系统稳定性。如果说我们把接口提供出来给第三方系统用,那么这个时候接口的限流是非常有必要的。
一方面,限流可以防止接口被刷,造成不必要的服务层压力,另一方面,是为了防止接口被滥用。
可以使用redis进行接口调用次数统计,ip+接口地址作为key,访问次数作为value,每次请求value+1,设置过期时长来限制接口的调用频率。
7、黑白IP白名单
ip白名单是指将接口的访问权限对部分ip进行开放。这样就能避免其他ip进行访问攻击。
ip黑名单是设置不能通过的ip,黑名单以外的ip都能通过。
显然白名单比黑名单限制的要更多一些。
设置ip白名单比较麻烦的一点就是当你的客户端进行迁移后,就需要重新联系服务提供者添加新的ip白名单。
设置ip白名单的方式很多,除了传统的防火墙之外,spring cloud alibaba提供的组件sentinel也支持白名单设置。
为了降低api的复杂度,推荐使用防火墙规则进行白名单设置。
8、敏感数据脱敏
在接口调用的过程中,可能会涉及一些敏感字段比如:身份证号
、银行卡号
、地址
、手机号
等等,这些数据通常需要做脱敏处理,比如手机号15912345678脱敏后显示159****5678。
9、请求接口的先决条件-token
它的大致流程是这样的,首先用户登陆成功后,生成一个token返回前端。同时后端将这个token作为key, value为用户信息存入redis缓存中。
那么以后访问其它接口的时候,在请求头带上这个token请求接口,后端通过拦截器拦截该接口,做校验看redis是否存在该key,如果不存在直接返回,用户未登陆!
当然有些接口想不登陆就可以访问比如注册接口,那将拦截器过滤这些接口就可以了。
10、记录接口请求日志
关键的接口一定要有日志,入参日志
、出参日志
、异常日志
,这样一旦出现问题,我们可以通过查看日志一下子定位问题所在。
当然如果觉得这样打印的日志太多了,我们可以设置日志级别,在特定的情况下才打印该种级别日志。
如果是我们把接口提供出来给第三方系统用,那么每一次请求过来,我们都要记录好请求入参
、出参
、包活它们给的唯一标识ID
.如果第三方反馈有问题,只要给到我们此次请求的唯一标识ID,我们通过这个ID,查询它这一次请求的完整链路,这样一来方便排查定位问题,二来如果不是我们接口的问题,日志也是证据,不用在扯皮了。
11、调用第三方接口要考虑异常、超时、重试
不论是我们将接口提供给第三方还是说我们请求第三方接口,我们所需考虑的都要多点,调第三方接口首先考虑就是超时、异常、重试。
超时
我们没法确定第三方接口多久返回,所以我们应该设置个超时时间,保证我们这边接口不被卡死。
异常
是接口都有可能出现异常,调第三方接口异常了,日志肯定要有,同时要考虑是重试还是告警处理。
重试
调第三方接口如果失败了,可能是网络的原因,需不需要重试?重试几次都是需要考虑的。
12、统一响应数据格式
在开发中,我们在定义接口时需要返回统一格式的响应体,响应数据会包含三个属性: 状态码
(code),信息描述
(message),响应数据
(data)。
客户端可以根据状态码可以快速接口请求是否成功,如果成功则开始处理数据,如果失败则直接抛出message信息。
13、接口单一职责
单一职责,接口功能明确单一,不是为了省事,各种融于;在明确接口职责的条件下,尽量做到接口单一,即一个接口只做一件事,而非两件以上。
很多非资深接口设计者,在设计接口时,总认为接口所做的事越多,越牛叉,这是非常严重的错误认识。
14、接口文档的可读性
无论我们的接口设计得多么规范,多么易懂,我们都需要提供一份完整的api文档,同时我们还需要提供一些请求范例,这样用户在使用的时候才会更加清楚,也能避免一些错误的请求。