Gem 一个基于 Rails 的 RESTful API 框架: Lina

lyfi2003 · 发布于 2015年02月27日 · 最后由 vbgfnd 回复于 2015年04月24日 · 9761 次阅读
121
本帖已被设为精华帖!

在最近这段日子( 2015 春节 ), 写了一个新的 API 框架: Lina, 主要特性如下:

  • 自动生成 APIDOC
  • Railser 零上手难度
  • 自校验参数与返回值, 高效构建安全的 API 接口

为什么造一个轮子

前后端分离了, Rails 提供 API 的场景越来越多了.

Rails + jbuilder 写 API 不错, 但维护 API 文档又是一个麻烦的事.

另外, grape 功能全面, 可以帮助声明参数, 但它自身一堆概念, 学习曲线还是有的.

于是, Lina 就诞生了.

杀手锏

自动生成 APIDOC 是 Lina 的杀手锏, 其妙招是编写如下控制器代码:

class PostsController < Lina::ApplicationController
  # def index
  define_action :index, {
    name: '获取所有文章',
    description: '这个接口用来获取所有文章, 参数及返回值请见下文',
    params: {
      properties: {
        created_order_by: {
          enum: [ :asc, :desc ],
          default: 'desc',
          description: '按创建时间正序或倒序排'
        }
      }
    },
    return: {
      type: 'array',
      items: {
        type: 'object',
        required: [:id, :name, :created_at],
        properties: {
          id: {
            type: 'integer',
            description: '文章ID',
          },
          name: {
            type: 'string',
            description: '文章标题',
          },
          created_at: {
            type: 'string',
            description: '创建时间, 格好的字符串'
          }
        }
      }
    }
  } do
    @posts = Post.order(created_at: params[:created_order_by].to_sym)
  end

end

如此, 通过 define_action 的定义, Lina 就可以将参数声明与返回值输出为 APIDOC, 并且, 参数声明还会帮你校验前端提交是否正确.

paramsreturn 是声明中最重要的部分, 这种约束是来自于: JSON Schema, 十分简洁易读, 并且非常完备.

除此之外, 这份声明在后面可以用于构造表单, 使得前端的 SimpleForm 成为可能.

除了控制器, Lina 继承了 Rails 绝大部分的优点, 整合了 jbuilder, 使得编写 RESTful API 更得心应手.

我编写了一个完整使用 Lina 的项目示例, 在这里: https://github.com/windy/lina_example

里面声明了两个 API, APIDOC 的生成效果你可以直接访问: http://linarb.org/apidoc 来查看.

内容较多, 欢迎前去项目主站围观点赞: http://linarb.org

额外知识

附, Ruby 社区 RESTful API 框架优劣分析:

先说专注于 API 的框架

1. grape

优点

  • 最早一批出现的专注于 API 的框架
  • 拥有自身一套完整的概念, 功能非常完备
  • intridea 开发维护, 质量颇高

缺点

  • 优点即缺点, 固执一套概念, 使得与 Rails 很多组件整合都有坑
  • 因为是新概念, 学习成本较高

2. rails-api

优点

  • 最早一批出现的专注于 API 框架
  • 基于 Rails, 与 Lina 类似, 充分利用了 Rails 的特点, 支持功能完备
  • 整合了一批 Rack 中间件, 一定程度上提高了 API 响应效率

缺点

  • 出现的太早, 更新比较慢, 无法充分利用 Rails4.1 出现的独立 Engine 系统.
  • 事实上, rails-api 无法支持 APIDOC, 又未缺省支持 jbuilder, 使得开发起来与 Rails 无样

3. Lina

优点

  • 自动生成 APIDOC, 节省了大部分的维护成本
  • 自动校验参数, 无须在控制器编写大量边界判断代码, 可靠性提升
  • 基于 Rails, 充分利用 Rails 的优点, 使得短时间内功能已经基本完备

缺点

  • 刚诞生, 还有不少特性需要添加
  • 稳定性与质量还需要更多时间来测试

其他非专注于 API 的方案:

  1. Rails + jbuilder

    我个人非常喜欢, 既简单又方便, 有了 Lina, 可以继续沿用.

  2. sinatra

    非常轻巧的方案, 适合于快速写一些简单的 API, 但对于大一点的项目, 就远不如 Rails 方便了.

最后, 欢迎关注 Lina 的发展: http://linarb.org

共收到 68 条回复
9442

集成doc非常赞,之前到处找gem来解析doc,提个建议 http://linarb.org/apidoc 没有响应式,没有模拟操作。

1342

请问,如果要手动返回一个错误消息的 json 比如 {"error": "not recode yet"} 的时候,jbuilder 文件应该怎么写。是直接判断 @post 是否存在么

12224

:plus1: ,为什么叫lina 呢 ?听着像 美女的名字 😄

121

#1楼 @flowerwrong 响应式好主意, 收下此建议. 你另说的模拟操作是指什么?

#2楼 @ywjno 如下:

#xx.json.jbuilder
if @post.blank?
  json.error 'not record yet'
else
  json.extract! @post, :id, :name 
end

#3楼 @chanshunli 一个游戏角色名字, 其实比较霸气的 😄

1342

#4楼 @lyfi2003 谢谢回复

1411

应该加入cache机制,虽然可以自己加,但是实际项目cache应用的好坏直接决定了整体性能

445

一直用grape等项目稳定后,再考虑使用lina,谢谢楼主

121

#6楼 @zeeler API 的 cache 我以为没有统一的方法, 所以像 Rails 那样提供标准的 Rails.cache 方法即可. #7楼 @stephen 😄 不建议轻易切换, 可以试着玩玩看, 发现啥问题欢迎提 issues :)

121

#9楼 @yesmeck 刚看到, 这个项目很像 Lina, 它自己定义了一套 DSL 来声明参数, 思路蛮像. 一个区别是, 它的定位是 Rails API 生成工具, Lina 的定位是一个 API 框架. 另外, 实际上做一套完备的 JSON 声明是非常麻烦( 我看了下, 它定义了众多的 DSL, 难于记忆 ), JSON Schema 已经发展到 v4 草案了. 我相信用 JSON Schema 来实现还是非常有优势的:

  1. JSON Schema 是用 JSON 来定义 JSON 数据, 数据结构纯粹, 只有 6 种.
  2. JSON Schema 是完备的, 见这里的一个例子: http://json-schema.org/example2.html
  3. JSON Schema 是有标准的且不断发展的: http://json-schema.org/latest/json-schema-core.html

谢谢你的介绍.

865

出现的太早, 更新比较慢, 无法充分利用 Rails4.1 出现的独立 Engine 系统.

LZ 这里说的“Rails4.1 出现的独立 Engine 系统”是指什么?

865

这个不是很早就有吗?为什么说是 4.1 出现的。

594

:plus1:

15924

很美的名字,很想念,呵呵

314

lina 生成的文档里没有响应样例,如果能把响应样例加上去,那就比较好了。

4584

支持一下

96

#15楼 @mogodb 每个中国人都认识一个叫lina的女孩就像每个美国人都认识一个叫jack的男人一样。

121

#16楼 @kayakjiang 嗯, 收到, #1楼 也提了这个建议. 接下来几个版本还会不断改善 APIDOC, 最后应该还要做到可以方便定制.

14526

grape 需要额外维护一份文档,文档没有约定,导致最终伙伴们东一句西一句, 最终发展的结果 看文档成了最头疼的事 有空去玩玩楼主的 lina。 Ps lina的字体能不能换一下 好丑啊... grape 看着多舒服 哈哈哈

6764

#20楼 @bxd602 grape 也可以自动生成文档, grape-swagger

14526

#21楼 @42thcoder 我去了解下

8121

#21楼 @42thcoder 正想说这个。 用grape+swagger 生成的文档可以直接用于 api test 页面也漂亮

9442

#4楼 @lyfi2003 用api之前总喜欢先试一下,心里有底。哈哈,类似谷歌的restful插件,做api请求。swagger也有这个功能,只是swagger-rails在rails 4.2下出问题。

121

#20楼 @bxd602 logo 还有很多改进空间, 欢迎给个设计图 PR.

#24楼 @flowerwrong 你说的是在线模拟测试吧, 很多 API 都会有登录限制, 或权限约束, 还不如直接用一些 chrome 插件或者本地的方案比如:

所以, 不太考虑这个特性.

172

我觉得 Grape 已经很好了 坑实在不能算多 而且代码写出来很好看 比这里一堆Hash好看多了

121

#26楼 @iBachue Grape 确实已经非常好, 不过有不少人比如我更喜欢 Rails 风格的 RESTful API 写法, Lina 便是另一个选择.

Hash 的方式不如 DSL 好看, 但我没有定义像 Grape 那样定义 DSL, 正是因为表面的简单带来了复杂的 DSL 学习成本高, 还不如直接找一个有官方维护的标准, 这便是 JSON Schema. 最主要的是完备性. 相信如果你看一个 更复杂的 JSON 的例子就明白了: http://json-schema.org/example2.html, 可以看看 Grape 中能否实现这个参数声明.

865

@lyfi2003 如果能这样写是不是更好看点?

desc(
  name: '获取所有文章',
  description: '这个接口用来获取所有文章, 参数及返回值请见下文',
  params: {
    properties: {
      created_order_by: {
        enum: [ :asc, :desc ],
        default: 'desc',
        description: '按创建时间正序或倒序排'
      }
    }
  },
  return: {
    type: 'array',
    items: {
      type: 'object',
      required: [:id, :name, :created_at],
      properties: {
        id: {
          type: 'integer',
          description: '文章ID',
        },
        name: {
          type: 'string',
          description: '文章标题',
        },
        created_at: {
          type: 'string',
          description: '创建时间, 格好的字符串'
        }
      }
    }
  }
)
def index
  @posts = Post.order(created_at: params[:created_order_by].to_sym)
end
15924

#18楼 @esseak 为何是这两个名字呢?还是泛指?你认识几个lina?

172

#27楼 @lyfi2003 好吧 但是我不得不表示 JSON Schema 的学习成本远远在 Grape 那几个简单的 DSL 之上,而且我记得 JSON Schema 标准还存在多个互不兼容的版本的问题,进一步提高了学习成本,而且Ruby JSON Schema 库的出错提示不够友好 都没法直接用。

2653

#2楼 @ywjno 我这样用,直接写到 Controller里面了……或者自己封装一个方法,因为错误就那些。

if @post
  render :show
else
  render status: :unprocessable_entity, json: {errors: "blalalalalalalalal"}
end
121

#31楼 @iBachue Grape 非常 Nice, 这个我很认同. 如果要讨论 JSON Schema 与 Grape 的 params DSL, 我要说, 完备性太重要重要了, 试想, 如果你要定义一个 JSON 结构, 结果 Grape DSL 支持不了怎么办? 更何况, 我个人认为对于参数自定义的 DSL 并不好用.

比如这个: http://json-schema.org/example2.html, 是否可以用 Grape DSL 定义出来?

而在正常情况下, 也有可能遇到这种情况, 比如返回值可能有多种 hash, 比如参数里还有子hash, 子数组.

172

#33楼 @lyfi2003 其实 Grape 的参数验证支持嵌套的 子hash 子数组并不会成为太大的问题,你这个example我看过,里面关于storage的验证确实够变态的,用 Grape 的话只能自定义Validator才能做到(为了方便,我可以在这个Validator里调用JSON Schema来验证,这可以做成一个通用的Validator),否则没有什么好的办法。不过我还是觉得为了完备性牺牲这么多不太划算,毕竟对于大部分Case,还是简单的DSL更加好用点。

121

#34楼 @iBachue 当然可以自己写个 validator, 但还要生成 APIDOC. Lina 就完全可以做到. 实际上这个需求并非多变态, 在处理返回值需求的时候, 很经常会用到多值( 例如 JSON Schema 的 oneOf ), 因为有错误处理, 用 Lina 之后, 这种 APIDOC 会非常漂亮, 而且可以帮助你检查返回值是否符合需求.

另外, Grape 非常不错, 适用于纯 API 场景, 但集成到 Rails 里总是感觉不舒服, Lina 更适合于 Railser 使用, 它本质上就是 Rails + jbuilder 的加强版, 既写页面很不错, 又写 API 更好( Lina 的支持 ), 何乐而不为呢?

172

#35楼 @lyfi2003 好吧,API Doc Grape也有,但仅仅只是针对参数的Doc,返回值不会有Doc。但是说起返回值,看上去你依然在用JSON Schema做验证,这是为什么,验证返回值的正确性意义何在?

121

#36楼 @iBachue 既然是完整的 API, 返回值要保证正确. 验证返回值就很有必要, APIDOC 中也要体现出来. 当然, 这些校验都是可选的.

配置如下:

# lina config file
Lina.setup do |config|

  #是否开启返回值参数校验
  #config.return_check = true

  #返回值校验时使用的 json schema 版本, 更多: http://tools.ietf.org/html/draft-zyp-json-schema-04
  #config.return_json_schema_version = :v4
end

你不想关闭全局校验的话也可以直接用 return: {} 留空来达到与 Grape 一样的效果.

172

#37楼 @lyfi2003 API的性能至关重要,尽可能节省更多的开销吧。但是如果return留空也就没文档了不是么? 另外你的返回值文档和jbuilder文件位于不同的地方,这可能会使文档失去维护,如果放在一个文件里则会好一点。另外我留意到了#2楼的建议,感觉你的方案不够优雅。其实很多API处理错误的代码是一样的,可以像rescue_from一样写在一起,所以他们的schema也理所当然的应该写在一起。

121

#38楼 @iBachue 理念不太合, 不多讨论谁优谁劣了. Lina 还有许多待完善的特性, 届时应该会更强大.

1342

2楼在此,我只是想得到这种情况我该如何写jbuilder的而已。而且 api 还有很多校验比如每小时调用次数过多、没有权限等等。有些都是一些主动返回信息而不算得上是返回服务器异常。

另外已经在用该 gem 搭建一个只有一个方法的 api 了

5390

还是比较喜欢grape dsl的方式 而且楼主提到grape的学习曲线我倒是完全没有感觉,很轻松就上手了,也没碰到什么坑,除了不允许我在api method以外修改response这点让我不愉快

15924

#39楼 @lyfi2003 lina是用vim开发的么?

121

#42楼 @mogodb 你关心的问题真奇怪, 是用的 vim, 我一直都用的 vim, 而且是命令行版本.

4113

我说 linadota 里火女的名字吧~

8972

#3楼 @chanshunli 主要是胸大

96

#45楼 @ane 见解相同

611

为啥return 还有require或者optional!

121

#48楼 @Lucifer 根据 API 的需求而来:

  1. 不需要 return 声明及判断: Lina 中可以通过 配置关闭校验.
  2. return 中 比如 User, 如果它所属有一个 city 字段, 用户未设置就没这个返回字段, 你就可以设为 optional.
930

在jbuilder 里使用router 的helper方法 直接报错,root_path 指到了,"/apidoc/"

121

#50楼 @chrishyman router 的 helper ? 不是很懂, 能否将错误放在 github 的 issues 中: https://github.com/windy/lina/issues

我来看下.

15696

好东西,太感谢了。

15696

@lyfi2003 , 飞哥,apidoc 每次刷新,左侧的菜单都收起来,又要展开,非常非常不方便。

121

#53楼 @pestd 嗯, 好主意. 我回头加一下.

17966

The property '#/examcat_id' of type String did not match the following type: integer

有这样的错误,post 过来的数据是数字

121

#55楼 @bigjoe Rails 参数进来的 ID 要用 string. Rails 为了安全, 全部转成 string 了.

15696

@lyfi2003 , 飞哥 ,请问,如果api需要做上传图片,那么type是啥呢?我用object报错: The property '#/logo' of type ActionDispatch::Http::UploadedFile did not match the following type: object

121

#57楼 @pestd 这个临时可以用 { type: ActionDispatch::Http::UploadedFile } 来校验, 不过应该可以用 type: 'object'

多谢这个反馈, 我在下一个版本将其修正为 { type: 'object' }

15696

@lyfi2003 , 用 ActionDispatch::Http::UploadedFile 或者 object 貌似都不行,我测试了。

如果用ActionDispatch::Http::UploadedFile,错误是: The property '#/properties/logo/type' of type Class did not match one or more of the required schemas. The schema specific errors were: - anyOf #0: - The property '#/properties/logo/type' value ActionDispatch::Http::UploadedFile did not match one of the following values: array, boolean, integer, null, number, object, string - anyOf #1: - The property '#/properties/logo/type' of type Class did not match the following type: array

用 object ,错误是: The property '#/logo' of type ActionDispatch::Http::UploadedFile did not match the following type: object

121

#59楼 @pestd 嗯, 着急的话, 你可先把这个校验的属性去掉, 在 description 里面写清楚这个上传文件的依赖即可.

我刚才研究了一下, 这个问题还需要花些功夫才能解决. 解决后我通知你.

15696

@lyfi2003 ,如果返回的是jsonp,那么type应该是 string了吧?

render json: @result.to_json, :callback =>'callback'

15696

飞哥,apidoc的右边内容区域加个刷新按钮,点击只需刷右侧。另外,搞个首页,可以让配置一些通用的说明文字,比如作者、注意事项等。

121

#63楼 @pestd :plus1: 好收到.

先加到 issues 里了, 我闲一点的时候去处理.

15696

又有个问题,本来已经用了paperclip 的话,怎么接受图片? 要自己另外实现吗?

121

#65楼 @pestd 可以继续用, 不影响的. 跟上传文件的处理类似.

96

#51楼 @lyfi2003 飞哥,你的帖子里把文字加粗,排版是怎么做到的?社区的发帖功能里貌似没有这些编辑工具。。。我word上编辑好的格式粘贴下来没了。。。。。。

121

#67楼 @vbgfnd 搜素 markdown

121

#67楼 @vbgfnd 搜素 markdown

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