如何设计优雅的API接口
前后端分离开发模式下,后端工作的对外表现就是一系列的API接口。如何设计一个优雅的API接口,以满足规范性、安全性、稳定性、易排查等需求?
几年工作下来也有了一些心得,但考虑仍不够全面,网上学习了下整体总结如下。
有【定义符合Restful规范、参数校验、统一返回值、敏感数据加密或脱敏、幂等执行、分页限制条数、异步处理】、【IP白名单、限流、签名鉴权】、【记录请求日志、压测、接口文档】等
接口实现
1 接口定义:遵循Restful规范,包括URL命名、HTTP请求类型等。关于Restful规范,可参阅 https://www.cnblogs.com/z-sm/p/5253254.html。
2 参数校验:提前做参数校验以尽早终止无效请求到数据库,如非空校验、数值范围校验等。Java中借助hiberate的Validator框架。
3 统一返回值:定义统一的数据格式(code、msg、data),返回给调用者的数据都用该格式。包括正常返回和异常返回两情况,前者要么每个方法返回值声明为该格式要么全局拦截进行统一封装、后者通过异常全局拦截进行封装。从实现上来说,包括【API故障码定义、API异常定义、普通/分页数据格式定义】、【正常返回、异常返回】的全局拦截和数据格式封装。若异常不进行拦截封装则可能把数据库访问等错误信息直接抛给调用者显然这不安全。
接口安全性
4 数据传输的安全性:传递的敏感数据要加密或转码,如如银行卡号用base64转码;返回的展示数据做脱敏处理,如电话号码中间用星号。
接口执行的安全性:
5 幂等执行:因网络异常等原因调用方可能会用相同的参数进行重复调用,服务端不能产生重复数据、且每次调用返回的结果都一样。具体可参阅这篇文章。
防重设计 和 幂等设计 的区别:前者主要为了避免产生重复数据,对接口返回没有太多要求;而后者除了避免产生重复数据外还要求每次请求都返回一样的结果。
非幂等例子:【表单重复提交、接口超时重试、队列里重复消息】等导致重复创建数据。通常是【未指定id的insert操作 和 某些update操作比如值增1】会出现非幂等问题。
解决方案:
加锁:数据库唯一索引 或 联合唯一索引 来防止重复数据,重复时捕获DuplicatedKey异常直接返回操作成功;分布式锁来使短期内的并发请求串行,防止连续点击提交按钮导致重复创建客户。
加是否重的标记:
数据本身的标记——先select再insert。并发场景下仍会有重复数据,可通过全局锁来解决。
另外的数据标记——借助其他表或KV组件来标记操作是否执行过,以免重复执行。分布式事务TCC的confirm或cannel操作的幂等性设计通常借助表记录该操作是否执行过来实现幂等。
生成token:每次操作前都向服务端申请token,然后带着token请求操作,服务端校验token有效则执行后续接口逻辑并删掉token。解决表单重复提交常用此法。
接口调用的安全性
6 IP白名单:配置IP白名单来加强接口安全性,防止签名或加密被暴力尝试破解,例如在网关层校验请求IP或域名白名单是否在白名单内。
7 限流:接口自身加上限流配置,防止高并发场景下接口因请求量太大不可用。关于接口限流算法,见接口限流算法小记-MarchOn。
8 签名鉴权:开放给第三方使用的接口,要考虑接口签名方案。通常把当前时间戳也作为一个请求参数,然后用秘钥对所有请求参数计算签名值sign也作为一个参数传给后台,后台用秘钥计算sign比较是否一样。时间戳用于为请求设置有效期;秘钥的一种方案是事先商量一个privateKey,另一更常用的方案是第三方来申请ak/sk 且ak作为请求参数sk用于计算sign。
接口速度优化:
9 分页限制记录条数:以免数据量太大造成接口超时等问题。
10 异步处理:业务逻辑比较复杂的接口可用异步处理来提高响应速度,例如支付、转账。通常是借助消息队列,API接口把消息发队列后就返回、由专门的消费者去处理、前端之后轮询调其他接口看处理完毕否。
接口监控:
11 请求日志:记录下请求的url、parameter、header、method、响应时间等到日志文件,并收集到实时日志分析平台ELK(ElasticSearch, Logstash, Kibana),以方便问题分析和定位。
接口测试:
12 压测:测试阶段对接口进行压测得到各接口qps,可用jmeter、apache benc等工具。
接口对接:
13 接口文档:选用swagger等工具,以代码最小侵入代价来自动生成接口文档,提高对接沟通效率。
参考资料:https://mp.weixin.qq.com/s/1WSvwtIxGHfdg-qlvSo__w