APIJSON
APIJSON copied to clipboard
How to make another APIJSON? / 如何实现其它语言的APIJSON?
目前已有 C#, Go, Java, Node, PHP, Python 对应的 APIJSON ORM 库实现 https://github.com/Tencent/APIJSON#%E7%94%9F%E6%80%81%E9%A1%B9%E7%9B%AE
一开始不要贪大求全,实现基础的功能就好,功能后面再完善、性能后面再优化。
可以参考Java版的实现,建议从这个版本做起 https://github.com/TommyLemon/APIJSON/tree/636fe2263c01acce4ad32751cbb8a579830d9f29
这个是最简单的实现,只有7个类,将JSON转换为SQL,但也能够实现最核心的 引用赋值、单表查询、数组分页查询 了。 先完成这个,后面再优化。如果直接实现复杂功能,很可能导致一大堆问题然后坚持不下去。
建议步骤:
1.实现单表查询
{
"User": {
"id": 82002
}
}
转换为
SELECT * FROM User WHERE id = 82002 LIMIT 1 OFFSET 0
ResultSet取第0条(总数最多1条),取出所有字段对应的值,封装为一个表对象返回:
{
"id": 82002,
"sex": 1,
"name": "Happy~",
"tag": "iOS",
"head": "http://static.oschina.net/uploads/user/1174/2348263_50.png?t=1439773471000",
"contactIdList": [
82005,
82001,
38710
],
"pictureList": [],
"date": "2017-02-01 19:21:50.0"
}
替换原来的
{
"id": 82002
}
2.实现数组查询
{
"[]": {
"count": 10,
"page": 1,
"User": {
"sex": 1
}
}
}
转换为
SELECT * FROM User WHERE sex = 1 LIMIT 10 OFFSET 10
ResultSet取所有,封装为一个包含表对象的数组返回:
[
{
"User": {
"id": 82005,
"sex": 1,
"name": "Jan",
"tag": "AG",
"head": "http://my.oschina.net/img/portrait.gif?t=1451961935000",
"contactIdList": [
82001,
38710
],
"pictureList": [],
"date": "2017-02-01 19:21:50.0"
}
},
{
"User": {
"id": 82006,
"sex": 1,
"name": "Meria",
"head": "http://static.oschina.net/uploads/user/998/1997902_50.jpg?t=1407806577000",
"contactIdList": [],
"pictureList": [],
"date": "2017-02-01 19:21:50.0"
}
}
]
替换原来的
{
"count": 10,
"page": 1,
"User": {
"sex": 1
}
}
3.实现两张表 一对一 关联查询
{
"Comment": {},
"User": {
"id@": "Comment/userId"
}
}
先解析Comment,转换为
SELECT * FROM Comment LIMIT 1 OFFSET 0
ResultSet取第0条(总数最多1条),取出所有字段对应的值,封装为一个Comment表对象返回:
{
"id": 4,
"toId": 0,
"userId": 38710,
"momentId": 470,
"date": "2017-02-01 19:20:50.0",
"content": "This is a Content...-4"
}
然后解析User,解析到
"id@": "Comment/userId"
时,从Comment中取出userId,把以上键值对改为
"id": 38710
然后把User转换为
SELECT * FROM User WHERE id = 38710 LIMIT 1 OFFSET 0
ResultSet取第0条(总数最多1条),取出所有字段对应的值,封装为一个User表对象返回:
{
"id": 38710,
"sex": 0,
"name": "TommyLemon",
"tag": "Android&Java",
"head": "http://static.oschina.net/uploads/user/1218/2437072_100.jpg?t=1461076033000",
"contactIdList": [
82003,
82005,
70793
],
"pictureList": [
"http://static.oschina.net/uploads/user/1218/2437072_100.jpg?t=1461076033000",
"http://common.cnblogs.com/images/icon_weibo_24.png"
],
"date": "2017-02-01 19:21:50.0"
}
4.实现两张表 一对多 关联查询
{
"Moment": {},
"[]": {
"Comment": {
"momentId@": "Moment/id"
}
}
}
5.实现两张表在数组内 一对一 关联查询
{
"[]": {
"Comment": {},
"User": {
"id@": "[]/Comment/userId"
}
}
}
其中
"id@": "[]/Comment/userId"
要根据Comment在数组中的位置index来动态变为
"id@": "[]/0/Comment/userId"
"id@": "[]/1/Comment/userId"
...
6.实现两张表在数组内 一对多 关联查询
实现了 1-6 后可以测试是否能通过这个用例(一对一、一对多、数组外、数组内、同层级、跨层级都有)
http://apijson.cn/api
POST http://apijson.cn:8080/get
{
"Moment": {
"id": 32,
"@column": "id,userId,praiseUserIdList"
},
"User": {
"id@": "Moment/userId",
"@column": "id"
},
"[]": {
"Moment": {
"userId{}@": "Moment/praiseUserIdList",
"@column": "id,userId"
},
"User": {
"id@": "/Moment/userId",
"@column": "id,name,head"
},
"Comment[]": {
"Comment": {
"momentId@": "[]/Moment/id"
}
}
}
}
7.实现SQL的 column, order by, group by等功能。
8.实现增、删、改
APIAuto 上有 200 左右测试用例,可以跑一遍看看哪些有问题 http://apijson.cn/api
管理员账号及密码 13000082001/123456
管理员账号及密码 13000088888/123456
测试数据 SQL 文件(MySQL, PostgreSQL, Oracle, SQL Server, DB2, ClickHouse 等) https://github.com/APIJSON/APIJSON-Demo
完整功能见设计规范(核心的任意多表关联查询功能完成后可逐步完善其它功能) https://github.com/Tencent/APIJSON/blob/master/Document.md#3
由于不限制嵌套层级和组合方式,所以必须用递归实现,从数据库查到结果(一开始可模拟测试数据,省去连接数据库的过程)后替换掉原来的内容再返回。
递归时由于不知道键值对key:value中的key和value,所以需要判断key和value的格式。
{
"[]": { // value 类型为 JSONObject && key 以 [] 结束
"User": { // value 类型为 JSONObject && key 符合正则表达式 ^[A-Za-z]+$
"sex": 1 // value 类型不为 JSONObject
}
}
}
最简单的解析过程可以是:
1.Parser递归遍历 请求JSON,提取出每个 表对象JSON 2.Parser用 表对象JSON 封装SQLConfig,交给SQLExecutor 3.SQLExecutor用SQLConfig封装SQL语句,并连接数据库执行 4.SQLExecutor返回执行结果给Parser 5.Parser替换对应请求JSON中的对象
APIJSON-C# Server: 创作不易,给热心的作者右上角点 ⭐Star 支持下吧 ^_^ https://github.com/liaozb/APIJSON.NET
@TommyLemon
上回说的python初步完成了 https://github.com/zeromake/restful_model 刚刚把单元测试写好。
json 的表现层完全自定义,都是为了对应 sql
。
import sqlalchemy as sa
from sanic import Sanic
from restful_model import DataBase
from restful_model.extend.sanic import ApiView
metadata = sa.MetaData()
# 这个model可以直接用工具连接数据库生成
User = sa.Table(
'user',
metadata,
sa.Column(
'id',
sa.Integer,
autoincrement=True,
primary_key=True,
nullable=False,
),
sa.Column(
'account',
sa.String(16),
nullable=False,
),
sqlite_autoincrement=True,
)
# python 可以动态生成 class 后面想批量注册model到view也很简单
class UserView(ApiView):
__model__ = User
# 请求方法过滤
# __method__ = ["get", "post"]
# 过滤 where 或者查询字段,支持全局或单个方法过滤
#__filter_keys__ = {"get": [["id"]]}
# 中间件模式
async def auth_filter(self, context: Context, next_filter):
return await next_filter()
app = Sanic(__name__)
db = DataBase("sqlite:///db.db")
app.db = db
@app.listener('before_server_start')
async def setup_db(app, loop):
if app.db.loop is None:
app.db.loop = loop
app.db.engine = await app.db.create_engine(echo=True)
if not await app.db.exists_table(User.name):
await app.db.create_table(User)
userView = UserView.as_view(app.db)
app.add_route(userView, "/user", HTTP_METHODS)
app.add_route(userView, "/user/<id:int>", HTTP_METHODS)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
$ # create
$ curl -X POST http://127.0.0.1:8000/user \
-H 'content-type: application/json' \
-d '{ "account": "test1" }'
> {
"status": 201,
"message": "Insert ok!",
"meta": {"count":1}
}
$ # select
$ curl -X GET http://127.0.0.1:8000/user
> {
"status": 200,
"message": "Query ok!",
"data": [{
"id": 1,
"account": "test1"
}]
}
$ # update
$ curl -X PUT http://127.0.0.1:8000/user \
-H 'content-type: application/json' \
-d '{"where": {"id": 1}, "values": {"account": "test2"}}'
> {
"status": 201,
"message": "Update ok!",
"meta":{
"count": 1
}
}
$ curl -X GET http://127.0.0.1:8000/user
> {
"status": 200,
"message": "Query ok!",
"data": [
{"id": 1,"account": "test2"}
]
}
$ # delete
$ curl -X DELETE http://127.0.0.1:8000/user \
-H 'content-type: application/json' \
-d '{"id": 1}'
> {
"status": 200,
"message": "Delete ok!",
"meta": {"count":1}
}
$ curl -X GET http://127.0.0.1:8000/user
> {
"status": 200,
"message": "Query ok!",
"data": []
}
现在还是觉得权限管理不太给力,虽然用中间件模式,什么样的权限都做的了。
以及多表连接在想是提供一个 class 然后把多张表设置上去,url 和权限独立,还是像你的 APIJSON
全局注册所有表随意连接任意表。
我这个库就是一个 python
库,然后不对任意一个 web 框架强绑定。
现在写了 Sanic
, Tornado
的支持。
后面有心情再考虑把 json 的表现层抽出来,做了 APIJSON
的 json 表现层。
现在还有个问题就是 Date
的格式化函数 sqlite
, mysql
, pg
每个都不一样,现在想要么都不用,全用 python
的格式..
@zeromake 赞,已Star。 不过URL里不要加user等符号哦,应该放到请求JSON里面,方便传任意表名
{
"User": {
"id": 70793
}
}
{
"Moment": {
"id": 12
}
}
... 还能自由组合
{
"Moment": {
"id": 12
},
"User": {
"id": 70793
}
}
不知道User.id的情况下可以通过 引用赋值 得到
{
"Moment": {
"id": 12
},
"User": {
"id@": "Moment/userId" // User.id = Moment.userId
}
}
{
"Moment": {
"id": 12
},
"[]": {
"User": {
"id{}@": "Moment/praiseUserIdList" // json_contains(Moment.praiseUserIdList, User.id)
}
}
}
@zeromake 你改好后我就在APIJSON主页加上这个项目的链接哈,和 C#版 一样 https://github.com/TommyLemon/APIJSON
@TommyLemon 我这个只是为了做基础控件的,对应某个表操作,全局可连接任意表的应当后面做一个单独的控制 view,全部由使用的人来选择是注册一个全局 url 进行控制,还是挂几个表view。后面都会做对应的包括单表和 APIJSON 的相同的全局可连接任意表。
@zeromake 这样啊,非常期待,记得通知我哦,有问题欢迎找我交流
@TommyLemon
issues交流比较麻烦,base64解密防搜索引擎
加我 wx:YWZseTM5MA==
;或者qq:MzkwNzIwMDQ2
@zeromake 可以
APIJSON Node.js 版,基于 typeorm ,使用 TypeScript 实现。
创作不易,给热心的作者右上角点 ⭐Star 支持下吧 ^_^ https://github.com/TEsTsLA/apijson
已赞 @TommyLemon
已经有热心的开发者实现了 PHP 版的 APIJSON 🎉 创作不易,给作者点 ⭐Star 支持下吧^_^ https://github.com/orchie/apijson
@TommyLemon 这个PHP版没有实现吧...
建议出一个功能清单.md,放在每个语言实现的代码库里,实现的功能就划掉,这样比较清晰.
有没有打算做非关系数据库mongodb的
@yuhongshuai 暂时没有计划,后续可能支持。
已经有热心的开发者实现了 Python 版的 APIJSON 🎉 经测试,除了基本的查询(分页、排序等),还实现了自动化的权限控制。 最近作者又新增了自动化 API post。
创作不易,给作者点 ⭐Star 支持下吧^_^ https://github.com/zhangchunlin/uliweb-apijson
@never615 另一个 PHP 版 APIJSON,看起来功能比原来那个多不少,可以试试,点 Star 支持下吧^_^ https://github.com/qq547057827/apijson-php
APIJSON Go 语言版 APIJSON https://github.com/crazytaxi824/APIJSON
@never615 @SwankyTigerYY @y449329998 @ben1one 最近发现了一个新的 APIJSON PHP 版实现,代码和文档看起来比之前的都要完善一些,可以试试,能用的话就 Star 鼓励下作者~ https://github.com/xianglong111/json-api
注:getQuote 是获取引号,避免表名、字段名等和关键词冲突,例如 MySQL 用反引号 `name`,PostgreSQL, Oracle, SQLServer, DB2 都可以用双引号 "date"
新增一个go版本的 https://github.com/glennliao/apijson-go
@TommyLemon 我想问一下,生成的SQL是有基于什么标准吗? 怎么才能做到生成的SQL兼容各种数据库?
@TommyLemon 我想问一下,生成的SQL是有基于什么标准吗? 怎么才能做到生成的SQL兼容各种数据库?
@jimisun123 根据配置的数据库类型和版本适配,例如 Java 版是在 DemoSQLConfig 重新父类 AbstractSQLConfig 的方法 getDatabase, getDBVersion 优先 MySQL/PostgreSQL 等热门开源数据库的 SQL 语法和 JDBC/ODBC。