移动端路由层设计

作者: Neo_joke | 来源:发表于2016-12-17 14:25 被阅读9127次

    什么是移动端路由层:

    路由层的概念在服务端是指url请求的分层解析,将一个请求分发到对应的应用处理程序。移动端的路由层指的是将诸如App内页面访问、H5与App访问的访问请求和App间的访问请求,进行分发处理的逻辑层。

    移动端路由层需要解决的问题:

    1. 对外部提供远程访问的功能,实现跨应用调用响应,包括H5应用调用、其他App应用调用、系统访问调用等
    2. 原生页面、模块、组件等定义,统称为资源(Resource),在跨应用调用和路由层在不同端实现的业务表现需要一致的前提下,需要对资源进行定义,在路由提供内部请求分发的时候则可以提供不依赖对外进行资源定义的功能
    3. 外部调用如何使用统一标示(Uniform)进行表示资源
    4. 如何在移动端统一定义访问请求的过程,从而达成移动端与web端的统一性
    5. 如何更好的兼容iOS、Android的系统访问机制、App链接协议、web端路由机制与前端开发规范等
    6. 如何兼容各平台(Android、iOS)App页面导航机制
    7. 如何解决安全访问问题
    8. 移动端在客户端进行动态配置

    移动端路由所应用的场景:

    • H5页面与App原生页面、模块与组件的交互
    • App与App之间的相互访问
    • App内部页面跳转、模块调度与组件加载等
    • 推送与通知系统解除硬编码的逻辑,动态访问原生资源,更好的支持通过通知和推送完成动态页面访问和逻辑执行
    • Extension等动态调用主App的资源
    • App实现更复杂的架构MVVM或者是VIPER架构,提供解除业务相互依赖的能力
    • 以组件化为目的的工程改造,隔离各个业务,以制作单独的组件

    对外如何定义资源

    在路由提供对外的资源请求转发的时候,因为要照顾到其他应用的请求表达方式,比如H5应用或者是其他App的应用的访问请求,定义单纯依赖业务的资源定义就显得有些必要了。
    举个例子,一个H5的商品详情页,被用户分享,当其他用户看到这个H5应用的页面的时候,点击,如果该用户装了有对应这个H5商品详情页的App的时候,应该跳转到该App的原生商品详情页,如果没有安装则加载这个H5页面,在这个过程中,H5的页面是通过URL进行标识的,那这个URL的标识也应该对照到App的原生页面,但是要只依赖业务标识而不能依赖App的代码实现,比如说iOS端的App的商品详情页叫做DetailViewController,那这个URL是不能包含这个名字的,Android端可能叫DetailActivity,如果不单纯依赖业务,那H5应用就要根据平台来重新发送不同的资源定义的URL,就造成了硬编码问题,H5应用要依赖App的实现逻辑,如果有一天,原生App的页面代码实现变成了GoodDetailViewController,所有依赖DetailViewController这个资源标示的H5应用都要进行更改,就会出现问题。所以路由层的设计应该具备根据业务定义来映射App内的资源定义。
    常常在设计路由层的时候,我们会更加关注通信行为的细节、如何改进特定通信机制的表现,常常忽略了一个事实,那就是改变应用程序的互动风格比改变协议对整体的表现有更大的影响。
    所谓资源,就是一个应用程序提供的不可分割的服务,从这个层面上看,App的资源即是一种实体的存在,可以进行获取和访问,必须进行良好的表示,在有些必要的情况下,必须是独一无二的识别符来表示一个应用程序所提供的服务是什么。表示资源我们更倾向于使用URI进行标示,因为移动端没有一个横跨iOS、Android、Web后端与H5应用的资源标示方式,而URI是web service模式的资源通用表示方式,包括后面将要提到的Android与iOS统一支持的universal link(通用链接)也是借用URI的概念,App路由层所涉及到的资源表示方法还是建议使用URI的标示方式,同时更应该借鉴RESTful风格来架构这一层,原因是App的页面、组件或者说一整套功能性的服务是非常复杂的,相比于H5有更加多与复杂的交互,相比于后端存在更加苛刻的网络环境与多设备多平台的技术考量,所以URI在标示横跨多平台多版本的资源的情况下,能够更好的表示某一个资源实体而不是资源的表现形式。
    在Android与iOS系统中,均支持URL Scheme,所以资源的标示通常会是这个样子:

    AppScheme://path
    //例如qq app:
    mqq:// 
    //支付宝:
    支付宝alipay:// 
    

    如果协议是Http或者是Https标示的是Web应用或者是H5应用,你的App也是一个与WebService相同级别的应用,那么URL的协议部分应该是App的唯一标示符,这个主机部分和路径部分则需要我们使用RESTful的风格进行重新设计。
    重点是如何标示资源,例如表示App中的登录服务,那可以表示为:

    AppScheme://host/login
    

    host为主机部分,在一般的WebService上,在业务表现形式上一般是比较大的业务条线的标示,比方说 https://news.sina.com.cn ,主机部分是news.sina.com.cn,则标示新浪新闻这条业务线,在App内你的业务条线也应该是清晰的,假如移动App的主UI框架是Tab分栏,那么每个Tab分栏就是你的业务条线的分割,这点跟WebService应用的导航栏类似,App的资源大多是页面或者是可交互的组件,与UI关系比较大,假如你的Tab有四个:分别叫首页、商品、发现、我的,那么我们可以这样定义:

    AppScheme://index/
    AppScheme://goods/
    AppScheme://discover/
    AppScheme://user/
    

    当然,也可以有额外的定义,比方说App有Api服务,Api提供实现一个纯数据同步的服务标示,那么这个URL可以设计为:

    AppScheme://api-asycn/collections?action='insert'&value='***'&&userUoken='*******'&&source="https//***.***.com/collection.html"
    

    由于RESTful风格强调URL的资源标示而不是行为表示,所以”AppScheme://api-asycn/collections” 是一个良好的资源标示,表示了一个收藏功能的实体,而”?”后面的GET方式的参数实际上是不得已为之,因为实际上没有Web的http request的实体,所以只能勉强借助GET参数来替代RESTful风格中强调的Accept和Content-Type字段来标示表现层的行为描述。
    当然action与value这样的描述可以根据业务划分,但是重点是要用参数表现形式。

    iOS与Android的系统访问机制、统一的链接协议

    苹果的URL Scheme由来已久: Apple URLScheme,Android平台同样也实现了该功能,使得App能够在沙盒机制的前提下,能够相互调用声明过的服务。由于URL Scheme天生没有返回的callBack机制,著名的App Drafts的作者联合Marco Arment、Justin Williams 等人开发了x-callback-URL来做出统一跳转的协议: x-callback-url,在此不过多表述。
    利用URL-Scheme的机制,可以定义如下的统一链接协议:

    1. 协议部分来标示App应用
    2. 主机Host部分用于标示业务线或者是应用提供的划分好的服务实体,比方说index、discover是业务条线,api-asycn是对外提供的api,pushService是App内部的推送服务等。
    3. 路径部分则可以是细分的页面、组件或者服务的标示
    4. 参数定义有一些是必要的,比如说action来标示动作,比方说可以使用get标示获取、insert增加,userToken表示安全的用户令牌,source表示来源,当然像是userToken与source这些都是路由层需要进行解析和验证的,而action则是业务相关的参数,这一点在路由曾设计的时候需要进行详细区分

    统一访问请求过程

    route流程图.png

    整个统一的访问请求过程如图,关于最后的response返回有一些说明:
    在WebService的工作栈中,http的request与response是有标准协议规范的,而App的路由层只是套用的URI的资源标示和RESTFul风格的交互,没有标准的request和response结构,这部分实现在App内部,response对外部调用系统而言关心的有三个重要元素,资源状态码、返回值与错误,在路由层在响应外部调用的时候需要返回这三种元素

    路由层逻辑结构

    App Route逻辑结构图.png

    路由层安全

    路由层的安全包含两个方面:

    1. 跨应用时,需要注意注入攻击,做到敏感参数加密防篡改,同时需要注意路由层应提供能够实现风控的机制
    2. 跨业务系统的时候,需要开启会话访问机制,通过令牌或者是session会话等来实现路由层身份认证

    路由层实现

    敬请期待下一篇文章:《一步步构建iOS路由》

    番外:App孤岛、API经济与App开放性讨论

    什么叫App孤岛

    移动操作系统中的App一般都采用沙盒机制来严格限制访问权限,App与App之间是不通的,用户往往会安装大量的App,比方说找吃饭的地方是大众点评,聊天是微信,地图是高德等等,那么我们想象一下没有URL Scheme的世界,你在大众点评上找到了一个好吃的地方,然后需要切换到高德去找找在哪,然后脑子记录下来地址然后在微信上发给你的朋友,这么一个过程中,众多App之间是不能传递信息和相互协作的,那一个个App就成了信息孤岛,给用户带来极大的不便,而实现了URL Scheme的App一般都是大厂,用户过亿,给上亿人带来了方便。

    打破App孤岛

    本质上URL Scheme是操作系统支持的,也就是说,打破App孤岛,必须过操作系统这一关,而无论是第三方开发者还是Apple与Google都在努力打破信息孤岛。
    Apple与Google分别在iOS9与Android M支持了universal link以打通H5应用和原生应用的屏障。
    Apple则在iOS操作系统中通过Spotlight应用内搜索、AppGroups、AppExtension、ShareExtension与SiriKit等打破原生应用之间的信息屏障。
    Google则通过PWA希望替代原生应用来实现大一统。
    第三方开发者们也积极推动着这一趋势。比如说:
    前面提到的著名的App Drafts的作者联合Marco Arment、Justin Williams 等人开发了x-callback-URL来做出统一跳转的协议: x-callback-url,希望大部分App开发者能够响应号召,更好的进行开发。
    国内的一些深度链接的开发者平台 DeepShare - Share your App with the world
    锤子手机开源的onestep等等。
    作为一名开发者,构建安全高效而开放的路由实际上不仅仅满足技术架构的需求更能为打破App孤岛,更好的发展移动端生态做出贡献。

    什么叫做API经济

    API经济是基于API所产生的经济活动的总和,在当今发展阶段主要包括API业务,以及通过API进行的业务功能、性能等方面的商业交易。API经济是当今各行业(零售、金融、物联网、医疗等)中驱动数字变革的主要力量。 ———百度百科

    为什么这里需要谈到API经济呢?我们都知道经济学的第一要务是效率优先原则,就像上面我们聊到的App孤岛,在日益便利的移动化时代,实际上降低了信息共享的效率,而增加了用户的操作成本,则会阻碍这个平台上用户的活跃度,那上层利用移动平台的可能性就会被限制。比如,二维码和NFC解决了pos终端、商家与支付App之间的信息共享问题,就导致了繁盛的线下支付经济,同样的道理,各系统之间无论是App、WebServices或者是其他应用能够开放API则会形成平台或者产业上的信息共享的规模效应,则会形成良性发展。
    作为App开发者,你需要实现路由这一层,才能够支持跨应用之间的调用,才能放开你想开发的API。
    如果一个App的后端Services能够和App一起开放API,那则更加具有优势。比方说微信,如果开放了收藏的WebService API接口,同时微信App也开放URLScheme的收藏接口,那么无论在浏览器、手机中都能无缝实现随时随地的收藏一切内容,极大的方便用户。

    App开放性讨论

    这个环节主要是讨论开放的时候要注意哪些:

    1. App类型(决定要不要开放)
    2. 路由安全(决定开放程度)
    3. 开放时机
      未完,希望大家多多评论,一起讨论。

    相关文章

      网友评论

      • 莫莫H:mark
      • Blutter:这个不错MDRouter https://github.com/Modool/MDRouter
        支持参数扩展,支持结果输出,支持异步处理,支持容错处理,支持scheme,host,port筛选过滤,支持适配器分组模式,支持目标解决方案
      • 轻诺:mark
      • 清水芦苇:您好,问一下,文章中的流程图和思维导图分别用什么制作的呢?
      • 我的月亮你的心:id handler = self[routeExpression]; 兄弟这句真的不懂
        我的月亮你的心:-(BOOL)handleRouteExpression:(NSString *)routeExpression withRequest:(WLRRouteRequest *)request error:(NSError *__autoreleasing *)error {
        id handler = self[routeExpression];
        if ([handler isKindOfClass:NSClassFromString(@"NSBlock")]) {
      • ccSundayChina:豁然开朗
      • 流浪_先生:mark,读了作者的文章,真的是旷若发蒙,发现了另外一座大山。之前看过关于这方面的文章,总感觉不能深得要旨,直到在这里看到了这篇文章,才明白Route的来历,机制和作用,跪谢👻
      • 学不会灬:我也对移动端的路由层很感兴趣,谢谢您的分享。
      • jiangamh:有没有好的例子啊
      • jiangamh:不错

      本文标题:移动端路由层设计

      本文链接:https://www.haomeiwen.com/subject/umwomttx.html