RAILS API TO BE PART OF RAILS 5

Rails API 项目整合工作已经完成,具体见https://github.com/rails/rails/pull/19832,下一步就等待 Rails 5.0 发布了!

关于 Rails API 项目

Rails API 项目是由一群 Rails 社区的知名技术大牛创建的:Yehuda Katz, José Valim, Carlos Antonio da Silva 和 Santiago Pastorino 等。该项目把 Rails 做了拆解,然后重新组合成了一个剪裁过的 Rails,这个剪裁过的 Rails 特别适用于 API-Only App 的场景,即 Rails 只用来提供 API 服务,以 JSON 或者 XML 作为主要的数据交换格式,API-Only App 的主要服务 API Client 或者各种重前端应用,比如各种基于 ReactJS / AngularJS / Backbone / Whatever 等 SPA 应用。

用 Rails API 的好处

一句话总结:Memory footprint 显著降低,性能有所提升。

Rails 社区流行一种观点,如果做 API-Only 服务的话,用 Rails 太重了,Sinatra 之类的轻量级框架更适合,性能更好。我部分认同这个观点,如果直接拿 Rails 来做 API-Only 服务的话,只要你对 Rails 框架非常熟悉并有能力做适当的裁剪,那么 Rails 也能大幅削减资源消耗,且性能跟 Sinatra 没什么差别。但是如果直接使用 Rails 默认的配置的话,的确是挺浪费资源的。关于性能并不在本文讨论范围内,API 服务的性能更多取决于服务模型,以及应用架构。剪裁过的 Rails 跟 Sinatra 或者其他轻量级框架比如 Grape 的性能表现其实差不多,如果追求更高的性能,建议考虑使用基于 Fiber 的 Goliath 或者基于 Eventmachine 的 Thin 并使用异步 IO 编程模型来提升 API 服务的性能,当然架构和应用层面进行优化也对性能有至关重要的影响。

所以 Rails API 项目相当于帮你把裁剪这件事给做了,且做的非常专业,彻底。包括去掉不必要的 middleware,去掉 cookie 支持,去掉视图,assets,剪裁 Controller 层,以及各种前端相关的特性等等,再此基础之上,还可以灵活的配置和扩展。更多相关的细节请参考项目介绍页面: https://github.com/rails-api/rails-api

关于合并到 Rails 5.0

DHH 在 RailsConf 2015 Open Keynote 上宣布 Rails API 项目将会成为 Rails 5.0 的一部分,但是这并不是第一次合并,早在 Rails 4.0 发布的时候就曾经这么干过一次了,但是却在发布之前又给 revert 撤销了,然后 Rails API 项目才以独立插件的方式出现在人们面前。这段历史可能鲜为人知,基于当时的环境,Rails Core Team 发布 Rails 4.0 的决策是大幅重构和砍掉过时(Deprecated)的 API,抽出所有可以被抽出去作为插件存在的功能,瘦身,灵活,更好的性能和安全性,同时引入更多对前端的支持和新特性等等。所以当时集成这么个一个东西并不符合 Rails 4.0 的精神。

这次合并其实算起来是第二次了,Rails API 项目经过两年多的发展也日趋成熟,且在 Rails 社区地位越来越重要,现如今前端领域的技术和社区有了长足的发展,对 Rails 作为纯 API 后端的需求越发强烈,所以 Rails Core Team 决定把 Rails API 项目吸收进来的确是一个很 make sense 的决策。

Rails 本身是一个 Full Stack 的框架,这意味着 Rails 自身提供了从前端到后端一套解决方案,整个解决方案集成得非常紧密。而 Rails API 的思路是先解构,然后削减掉所有跟前端相关的部分,且仅仅保留后端核心功能,且有一套自己的方法论和解决方案。所以集成过程中就涉及到要对 Rails 自身架构进行前后端分离,分离后的前端组件需要考虑如何协同工作,比如 Controller 层的 flash,assets,render, 以及 assets generation 等属于前端领域,而真正为后端服务的 Controller 其实非常轻薄。后端还有一些组件的选型问题也是社区讨论的焦点,而讨论并解决这些问题都是为了让用户能直接用到最佳预设的框架,而不需要把时间精力放在技术选型和配置上。

ActiveModel::Serializer 还是 JBuilder?

JBuilder 是 DHH 大神的作品,是目前 Rails 的默认组件,用于 API 请求时生成 JSON 数据的模版。优点是 DSL 声明语法灵活强大,对 russian-doll caching 支持良好。缺点是生成 JSON 的时候依赖 view render 机制,在视图环境之外,比如你需要在 API Client 端生成一段 JSON,这个时候 JBuilder 就排不上用场。

ActiveModel::Serializer 是 Rails API 项目默认的 JSON 生成方案,特点是引入了一套类似 ActiveRecord 的方式定义 Serializer model,然后通过 model 生成 JSON,整个过程非常的 Object-Oriented。

DHH 的态度非常明确,希望 JBuilder 作为默认视图层的 JSON Generator 能够继续保持下去,当使用 API 模式的时候,AMS 可以作为默认的 JSON Generator。

除了 JBuilder 和 AMS,社区流行的 JSON 模版生成工具还有 RABL,也是可选的组件之一。但是在 API 模式下,除了 Controller 层和 Model 层之外,几乎没有 View 层,所以不依赖 View Render 机制的 AMS 是最好的选择。AMS 在性能上比 JBuilder 要好一点,但是差距不大,不到一个数量级。

$ mkdir movies && mv jbuilder.json.jbuilder movies/
$ bundle exec ruby benchmark.rb 
Calculating -------------------------------------  
            jbuilder    24.000  i/100ms
     AM::Serializers    23.000  i/100ms
-------------------------------------------------
            jbuilder    240.349  (±10.4%) i/s -      1.200k
     AM::Serializers    285.601  (±14.0%) i/s -      1.403k

Comparison:  
     AM::Serializers:      285.6 i/s
            jbuilder:      240.3 i/s - 1.19x slower

AMS 还有个特点是倾向在生成 JSON 的时候遵循标准,当遵循标准的时候,你只需要声明需要暴露的 attributes 即可,而不是采取 Template Base 的生成方式,需要自己定义 JSON 文件的结构,以及格式。

结论:在 API 模式下, AMS 胜出。

关于 CSRF Protection 和 Session

如果 API-Only App 作为后端,如果仅仅服务于 API Client 的话,是不需要 CSRF Protection 的,但是如果是服务于 Single Page javascript Application 的话,那么 CSRF Protection 还是有意义的。不过 Rails API 的集成做的比较彻底,没有 CSRF Protection 且不支持 Session。

如何使用 Rails 5.0 的 API 模式

如果不出意外的话,Rails 5.0 发布后,创建新项目就可以用下面的命令

rails new myapp --api  

效果就像你在使用 Rails API 项目一样,生成一个裁剪过的 API-Only 的 Rails 项目,然后就可以愉快的开发了。如果不添加 --api 参数,那么生成的应用则仍然是标准的 Rails 项目。至于混合项目,即 Rails 应用即包含前端,又提供 API 功能的时候应该怎么搞?我现在也不太确定,估计 Rails 5.0 正式版发布时就应该有结论了吧。