Files
easygoadmin-wiki/EasyGoAdmin_GoFrame_NaiveVue/zh/guide/validator.md
T
2026-10-20 21:53:26 +08:00

129 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 3.4 请求与校验
:::tip 概述
本项目没有独立的"验证器文件"——请求校验由 `api/v1` 请求结构体上的 gvalid 标签完成,路由/权限/日志元数据由内嵌 gmeta.Meta 声明。一个结构体即接口契约的全部。
:::
## 请求结构体解剖(真实代码,摘自 api/v1/example.go)
```go
// ============================== 案例演示模块(goframe_example) ==============================
// ExamplePageReq 案例演示分页请求
type ExamplePageReq struct {
gmeta.Meta `method:"get" path:"/api/example/page" summary:"案例演示分页" permission:"sys:example:page" log:"案例演示-查询分页记录" logType:"query"`
PageReq
Name string `json:"name" dc:"案例名称"` // 案例名称
Type int `json:"type" dc:"案例类型"` // 案例类型
Status int `json:"status" dc:"案例状态"` // 案例状态
Keyword string `json:"keyword" dc:"关键词"` // 关键词
}
// ExampleDetailReq 案例演示详情请求
type ExampleDetailReq struct {
gmeta.Meta `method:"get" path:"/api/example/detail/:id" summary:"案例演示详情" permission:"sys:example:detail" log:"案例演示-查询详情" logType:"query"`
Id int64 `json:"id" v:"required#缺少ID" dc:"记录ID"` // 记录ID
}
// ExampleAddReq 新增案例演示请求
type ExampleAddReq struct {
gmeta.Meta `method:"post" path:"/api/example/add" summary:"新增案例演示" permission:"sys:example:add" log:"案例演示-新增记录" logType:"add"`
Name string `json:"name" v:"required#请输入案例名称" dc:"案例名称"` // 案例名称
Avatar string `json:"avatar" dc:"案例图片"` // 案例图片
Type int `json:"type" dc:"案例类型"` // 案例类型
Status int `json:"status" dc:"案例状态"` // 案例状态
Sort int `json:"sort" dc:"案例排序"` // 案例排序
}
```
## 标签体系一览
| 标签 | 来源 | 作用 |
|------|------|------|
| `gmeta.Meta` | gf 框架 | 内嵌后由本项目 bind.go 扩展解析 |
| `method/path/summary` | gmeta | HTTP 方法、路由模板(`:id` 路由参数)、OpenAPI 摘要 |
| `permission` | gmeta | 权限节点 `sys:{module小驼峰}:{action}`;空 = 登录即可 |
| `log/logType` | gmeta | 操作日志标题与类型(query/add/update/delete/import/export…) |
| `public` | gmeta | `"true"` = 免登录(须与 middleware/public.go 白名单一致) |
| `json` | 标准 | 出入参字段名,**一律驼峰**(例外仅登录/令牌接口的下划线字段) |
| `v` | gvalid | 校验规则,如 `required#请输入案例名称`(# 后为自定义错误文案) |
| `dc` | goai | OpenAPI 文档中的字段说明 |
| `p` | ghttp | multipart 上传字段名(如 `p:"file"`) |
## gvalid 常用规则
```go
Id int64 `json:"id" v:"required#缺少ID"` // 必填
Age int `json:"age" v:"min:0#年龄不能为负"` // 数值范围
Ids []int64 `json:"ids" v:"required#缺少ID列表"` // 数组必填
Email string `json:"email" v:"email#邮箱格式不正确"` // 格式
Status int `json:"status" v:"in:1,2#状态值不合法"` // 枚举(如角色 dataScope 用 in:1,2,3,4)
```
校验失败由框架返回 code=400 信封,错误文案取 `#` 后缀;无需在业务代码里手写 if 判断。
## 路由参数承接
gmeta path 中的 `:id` 由请求结构体同名(驼峰 json)字段承接:
```text
路由模板 GET /api/example/detail/:id
└─▶ ExampleDetailReq.Id int64 `json:"id"` ← r.Parse 自动绑定
```
## 请求解析流程(Bind 包装层完成)
```text
1. r.Parse(req):路由参数 + query + body 一次性绑定(含 gvalid 校验)
2. 实现 BodyUnmarshaler 接口的请求对象走自定义解析逃生舱
(请求体顶层形态无法用结构体标签表达时使用,gvalid 需在实现内自完成)
3. 校验失败 → response.Fail(code=400) 短路返回,业务方法不执行
```
## 响应结构体(真实代码,摘自 api/v1/example.go)
```go
// ExamplePageRes 案例演示分页结果
// 统一壳:records/total/size/current/pages
type ExamplePageRes = PageData[ExampleItem]
// ExampleListRes 案例演示平铺列表结果
// data 直接为数组,树形结构由前端 buildTree 构建
type ExampleListRes = []ExampleItem
// ExampleIdRes 新增返回ID结构
type ExampleIdRes struct {
Id int64 `json:"id"` // 主键ID
}
// ExampleUpdateRes 修改案例演示结果
type ExampleUpdateRes struct{}
```
要点:
+ 分页结果统一用泛型壳 `PageData[T]`(`v1.NewPageData` 构造)
+ 新增返回统一 `{id}` 结构(模块自定义 `XxxIdRes`;公共场景用 `common.IdRes`)
+ 修改/删除等空结果返回空结构体 `struct{}`
+ 行结构体含 `{Field}Text string` 字段时由基座自动填字典显示名
## 行结构体示例(含字典文本字段)
```go
// ExampleItem 案例演示行数据
// 用于分页列表等场景的基础信息
type ExampleItem struct {
Id int `json:"id"` // 主键ID
Name string `json:"name"` // 案例名称
Avatar string `json:"avatar"` // 案例图片
Type int `json:"type"` // 案例类型
Status int `json:"status"` // 案例状态
Sort int `json:"sort"` // 案例排序
CreateUser string `json:"createUser"` // 创建人
CreateTime *gtime.Time `json:"createTime"` // 创建时间
StatusText string `json:"statusText"` // 状态显示名(dict_item 解析)
}
```
`StatusText` 与 `Status` 配对,配合 logic 里 `SerializeMaps = {"status": "example_status"}` 声明,读取时自动填充(见 3.5 逻辑层开发)。