老板:限你三天,搞个 API 接口

前言
实际工作中,我们经常需要和第三方平台打交道,可能要开放一个 API 给他人调用。 那么问题来了:怎样做出一个优雅的 API 接口? 协议
API 一般使用的是 Http 协议,也可以和他人约定一种调用协议,但通常使用 Http 即可。 请求方法
API 的请求方法不像平时使用 Get,通常还可以使用 Post。Get 的请求参数在 URL 或请求头部中,而 Post 的请求参数一般在请求正文中。 如果请求不带参数或参数不敏感,可以使用 Get;否则尽量使用 Post。 请求 URI
URI 一般遵循驼峰命名法,例如:/v1/app/getUniqueAddress。强烈建议将主要版本号添加在 URI 前端,例如 /v1,能在重大更新时保证接口不出错。 在 URI 命名中,尽量缩短其长度,避免意外错误。例如,App 相关的接口需要使用 app 而不是 application。 加密
双方可以约定一个加密算法和一个密钥,且加密密钥最好不要和其它密钥重复。 注意:Base64 是编码算法,不是加密算法,出了问题别来找我! 签名
在 API 调用中,为了防止数据被篡改或被恶意利用,需要对请求进行校验。 可以计算请求参数、时间戳、密钥的摘要,加在请求头的末尾;服务器收到后,将同样的数据计算摘要,然后比对这些摘要,如果不相同,直接在网关返回错误。 请求应当设置一个过期时间,例如 5 分钟或 10 分钟。 密钥可以让双方约定私钥,然后计算一个 UUID 作为 Key,私钥作为 Value 保存在服务器网关中,请求中带上这个 UUID,计算摘要时使用对应的私钥。服务器便可以通过 UUID 查到对应的私钥,然后进行比对。 也可以将加密密钥保存到对应 UUID 下。 IP 白名单
IP 查验也可以做在网关上,如果 IP 在白名单上即可调用,否则直接返回无权限。 限流
限流方式(包括但不限于)以下几种: 1. 限制同一 IP 对所有接口的调用频率,例如:同一 IP 每分钟对所有接口最多调用 3w 次。 2. 限制同一 IP 对单个接口的调用频率,例如:同一 IP 每分钟对单个接口最多调用 1w 次。 3. 限制同一个私钥对所有/单个接口的调用频率。 同样做在网关上,如果网关发现调用频率过高,则直接返回错误。 参数查验
在网关确认请求有效后,会递交处理程序进行处理。处理程序要做的第一件事情就是参数查验,避免不必要的损失。 例如:消费金额必须为非负数且仅允许两位小数;身份证号必须为 18 位整数且校验码准确(校验码算法在某度百科有);(大陆)电话号码必须为 11 位整数;邮政编码必须为 6 位整数;姓名必须在两位字符与六位字符之间;某字段长度不大于 128 位等。 否则想象一下,某人消费 -114 元,会是什么后果?数据库直接把余额减去 -114,也就是加上 114 元! 返回值
API 接口返回值首先要保证:状态代码的 Key 必须统一。 比方说,正常返回值为{"code": 0, "message": "……"},错误返回值就应该是{"code": XXX, "message": ……},Key 绝对不能更改。 最容易出现的问题就是:处理程序的错误和网关的错误不统一,导致调用方无法理解。 错误处理
如果处理中出现了 Exception,或者服务器内部错误,应该怎么返回? 如果直接把 Exception 转为 String,封装发送,那么会有很多敏感信息,不法分子就可以利用。 因此,我们可以直接返回 Internal Server Error,让调用方联系处理。 日志记录
收到请求之后,可以计算请求的摘要,然后建立新文件,记录该请求的日志。 例如:某请求摘要为 3c1b2cca587f,那么日志文件就可以建立在 logs/3c/3c1b2cca587f.log 中。 请求参数也可以带上一个 requestID,还能保证请求的幂等。 限制参数长度
为了保证接口的稳定性,可以限制请求中参数的长度,超过长度直接返回错误。此处不再详细说明。 异步处理
如果某接口的处理程序非常的复杂,那么就需要建立处理线程,然后进行异步处理,避免接口卡死。 压力测试
API 上线前,必须先进行压力测试,然后再确认限流的阈值。一般限流阈值要略小于最大可承受压力。 数据打码
没啥好说的,就是把敏感数据进行处理,例如电话号码 114*****514,身份证号 11451**********514,不法分子即使拿到了也 p 用没有。 处理流程总结
网关:收到请求 => 校验 IP => 确认流量(限流) => 签名校验 => 参数校验 => 解密请求参数 => 递交处理程序 处理程序:建立处理线程 => 按照处理流程进行处理 => 记录日志 => 将处理信息递交网关 网关:删除隐私信息 => 数据打码 => 生成响应 => 返回响应 接口文档
最后来到了最简单,但也最重要的一部分:接口文档。良好的文档能够减少很多沟通成本,建议使用 Markdown 编写文档。 文档需要包括: 请求 URI;
请求方法;
Http 版本;
请求头;
请求消息体;
返回格式;
加密规则;
签名规则;
IP 白名单规则;
参数长度限制;
联系方式;
协议。
文档示例
协议
1. 本 API 为非开源项目; 2. API 内所有文件归本 API 所有; 3. 使用本 API,必须在用户可视页面注明 API 赞助链接; 4. API 为非开放性质,必须进行申请才可调用; 5. 禁止在本协议上二次封装其它协议。 调用说明
普通用户不能调用,需要提供 IP 地址等待审核。过审后,会提供加密密钥、私钥、UUID。联系邮箱:someone@example.com。 基本信息
如无特殊说明,服务器使用 example.com,Http 端口 80,Https 端口 443。 摘要算法统一使用 MD5,时间戳使用 GMT 标准时间整形数值格式。 同一个 IP 请求频率不能超过每分钟 50000 次,请求参数长度不能超过 8192 字节。 Http 版本统一使用 HTTP/1.1,Http 头 userUuid 为提供的 UUID。 加密说明:Http 消息体使用 Aes 加密,密钥使用提供的加密密钥。 签名说明:1. 将请求报文的摘要、时间戳、提供的私钥拼接为字符串,其间使用空格分割;2. 计算该字符串的摘要,作为请求头 sign。 注册
请求方法:POST URI:/v1/user/signup 请求头 Content-Type:text/plain 请求消息体 Json 字段如下, email:string,注册用户的邮箱; username:string,注册用户的用户名; password:string,注册用户的密码; login:可选,boolean,是否立刻生成 Token,默认为 true。 响应如下, code:int,状态代码; message:string,错误说明; primaryToken:string,主令牌; accessToken:string,访问令牌。 登录
请求方法:POST URI:/v1/user/signin 请求头 Content-Type:text/plain 请求如下, …… 总结
做好一个 API 接口,一是数据安全,二是服务器安全,三是提供数据准确、高效,封装良好的接口为他人调用。 做事都要做到安全第一,在安全条件下增加用户体验。 这就是本期全部内容了,我们下期再见。 字数:3135