Skip to content

3.4 请求与校验 ​

概述

本项目没有独立的"验证器文件"——请求校验由 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.Metagf 框架内嵌后由本项目 bind.go 扩展解析
method/path/summarygmetaHTTP 方法、路由模板(:id 路由参数)、OpenAPI 摘要
permissiongmeta权限节点 sys:{module小驼峰}:{action};空 = 登录即可
log/logTypegmeta操作日志标题与类型(query/add/update/delete/import/export…)
publicgmeta"true" = 免登录(须与 middleware/public.go 白名单一致)
json标准出入参字段名,一律驼峰(例外仅登录/令牌接口的下划线字段)
vgvalid校验规则,如 required#请输入案例名称(# 后为自定义错误文案)
dcgoaiOpenAPI 文档中的字段说明
pghttpmultipart 上传字段名(如 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 逻辑层开发)。

小蚂蚁云团队 · 提供技术支持