在线阅读地址:http://www.share345.com/books/design-app-backend/design-app-backend.html
github 地址: https://github.com/ALawating-Rex/design_app_server
1. 前言
文章是根据实际项目设计以及理想状态下的优化方案,分享的app 设计思路。文章有很多不成熟甚至不合理的地方欢迎指点。
希望通过阅读理解文章可以帮助你做好APP开发或者服务端接口开发之前的准备工作,文章会尽量总结遇到的坑以及设计过程中总结的问题。
2. 版本控制
服务端接口要做好版本控制,除非客户端只维护一个版本,那么意味着每次都要强制升级App,但是一般不会这么做。
版本控制的目录结构:
第二种添加了区分不同平台,默认都走Default 代码,这样分的更细但是也增加了维护成本。
相应的访问资源的路由也可以采用两种方式:
一种就是通过 app 传参 平台(os)和版本(version) 来执行不同代码;
另一种是访问资源 URL 使用类似方式 /v1/getCustomer/id/10 /v2/getCustomer/id/10。
不同的版本维护不同 key,用于版本认证的 user auth 数据表举例:
表:app_user_auth
user_id 为自增id字段
user_auth 和 secret_key 分别是 认证的用户名 和 key 用于完成之后通信的 加解密
os 为平台
created_at 创建时间
updated_at 更新时间
也可以采用 OAuth 2 协议进行授权认证。
3. 请求数据和响应数据
请求参数和响应的结果可以选择全部加密,也可以选择只针对敏感数据进行加密,加解密的key就是 上面的secret_key。 响应数据也可以是压缩过的,例如使用 gzip压缩。
无论服务端还是客户端,对于请求和响应都应当各自封装到一个公共函数中处理,方便之后对数据的处理。
3.1 请求数据规范
出于安全考虑等因素,请求的接口应当采用 https 传输数据,可以采用 restful 风格的 web service 接口规范(本文没有使用 restful 风格)。所有参数使用 post方式传给服务端,敏感数据需要进行加密处理,
例如用户的密码、手机号、地址等信息。
请求的固定参数包含:
os: 平台 (iOS/Android)
net_status: 网络环境(Wi-Fi/3G/2G)
version: 版本
token: 会话标识(没有初始值,此值在第一个接口确定下来)
3.2 响应数据规范
数据返回采用 json 格式
action: 请求的接口名称
status: 相应的状态码(自定义的错误状态码可以设置到600 以后 前面的用于 通用 http 返回的状态码 200 代表成功)
message: 状态描述 例如 请求成功
data: 完整的数据
敏感数据进行加密处理
4. 通用接口设计
除了第一个接口不需要判断 token参数的合法性,其它接口都需要判断,即 token存在且不过期。
4.1 请求的第一个接口——get_server()
get_server() 接口用于告诉客户端服务器的地址,以及完成请求的认证和建立。
接口调用优先级:启动的第一个接口,只有这个接口完成才能进行下面的接口
请求参数:(固定的参数忽略)
首先服务端和客户端协商好 user_auth 和 de_key 各自保存,就好比用户名和密码一样(可以每发一版app 协商一套 user_auth 和 de_key) 。请求get_server接口只需要传递 user_auth 即可,服务端根据 user_auth 确定请求的客户端的 de_key。未找到直接退出,结束通信。找到后将 token 和 使用de_key加密后的服务器地址返回给客户端。客户端保存了 de_key 自然能够正确解密,由此通信建立完成。之后服务端和客户端的相互认证都采用 token 来认证,token 被截取存在中间人攻击,所以建议采用 https 保障数据安全。token也应当设计过期时间,且token 和 用户id应当一一对应,保证客户只能在一处登录。
如下是会话列表数据表设计举例:
app_token
id 为自增主键
customer_id 为用户id
token 为会话id(不可重复)
user_id 是表 app_user 的user_id 用于找到加解密使用 de_key
成功调用 get_server() 接口,添加一行数据 完善 token, user_id, created_at, updated_at 之后每次调用其它接口都更新下 updated_at (判断过期与否是当前时间和 updated_at 做比较),直到用户登录完善 customer_id
这里存在几种情况:
1.用户之前从未登录过: 那么直接更新这一行数据 添上 customer_id 即可
2.用户之前登录过: 那么可以将token, user_id, updated_at 更新到原来那行数据,然后删除此条数据(当然也可以反过来,保留最新这条数据,删除以前的。但无论那种都要采用事务,保证完成)
4.2 发送设备标识接口——send_flag()
用于后续发送推送等需求。
接口调用优先级:系统启动时候调用即可
正常情况下,每个设备都能获取到对应的唯一标识,用于完成消息的推送,Android手机通常根据 MEI 来做标识,但是仍然存在获取不到的情况,所以此时服务端就要生成一个随机的且唯一的标识返回给客户端。客户端接收到参数保存下来即可。
如下是设备标识列表数据表设计举例:
id 为自增主键
flag 为设备的唯一标识
badge 角标,通常 iOS 设备会有角标
token 会话id
customer_id 用户id
os 设备平台 iOS 或者 Android
version app版本
成功调用 send_flag() 接口,判断flag 是否已经存在,不存在则添加,存在则更新数据。每发一条消息 badge 加1 之后还有设置角标的接口可以把角标清0等。
4.2 设置角标接口——set_badge()
这个接口只需要给 iOS系统调用,可以采用最简单的方式,即每次调用就将badge清0
接口调用优先级:需要清除角标的时候调用即可。
4.3 升级检测——check_version()
用于检测是否有新版本,并提示升级。
接口调用优先级:启动成功后调用,有些app在设置里有检测最新版本功能,在那里也可以调用此接口
5. 一般接口设计
一般的接口的实现逻辑大致为:先统一接收参数经统一函数处理(解密、validate判断) 后交给数据层验证 token 是否可用和是否过期,检测无误后检测登录,
根据接口的实际情况判断是否需要必须登录,做后续逻辑处理。在实际的业务逻辑层执行之前可以先判断缓存是否命中,命中结合动态数据后直接返回。未命中或不需要
缓存则执行业务逻辑代码,执行成功根据实际情况生成缓存并将数据交给数据响应函数统一返回。
6. 推送设计
iOS 有自己的推送服务 APNs 当然也可以和 Android 都采用同一个平台的推送服务, Jpush(极光推送)或者友盟推送服务等。
推送消息可以是推送活动信息,推送状态信息例如订单状态,物流信息等。关联的就需要设计推送给谁,推送时间,推送动作等。
推送消息列表数据表设计举例:
id 为自增主键
title 为消息标题
content 为消息内容
operation 为消息点击后触发的动作(和客户端协商如何处理动作)
image 为消息的图片
platform 为消息推送给的平台
version 为消息推送给的app版本
customer_id 为消息推送给的用户
flag 为消息推送给的设备标识
is_show 是否显示通过推送显示
created_time 为消息创建的时间
send_time 为消息发送时间(可以通过队列处理要发送的消息)
队列处理消息数据表中的内容,到发送时间的消息被推送出去,推送如果根据平台那么就结合表 app_equipment_flag 找到合适的平台推送推送消息给他们。同理version, customer_id, flag
operation需要服务端开发者和客户端开发者协商数据格式相应不同的操作例如:
scheme://jumpUrl/?url=xxx.com/activity.html 协商为点击跳转到 H5 页面。(这个规则可以同时适用于 H5 和 原生交互规则 )
这个消息表还可以用于显示在app 的消息功能列表中。