Gem 一个基于 Rails 的 RESTful API 框架: Lina
在最近这段日子( 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, 并且, 参数声明还会帮你校验前端提交是否正确.
params
与 return
是声明中最重要的部分, 这种约束是来自于: 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 的方案:
-
Rails
+jbuilder
我个人非常喜欢, 既简单又方便, 有了
Lina
, 可以继续沿用. -
sinatra
非常轻巧的方案, 适合于快速写一些简单的 API, 但对于大一点的项目, 就远不如
Rails
方便了.
最后, 欢迎关注 Lina 的发展: http://linarb.org
集成doc非常赞,之前到处找gem来解析doc,提个建议 http://linarb.org/apidoc 没有响应式,没有模拟操作。
请问,如果要手动返回一个错误消息的 json
比如 {"error": "not recode yet"}
的时候,jbuilder 文件应该怎么写。是直接判断 @post
是否存在么
:plus1: ,为什么叫lina 呢 ?听着像 美女的名字
#1楼 @flowerwrong 响应式好主意, 收下此建议. 你另说的模拟操作是指什么?
#xx.json.jbuilder
if @post.blank?
json.error 'not record yet'
else
json.extract! @post, :id, :name
end
#3楼 @chanshunli 一个游戏角色名字, 其实比较霸气的
应该加入cache机制,虽然可以自己加,但是实际项目cache应用的好坏直接决定了整体性能
一直用grape等项目稳定后,再考虑使用lina,谢谢楼主
#9楼 @yesmeck 刚看到, 这个项目很像 Lina, 它自己定义了一套 DSL 来声明参数, 思路蛮像. 一个区别是, 它的定位是 Rails API 生成工具, Lina 的定位是一个 API 框架. 另外, 实际上做一套完备的 JSON 声明是非常麻烦( 我看了下, 它定义了众多的 DSL, 难于记忆 ), JSON Schema
已经发展到 v4 草案了. 我相信用 JSON Schema
来实现还是非常有优势的:
- JSON Schema 是用 JSON 来定义 JSON 数据, 数据结构纯粹, 只有 6 种.
- JSON Schema 是完备的, 见这里的一个例子: http://json-schema.org/example2.html
- JSON Schema 是有标准的且不断发展的: http://json-schema.org/latest/json-schema-core.html
谢谢你的介绍.
出现的太早, 更新比较慢, 无法充分利用 Rails4.1 出现的独立 Engine 系统.
LZ 这里说的“Rails4.1 出现的独立 Engine 系统”是指什么?
这个不是很早就有吗?为什么说是 4.1 出现的。
:plus1:
很美的名字,很想念,呵呵
lina 生成的文档里没有响应样例,如果能把响应样例加上去,那就比较好了。
支持一下
#16楼 @kayakjiang 嗯, 收到, #1楼 也提了这个建议. 接下来几个版本还会不断改善 APIDOC, 最后应该还要做到可以方便定制.
用 grape
需要额外维护一份文档,文档没有约定,导致最终伙伴们东一句西一句,
最终发展的结果 看文档成了最头疼的事
有空去玩玩楼主的 lina。
Ps lina的字体能不能换一下 好丑啊... grape
看着多舒服 哈哈哈
#21楼 @42thcoder 我去了解下
#21楼 @42thcoder 正想说这个。 用grape+swagger 生成的文档可以直接用于 api test 页面也漂亮
#20楼 @bxd602 logo 还有很多改进空间, 欢迎给个设计图 PR.
#24楼 @flowerwrong 你说的是在线模拟测试吧, 很多 API 都会有登录限制, 或权限约束, 还不如直接用一些 chrome 插件或者本地的方案比如:
- @fredwu 的 api_taster
- chrome 插件: advanced-rest-client
所以, 不太考虑这个特性.
我觉得 Grape 已经很好了 坑实在不能算多 而且代码写出来很好看 比这里一堆Hash好看多了
#26楼 @iBachue Grape
确实已经非常好, 不过有不少人比如我更喜欢 Rails
风格的 RESTful API 写法, Lina 便是另一个选择.
Hash 的方式不如 DSL 好看, 但我没有定义像 Grape
那样定义 DSL, 正是因为表面的简单带来了复杂的 DSL 学习成本高, 还不如直接找一个有官方维护的标准, 这便是 JSON Schema
. 最主要的是完备性. 相信如果你看一个 更复杂的 JSON 的例子就明白了: http://json-schema.org/example2.html, 可以看看 Grape
中能否实现这个参数声明.
@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
#31楼 @iBachue Grape 非常 Nice, 这个我很认同. 如果要讨论 JSON Schema 与 Grape 的 params DSL, 我要说, 完备性太重要重要了, 试想, 如果你要定义一个 JSON 结构, 结果 Grape DSL 支持不了怎么办? 更何况, 我个人认为对于参数自定义的 DSL 并不好用.
比如这个: http://json-schema.org/example2.html, 是否可以用 Grape DSL 定义出来?
而在正常情况下, 也有可能遇到这种情况, 比如返回值可能有多种 hash, 比如参数里还有子hash, 子数组.
#34楼 @iBachue 当然可以自己写个 validator, 但还要生成 APIDOC. Lina 就完全可以做到. 实际上这个需求并非多变态, 在处理返回值需求的时候, 很经常会用到多值( 例如 JSON Schema 的 oneOf ), 因为有错误处理, 用 Lina 之后, 这种 APIDOC 会非常漂亮, 而且可以帮助你检查返回值是否符合需求.
另外, Grape 非常不错, 适用于纯 API 场景, 但集成到 Rails 里总是感觉不舒服, Lina 更适合于 Railser 使用, 它本质上就是 Rails + jbuilder 的加强版, 既写页面很不错, 又写 API 更好( Lina 的支持 ), 何乐而不为呢?
#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 一样的效果.
2楼在此,我只是想得到这种情况我该如何写jbuilder的而已。而且 api 还有很多校验比如每小时调用次数过多、没有权限等等。有些都是一些主动返回信息而不算得上是返回服务器异常。
另外已经在用该 gem 搭建一个只有一个方法的 api 了
还是比较喜欢grape dsl的方式 而且楼主提到grape的学习曲线我倒是完全没有感觉,很轻松就上手了,也没碰到什么坑,除了不允许我在api method以外修改response这点让我不愉快
我说 lina
是 dota
里火女的名字吧~
#3楼 @chanshunli 主要是胸大
为啥return 还有require或者optional!
在jbuilder 里使用router 的helper方法 直接报错,root_path 指到了,"/apidoc/"
#50楼 @chrishyman router 的 helper ? 不是很懂, 能否将错误放在 github 的 issues 中: https://github.com/windy/lina/issues
我来看下.
好东西,太感谢了。
@lyfi2003 , 飞哥,apidoc 每次刷新,左侧的菜单都收起来,又要展开,非常非常不方便。
The property '#/examcat_id' of type String did not match the following type: integer
有这样的错误,post 过来的数据是数字
@lyfi2003 , 飞哥 ,请问,如果api需要做上传图片,那么type是啥呢?我用object报错: The property '#/logo' of type ActionDispatch::Http::UploadedFile did not match the following type: object
@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
@lyfi2003 ,如果返回的是jsonp,那么type应该是 string了吧?
render json: @result.to_json, :callback =>'callback'
飞哥,apidoc的右边内容区域加个刷新按钮,点击只需刷右侧。另外,搞个首页,可以让配置一些通用的说明文字,比如作者、注意事项等。
又有个问题,本来已经用了paperclip 的话,怎么接受图片? 要自己另外实现吗?