实战一:用户管理 API
把前面章节串起来做一个小而完整的“用户管理”API:先建表并执行 gf gen dao 生成 dao/do/entity(见代码生成章节),再用规范路由定义接口、logic 层做 CRUD 与密码哈希,错误统一走 gerror 错误码,main 注册启动。
接口定义(api/user/v1)
package v1
import "github.com/gogf/gf/v2/frame/g"
type UserCreateReq struct {
g.Meta `path:"/user" method:"post" tags:"User" summary:"create user"`
Name string `v:"required|length:2,30" json:"name"`
Password string `v:"required|length:6,64" json:"password"`
}
type UserCreateRes struct{ Id int64 `json:"id"` }
type UserGetOneReq struct {
g.Meta `path:"/user/{id}" method:"get" tags:"User" summary:"get one user"`
Id int64 `v:"required" in:"path" json:"id"`
}
type UserGetOneRes struct{ Id int64 `json:"id"`; Name string `json:"name"` }
入参校验声明在 v 标签,in:"path" 表示参数来自路径;Update/Delete 等结构同构扩展。密码用通用 bcrypt(golang.org/x/crypto/bcrypt)哈希后入库——GoFrame 未内置哈希组件,选型以官方为准。
业务逻辑(internal/logic/user)
package user
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"golang.org/x/crypto/bcrypt"
"userdemo/api/user/v1"
"userdemo/internal/code"
"userdemo/internal/dao"
"userdemo/internal/model/do"
"userdemo/internal/model/entity"
)
func Create(ctx context.Context, in *v1.UserCreateReq) (out *v1.UserCreateRes, err error) {
hash, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
if err != nil { return nil, err }
id, err := dao.User.Ctx(ctx).Data(do.User{Name: in.Name, Password: string(hash)}).InsertAndGetId()
if err != nil { return nil, err }
return &v1.UserCreateRes{Id: id}, nil
}
func GetOne(ctx context.Context, in *v1.UserGetOneReq) (out *v1.UserGetOneRes, err error) {
var user entity.User
if err = dao.User.Ctx(ctx).Where("id", in.Id).Scan(&user); err != nil { return nil, err }
if user.Id == 0 { return nil, gerror.NewCode(code.UserNotExist) } // 业务错误码 >1000
return &v1.UserGetOneRes{Id: user.Id, Name: user.Name}, nil
}
错误码常量放独立包:code.UserNotExist = gcode.New(1001, "用户不存在", nil),统一由响应中间件输出。
注册启动(main)
规范路由可用 BindHandler 单路由注册直接绑定业务函数;大批量接口则用 controller 包配合 group.Bind(new(v1.Controller)),可交给 gf gen ctrl/service 生成骨架:
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
apiv1 "userdemo/api/user/v1"
"userdemo/internal/logic/user"
)
func main() {
s := g.Server()
s.Use(ghttp.MiddlewareHandlerResponse) // 统一 {code,message,data}
s.BindHandler("/user", func(ctx context.Context, req *apiv1.UserCreateReq) (res *apiv1.UserCreateRes, err error) {
return user.Create(ctx, req)
})
s.BindHandler("/user/{id}", func(ctx context.Context, req *apiv1.UserGetOneReq) (res *apiv1.UserGetOneRes, err error) {
return user.GetOne(ctx, req)
})
s.Run()
}
启动后 POST /user 创建、GET /user/{id} 查询均返回统一结构;开启 openapiPath 后 /api.json 自动出文档,Swagger UI 可直接调试。
要点回顾
- DAO 调用必须
Ctx(ctx);写入用do,查询结果映射entity,参数全部走 ORM 参数化。 - 校验写在
v标签、错误抛 gerror 错误码,接口层零手写防御,返回结构全局统一。 - 密码只存哈希;单接口结构体一对 Req/Res,扩展成列表/更新/删除照抄即可。