共计 6690 个字符,预计需要花费 17 分钟才能阅读完成。
接口多了, 对应函数名和参数就多了, 前端在请求 api 接口时难找. 容易出现重复的接口
RESTful: 翻译成中文: 资源状态转换.(表征性状态转移)
把服务端提供的所有的数据 / 文件都看成资源,那么通过 api 接口请求数据的操作,本质上来说就是对资源的操作了. 因此,Restful 中要求,我们把当前接口对外提供哪种资源进行操作,就把资源的名称写在 url 地址。
web 开发中操作资源,最常见的最通用的无非就是增删查改,所以 restful 要求在地址栏中声明要操作的资源是什么。然后通过 http 请求动词来说明对该资源进行哪一种操作。POST http://www.xxx.com/api/students/ 添加学生数据 GET http://www.xxx.com/api/students/ 获取所有学生 DELETE http://www.xxx.com/api/students/
也就是说,我们仅需要通过 url 地址上的资源名称结合 HTTP 请求动作,就可以说明当前 api 接口的功能是什么了。
Restful 是以资源为主的 api 接口规范,体现在地址上就是资源就是以名词表达。RPC 则以动作为主的 api 接口规范,体现在接口名称上往往附带操作数据的动作。
3. 为什么要编写接口文档
为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们都需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少客户端和服务端双方之间的合作成本。由于接口所包含的内容比较细,在项目中常常需要使用接口文档。研发人员可以根据接口文档进行开发、协作,测试人员可以根据接口文档进行测试,系统也需要参照接口文档进行维护等。
二、API 接口规范1. 协议
API 与客户端用户的通信协议,推荐使用 http 协议,同时兼容 HTTP,以确保交互数据的传输安全。
2. 域名
应该尽量将 API 部署在专用域名之下。http://api.xxx.com
如果确定 API 很简单,不会有进一步扩展,可以考虑放在主域名下。http://www.xxx.com/api/
3. 版本(Versioning)
推荐将 API 的版本号放入 URL。
http://api.xxx.com/app/v1.0/foohttp://api.xxx.com/app/v1.1/foohttp://api.xxx.com/app/v2.0/foo
另一种做法是,将版本号放在 HTTP 头信息中,但不如放入 URL 方便和直观。
版本号规范:1)采用多版本并存,增量发布的方式。2)版本号可以分为整型和浮点型整型:大功能版本,如 v1、v2、v3 … 浮点型:补充功能版本,如 v1.1、v1.2、v2.1、v2.2 …
关于版本兼容性,小版本变化向下兼容的,只要大版本不变化。3)对于一个 API 或服务,应在生产中最多保留 3 个最详细的版本
4. 路径(Endpoint)
路径又称 ” 终点 ”(endpoint),表示 API 的具体网址,每个网址代表一种资源(resource)
接口命名应该是一个动宾结构,由动词 名词组成,采取驼峰式命名规范,例如:
product/v1.0/getProducts 获取产品 order/v1.1/saveOrder 保存订单
接口命名常见通用动词可以参考如下:
动作 | 前缀 | 备注 |
获取 | get | get{XXX} |
获取 | get | get{XXX}List |
新增 | add | add{XXX} |
修改 | update | update{XXX} |
保存 | save | save{XXX} |
删除 | delete | delete{XXX} |
上传 | upload | upload{XXX} |
发送 | send | send{XXX} |
5. 基本规范5.1 请求参数
公共参数是每个接口都要携带的参数,描述每个接口的基本信息,用于统计或其他用途,放在 Header 或 url 参数中。
Queryurl? 后面的参数,存放请求接口的参数数据。
Header请求头,存放以下公共参数、APP 端公共参数等,也可以存放一些特殊加密字段。
BodyBody 体,存放请求接口的参数数据。
公共参数:
参数 | 说明 | 备注 |
app_id | 唯一标识用户 ID | app_id 是全局唯一的,每个 app_id 将对应一个客户 |
app_key | 加密 key | app_key 用于参数签名使用,可以理解为加密盐值,注意 app_key 保存到客户端, 不需要作为参数传递,需要做一些安全处理,防止泄露。 |
timestamp | 时间戳 | 时间戳,是客户端调用接口时对应的当前时间戳,时间戳用于防止 DoS 攻击。当黑客劫持了请求的 url 去 DoS 攻击,每次调用接口时接口都会判断服务器当前系统时间和接口中传的的 timestamp 的差值,如果这个差值超过某个设置的时间(假如 5 分钟),那么这个请求将被拦截掉,如果在设置的超时时间范围内,是不能阻止 DoS 攻击的。timestamp 机制只能减轻 DoS 攻击的时间,缩短攻击时间。如果黑客修改了时间戳的值可通过 sign 签名机制来处理。 |
request_id | 请求 ID | 用户请求 ID,是客户端随机生成的值, 要保证全局唯一,可以参考 snowflake 算法,作为参数传递过来,增加 request_id 的目的是一方面增加 sign 签名的多变性,另一方面主要用于防重放攻击,还可以作为全链路跟踪排查问题手段。 |
sign | 签名 | 一般用于参数签名,防止参数被非法篡改,最常见的是修改金额等重要敏感参数,sign 的值一般是将所有非空参数按照升续排序然后 token app_key timestamp request_id 拼接在一起,然后使用某种加密算法进行加密,作为接口中的一个参数 sign 来传递,也可以将 sign 放到请求头中。接口在网络传输过程中如果被黑客挟持,并修改其中的参数值,然后再继续调用接口,虽然参数的值被修改了,但是因为黑客不知道 sign 是如何计算出来的,不知道 sign 都有哪些值构成,不知道以怎样的顺序拼接在一起的,最重要的是不知道签名字符串中的 app_key 是什么,所以黑客可以篡改参数的值,但没法修改 sign 的值,当服务器调用接口前会按照 sign 的规则重新计算出 sign 的值然后和接口传递的 sign 参数的值做比较,如果相等表示参数值没有被篡改,如果不等,表示参数被非法篡改了,就不执行接口了。 |
token | 系统调用的唯一凭证 | 访问令牌 access token, 用于接口中, 用于标识接口调用者的身份、凭证,减少用户名和密码的传输次数。一般情况下客户端 (接口调用方) 需要先向服务器端申请一个接口调用的账号,服务器会给出一个 app_id 和一个 app_key, app_key 用于参数签名使用,注意 app_key 保存到客户端,需要做一些安全处理,防止泄露。 Token 的值一般是 UUID,服务端生成 Token 后需要将 token 做为 key,将一些和 token 关联的信息作为 value 保存到缓存服务器中(redis),当一个请求过来后,服务器就去缓存服务器中查询这个 Token 是否存在,存在则调用接口,不存在返回接口错误,一般通过拦截器或者过滤器来实现。 |
一般 token、timestamp、request_id 和 sign 四个参数会在接口中会同时作为参数传递,每个参数都有各自的用途,其中首次获取 token 需要 app_id、timestamp、request_id、sign,客户端获取 token 后不再需要传递 app_id。APP 端请求公共参数
APP 端请求参数除了上述公共参数外,还需要以下额外公共参数:
参数 | 说明 | 备注 |
network | 网络 | WIFI、4G |
operator | 运营商 | 中国联通 / 移动 |
platform | 平台 | iOS、Android |
system | 系统 | ios 13.3、android 9 |
device | 设备型号 | iPhone XR、小米 9 |
udid | 设备唯一标示 |
过滤参数:
若记录数量很多,服务器不可能返回全部记录给用户。API 应该提供分页参数及其它筛选参数,过滤返回结果。
参数示例如下 limit=10:指定返回记录的数量 offset=10:指定返回记录的开始位置。page=2&per_page=100:指定第几页,以及每页的记录数。sort_by=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
注意:
1)上传 / 下载上传 / 下载,参数增加文件 md5,用于完整性校验(传输过程可能丢失数据)。2)避免精度丢失缩小单位保存数据,如:钱以分为单位、距离以米为单位。
5.2 响应数据
为了方便给客户端响应,响应数据会包含三个属性,状态码(code), 信息描述(message), 响应数据(data)。客户端根据状态码及信息描述可快速知道接口,如果状态码返回成功,再开始处理数据。array 类型数据。通过 list 字段,保证 data 的 Object 结构。
返回示例:
|
分页类型数据。返回总条数,用于判断是否可以加载更多。返回示例:
|
响应状态码 code 统一使用英文组合字符串,多层分级使用“.”分隔,例如:PARAMETER.ILLEGALL
PARAMETER.ILLEGALL 代表参数错误,不推荐使用数字,数字错误码可读性太差。
注意:1)返回属性名命名时,建议使用驼峰命名,首字母小写。2)返回属性值为空时,严格按类型返回默认值。3)返回金额类型 / 时间日期类型的属性值,如果仅用来显示,建议后端返回可以显示的字符串。4)返回业务逻辑的状态码和对应的文案,建议后端两者都返回,中间添加“|”分隔,例如“SUCCESS| 成功”,SUCCESS 表示接口状态成功,显示给客户表示“成功”。5)调用方不需要的属性,不要返回。
5.3 使用 GET/POST 作为接口请求方式
一般调用接口最常用的两种方式就是 GET 和 POST。两者的区别也很明显,GET 请求会将参数暴露在浏览器 URL 中,而且对长度也有限制。为了更高的安全性,所有接口都采用 POST 方式请求。另外不推荐使用 rest 的 PUT 和 DELETE,因为很多浏览器不支持,很多框架也不支持。
我们这里用的的 GET 和 POST 同 RESTFul 中的 GET、POST 是不一样的。通常使用 GET、POST 的选择点在于,简单的用 GET、复杂对象用 POST,并没有动作的含义,例如我也可以使用 get 来执行添加的动作,如果参数很多,我也可以使用 POST 来执行查询操作;但在 REST 里,GET 对应的是查询一个资源,而 POST 对应的是新增一个资源,意义是决然不同的。理解这一点非常重要。
5.4 返回格式
返回响应数据采用 JSON,不推荐使用 XML,XML 是 W3C 为了替换 HTML 研发出来的,但是现在很明显失败了。默认情况下要支持 gzip
三、接口安全规范3.1 安全设计规范
获取 token 一般会涉及到几个参数 app_id,app_key,timestamp,request_id,sign。我们通过以上几个参数来获取调用系统的凭证。
token 作为系统调用的唯一凭证,token 可以设置一次有效,也可以设置时效性,这里推荐设置时效性。如果一次有效的话这个接口的请求频率可能会很高。token 推荐加到请求头上,这样可以跟业务参数完全区分开来。
这里面主要涉及到 sign 签名设计规范和 token 生成规范,需要遵守如上规范,能够保证 API 接口的安全性和幂等性。
3.2 客户端 IP 白名单
ip 白名单是指将接口的访问权限对部分 ip 进行开放。这样就能避免其他 ip 进行访问,设置 ip 白名单比较麻烦的一点就是当你的客户端进行迁移后,就需要重新联系服务提供者添加新的 ip 白名单。设置 ip 白名单的方式很多,除了传统的防火墙之外,spring cloud alibaba 提供的组件 sentinel 也支持白名单设置。为了降低 api 的复杂度,推荐使用防火墙规则进行白名单设置或者在 API 网关层面设置 IP 白名单,比如 shenyu 网关支持 IP 白名单设置。
3.3 单个接口针对 ip 限流
限流是为了更好的维护系统稳定性。使用 redis 进行接口调用次数统计,ip 接口地址作为 key,访问次数作为 value,每次请求 value 1,设置过期时长来限制接口的调用频率。不过这里还是推荐在网关层面进行设置,比如 shenyu 网关支持 IP 限流。
3.4 敏感数据加密与脱敏
参数安全:登录密码、支付密码,需加密后传输,建议使用非对称加密
响应结果:用户手机号、用户邮箱、身份证号、支付账号、邮寄地址等要进行脱敏,部分数据加 * 号处理。
在接口调用过程中数据通常需要脱敏安全处理,最常用的方式就是加密。加密方式使用安全性比较高的 RSA 非对称加密。非对称加密算法有两个密钥,这两个密钥完全不同但又完全匹配。只有使用匹配的一对公钥和私钥,才能完成对明文的加密和解密过程。
四、API 接口幂等性
幂等性是指任意多次请求的执行结果和一次请求的执行结果所产生的影响相同。说的直白一点就是查询操作无论查询多少次都不会影响数据本身,因此查询操作本身就是幂等的。但是新增操作,每执行一次数据库就会发生变化,所以它是非幂等的。
我们无法保证接口的每一次调用都是有返回结果的,要考虑到出现网络异常的情况。举个例子,订单创建时,我们需要去减库存,这时接口发生了超时,调用方进行了重试,这时是否会多扣一次库存?
对于一些重要的操作需要防止客户端重复提交的(如非幂等性重要操作),具体办法是当请求第一次提交时将 request_id 作为 key 保存到 redis,相应的返回结果集作为 value 存储到 redis,并设置超时时间。当同一个请求第二次访问时会先检测 redis 是否存在该 request_id,如果存在则证明重复提交了,接口直接返回不再继续调用了。
五、API 调用流程
1. 接口调用方 (客户端) 向接口提供方 (服务器) 申请接口调用账号,申请成功后,接口提供方会给接口调用方一个 app_id 和 app_key
2. 客户端携带参数 app_id、timestamp、request_id、sign 去调用服务器端的 API token,其中 sign= 加密(app_id timestamp request_id app_key)
3. 使用参数 app_id,timestamp,request_id,sign 来获取 token,token 作为系统调用的唯一凭证
4. 客户端拿着 token 去访问相应的接口
5. 如果 token 过期需要获取刷新 token
sign 的作用是防止参数被篡改,客户端调用服务端时需要传递 sign 参数,服务器响应客户端时也可以返回一个 sign 用于客户端校验返回的值是否被非法篡改了。
六、接口文档
1、尽量采用自动化接口文档,可以做到在线测试,同步更新, 推荐使用 swagger、yapi。2、应包含:接口 BASE 地址、接口版本、接口模块分类等。3、每个接口应包含:接口地址:不包含接口 BASE 地址。请求方式: GET、POST。请求参数:数据格式【默认 JSON、可选 form data】、数据类型、是否必填、中文描述。响应参数:类型、中文描述。
七、总结
关于限流设计、熔断设计、降级设计,目前主流网关都有相关功能(比如 shenyu 网关),可以不在 API 实现中开发这些功能。
另外推荐把 API 相关日志存储到日志平台,日志平台有利于故障定位和日志统计分析以及接口监控。日志平台的搭建可以使用的是 ELK 组件,使用 Logstash 进行收集日志文件,使用 Elasticsearch 引擎进行搜索分析,最终在 Kibana 平台展示出来。
原文地址: api 接口详解大全(看这篇就足以了)