怎么样优雅设计后端接口api
目录
前言
基本规范
通用参数
请求路径
响应数据
命名规范
字段类型
上传/下载
轻客户端
展示逻辑大于业务逻辑
字段验证
扩展性
文案和图片
数据列表化
安全性
兼容性
新增修改(开闭原则)
不同版本相同功能升级
性能优化
合并接口
字段精简
缓存
返回使用的字段
文件压缩传输
局部刷新
wifi与移动网络的区别对待
体验优化
前言
在一款产品的各个版本迭代中,开始敲代码的时候,首先是需求,交互,UI等评审准备阶段,然后首当其冲的是接口的设计与评审,最后再是开发阶段。一个前端功能的实现流程与逻辑,有时候只有客户端才清楚,从某种意义来说,客户端算是接口的需求方。所以在前期接口设计和评审时,客户端的开发人员应该参与,什么时机调什么接口?每个接口需要哪些字段?数据含义怎么给?只有这些都考虑清楚,且达成一致并产出接口文档后,才能保证前后端顺利进行,根据接口协议进行开发,才能尽量避免各种不确定因素对项目整体进度的影响。本文介绍了接口设计中常见的规范,帮助团队更清晰的了解到接口设计的注意事项和细则。
基本规范
通用参数
每个请求都要携带的参数,用于描述每个请求的基本信息,后端可以通过这些字段进行接口统计,或APP终端设备的统计,一般放到header或url参数中。
字段名称 | 说明 |
version | 客户端版本version,例:1.0.0 |
token | 登陆成功后,server返回的登陆令牌token |
os | 手机系统版本(Build.VERSION.RELEAS)例:4.4,4.5 |
from | 请求来源,例:android/ios/h5/pc/pad |
screen | 手机尺寸,例:1080*1920 |
model | 机型信息,例:Redmi Note 3 |
channel | 渠道信息,例:当前应用使用的代理机构 |
net | APP当前网络状态,例:wifi,mobile;部分接口可以根据用户当前的网络状态,下发不同数据策略,如:wifi则返回高清图,mobile情况则返回缩略图 |
appid | APP唯一标识,有的公司一套server服务多款APP时,需要区分开每个APP来源 |
请求路径
尽量保持良好的可读性,见名知意
行为 | method | Path |
查找 | GET | getXxx |
增加 | POST | addXxx/saveXxx |
修改 | POST | modifyXxx/updateXxx |
删除 | POST | delXxx |
示例:
行为 | method | Path |
获取用户信息 | GET | getUserInfo |
增加用户 | POST | addUser |
修改密码 | POST | modifyPwd |
删除联系人 | POST | delContact |
登陆 | GET | login |
发送短信验证码 | GET | sendSms |
支付 | POST | pay |
响应数据
字段名称 | 说明 |
code | 响应状态码,200:成功;非200:失败 |
msg | 请求失败时的message |
data | 数据实体 |
code=200时,msg=登陆成功/修改成功/提交成功;
code!=200时,msg=错误提示信息;比如login接口,"账号或密码错误","账号不存在"类似这些的业务提示文案放在msg字段。不过需要提醒后端同学,错误提示不能自己觉的什么合适就提示什么,需要按需求文档备注的,或和PM确认。
Ojbect的data类型数据:
示例
// json
{
"code":200,
"msg":"成功",
"data": {
"name":"张三",
"age":20
}
}
// Model.java
public class Model {
public String name;
public Integer age;
}
前端正常情况下在解析json的时候
1.先解析code和msg,判断code==200的情况下继续解析data。
2.将data下面的json串解析成当次请求需要的model数据结构。
Array的data类型数据:
示例:
// json
{
"code":200,
"msg":"成功",
"data": [
{
"name":"张三",
"age":20
},
{
"name":"李四",
"age":18
}
]
}
Array page的data类型数据,需要额外返回total字段,客户端需要通过total判断本地加载的list是否还有更多可以加载。
请求参数:
字段名称 | 说明 |
pageNum | 当前第几页,例:1,2,3 |
pageSize | 每页条数,例:10 |
响应数据:
// json
{
"code":200,
"msg":"成功",
"data": {
"list":["张三","李四"],
"total":10
}
}
// Model.java
public class Model {
public List<String> list;
public int total;
}
命名规范
统一命名:与后端约定好即可,推荐使用驼峰命名,不要同时存在驼峰"userName",下划线"phone_number"两种形式就可以了。
避免冗余字段:每次在新增接口字段时,注意是否已经存在同一个含义的字段,保持命名一致,不要同时存在"userName","username","uName"多种同义字段。
注释清晰(重要):每个接口/字段都需要有详细的描述信息,很多时候接口体现业务逻辑,是团队中很重要的文档沉淀。详细的接口文档,可以帮助新人快速熟悉业务,建议使用工具SwaggerUI(REST API Documentation Tool | Swagger UI )描述接口信息。
字段命名示例:
字段类型 | 字段名称 | 说明 |
Boolean | isVip | 是否是Vip用户,1:是,0:否 |
金额 | realPay | 订单实际付款金额,单位:元 |
时间 | payTime | 订单付款时间,单位:毫秒 |
日期 | payDate | 订单付款日期,格式"yyyy-MM-dd" |
字段类型
前端和后端的字段类型必须是一一匹配
字段类型 | 返回值 |
布尔类型Boolean | ture/false |
字符类型String | 例如:“hello world” 字符串 |
整型Integer | 例如:2 |
数字型BigDecimal | 例如:3.14 |
这里的数字型,在一些特定业务场景下可能会有单位,但是返回字段不应该带单位,如果前端需要明确单位,那么需要单独定义一个字段返回
示例:
{
"code":200,
"msg":"成功",
"data": {
"distance":3.14, //返回距离值
"unit":"km" //距离的单位
}
}
上传/下载
上传,下载文件文件时,除了file本身,还要携带该file的md5,在传输过程中可能丢失部分数据,导致文件损毁,所以需要通过md5值进行完整性校验。
上传成功后,正常情况后端只需要返回code表示成功/失败,在开发阶段,可以让后端将上传成功后的文件url返回,这样当我们调用完接口以后,就可以通过该url字段查看文件是否上传成功,存储的尺寸大小,模糊度等,不过上线后需要将这个不必要的字段去掉。
示例:
{
"code":200,
"msg":"成功",
"data": {
"url":"http://www.online.com/path/pic.jpg"
}
}
轻客户端
我们知道客户端任何的修改都是需要发版的,分为2种方式:
- 如果是H5或一般网页的修改则不需要用户手动更新升级app
- 如果是App原生页面的修改则需要用户手动更新升级app,这会影响用户的体验,所以在接口设计的时候,也需要适当考虑这点,将业务重心交由后端,客户端保持简单的展示逻辑。
展示逻辑大于业务逻辑
客户端应该主要关注展示的逻辑,业务逻辑主要由后端完成,客户端关注的是怎么展示数据和搜集数据,在展示逻辑的时候,在特定的业务常见下,可以由后端返回配置信息。
例如:客户端有个文本展示,后端只给个status字段
status=1时,展示文案1;
status=2时,展示文案2;
这样设计的缺点是,如果以后要修改status=3时,展示文案3,那么这个status判断逻辑写死在客户端,就没办法支持这种修改,且这种设计限定死了客户端只能展示2种文案。推荐方案是后端直接将需要展示的文案返回,这样不管是status的判断,还是文案的展示,后期都是可变的。
字段验证
前端在往后端发送数据时,需要做字段校验,这样做的目的可以降低后端服务器的压力,同时也可以提高用户体验。
一般分为以下几种校验:
- 特定号码校验(包括手机号,身份证,验证码)
- 文本字符类型校验(是否包含中文,特殊字符,英文等)
- 日期时间校验
- 其他校验(邮件,ip地址,整数浮点数等)
不依赖后端数据的校验,都需要前端校验输入的正确性,如果需要后端校验的字段,应在用户录入后失去焦点去查询后端数据,如果有错误提示错误的描述信息。
扩展性
文案和图片
对于界面上的文案,图片,特别是"xxx20分钟之内","xxx7天到期"这些带数字的文案,不可能永远不变的,即使和BA确认了打死不变,也最好通过常量配置接口进行下发(未下发时使用APP本地默认文案,下发时使用下发的文案),我们的原则是:变与不变我们都能支持。
数据列表化
数据列表化可以更灵活的适配后端数据的变化
用List(key, value)的数据格式定义返回数据,更通用的适配 不断变化数据的展示。
示例显示的界面如下:
返回的数据:
{
"name": "张三",
"sex": "男",
"age": "20岁",
"nickName": "小张"
}
按照上面的数据返回能正确显示到页面上,但是如果出现以下情况就需要修改代码:
新增一个 “个人爱好” 的展示。不仅前端需要修改新增 “个人爱好”的显示逻辑,后端也需要修改代码和接口。所以可以修改返回接口数据如下:
{
"userInfos":[
{
"key":"姓名",
"value":"张三"
},{
"key":"性别",
"value":"男"
},{
"key":"年龄",
"value":"20岁"
},{
"key":"昵称",
"value":"小张"
}]
}
这样的返回结果如果需要扩展,新增或删除一个条目,都可以通过后端控制。
不过采用这种形式,也需要考虑实际场景,对于变化不那么比较频繁,数据item较少,较固定的情况下其实没有必要设计的太灵活,只会增加开发成本。
安全性
1. 响应数据中包含用户隐私的字段数据,需要加*号。
如:手机号,身份证,用户邮箱,支付账号,邮寄地址等。
{
"phone":"150*****000",
"idCard":"3500**********0555",
"email":"40*****00@qq.com"
}
2. 请求参数中包含用户隐私的字段参数,如:登陆接口的密码字段,需要进行加密传输,避免被 代理捕捉请求后获取明文密码。
3. 客户端和服务器通过约定的算法,对传递的参数值进行签名匹配,防止参数在请求过程中被抓 取篡改。(如果接口提供给外部使用的,建议加上此安全校验)
兼容性
新增修改(开闭原则)
所谓开闭原则就是:对新增开发,对修改关闭
接口的升级尽量避免修改原有的字段返回值,对于已经存在的接口进行修改,需要考虑对当前版本的影响,尽量是新增字段,而不是去修改。
以下情况除外:
在特定场景下如果业务中字段增加值对原有功能影响不大,并且在充分测试的情况下可以考虑在原字段修改返回值。
综合评估变化带来的风险,选择最优的方式。
不同版本相同功能升级
不同 版本升级的时候,如果可能对上一个版本的功能影响比较大,应该考虑定义新的接口访问path,path中需要带有版本号,和上一个版本功能互不影响,独立运行。
例如:
APP1.0在使用接口A,如果此时在开发1.1的时候修改了接口A的逻辑,在1.1发版的时候线上就会出现2个版本的客户端访问同一个接口A,为了保证1.0客户端调用接口A不会出错,就需要通过version字段或path中的"v1/login","v2/login"进行区分,不同版本客户端访问同一接口时处理逻辑要各自独立。
性能优化
合并接口
为了减少客户端和服务器建立连接和断开连接消耗的时间,尽量避免频繁的间隔网络请求。1个页面最好只对应1个接口,对于在调用B接口前需要A接口的前置数据的情况,可以在调用A接口时直接返回B接口的数据,减少1次请求。
字段精简
定义字段名时,在保证良好可读性的前提下,尽量精简,减少流量的消耗
{
"orderDescription" >> "orderDesc"
"oldPassword" >> "oldPwd"
"longitude" >> "lng"
"latitude" >> "lat"
}
缓存
对于频繁调用,且数据不常变化的接口(config配置接口),可以在返回的数据中添加md5字段(用于校验除md5外其他数据是否变化),在下次请求的时候将这个md5作为参数传给后端,md5没有变化的情况下,不返回data,客户端可以直接使用上次请求缓存在本地的data。
返回使用的字段
每个版本的接口更新后,需要将无用字段进行清理。或者同个接口不同状态下需要返回的字段各不相同的时候,当次请求不需要的字段需要提醒后端不必下发,避免传输无用数据。
文件压缩传输
数据量比较大时,特别时文件,需要客户端和服务端在发送数据前压缩文件传输。
客户端:客户端上传的图片和文件需要压缩上传
服务的:服务端下方文件时也应压缩发送
局部刷新
一个页面,如果之前已经加载了20%的数据,那么就不需要每次都返回100%数据,只要返回剩余80%即可。
例:某产品列表页面,每个产品已经具有类似productId,productDesc等字段,那么点击进入产品详情的时候,productId,productDesc就可以从产品列表传递过来即可,详情页的请求只需要返回产品相关的剩余数据,客户端需要额外处理数据组装逻辑,将前一个页面传递过来的字段和详情页请求到的字段组装成显示完整的model数据。
wifi与移动网络的区别对待
WiFi连接下,网络传输的电量消耗要比移动网络少很多,应该尽量减少移动网络下的数据传输,多在WiFi环境下传输数据。例:日志上报,数据统计接口等,可以在移动网络的情况下请求频率降低,在wifi网络时上调请求频率,或将缓存的数据统一上报。
体验优化
设计接口时,不能只考虑减少流量消耗,性能优化等,特定场景下用户体验的优化才是最高优先级的。
通过预加载降低对网络的依赖。
例如:对于一些阅读类APP,前几页的文章,用户查看详情的概率较高,可以在返回文章列表的时候携带正文内容,则可以实现秒开详情,也可以判断网络状态,wifi场景下可以将详情数据都返回。