app:/app/refund/create
运营系统:/operation/refund/create
商家端:/merchant/refund/create
依次类推 xxx渠道:/xxx/controllerName/actionName
范例: https://api.weibo.com/2/friendships/friends.json
当前: http://192.168.0.130:16000/pipi-micro-server/pipi/friend/queryFriendData
命名不规范导致无法通过参数名了解参数含义。
错误范例:
{
"resultCode": "200",
"resultMsg": "查询用户基本信息成功",
"resultBody": {
"user": {
"userid": "e85f83b7e3cc40a585cce8831b9913e0",
"level": "0",
"birthday": null,
"sex": null,
"weight": null,
"height": null,
"telephone": "15855179814",
"createtime": "2020-07-13 06:19:36",
"petname": "路人丁",
"professional": "理发师",
"ppmoney": null,
"area": "341222",
"identity": "1",
"heartrate": null,
"powerrate": null,
"disease": null,
"emergencycontact": null,
"emergencycontactnumber": null
}
}
}
当前:
{
"resultCode": "100",
"resultMsg": "用户名或者密码错误,请重试!",
"resultBody": null
}
当前行为是只要是错误就会返回100,成功则返回200。具体什么错误用resultMsg返回。
客户端需要做国际化,服务端返回为中文字段,有可能遇到乱码问题,二是如果国外使用返回的也是中文字段。对应不同的错误应该定义一套完整的错误码对应,resultMsg只是辅助,客户端可以通过错误码知道是何种错误,再使用用户选择的语言展示出来。
范例:
正常响应示例
{
"status": 1,//状态值是1
"error_code": 0,//同时错误代码是0表示无错误
"message": "获取成功",//提示操作成功信息
"data": []//主体返回内容
}
错误响应示例
{
"status": 0,
"error": 20102,//2:业务级错误(1代码系统级错误)固定一个字符;01:指的是01这个业务比如保洁 固定两个字符;02具体错误信息固定两个字符,会整理成一个对照表格,前端需要翻译成友好的提示
"message": "用户id不能为空",//给前端程序员的不友好提示,指明错误的原因
"data": {} //data字段固定
}
Restful风格:RESTfu设计原则,它被RoyFelding提出(在他的”基于网络的软件架构”论文中第五章)。而REST的核心原则是将你的API拆分为逻辑上的资源。这些资源通过http被操作(GET,POST,PUT,DELETE)。但现在看,一般的操作只有两种:GET ,POST。
这个设计原则最简单的应用就是面向对象设计而不是页面来设计api。最开始的时候,app的一个页面需要什么数据,api就返回什么数据。结果随着app的UI不断改版,需要的数据不断变化,不停地修改api,最后当api的改动会影响以前的版本的时候,只能写一个新的api版本,最后弄得api中有很多version/2,version/3这样的标志,恶梦!
但根据object来设计,又有一个问题,一个大object可能包含很多小object,是一个api返回全部小object,还是分为多个api返回?根据业务和技术,带宽等仔细考虑吧。
目前我们的接口设计是根据业务来定制接口和返回,假设页面上只显示五个字段,那么后端就需要针对这个页面进行设计。
当然这这样的好处是显而易见的,在和客户端交互的过程中,传输的数据全是有用的数据,极大地节约了网络资源,而且只需请求一个接口,接口就返回了所有界面显示的数据,在弱网状态下,加快响应速度。
新浪微博的做法:打开个人中心,会分多次进行请求,’users/counts’批量获取用户的粉丝数、关注数、微博数,’users/domain_show’个性域名相关,’users/show’获取主要信息,这是比较极端的做法,仅供参考。
示例:
返回的数据结构如下
{
"brand_name": "奥迪",
"car_model": "SUV",
"emission_standard": "国五",
"car_owner": {
"name ": "张三 ",
"driving_years": "5年",
"id": "666"
}
}
其中车主是一个对象,车子是一个对象,两者是既有关系又相对独立。
总结建议是,新设计的接口,需要考虑到多端不同展示,尽可能的偏向于面向对象,少量特殊处理可以面向页面。当然,后端代码上,都是以对象的形式存在,逻辑必须清楚。
对于移动APP开发者来说, 网络流量和电池电量是不得不考虑的问题。不过,您也许会说,这些跟接口没啥关系吧,服务器端的接口还能管得了客户端的网络流量和电量?
对于网络情况,接口应该具备为不同的网络提供不同的内容的能力, 通常,移动端的上网方式无非是2G(GSM、GPRS、EDGE)、3G(CDMA、TDSCDMA、WCDMA)、4G/5G、WIFI。设想一下,如果用户在流量需要花钱的情况下,你的app给用户展示了视频、音频、大量的图片和大量无用数据(接口中存在大量跟app页面无关的数据,俗称大而全的接口)。而没有通知用户的情况下用户会怎么想,毕竟国内的流量费用还是很贵的,应在不同的环境下提供不同的策略。
对于电量,app的哪些方面会消耗电量?比如app有大量的计算、有很炫的视觉画面都会消耗电量, 另外,不断的移动网络链接也会消耗大量的电量,我们都知道移动网络是通过无线电波来通讯的,那么发射装置就需要消耗一定的电量来发射和接收无线信号。特别的是,频繁的链接会不断的切换网络设备与移动基站之间连接状态,这都会消耗一部分电量。
所以,对于接口而言,尽量用少的链接传输多的数据,和尽量少的数据接收交互。
问题:3G/4G/5G时代需不需要考虑用户的流量成本? 对App的追求和定位到底是什么,只是满足功能需求,还是提供优质服务体验?
相对于我目前而言第二种较为合理。提供一下例证,欢迎探讨。
接口不仅仅是提供数据和功能就完事了,更应该充分考虑移动端的特性,为移动端提供更加方便、快捷的接口。
比如,在移动端里,下拉刷新和上拉加载更多是很常见的功能,如果接口仍然按照传统的web思路,
只提供按页读取的话,就会造成移动端的额外的数据请求和计算。 这时,接口就应该针对这两种类型的操作提供额外的支持。
再比如,对于一个新闻阅读类的app来说,最新的新闻列表里的文章,特别是前几条,用户很容易点击进去看,而后面的老的文章列表,一来用户下滑加载好几页的情况较少,二来过时的新闻用户也很少点。如果,接口在返回新闻列表时,对于最新的列表,可以直接把文章的正文(或者部分正文,比如一屏的内容)信息一起传给客户端,
这样,用户在打开新闻详情页的时候,就不用再从服务器端获取了,自然可以做到秒开。
比如访问第一页时,接口可以返回文章内容,如下所示 ,
content=1表示加载文章内容
newslist?page=1&pagesize=20&content=1
其他页时
newslist?page=5&pagesize=20&content=0 ,
不用加载文章内容。
当然,客户端要跟接口做好配合,搭配好,才能最大化的提高性能。
比如,移动端都有左右滑动来看上一篇、下一篇文章或者图片的功能,
如果,当用户请求某篇文章的时候,服务器端顺便也把下一篇文章的内容返回回来了,
那么当用户看下一篇的时候,是不是就很快了呢。
当然这种preload的方案也不能滥用,如果预加载数据的命中率较低的话,也不行,白白浪费了很多的流量。
接口执行效率(接口访问速度)
APP有别于WEB服务,对服务器端要求是比较严格的,在移动端有限的带宽条件下,要求接口响应速度要快,对数据要求也比较严格,app需要什么数据就传什么数据,不可多传,过多的数据量影响处理速度,最重要的是影响传输效率和浪费用户流量。接口要规范,以面向对象的思想设计接口。
易用性
接口、参数命名准确:无论是接口还是参数,命名都应该有意义,让人一目了然。(接口推荐根据APP效果图栏目进行命名)
一个页面尽可能就用一个接口: 现在很多的APP页面都有广告、焦点图、文章列表等,对于这些不同格式的数据,不可能都分配一个接口,这样加大了APP请求接口数,影响响应速度。建议服务器端尽可能处理好数据后通过一个接口返回给APP客户端,或把相似的部分功能合并已达到想要的效果,尽量不要出现一个页面七八上十个接口的情况。
接口数据、状态:接口必须提供明确的数据状态信息,不管是成功的,还是失败的,都必须返回给APP客户端,系统级异常,业务级异常,参数及异常都应有所体现。否则,接口的协议失去了所有的意义
接口要有可扩展性:方便后期功能性调整,接口应具备可扩展性,但扩展性也不能盲目不切实际的幻想可能会出现不同的数据逻辑。
安全性
接口安全:目前一般都是在APP客户端和服务器通过约定的算法,对传递的参数值进行验证匹配。但是如果APP程序被反编译,这些约定的算法就会暴露,特别是在安卓APP中,有了算法,完全就可以通过验证模拟接口请求。或者引入jwt令牌(token)等解决方案。
加密规范:在传递用户名密码时,应采用规范的加密算法如MD5、RSA、DES,进行数据通信请求。
https协议 :对于敏感的api接口,使用https协议 ,也是一种不错的选择,这也是一个趋势。https是在http超文本传输协议加入SSL层,它在网络间通信是加密的,所以需要加密证书。https协议需要ca证书,一般需要相应的费用成本。
其中一个原则,一看api名字就知道这个api是干啥。但是有个问题就是当你要负责几十甚至上百个api,你就知道不能”望名知api”是个什么样的痛苦。
就拿一个接口来举例吧
‘/User/userRedDot/version/1’
这是我们在使用的一个接口,从接口名字来看,不难看出User这个是用户相关的一个功能,然后userRedDot小驼峰命名指的是用户小红点,然后接口的版本号是第一版。以上4部分构成了一个完整的接口命名。
接口文档中是会注明不同的接口该使用不同的传参方式
header参数部分:请求头部一般放入鉴权的相关参数,比如用户的token和签名,设备id,APP的标识,userAgent自定义等。
除去鉴权的参数,其他就是接口的入参传递(文档会注明传递方式):
一维参数 按照POST/GET按照普通的form-data和urlencode方式即可
多维参数 按照POST方式,并把body放入json的形式传递。
新增数据 POST
获取数据和修改数据用GET
接口不可能永远不变,它会随着需求的变化而做出相应的变动,这种变动也可以理解为兼容或者不兼容。大部分情况下直接在这个接口上叠加版本号,并兼容旧版本。App的新版本开发传参时则将传入新版本的version。
接口的变化一般会有几种:
数据的变化,比如增加了旧版本不支持的数据类型(兼容:新增版本号,接口增量更新)
参数的变化,比如新增了参数(兼容:新增版本号,接口增量更新)
接口的废弃,不再使用该接口了(不兼容:原接口指定版本废弃,后端逻辑处理;原接口所有版本废弃,如果是业务流程修改,则停用原接口,并新开接口)
如果整个接口系统的根基都发生变动的话,比如微博API,从OAuth1.0升级到OAuth2.0,整个API都进行了升级,就无法兼容,只能进行版本强制升级了。
有时候,一个接口的变动还会影响到其他接口,但做的时候不一定能发现。
服务端的程序在运行的时候,可能因为一个数据的转化或空指针异常什么的,都不能让程序奔溃,需要捕获异常并对异常进行处理,并返回明确的数据状态信息,不管是成功的,还是失败的,都必须要有数据返回给APP客户端,否则,接口的协议失去了所有的意义
app客户端的语言 java和object-c都是强类型语言,所以怎么处理空值显得特别重要,不合理的设计很容易造成app的闪退。
从后台的角度来说,api中返回的数据中,正确值和空值的类型必须一样,举例,用户名的字段是“realname”: “xxx”,如果用户名为空,则应该返回“realname”:”“。如果返回值是一个array,空数据则返回一个空array,如果返回值是一个对象,空数据则返回一个空对象,绝对禁止null值。
对于客户端,必须用个全局的函数来处理所有api的返回数据,需要有一个机制:对于某个客户端需要数据,如果api中缺失,客户端自动补上并给予默认值。
同时,在数据库设计的时候,一个合理的设计必须是所有字段都有默认值,不应该允许null值。null在大量的语言和数据库中,会带来无穷的问题。
如果服务端是php,还有一个问题,php中数组和字典都是array,但是可以用(object)[]返回对象,但在java和object-c中是不一样,这个问题一定要注意。
补充说明下json的六种数据类型数据类型和约定
Number:整数或浮点数
String:字符串
Boolean:true 或 false
Array:数组包含在方括号[]中
Object:对象包含在大括号{}中
Null:空类型
前后端需要对数据类型进行约定:
- 时间日期型数据:直接返回格式化后的时间字符串或者直接返回时间戳
- 数字类型和文本类型:统一使用字符串格式
- 布尔值类型:统一使用字符串’0’和’1’来表示假和真
- 不返回Null类型数据