开源项目 开源一个超酷的 Restful API 自动生成器:Kaola

ylt · 发布于 2017年07月21日 · 最后由 martin91 回复于 2017年07月22日 · 1302 次阅读
5700

项目的地址是: https://github.com/yuanxinyu/kaola

0. 什么是Kaola

Kaola(考拉,英文koala)是一个全自动的restful api代码自动生成系统。给定一个数据库,只需要配置好数据库连接,koala可以通过扫描数据库自动生成全套的restful api的后端代码。通过预先约定好的接口调用规范,前端就可以直接开发应用系统了。

1. 为什么开发Kaola

类似koala的代码生成系统之前也有很多,比如rails自带的scaffold功能,以及更完善的Active Admin/Rails Admin等。但是这些系统都有两点不符合要求:

  1. 这些系统都是一套完整的系统,从后端到页面都一次性生成。实际的情况是,后端的接口比较通用,而界面和操作流程往往需要深度的定制。修改这些系统自动生成的页面是很困难的。
  2. 这些系统往往都是只支持单表CRUD操作,对多表之间的关联操作支持不够。

所以我开发了这套koala系统,它的主要特点是:

  1. 只提供后端的restful api接口,前端的代码还是需要每个应用自己编写,采用前后端分离架构;
  2. 接口不仅支持常规的单表CRUD操作,还自动支持多表关联(一对多、多对多、自引用的树形结构),支持复杂的查询(分页、排序、计数、索引)、支持批量操作、以及拥有一套透明高效的缓存体系。

当然,还有一点很重要的,就是定义了一套restful的基于约定的接口协议。基于这套约定的协议,前端程序员不需要后端提供繁琐的不一致接口文档,可以轻松上手开始使用这套接口。

2. Kaola的接口协议约定

Kaola生成的Api接口是基于http的web接口,URL的命名基本沿用rails框架的命名约定,其中的表名都是复数形式。基本的CRUD接口的约定如下:

操作 HTTP Method URI
获取列表数据 GET /表名(.:format)
添加新数据 POST /表名(.:format)
修改已有数据 PUT /表名/:id(.:format)
查看已有数据 GET /表名/:id(.:format)
删除已有数据 DELETE /表名/:id(.:format)

这只是对单表资源的CRUD操作,koala针对下列情况也定义了一套的Restful规范:

  1. 查询/分页/排序的支持。 标准Restful接口只有一个列表的接口,对查询相关的功能没有约定,koala自行扩展了一套约定。
  2. 批量操作的支持。 Restful接口默认只支持单个资源的操作,而实际的业务场景中经常需要有批量操作的需求,比如商品的批量上架、数据的批量删除等。
  3. 有关联的数据表的支持。Restful只支持单个资源的CRUD,而实际业务中经常有主子表的级联保存,关联表的查询等。

查询的约定

Kaola的查询参数一一对应到数据库中的字段,格式是通过把json格式的查询参数扁平化得来的。比如下面的查询条件,

{
    "s" : {
        gender : 'm',
        "like" : {
            name : 'b'
        }
    },
    "order" : "id asc"
}

它的含义是查找所有gender等于'm'并且name包含'b'的记录,按'id asc'排序,扁平化以后就是:

s[gender]=m&s[like[name]]=b&order=id asc

如果表的名字是‘users’,那么请求‘users.json?s[gender]=m&s[like[name]]=b&order=id+asc’就可以得到所有符合条件的json格式的数据。

针对查询/分页/排序/批量操作/关联表操作/导入导出等的具体的约定(前端开发者需要细看)可以参考:

3. 开始使用Kaola

Kaola是基于ruby on rails开发的,主要在Mac和Linux操作系统下完成开发 ,数据库使用的是mysql。如果你的操作系统是windows,或者数据库不是mysql,大体上是兼容的,但可能会碰到问题。

  1. 安装ruby2.2以上版本,如何安装可参考这个链接。安装完成ruby以后,在命令行运行”gem install bundle”以安装bundle;

  2. 下载koala的代码,在项目根目录运行“bundle install”安装依赖的第三方库;

  3. 配置数据库连接,具体参考数据库配置

  4. 在项目的根目录运行“./autogen.sh”,自动生成所有的后端api代码;

  5. 启动api服务器,在开发环境下就是运行“rails server”

然后打开浏览器访问这个链接就可以看到生成的所有接口了。在开发环境下,koala除了api接口,也提供完整的CRUD的html页面(其实就是rails默认的scaffold生成的页面)。在发布环境下,只有接口调用可以访问,基本就是以".json"结尾的url访问。

4. 实现原理

扫描数据库实现所有单表的CRUD功能接口,这个不难实现。比较难的是怎么处理数据关联关系。数据关联关系主要有三种:一对多,一对一,多对多。Kaola在实现关联关系的时候,分两个阶段实现的。

识别一对多关系

第一个阶段,在2016年项目最初开发的时候 ,只支持一对多关系。一对一关系是一对多关系的特例,而多对多关系则可以表示为两个一对多关系,所以只支持一对多关系也不算严重的缺陷。

那么如何自动发现数据库所有的一对多关系呢,主要通过三种方法:

  1. 通过数据库的外键。如果A表有一个外键指向B表,那么B表和A表就是一对多关系。
  2. 通过命名约定。外键的命名约定采用rails默认的约定,外键的名字都是“表名单数_id”。同时对 rails的约定有扩展,如果一张表里有多个关联到另外一张表的外键,命名规则是“前缀_表名单数_id”。所有符合命名约定的表,即使没有设置数据库层的外键,也自动建立一对多关系。

3.通过配置文件。有些遗留的数据库即没有配置外键,也不符合命名约定,那么在配置文件custom_fk.txt文件中配置好外键关系也可以。

关联关系是双向的,对于两个表table1s和table2s,如果table1有一个字段table2_id,那么table1是多方,table2是一方,用rails来描述就是:

Table1 belongs_to table2
Table2 has_many  table1s

识别多对多关系

第二个阶段是2017年,kaola开发完成接近一年的时候,有个项目组提出要多对多关系的支持,在压力下想明白了多对多关系。首先,rails支持两种多对多关系:直接式的“has_and_belongs_to_many”和间接式的“has_many through”, 对应的例子见下图:

has_and_belongs_to_many

has_many through

Rails已经不建议使用直接式的多对多关系参考,Kaola也不支持这种方式。 下面就是自动发现多对多关联的最关键一步:所有包含两个及以上外键(也包括命名约定/配置文件定义的外键 )的表自动形成多对多关系。一张表有2/3/4个外键,分别会定义1/2/6个多对多关系,也就是给定n个外键,生产组合C(2,n)个多对多关系。

除了常规的三种关系,还有一种特殊的自引用关系:树形结构。树形结构最常见的例子有组织结构、产品类别等。为了存储树形结构,要求给定的表有一个指向自己的外键。外键值为空的节点是树的根节点。树形结构在kaola中被定义为一个指向自己的一对多关系。

关系的增删改

识别了这些常见的数据关联关系后,接下来的任务是如何支持对这些关系的增删改查操作。目前对关系数据的增删改还只支持一对多关系,不支持多对多关系。 由于kaola采用json格式提交数据,所以新增和修改有关系的数据是比较容易的,json格式很容易支持用嵌套结构来表达一对多关系。比如单个新增的话,提交的是一个hash对象

{
    "表名单数": {id:id, key:value,...}
}

那么对于一次性提交主子表的数据,格式就是

{
    "主表单数": {id:id, key:value,...},
    "子表复数": [
        {id:id, key:value,...},
        {id:id, key:value,...}
    ],
    其它子表...
}

关系的查询

下一步是如何定义和实现关联关系的查询。Kaola使用了面向对象语言的“.”操作符来表达主子表的关系,从而可以达到比sql语句更简单自然的查询表达式。比如有两张表:公司表companies和仓库表warehouses,一个公司可以有 多个仓库,那么下面的查询

warehouses.json?s[company.name]='公司A'

表示查询所有的name为‘公司A’的公司所有的仓库。对应的Sql查询是:

select * from warehouses join companies on warehouses.company_id = companies.id where companies.name='公司A'

关联表的查询支持所有单表查询的功能,包括等于/Like/日期/数值范围/枚举查询。 此外,针对一对多关系,还支持两种特殊的查询:Exists查询(给定主表是否有子表数据)和树形结构查询(给定数据节点的所有深层嵌套子节点)。

具体的技术实现细节可以参考:

5. 案例

Kaola主要的使用场合是内部IT系统的后端,比如各类管理后台、各类信息管理系统(CRM/SCM/ERP/HIS)等。

单语言案例:供应链系统

Kaola最初的场景是用在一个供应链系统中。这个系统有几百张表,表间的关联复杂。应用Kaola后,整个后端代码量大大减少,所有增删改查的api都是自动生成的。最后只有三个文件,几百行代码是需要为供应链的逻辑定制的。这三个文件分别处理各类订单流水号生成逻辑、库存计算逻辑、订单流转逻辑。这些逻辑都是通过rails的数据库钩子实现的,不修改自动生成的代码,所以维护也很方便。比如下面就是序列号生成逻辑/订单流转逻辑的模版代码。

class ActiveRecord::Base
  before_validation :gen_seq

  def gen_seq
    #序列号生成逻辑
  end
end
class ActiveRecord::Base
  before_update :order_process

  def order_process
    #订单流转逻辑
  end
end

如果你的团队后端有ruby程序员,这是推荐使用kaola的方式,增删改查的api用kaola自动生成,其它的功能通过修改kaola的代码实现。

混合语言后端案例

如果你的团队后端没有ruby程序员,也可以使用kaola来帮助减少后端的开发工作量。通过采用前后端分离架构,前端开发其实不关心后端的api是用什么语言实现的。

我们在一个健康干预系统项目中采用了混合语言后端的案例。这个项目组都是java后端程序员,通过kaola来帮助java程序员完成增删改查的api,而健康干预计划/健康报告生成等功能则是独立部署的java后端实现的。

6. 杂项

发布

由于kaola提供api接口能力太广泛,不能直接暴露在网络上。所以在发布的时候,需要部署在一个api网关后面,比如Netflix/zuul这样的网关。

License

Kaola采用 MIT License, https://opensource.org/licenses/MIT

为何取名Kaola

俗话说,懒惰是程序员的美德,能够让计算机自动完成的事情,就不要重复劳动了。取名Kaola是希望这套系统让程序员可以像考拉一样悠闲,同时restful也有宁静的含义,和考拉的形象比较匹配。

去年的技术分享

2016年在高可用架构社区做的一次技术分享:

共收到 17 条回复
96

这种直接包装一下数据库需求,如果是 PostgreSQL 的话,直接上 PostgREST 是最爽的,全套解决方案都有了,性能还好

5700
32qinix 回复

去年开始写kaola的时候还真不知道PostgREST,看来也是一种趋势。 不过用rails实现的好处是多种数据库都支持。

5700
5700ylt 回复

测试的时候,mysql和oracle都试过,没问题。其它rails支持的数据库,使用kaola应该问题也不大。 生产环境目前都是mysql数据库

4215

这个好。那不是后端的要失业了,都去前端吧。

1107

kaola 2016技术分享 链接是错的 https://ruby-china.org/topics/doc/share%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB.md

5700
1107jasl 回复

谢谢提醒,kaola 2016技术分享 链接已修复

1107
5700ylt 回复

👍

23315

👍 学习了

27

不错,但是权限控制怎么做?

5700
27numbcoder 回复

权限确实是个麻烦的东西。 看了PostgREST,它是直接利用数据库的角色进行权限控制,太死板。 目前还在构思一个优雅的内置解决方案。

5700
27numbcoder 回复

在目前几个内部项目中,kaola都是配合其它api一起使用的。比如老白的供应链系统,需要对接京东/网易/顺丰等外部接口,这部分是一个独立部署的项目。

所以url鉴权是在更高一层的网关实现的,基于Netflix/zuul。而细粒度的权限控制,则每个项目都是自行实现的。

27936

CouchDB 似乎支持这个?

Dd7d50

查询是不是还可以扩展一下,支持多层and or的嵌套就太棒了!

5700
Dd7d50dfzy5566 回复

实现不难,关键是要有一个好的查询表达规范,符合前端程序员的直觉。你能用嵌套的json合理表达出你的需求,就可以实现这个功能。说实话,我认为PostgREST的查询语法很丑陋。

5700
Dd7d50dfzy5566 回复

目前kaola支持的and/or嵌套查询是这种的 比如

s:{
    'f1,f2':v1,
    'f3':v2
}

表示(f1=v1 or f2=v1) and (f3=v2), and/or逻辑都是隐含的。如果要最外层用or,也许更方便的是调用两次api?

5700
5700ylt 回复

吐槽一下PostgREST的查询语法设计:

  1. 查询字段没有独立的名称空间,所以它的limit/order/select等关键字会和要查询的字段名冲突
  2. 查询类型没有独立的名称空间,查询类型和查询内容混合,比如age=eq.13, 这里eq和13分别是查询类型和查询内容,所以PostgREST难以支持or查询
  3. 不支持单个对象的获取
  4. 支持按搜索条件批量删除,容易误操作
4755

建议提供英文版 README

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