在最近这段日子 ( 2015 春节 ), 写了一个新的 API 框架: Lina
, 主要特性如下:
前后端分离了, 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 的框架
grape
优点
缺点
rails-api
优点
缺点
Lina
优点
缺点
其他非专注于 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
是否存在么
#1 楼 @flowerwrong 响应式好主意, 收下此建议. 你另说的模拟操作是指什么?
#xx.json.jbuilder
if @post.blank?
json.error 'not record yet'
else
json.extract! @post, :id, :name
end
#3 楼 @chanshunli 一个游戏角色名字, 其实比较霸气的
#9 楼 @yesmeck 刚看到, 这个项目很像 Lina, 它自己定义了一套 DSL 来声明参数, 思路蛮像. 一个区别是, 它的定位是 Rails API 生成工具, Lina 的定位是一个 API 框架. 另外, 实际上做一套完备的 JSON 声明是非常麻烦 ( 我看了下, 它定义了众多的 DSL, 难于记忆 ), JSON Schema
已经发展到 v4 草案了. 我相信用 JSON Schema
来实现还是非常有优势的:
谢谢你的介绍.
出现的太早, 更新比较慢, 无法充分利用 Rails4.1 出现的独立 Engine 系统.
LZ 这里说的 “Rails4.1 出现的独立 Engine 系统” 是指什么?
#16 楼 @kayakjiang 嗯, 收到, #1 楼 也提了这个建议. 接下来几个版本还会不断改善 APIDOC, 最后应该还要做到可以方便定制.
用 grape
需要额外维护一份文档,文档没有约定,导致最终伙伴们东一句西一句,
最终发展的结果 看文档成了最头疼的事
有空去玩玩楼主的 lina。
Ps lina 的字体能不能换一下 好丑啊... grape
看着多舒服 哈哈哈
#20 楼 @bxd602 logo 还有很多改进空间, 欢迎给个设计图 PR.
#24 楼 @flowerwrong 你说的是在线模拟测试吧, 很多 API 都会有登录限制, 或权限约束, 还不如直接用一些 chrome 插件或者本地的方案比如:
所以, 不太考虑这个特性.
#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 这点让我不愉快
#50 楼 @chrishyman router 的 helper ? 不是很懂, 能否将错误放在 github 的 issues 中: https://github.com/windy/lina/issues
我来看下.
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'