如何设计一个良好的 API 接口?

image-20200410132349153

标准化

标准化的关键在于,尽可能少地创建自定义规范和机制,而是遵守业内通用的规范和标准。

一个非常好的案例是 RESTful API。

image-20200410125315723

如果存在多个资源组合的情况怎么办?

可以引入子资源的概念。

image-20200410125810259

例如,查看用户编码为 1001 的用户的权限信息。

[GET] /v1/users/1001/roles/101

image-20200410125923178

资源变化情况难以用标准 RESTful API 来命名怎么办?

可以用一些特殊的 actions 来命名,例如密码修改接口。

[PUT] /v1/users/1001/password/actions/modify

image-20200410130145437

异常响应格式

image-20200410130352294

兼容性

接口不能向下兼容,业务就会收到很大影响。

兼容性问题可能有:

  • 多端(安卓、IOS、PC、Pad 等)之间的接口兼容性,版本兼容性
  • 服务端不停机升级,由于 API 不兼容导致的服务故障
  • ...

引入版本概念就可以解决这些问题。

image-20200410130739556

抽象性

接口抽象都基于业务需求。

因此,我们一方面要定义出清晰的业务问题域模型(如数据模型、领域模型等),并建立起某个问题的现实映射,这样有利于拉起不同角色对于 API 设计认知上的统一。

另一方面,API 的抽象性,就可以很好地屏蔽具体的业务实现细节。为我们提供更好的可拓展性。

简单性

遵守“最少知识原则”。

即,客户端不需要知道太多服务的 API 接口和这些 API 接口的调用细节。

参考设计模式中的外观模式和中介者模式。

image-20200410131339888

高性能

上面说到的外观接口虽然保证了简单性,但是也有缺陷:

  1. 增加了服务端的业务复杂度。
  2. 多个服务之间的聚合,导致接口性能比较差。
  3. 入参字端的各种组合,可能会导致数据库的性能问题。

针对上面的第 3 点,举个例子,查询场景下,暴露了过多的字段给外部组合使用,但是数据库没有相应的缩印,就会发生全表扫描。

所以,我们可以只提供存在索引的字段组合,给外部调用。

Result<Void> agree(Long taskId, Long caseId, Configger configger)

上面要求用户必填taskIdcaseId来保证数据库合理使用索引。