系统间的接口交互本来是一个偏向开发领域的范畴,但却经常由产品经理负责。原因大概有开发无法提前参与到系统间需求对接过程中,或公司的职责安排本就如此等等原因。
此文是我站在一个产品经理的视角,阐述自己对系统间接口文档设计的见解。由于我只是对后端开发有所涉猎,并不具备丰富的开发领域的知识储备,可能会有事实性错误或理解偏差。
系统间的数据交互有很多种,但本文将局限于目前最常见的采用 HTTP 协议实现的接口。
我的定义
接口文档就是对如何使用 HTTP 协议传输信息的声明。这个声明构建了接口文档的核心元素,在此基础上再增加一些利于受众阅读、理解的辅助元素。
HTTP 协议结构
我并没有也不打算对 HTTP 协议本身做深入的研究,仅借助 Postman 这种可视化的 API 测试工具来找出接口实现必要的部分。需要注意的是,除了下述 Request Body 部分和业务逻辑高度耦合,产品经理可以主动负责以外,其他部分都应该以研发为准,如果有冲突,主动 step aside.

下面将根据图片中从左到右,从上到下的顺序描述。
请求方法 method
定义了每个资源路径支持的操作,例如 GET、POST、PUT、DELETE 等。目前来看遇到的场景基本上倾向于 RPC 风格,都使用 POST 方法,并不会按照 RESTful 风格进行各种抽象。
请求地址 URL
URL 实际上是由请求地址 + 请求参数两部分组成的。
请求地址
实际上这个小节是最令我难以下笔的地方,涉及到 URI、RESTful 风格、RPC 风格等诸多概念。又因为目前见到的不管是公司内部还是各大公司开放平台对如何实现都各不相同,导致我很难表达,甚至说可能还没有搞清楚。
首先我们向服务端请求,首先要确定服务端应用主机在网络上的地址。当使用 IP 地址时通常由 IP 地址和端口组成。这部分也常被称为 BaseURL,是伴随应用环境变化而变化的部分。
当应用 RESTful 风格(这种风格在表达利用 HTTP method 对服务端的特定资源进行操作)时,往往使用 URI 表达不同资源的路径,反映了业务层级和结构,使用 HTTP method 中的 POST、GET 等方法表达如何操作资源。由于 URI 表达的是服务端的一个资源,所以命名上往往是资源名词的复数形式,例如,/users 表示用户资源集合,使用 GET 方法获取。URI 上也会出现参数,例如 /users/{id} 则是表达请求特定 id 的用户。
当应用类似 RPC 风格(这种风格在表达调用服务端一个特定方法)时,以目前我常见到的两大类实现方式而言,要么在 URL 上直接表达一个远端方法,例如 /users/create,要么利用请求参数,使用一个参数表达具体方法名,例如 /prc?method=getUserById 。这种接口名(方法名)的命名要注意清晰而简洁,准确传达接口的用途。避免过于复杂或冗长的名字,使得开发者在一瞥之间能够理解接口的作用。考虑使用动词和名词: 使用动词描述操作,使用名词描述资源。例如,getUserInfo 表示获取用户信息,createOrder 表示创建订单。
注意要保持接口命名的一致性,使用相似的命名规范,以便开发者更容易理解整个 API 的设计风格。
还有一点需要注意的是有关版本信息:如果采用版本控制,将版本信息包含在接口名中,例如 /v1/getUserInfo。RPC风格也可以考虑使用请求参数控制版本。
为了避免可能的大小写问题,最好统一成小写(基本不会有人主动改为驼峰,却极易发生全都小写的情况)。
请求参数 Query Params
请求参数位于 URL 的查询字符串中,通常跟在问号后面,使用键值对的形式传递,多个参数之间使用 & 分隔,形如 ?param1=value1¶m2=value2。例如 /api/users?name=John&age=25。
在 GET方法中,通常用于过滤、分页、排序等场景。在用于 RPC 风格的接口统一使用 POST 方法时,常用来表达接口名、请求时间、身份等。
请求参数是公开可见的,因为它们出现在 URL 中,很显然要注意不应该将个人隐私信息等敏感类型的数据放在请求参数当中。
对于接口,应该明确描述请求体参数,包括参数名、类型、是否必须、用途要求等。
请求头 Headers
请求头一般情况下需要注意的是内容类型 Content-Type 的定义。当使用 JSON 作为请求体格式时,应该声明 application/json. 这个声明主要用于服务端正确的进行反序列化解析请求体。另外还应该注意由于我们使用汉字,需要声明字符集 charset=utf-8,以免出现乱码。
至于其他对 Headers 的使用场景,常见的是在此处放置签名,具体包含哪些内容与开发的实现方式有关,需要后续开发补充。
请求体 Request Body
如果请求的信息没有完全通过请求参数表达的话,那么就会用到请求体了。这个才是产品经理需要重点参与的与业务逻辑相关的部分。
请求体通常用于 POST、PUT、PATCH 等带有主体的请求方法,GET 方法是没有请求体的。
请求体中的数据通常不在 URL 中可见,因此更适合传递敏感信息。
请求体可以使用不同的格式,如 JSON、XML、表单(键值对)等。平时最常见的就是 JSON 格式了,本文也限于描述 JSON 格式的请求体。XML 限于历史原因积重难返的场景中还会继续使用,但全新的系统一般都默认采用更轻、更对人类阅读友好的 JSON 格式。
在继续之前,不得不先大概说下 JSON 格式。
- JSON 有两种基本结构:对象和数组。
- 对象:是由大括号{}括起来的键值对的集合。
- 键(Key):是由双引号括起的字符串;
- 值(Value):可以是数值、字符串、布尔值、对象、数组,其中字符串型的值需要双引号括起,数值和布尔型的值不需要引号。
- 数组:是由方括号 [] 括起的元素( members )的集合,之间用逗号隔开,元素可以是数值、字符串、布尔值、对象、数组。
- 对象可以在键值对的值部分包含数组,数组又可以包含对象。
这是一个综合例子:
{
"class": "3-1",
"headMaster": "Jack Jones",
"teachers": [
"Jack Jones",
"Jane Smith"
],
"students": [
{
"name": "Tom",
"age": 12,
"hobby": [
"piano",
"football"
]
},
{
"name": "Jerry",
"age": 12,
"hobby": [
"swimming"
]
}
],
"location": {
"District": "Ork Street",
"campus": "No.1",
"building": "Main1",-
"floor": 2
},
"studentsLimits": 20,
"isNew": true
}
上述例子对应的结构可视化之后就是这样:

对于产品经理而言,只能将 JSON 结构用于表达实体映射关系,设计出的结构通常是对象最少的一种结构。而研发往往还可能出于业务逻辑上的关联性、代码复用、数据处理的便利性、数据结构的清晰度等方面考量,会进行进一步对象的抽象。例如一张订单表,虽然收寄信息、打印信息、时间要求等都和订单本身是 1:1 的映射关系,但开发可能会将这三部分抽离出单独的 3 个对象处理。
字段名
- 清晰的字段名:
- 字段名应该尽量自解释,让开发者不需要额外的文档也能理解字段的含义、作用。 避免使用过于晦涩的命名,使得开发者需要查阅文档才能理解字段的用途。
- 避免使用缩写或过于简短的字段名,除非是广泛认可的缩写。
- 如果你不精通专业术语英文,不要直接随意采用机器翻译结果。请至少将多个同义词的释义例句看一遍,选择最接近的那个。有一点羞耻心,不要被笑话。
- 一致性:
- 保持字段命名的一致性,使用相似的命名规范,例如采用驼峰命名法或下划线命名法。 为什么程序员经常选择首字母小写的驼峰命名法?(最小成本分词) 目标是为了将连在一起的多个单词区分开来,所以首词并不需要额外区分,也就少按一次shift,效率更高。很显然下划线的方式要多多按的是个组合键,更不经济,也增加了长度。
- 在整个 API 中使用相同的字段名,以减少开发者的学习适应成本。
- 避免特殊字符:
- 避免在字段名中使用特殊字符,以确保兼容性和易读性。
- 在字段名中体现数据类型,例如:
- qty\cnt 表达 Number
- is/has 表达 Boolean
- date 表达 String 格式是个日期
- datetime 表达 String 格式是个日期时间
- 合理的字段长度:这里的字段长度是指 Key 自身命名长度,应该合理,既能够满足实际需求,又不至于过长导致难以阅读。如果遇到过长问题,可以采取一些缩写方案,注意不同单词有不同的适合方式,关键是可读、一致。
- 约定俗成
- 首尾各取1个字母,中间使用数字代替
- 取首或尾的关键部分
- 使用类似发音的数字代替音节
- 取每个音节的第一个字母,基本等于去掉元音
- ……
字段数据类型
JSON的数据类型:
- 字符串(string)
- 数字(number)
- 布尔值(boolean)
- 对象(object)
- 数组(array)
注意不要滥用 String. 这会造成必须额外转换、不能直接验证、浪费计算机资源等问题。而且这种特征会让接口文档的受众失去文档质量的信心,造成信任危机。
是否(选择性)必须
可以从以下几点考虑是否限制:
- 业务逻辑要求:理解业务需求,确定哪些字段对于系统的正常运作是必不可少的。有时候,某些字段的值对于后续的计算、判断或决策是必需的。
- 安全性要求:一些安全性考虑可能需要某些字段的值是必填的,以确保系统能够正常进行身份验证或授权等操作。
- 客户端需要:客户端后续还要继续用于查询依据。
- 数据完整性:考虑数据的完整性要求。某些数据在系统中必须是完整的,缺失某些字段可能会导致数据的不一致性。
- 数据关系和约束:考虑数据之间的关系和约束。如果某个字段与其他字段存在关联或依赖关系,确保这些字段都被正确填充,以维护数据的一致性。例如一个度量类字段必填,则其表达单位的字段也势必必填。
- 历史记录和审计:如果需要跟踪历史记录或进行审计,确保关键字段是必填的,以便准确记录系统操作的细节。
- 第三方集成:考虑与其他系统的集成,其他系统要求必填,则也要考虑必填。
注释
为每个字段提供清晰注释,解释字段的业务含义、取值范围和使用规则等。
-
业务含义无需赘述。
-
对于 Number 类型的字段,要特别注意数值本身是否孤立时无法表达完整的含义,例如货币、长度、重量、时间等。若无单位对应的 Key 的定义,则务必在备注中声明。这类字段还往往要注明精确度的要求。
-
指定字段长度和范围:
- 对于字符串类型的字段,可以指定其最大长度,以便客户端能够正确处理输入。
- 对于数值类型,可以定义其取值范围,以避免不合理的数值输入。
- 对于字段的长度实际上是由数据库采用的数据类型和长度限制的,往往对每一个字段定义长度是一个很无趣也大多数时候无用的过程。可以采取合理的业务推断判定其最大长度,数据库进行一定宽放。只对灵活无法预判的字段指明长度约束。
- 接口定义的数据长度将给应用开发语言选择合适的数据类型以及数据库字段数据类型和长度选择提供参考,以节省内存或硬盘开销,提高应用的性能或查询性能。
-
处理日期和时间: 如果接口涉及到日期和时间,明确约定使用的日期时间格式,以便各方正确解析。
-
处理枚举值: 如果某个字段只能取特定的几个值,可以使用枚举类型或字符串常量表示。这样有助于避免非法数值的输入。最好同时提供对应的描述字段,这样下游如果只是展示,便不需要维护数据字典,可以直接取用现成的描述。
-
是否有缺省值:注意区分“缺省”和“默认”。“缺省”是服务端对未赋值字段缺失时的处理方式,而“默认”更容易被理解为诱导客户端:嘿,如果你不知道这个字段什么意思,就这样赋值吧。基于他人是不可靠的假设,我更倾向于对必要的且最常规的场景中,己方设计缺省。
响应 Response
- 错误处理和状态码:
- 明确错误处理机制,包括可能的错误状态码、错误消息的格式等。
- 系统宕机 http 状态码自然会反映
- 系统正常,但对报文解析出错或内部执行过程中发生了不可预见的系统错误例如数据库事务执行失败等异常
- 系统正常,但业务验证报错
- 提供常见错误场景的示例,以便开发者了解如何处理异常情况。
- 明确错误处理机制,包括可能的错误状态码、错误消息的格式等。
其他非结构性的必要元素
认证鉴权等安全性问题
- 安全认证和权限:
- 描述接口所需的认证方式和权限,如何获取访问令牌等。
- 提供示例认证流程。
- 请求数据里是否有敏感信息需要加密。
- 是否采用 IP 白名单方式
- 是否使用 HTTPS 协议
- ……
其他元素
确定明确目标和受众,然后根据不同的受众需要组织接口文档的组成部分。
-
清晰的概览和方便的跳转:
- 使用包含文档内超链接的目录。
- 提供接口的概览,包括主要功能、使用场景和核心概念。
- 提供时序图。
- 设计入口文档页面,使用户可以迅速找到他们感兴趣的信息。
-
及时更新和维护
- 及时更新文档以反映实际接口的变化。
- 记录包含日期、人员、改动内容概述的更新日志,方便定位最新的更新部分
-
提供示例
- 提供测试环境和测试数据,帮助开发者更好地理解接口的使用方式。
- 提供具有详细注释的示例请求和响应,以便开发者更好地理解如何使用接口。
- 考虑提供不同情况下的示例,例如成功响应、错误响应等。
- 提供的示例应尽量真实以便开发人员能够理解字段的预期格式和内容。但注意敏感数据的脱敏。
-
反馈和支持:
- 提供反馈渠道,让用户能够报告文档错误或提出疑问、建议。
我所说的一切都只是我的观点。
我的观点都可能是错的。
访问量统计:0