ObjC/Swift 在 Swift 下实现了 Rails 风格的路由库

jasl · 2016年02月28日 · 最后由 angelfan 回复于 2016年08月30日 · 9720 次阅读

继续为 Swift 节点添砖加瓦...

项目地址:https://github.com/jasl/RouterX

最终下决定造这个轮子主要有几个动机:

  • 一篇来自天猫团队的分享 解耦神器 —— 统跳协议和 Rewrite 引擎
  • 之前帮公司的 iOS 团队设计过推送跳转指定到特定界面、从应用外部通过自定义 Scheme 跳转到应用内指定界面
  • 最近刚刚为网站添加 iOS Universal links 支持(大致原理是在网站根目录建立一个配置文件,访问指定域的所有 URL 都会自动跳转到应用,iOS 会将用户访问的 URL 传递给应用)。

我发现这几个场景的需求都是一致的并且和我们熟知的 Web 框架中的路由层相似。

利用双休日分析了下C#的ASP.net MVC、PHP 的 Laravel、Symphony,Go 的 HttpRouter 还有若干小型的 Web 框架和 iOS 的 Router 库,首先先给一个结论:

Rails 是 Web 开发最强框架绝对不是偶然的,Journey(tenderlove 为 Rails 设计的路由规则解析库)的原理是为路由设计了一门表达式语言(使用 RACC 定义),然后构造出 AST 进行匹配,没有任何一个语言的同类库把这个问题上升到如此高度去解决。

这并不是炫技,通过这个方式可以实现很多其他途径很难做到的功能并且保持在匹配时的高性能,比如:/articles/(/page/:page)(/sort/:sort) 这种双可选的情况,除了 Rails 没有任何一家可以支持。

另外,Journey 可以把定义好的路由导出成状态迁移图,进行可视化浏览,在 Rails Console 下输入 File.write 'fsm.html', app._routes.router.visualizer 然后去项目目录打开 fsm.html 即可 tenderlove 的示例

不过,Journey 的 Parser 因为是用 RACC 生成的,Swift 下没有类似工具,此外代码没有文档,写得也比较绕(我是没读懂...),所以 RouterX 是基于我的思路实现的,目前功能已经完成。

一个典型的复杂路由:/articles(/page/:page(/per_page/:per_page))(/sort/:sort)(.:format) 可以正确的生成 AST 匹配如下模式

/articles
/articles/sort/:sort
/articles/sort/:sort.:format
/articles/page/:page
/articles/page/:page/sort/:sort
/articles/page/:page/sort/:sort.:format
/articles/page/:page/per_page/:per_page
/articles/page/:page/per_page/:per_page/sort/:sort
/articles/page/:page/per_page/:per_page/sort/:sort.:format
/articles/page/:page/per_page/:per_page.:format
/articles/page/:page.:format
/articles.:format

也可以正确的进行匹配,主要代码都已经用单元测试覆盖好。

我没学过编译原理,之前有请教过 @luikore 大神一些问题,消化一下看看如何重构让代码质量和性能更好。

PS:对 Rails 的路由原理感兴趣的同学可以读一下 Journey into Rails Routing -- an under the hood look at how routing works Rex, Rexical and Rails routing 这两篇文章(这个 Blog 的文章都挺不错的),不过作者后来也是坑掉了...

Go 的 HttpRouter 是利用 Trie 数据结构实现的,在 README 里有讲原理,比较有意思。

学习了!

jasl 尝试理解 ActionDispatch::Routing::RouteSet 提及了此话题。 08月29日 16:10

初次看Journey真的有种望而却步的赶脚 不知道大大是如何看源码的 有什么技巧可以分享嘛

#3 楼 @angelfan 好帖子,要看懂 Journey 需要一些编译原理知识,最基本的需要指定,语法分析,语义分析,LR, AST,等语言设计的前端概念. 在工具上需要会使用 Yacc, Flex.

#3 楼 @angelfan Journey 的代码可读性比较差,而且 parser 是通过 RACC 生成的,就没有阅读意义了

路由匹配这块我建议学习 https://github.com/sinatra/mustermann

需要 登录 后方可回复, 如果你还没有账号请 注册新账号