GoFrame框架
Hello World
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello World!")
})
s.SetPort(8000)
s.Run()
}
细嗦
- 任何时候,都可以通过
g.Server()
方法获得一个默认的Server
对象,该方法采用单例模式设计, 也就是说,多次调用该方法,返回的是同一个Server
对象。其中的g
组件是框架提供的一个耦合组件,封装和初始化一些常用的组件对象,为业务项目提供便捷化的使用方式。 - 通过
Server
对象的BindHandler
方法绑定路由以及路由函数。在本示例中,我们绑定了/
路由,并指定路由函数返回Hello World
。 - 在路由函数中,输入参数为当前请求对象
r *ghttp.Request
,该对象包含当前请求的上下文信息。在本示例中,我们通过r.Response
返回对象直接Write
返回结果信息。 - 通过
SetPort
方法设置当前Server
监听端口。在本示例中,我们监听8000
端口,如果在没有设置端口的情况下,它默认会监听一个随机的端口。 - 通过
Run()
方法阻塞执行Server
的监听运行。
获取请求参数
在GoFrame
框架中,获取参数非常便捷。通过r.Get
方法获取客户端提交的参数,该方法能够获取所有HTTP Method
提交的参数, 比如Query String/Form/Body
等,其内部将会根据客户端提交的类型自动识别解析,比如支持自动识别参数格式例如json/xml
等。该方法的定义如下:
func (r *Request) Get(key string, def ...interface{}) *gvar.Var
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
// 获取参数
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writef(
"Hello %s! Your Age is %d",
r.Get("name", "unknown").String(),
r.Get("age").Int(),
)
})
s.SetPort(8000)
s.Run()
}
请求数据结构
通过r.Parse
方法将请求参数映射到请求对象上,随后可以通过对象的方式来使用参数。 r.Parse
方法支持自动解析客户端提交参数,并赋值到指定对象上。
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
// 请求参数结构体
type HelloReq struct {
Name string // 名称
Age int // 年龄
}
// main函数是程序的入口点。
func main() {
s := g.Server()
// 绑定根路径的请求处理函数
s.BindHandler("/", func(r *ghttp.Request) {
var req HelloReq
// 解析请求参数到结构体中。
err := r.Parse(&req)
if err != nil {
// 如果解析出错,写入错误信息并返回。
r.Response.Write(err.Error())
return
}
// 检查Name参数是否为空。
if req.Name == "" {
// 如果Name为空,写入提示信息并返回。
r.Response.Write("参数name不能为空")
return
}
// 检查Age参数是否小于等于0。
if req.Age <= 0 {
// 如果Age不合法,写入提示信息并返回。
r.Response.Write("参数age不能小于0")
return
}
// 如果请求参数有效,构建并发送问候消息。
r.Response.Writef(
"Hello %s! Your Age is %d",
req.Name,
req.Age,
)
})
s.SetPort(8000)
s.Run()
}
规范路由
在请求对象中,我们多了一个g.Meta
对象的引用,并给定了一些结构体标签。该对象为元数据对象,用于给结构体嵌入 一些定义的标签信息。
path
:表示注册的路由地址。method
:表示注册绑定的HTTP Method
。
在属性中同样出现两个新的标签名称:
v
:表示校验规则,为valid
的缩写,用于自动校验该参数。这里使用v:"required"
表示该参数为必需参数,如果客户端未传递该参数时,服务端将会校验失败。dc
:表示参数描述信息,为description
的缩写,用于描述该参数的含义。
通过s.Group
的分组路由方式定义一组路由注册,在其回调方法中注册的所有路由,都会带有其定义的分组路由前缀/
。
通过group.Bind
方法注册路由对象,该方法将会遍历路由对象的所有公开方法,读取方法的输入输出结构体定义,并对其执行路由注册。
中间件
中间件是一种拦截器设计,在Web Server
中可以拦截请求和返回结果,并在其前后进行自定义处理逻辑。
中间件的定义和普通的路由函数一样,但是可以在 Request
参数中使用 Middleware
属性对象来控制请求流程。
中间件的类型分为两种:前置中间件和后置中间件。前置即在路由服务函数调用之前调用,后置即在其后调用。
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
// 请求参数结构体
type HelloReq struct {
g.Meta `path:"/" method:"get"`
Name string `v:"required" dc:"姓名"`
Age int `v:"required" dc:"年龄"`
}
// 响应结构体
type HelloRes struct{}
// 控制器(
type Hello struct{}
func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
// 获取请求对象
r := g.RequestFromCtx(ctx)
r.Response.Writef(
"Hello %s! Your Age is %d",
req.Name,
req.Age,
)
return
}
// 错误处理(使用中间件)
func ErrorHandler(r *ghttp.Request) {
// 执行路由函数
r.Middleware.Next()
// 判断是否产生错误
if err := r.GetError(); err != nil {
r.Response.Write("error occurs: ", err.Error())
return
}
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ErrorHandler)
group.Bind(
new(Hello),
)
})
s.SetPort(8000)
s.Run()
}
统一返回结构
package main
import (
"context"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
type Response struct {
Message string `json:"message" dc:"消息提示"`
Data interface{} `json:"data" dc:"执行结果"`
}
type HelloReq struct {
g.Meta `path:"/" method:"get"`
Name string `v:"required" json:"name" dc:"姓名"`
Age int `v:"required" json:"age" dc:"年龄"`
}
type HelloRes struct {
Content string `json:"content" dc:"返回结果"`
}
type Hello struct{}
func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
res = &HelloRes{
Content: fmt.Sprintf(
"Hello %s! Your Age is %d",
req.Name,
req.Age,
),
}
return
}
func ResponseMiddleware(r *ghttp.Request) {
r.Middleware.Next()
var (
msg string
res = r.GetHandlerResponse()
err = r.GetError()
)
if err != nil {
msg = err.Error()
} else {
msg = "OK"
}
r.Response.WriteJson(Response{
Message: msg,
Data: res,
})
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ResponseMiddleware)
group.Bind(
new(Hello),
)
})
s.SetPort(8000)
s.Run()
}
接口文档
g.Meta
中增加了两个标签:
tags
: 该接口属于哪个分类,或者接口模块。summary
: 接口描述。
通过s.SetOpenApiPath("/api.json")
启用OpenAPIv3
的接口文档生成,并指定生成的文件路径/api.json
。
通过s.SetSwaggerPath("/swagger")
启用内置的Swagger
接口文档UI,并指定客访问的UI地址为/swagger
。内置的Swagger UI
可自定义修改,具体可参考开发手册相应章节。
package main
import (
"context"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
type Response struct {
Message string `json:"message" dc:"消息提示"`
Data interface{} `json:"data" dc:"执行结果"`
}
type HelloReq struct {
g.Meta `path:"/" method:"get" tags:"Test" summary:"Hello world test case"`
Name string `v:"required" json:"name" dc:"姓名"`
Age int `v:"required" json:"age" dc:"年龄"`
}
type HelloRes struct {
Content string `json:"content" dc:"返回结果"`
}
type Hello struct{}
func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error) {
res = &HelloRes{
Content: fmt.Sprintf(
"Hello %s! Your Age is %d",
req.Name,
req.Age,
),
}
return
}
func ResponseMiddleware(r *ghttp.Request) {
r.Middleware.Next()
var (
msg string
res = r.GetHandlerResponse()
err = r.GetError()
)
if err != nil {
msg = err.Error()
} else {
msg = "OK"
}
r.Response.WriteJson(Response{
Message: msg,
Data: res,
})
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ResponseMiddleware)
group.Bind(
new(Hello),
)
})
s.SetOpenApiPath("/api.json")
s.SetSwaggerPath("/swagger")
s.SetPort(8000)
s.Run()
}
脚手架
安装
go install github.com/gogf/gf/cmd/gf/v2@latest
PS D:\code\study\go\doframe_demo> gf -v
v2.9.0
Welcome to GoFrame!
Env Detail:
Go Version: go1.24.4 windows/amd64
GF Version(go.mod):
github.com/gogf/gf/v2@v2.9.0
CLI Detail:
Installed At: D:\software\go_repository\bin\gf.exe
Built Go Version: go1.24.4
Built GF Version: v2.9.0
Others Detail:
Docs: https://goframe.org
Now : 2025-06-21T10:57:01+08:00
PS D:\code\study\go\doframe_demo>
初始化:gf init demo -u
使用
项目启动 | GoFrame官网 - 类似PHP-Laravel,Java-SpringBoot的Go语言开发框架
接口开发
设计数据表
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'user id',
`name` varchar(45) DEFAULT NULL COMMENT 'user name',
`status` tinyint DEFAULT NULL COMMENT 'user status',
`age` tinyint unsigned DEFAULT NULL COMMENT 'user age',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
生成/dao/do/entity
检查本地cli配置
gf_cli_demo/hack/config.yaml
gfcli:
gen:
dao:
- link: "mysql:root:root@tcp(127.0.0.1:3306)/go_test"
descriptionTag: true
docker:
build: "-a amd64 -s linux -p temp -ew"
tagPrefixes:
- my.image.pub/my-app
其中的dao
部分配置即本次将要执行的命令的配置,其中的link
为需要连接的数据库配置。descriptionTag
表示为生成的entity
代码文件增加字段描述到description
标签中,如果数据表的entity
对象被用到了接口api
定义中,该标签则可作为参数描述。
生成命令
win:gf gen dao
mac/linux:make dao
每张表将会生成三类Go
文件:
dao
:通过对象方式访问底层数据源,底层基于ORM
组件实现。do
:数据转换模型,用于业务模型到数据模型的转换,由工具维护,用户不能修改。工具每次生成代码文件将会覆盖该目录。entity
:数据模型,由工具维护,用户不能修改。工具每次生成代码文件将会覆盖该目录。
dao
生成两个dao
internal/dao/internal/user.go
用于封装对数据表user
的访问。该文件自动生成了一些数据结构和方法,简化对数据表的CRUD
操作。该文件每次生成都会覆盖,由开发工具自动维护,开发者无需关心。internal/dao/user.go
其实是对internal/dao/internal/user.go
的进一步封装,用于供其他模块直接调用访问。该文件开发者可以随意修改,或者扩展dao
的能力。
do数据模型转换
entity实体
接口定义
- 接口我们使用
RESTful
风格设计,充分使用GET/POST/PUT/DELETE
的HTTP Method
,这样规范设计的接口会非常优雅。 - 同样的,我们默认开始使用
v1
版本。使用版本号做为良好的开发习惯,有利于未来接口的兼容性维护。
package v1
import (
"gf_cli_demo/internal/model/entity"
"github.com/gogf/gf/v2/frame/g"
)
// 增
type CreateReq struct {
g.Meta `path:"/user" method:"post" tags:"User" summary:"Create user"`
Name string `v:"required|length:3,10" dc:"user name"`
Age uint `v:"required|between:18,200" dc:"user age"`
}
type CreateRes struct {
Id int64 `json:"id" dc:"user id"`
}
// 删
type DeleteReq struct {
g.Meta `path:"/user/{id}" method:"delete" tags:"User" summary:"Delete user"`
Id int64 `v:"required" dc:"user id"`
}
type DeleteRes struct{}
// Status marks user status.
type Status int
const (
StatusOK Status = 0 // User is OK.
StatusDisabled Status = 1 // User is disabled.
)
// 改
type UpdateReq struct {
g.Meta `path:"/user/{id}" method:"put" tags:"User" summary:"Update user"`
Id int64 `v:"required" dc:"user id"`
Name *string `v:"length:3,10" dc:"user name"`
Age *uint `v:"between:18,200" dc:"user age"`
Status *Status `v:"in:0,1" dc:"user status"`
}
type UpdateRes struct{}
// 查 (单个)
type GetOneReq struct {
g.Meta `path:"/user/{id}" method:"get" tags:"User" summary:"Get one user"`
Id int64 `v:"required" dc:"user id"`
}
type GetOneRes struct {
*entity.User `dc:"user"`
}
// 查 (列表)
type GetListReq struct {
g.Meta `path:"/user" method:"get" tags:"User" summary:"Get users"`
Age *uint `v:"between:18,200" dc:"user age"`
Status *Status `v:"in:0,1" dc:"user status"`
}
type GetListRes struct {
List []*entity.User `json:"list" dc:"user list"`
}
controller生成
当api
定义完成后,我们通过make ctrl
命令(或者gf gen ctrl
)生成controller代码。
Step4 - 生成controller代码 | GoFrame官网 - 类似PHP-Laravel,Java-SpringBoot的Go语言开发框架
接口逻辑实现
Step5 - 完成接口逻辑实现 | GoFrame官网 - 类似PHP-Laravel,Java-SpringBoot的Go语言开发框架
配置与路由
数据库配置
main.go 加上驱动:_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
工具配置 hack/config.yaml
在前面的章节我们已经有过介绍。这个配置文件主要是本地开发时候使用,当cli
脚手架工具执行时会自动读取其中的配置内容。
业务配置 manifest/config/config.yaml
主要维护业务项目的组件配置信息、业务模块配置,完全由开发者自行维护。在程序启动时会读取该配置文件。
默认提供了3
项组件的配置,分别为:
server
:Web Server
的配置。这里默认配置的监听地址为:8000
,并启用了接口文档特性。logger
:默认日志组件的配置。这里的日志级别是所有日志都会打印,并且都会输出到标准输出。- xxxxxxxxxx package mainimport ( "log" "net/http" "time" "github.com/gin-gonic/gin" "golang.org/x/sync/errgroup")var ( g errgroup.Group)func router01() http.Handler { e := gin.New() e.Use(gin.Recovery()) e.GET("/", func(c *gin.Context) { c.JSON( http.StatusOK, gin.H{ "code": http.StatusOK, "error": "Welcome server 01", }, ) }) return e}func router02() http.Handler { e := gin.New() e.Use(gin.Recovery()) e.GET("/", func(c *gin.Context) { c.JSON( http.StatusOK, gin.H{ "code": http.StatusOK, "error": "Welcome server 02", }, ) }) return e}func main() { server01 := &http.Server{ Addr: ":8080", Handler: router01(), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } server02 := &http.Server{ Addr: ":8081", Handler: router02(), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } // 借助errgroup.Group或者自行开启两个goroutine分别启动两个服务 g.Go(func() error { return server01.ListenAndServe() }) g.Go(func() error { return server02.ListenAndServe() }) if err := g.Wait(); err != nil { log.Fatal(err) }}go
路由配置
添加我们新填写的api
到路由
启动
gogogo