直奔主题~
命名
看一份API文档的时候可能最先看的就是字段命名,常规的API文档会逐个解释一下每个字段的含义,但是最好不要用一些常用语言的关键字来命名一个对象的属性,比如int,id。id的话尽量加上一个描述,比如uid。一来避免API使用者需要重新进行映射关系调整。二来一个对象中可能会存在很多个id,避免混淆。当然这种设计本身不是什么严重问题,可以算在可优化之列。
类型
很多服务端Response一般采取JSON格式返回数据。常用JSON类型一般分为Number,String,Boolean,Array,Object这几种。一些服务端语言对类型并不敏感,尤其是脚本语言,内部本身会对类型做一些转化,比如js或者php。但是这并不代表这门语言没有类型的概念。JSON中也是一样,JSON本质上就是JS的对象。
下面是不同类型的对象在JSON里的一些表现
Number >>> {"value": 1}
String >>> {"value": "1"}
Boolean >>> {"value": true}
Array >>> {"value": []}
Object >>> {"value": {}}
因此,文档上如果区分好不同字段的类型对于使用者和开发者都更加有意义。比如status表示状态,一般支持枚举的语言都会把这种状态转换成枚举类型,所以经常我们会定义这种数据为Number型。再比如金额,数量,时间间隔这种可能会参与运算的字段,我们一般也会定义成Number型。一些API会把所有的非容器字段都定义成String类型来表示,从实现的角度来说也可以的。只是服务端要把一些Number类型的先转换成String(不管是隐式的还是显式的)。客户端也一样,在需要这些字段需要作为数字类型参与运算的时候也做一次转换。然而这一步操作从设计上来讲是可以避免的,减少了操作也意味着减少了问题出现的概率,何乐而不为。
另外,有的时候会出现这样的情况,文档上会写:
name | type |
---|---|
objectModelList | Array |
然后实际接口返回了
{
"objectModelList":null
}
or
{
"objectModelList":[]
}
明显后面的更准确Array就是Array,空的也应该是个Array
层级
举个例子,有个Area对象,下面对应这AreaCoordinate以及AreaID和AreaDescription几个属性。然后有个Person对象,对象下面有一些其它类型的属性和一个Area类型的属性。例如:
class Area {
var a_coordinate
var a_id
var a_description
}
class Person {
var a_coordinate
var a_id
var a_description
var p_name
var p_gender
...
}
why not
class Person {
var p_area
var p_name
var p_gender
...
}
无论服务端的json_encode还是客户端的json_decode对于后面这一种数据结构都能更容易的处理,而且整个的模型不会显得流水账,更有层次感。
data对象
{
"status": 200,
"data": {},
"reason": ""
}
这是一个基本的返回结构,很多App的API也是使用了类似的结构将数据返回给App。status一般用来表示这次请求的状态,在异常状态时reason会抛出异常的原因用来给予开发人员调试用,还有些异常需要直接抛给用户的可能还要再加一个tips的字段。data中返回正常逻辑的数据。那么对比一下下面两组数据结构
{
"status": 200,
"data": [
"177*****383",
"177*****452",
"177*****383"
],
"reason": ""
}
and
{
"status": 200,
"data": {
"mobileList": [
"177*****383",
"177*****452",
"177*****383"
],
},
"reason": ""
}
data被定义为返回的数据内容,其本身应该作为数据集合定义在了基本的数据接口中。作为客户端来讲非常希望data的数据类型能固定。原因之一,客户端和服务端可以将data本身作为通用逻辑处理而不需要考虑对不同的类型做出不同的操作。原因之二,如果使用自动化生成model类,后面一种json很容易推测出model的字段包括Model本身的名称从而完全准确的自动生成Model类。如果是前面一种结构,无从推断数组中的内容是什么,自动化也就无从谈起了。
避免冗余
同样的信息在一个API中不要通过两个渠道传递例如:
请求参数
name | type |
---|---|
uid | number |
token | string |
很多时候,客户端在与服务端交互的时候是用token,cookie或者SessionID来完成对客户端身份的验证的。服务端可以根据这些字段拿到对应的用户的所有信息,此时API要求用户传uid显然就没有什么必要了。相反,在两个字段中传入了相同的冗余数据这种操作至少增加了一种异常判断,就是这两个数据不匹配的话如何处理。没有必要的增加异常逻辑无论是服务端还是客户端都是非常不愿意看到的事。
版本
每个API都应该有对应的版本号,API在做修改的时候,无论是增删字段还是变更数据结构,都要在版本上体现出来。升级新版本的同时保持老版本的API存在一段时间是一个常用的做法。在APP发布时,这个版本的APP对应的API版本已经固定了。所以,不要在APP发布后修改API,但是可以修改内部实现,文档上的内容无论是增加还是删除,都可能对已经发布的APP造成影响。如果有需求,可以再增加一个新的版本的API。这样可以保证QA人员进行测试时候的环境和后面生产的环境一致,不至于因此出现老版本的APP异常的问题。另外,最好在定义API的时候加入当前API不再支持的异常状态,以便于在不支持老版本的时候,客户端可以给用户正确的提示。
安全性
目前市面上的很大一部分采用HTTP传递数据的APP是有安全风险的,详情参见API安全-破译HTTP传递的对称加密密文
以上都是一些比较常见的问题,还有更多的问题文章中没有提到,归根到底,API的设计还是大道至简的原则,异常逻辑越少越有利于整个项目的稳定运行。设计上的问题对于很多团队来说后续是很难再修改的,可能因为时间等一些原因,产品可以用就不再优化设计了。所以还是建议在最开始设计API的时候就对这些细节问题考虑进去,根基深才得以筑高楼。