API设计的几条原则
API 设计是微服务设计中非常重要的环节,代表服务之间交互的方式,会影响服务之间的集成。通常来说,一个好的 API 设计需要满足两个主要的目的。
API 本身的含义指应用程序接口,包括所依赖的库、平台、操作系统提供的能力都可以叫做 API。我们在讨论微服务场景下的 API 设计都是指 WEB API,一般的实现有 RESTful、RPC等。API 代表了一个微服务实例对外提供的能力,因此 API 的传输格式(XML、JSON)对我们在设计 API 时的影响并不大。
API 设计是微服务设计中非常重要的环节,代表服务之间交互的方式,会影响服务之间的集成。通常来说,一个好的 API 设计需要满足两个主要的目的:
平台独立性。任何客户端都能消费 API,而不需要关注系统内部实现。API 应该使用标准的协议和消息格式对外部提供服务。传输协议和传输格式不应该侵入到业务逻辑中,也就是系统应该具备随时支持不同传输协议和消息格式的能力。
系统可靠性。在 API 已经被发布和非 API 版本改变的情况下,API 应该对契约负责,不应该导致数据格式发生破坏性的修改。在 API 需要重大更新时,使用版本升级的方式修改,并对旧版本预留下线时间窗口。
使用成熟度合适的 RESTful API
因此在设计 RESTful 风格的 API 时候,需要参考 RESTful 成熟度模型。
避免简单封装
POST /orders/1/pay
这样的API。而非
PATCH /orders/1
,然后通过具体的字段更新订单。
订单状态
这个内部使用的字段。
关注点分离
/users/1/update
URI。
User
这个类:
{"username": "用户名",
"password": "密码"
}
password
是不必要的,但是在修改密码的操作中,一个
password
字段却不够用了,可能还需要
confirmPassword
。
{
"username": "用户名",
"password":"密码",
"confirmPassword":"重复密码"
}
// POST /users/{userId}/password
{
"password":"密码",
"confirmPassword":"重复密码"
}
// PATCH /users/{userId}
{
"username":"用户名",
"xxxx":"其他可更新的字段"
}
完全穷尽,彼此独立
PUT /orders/1/order-items/1
这样的接口去修改订单项,接口
PUT /orders/1
就不应该具备处理某一个
order-item
的能力。
版本化
-
URI 前缀 Header
-
Query
/v1/users/
表达获取 v1 版本下的用户列表。
/users/1/updateV2
。这样做的缺陷是版本信息侵入到业务逻辑中,对路由的统一管理带来不便。
合理命名
尽可能和领域名词保持一致,例如聚合根、实体、事件等
RESTful 设计的 URI 中使用名词复数
尽可能不要过度简写,例如将 user 简写成
usr
尽可能使用不需要编码的字符
/orders/1/order-items
安全
-
错误的调用方式 接口滥用
-
浏览器消费 API 时因安全漏洞导致的非法访问
API 设计评审清单
-
URI 命名是否通过聚合根和实体统一 -
URI 命名是否采用名词复数和连接线 -
URI 命名是否都是单词小写 -
URI 是否暴露了不必要的信息 ,例如 /cgi-bin
-
URI 规则是否统一 -
资源提供的能力是否彼此独立 -
URI 是否存在需要编码的字符 -
请求和返回的参数是否不多不少 -
资源的 ID 参数是否通过 PATH 参数传递 -
认证和授权信息是否暴露到 query 参数中 -
参数是否使用奇怪的缩写 -
参数和响应数据中的字段命名统一 -
是否存在无意义的对象包装 例如 {"data":{}'}
-
出错时是否破坏约定的数据结构 -
是否使用合适的状态码 -
是否使用合适的媒体类型 -
响应数据的单复是否和数据内容一致 -
响应头中是否有缓存信息 -
是否进行了版本管理 -
版本信息是否作为 URI 的前缀存在 -
是否提供 API 服务期限 -
是否提供了 API 返回所有 API 的索引 -
是否进行了认证和授权 -
是否采用 HTTPS -
是否检查了非法参数 -
是否增加安全性的头部 -
是否有限流策略 -
是否支持 CORS -
响应中的时间格式是否采用 ISO 8601
标准 -
是否存在越权访问