gormpher 是一个轻量级的 Golang 库
gormpher github:https://github.com/restsend/gormpher
使用 gormpher 的开源项目 rabbit-admin:https://github.com/szluyu99/rabbit-admin
建议看完本文,理解 gormpher 后可以查看该开源项目,体会其在实际开发中到底能节约多少工作量
先直观的体会一下这个项目最基础的功能是用来做什么的。
go get github.com/restsend/gormpher
示例源于 gormpher 仓库下 example/main.go:(下面是其简化版)
代码版本可能会更新,文章中不一定是最新的,可以参考上面的 example/main.go 文件获取最新用法
使用流程:
primarykey
package main
import (
"flag"
"time"
"github.com/gin-gonic/gin"
"github.com/restsend/gormpher"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// 1. 定义结构体,指定 primarykey
type User struct {
ID uint `json:"id" gorm:"primarykey"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Name string `json:"name"`
Age int `json:"age"`
Enabled bool `json:"enabled"`
LastLogin *time.Time `json:"lastLogin"`
}
func main() {
var dsn string
var addr string
flag.StringVar(&dsn, "n", "", "DB DSN")
flag.StringVar(&addr, "a", ":8890", "Api Server Addr")
flag.Parse()
// 2. gorm db, gin router
db, _ := gorm.Open(sqlite.Open(dsn), nil)
db.AutoMigrate(User{})
r := gin.Default()
// 3. 创建 gormpher WebObject 对象(User 为模型对象)
objs := []gormpher.WebObject{
{
Name: "user",
Model: &User{},
SearchFields: []string{"Name", "Enabled"}, // 可模糊搜索的字段
EditFields: []string{"Name", "Age", "Enabled", "LastLogin"}, // 可编辑的字段
FilterFields: []string{"Name", "CreatedAt", "Age", "Enabled"}, // 可条件查询的字段
OrderFields: []string{"CreatedAt", "Age", "Enabled"}, // 可排序的字段
GetDB: func(ctx *gin.Context, isCreate bool) *gorm.DB { return db }, // 返回 *gorm.DB 的方法
},
}
// 4. 注册 gormpher WebObject 对象到指定路由组,生成以下 API
// PUT http://localhost:8890/api/user
// GET http://localhost:8890/api/user/:key
// PATCH http://localhost:8890/api/user/:key
// POST http://localhost:8890/api/user
// DELETE http://localhost:8890/api/user/:key
gormpher.RegisterObjects(r.Group("api"), objs)
// 5. (可选)将 WebObject 对象注册到 Admin 中,展示一个测试用的 Web 界面
// 访问 URL: http://localhost:8890/admin
gormpher.RegisterObjectsWithAdmin(r.Group("admin"), objs)
r.Run(addr)
}
运行该程序:
go run main.go
经过上面简短的代码,我们就生成了以下 API:
[GIN-debug] GET /api/user/:key --> github.com/restsend/gormpher.(*WebObject).RegisterObject.func1 (3 handlers)
[GIN-debug] PUT /api/user --> github.com/restsend/gormpher.(*WebObject).RegisterObject.func2 (3 handlers)
[GIN-debug] PATCH /api/user/:key --> github.com/restsend/gormpher.(*WebObject).RegisterObject.func3 (3 handlers)
[GIN-debug] DELETE /api/user/:key --> github.com/restsend/gormpher.(*WebObject).RegisterObject.func4 (3 handlers)
[GIN-debug] POST /api/user --> github.com/restsend/gormpher.(*WebObject).RegisterObject.func5 (3 handlers)
目前你可能不太理解这些接口的用法,具体参数本文后面会介绍:核心约定。
访问:http://localhost:8890/admin 查看测试用的 Web 界面
先尝试体验一下,通过上面这么简单的几行代码可以实现的接口效果。
平时开发中的容易重复的 CRUD 代码可以简单分为以下情况:
概述:Gormpher 的核心思想是基于模型对象来生成其 API,例如对于一个 user
模块
key
的 user 数据key
的 user 数据
key
的单个 user 数据涉及到请求体全部使用
Content-Type: application/json
传输数据
即对于 user
模块,最终我们生成的 API 如下:
GET /user/:key
PUT /user
PATCH /user/:key
DELETE /user/:key
POST /user
然后再解析一下以上的行为,并给出对应的接口请求与响应示例。
GET /user/:key
key
为数据的主键# Path
GET /user/1
{
"id": 1,
"createdAt": "2023-06-13T23:43:27.590377962+08:00",
"updatedAt": "2023-06-13T23:43:27.590377962+08:00",
"name": "u1",
"age": 10,
"enabled": false,
"lastLogin": "2023-06-01T23:43:00Z"
}
DELETE /user/:key
key
为数据的主键# Path
DELETE /user/1
Boolean
,表示是否删除成功true
PUT /user
# Path
PUT /user
# Request Body
{
"name": "u2",
"age": 5,
"lastLogin": "2023-06-01T23:49",
"enabled": true
}
{
"id": 2,
"createdAt": "2023-06-13T23:50:01.615012885+08:00",
"updatedAt": "2023-06-13T23:50:01.615012885+08:00",
"name": "u2",
"age": 5,
"enabled": true,
"lastLogin": "2023-06-01T23:49:00Z"
}
PATCH /user/:key
key
为数据的主键# Path
PATCH /user/1
# Request Body
{
"enabled" : true,
"name": "aaaa"
}
Boolean
,表示是否编辑成功true
注意:
EditFields
EditFeilds
中指定的字段,则无法编辑成功示例中指定了 user 可编辑字段为:Name
、Age
、Enabled
、LastLogin
{
// ...
Model: &User{},
EditFields: []string{"Name", "Age", "Enabled", "LastLogin"}, // 可编辑的字段
// ...
}
POST /user
QueryForm
对象
QueryResult
对象这是一个非常重要且复杂的行为,大部分重复的代码都出自它,为抽象出一个通用规则,我们将以下字段暴露成可配置项:
SearchFields
:可以被模糊搜索的字段FilterFields
:可以被条件查询的字段OrderFields
:可以被排序的字段{
// ...
Model: &User{},
SearchFields: []string{"Name", "Enabled"}, // 可模糊搜索的字段
FilterFields: []string{"Name", "CreatedAt", "Age", "Enabled"}, // 可条件查询的字段
OrderFields: []string{"CreatedAt", "Age", "Enabled"}, // 可排序的字段
// ...
}
查询表单对象:QueryForm
Name | Type | Desc | Default |
---|---|---|---|
pos | number | 分页参数,数据查询位置 | 0 |
limit | number | 分页参数 ,数据查询范围 | 50 |
keyword | number | 模糊搜索关键字,字段需要配置在 SearchFields 中 |
“” |
filters | []Filter | 条件查询对象数组,字段需要配置在 FilterFields 中 |
null |
orders | []Order | 排序对象数组,字段需要配置在 OrderFields 中 |
null |
Filter:
Name | Op | Desc |
---|---|---|
name | string | 字段 |
op | string | = , <> , in , not_in , > , >= , < , <= |
value | string | 值 |
示例:检索 age > 10 且 enabled = true 的数据,请求参数如下
{
filters: [
{ "name": "age", "op": ">", "value": 10 },
{ "name": "enabled", "op": "=", "value": true }
]
}
Order:
Name | Op | Desc |
---|---|---|
name | string | |
op | string | asc, desc |
示例:检索按 age 降序排序的数据,请求参数如下
{
orders: [
{ "name": "age", op: "desc" }
]
}
请求示例:检索条件如下
{
"pos": 0,
"limit": 10,
"keyword": "u",
"filters": [
{ "name": "age", "op": ">", "value": 10 },
{ "name": "enabled", "op": "=", "value": true }
],
"orders": [
{ "name": "age", op: "desc" }
]
}
查询结果:QueryResult
Name | Type | Desc |
---|---|---|
pos | number | 本次查询中分页参数 |
limit | number | 本次查询分页参数 |
keyword | string | 本次查询模糊搜索关键字 |
total | number | 本次查询数据总数 |
items | []object | 本次查询数据数组 |
响应示例:
{
"total": 2,
"pos": 0,
"limit": 10,
"items": [
{
"id": 1,
"createdAt": "2023-06-13T23:43:27.590377962+08:00",
"updatedAt": "2023-06-14T00:23:36.46322106+08:00",
"name": "u1",
"age": 10,
"enabled": true,
"lastLogin": "2023-06-01T23:43:00Z"
},
{
"id": 2,
"createdAt": "2023-06-13T23:50:01.615012885+08:00",
"updatedAt": "2023-06-13T23:50:01.615012885+08:00",
"name": "u2",
"age": 5,
"enabled": true,
"lastLogin": "2023-06-01T23:49:00Z"
}
]
}
下面的内容后续完善。