Skip to content

GoFrame框架

Hello World

go
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等。该方法的定义如下:

go
func (r *Request) Get(key string, def ...interface{}) *gvar.Var
go
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方法支持自动解析客户端提交参数,并赋值到指定对象上。

go
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 属性对象来控制请求流程。

中间件的类型分为两种:前置中间件后置中间件。前置即在路由服务函数调用之前调用,后置即在其后调用。

go
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()
}

统一返回结构

go
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可自定义修改,具体可参考开发手册相应章节。

go
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
go install github.com/gogf/gf/cmd/gf/v2@latest
sh
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>
go
初始化:gf init demo -u

使用

项目启动 | GoFrame官网 - 类似PHP-Laravel,Java-SpringBoot的Go语言开发框架

接口开发

设计数据表

sql
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

go
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定义中,该标签则可作为参数描述。

生成命令
sh
win:gf gen dao
mac/linux:make dao

每张表将会生成三类Go文件:

  • dao:通过对象方式访问底层数据源,底层基于ORM组件实现。
  • do:数据转换模型,用于业务模型到数据模型的转换,由工具维护,用户不能修改。工具每次生成代码文件将会覆盖该目录。
  • entity:数据模型,由工具维护,用户不能修改。工具每次生成代码文件将会覆盖该目录。
dao

生成两个dao

  1. internal/dao/internal/user.go用于封装对数据表user的访问。该文件自动生成了一些数据结构和方法,简化对数据表的CRUD操作。该文件每次生成都会覆盖,由开发工具自动维护,开发者无需关心。
  2. internal/dao/user.go其实是对internal/dao/internal/user.go的进一步封装,用于供其他模块直接调用访问。该文件开发者可以随意修改,或者扩展dao的能力。
do数据模型转换
entity实体

接口定义

  1. 接口我们使用RESTful风格设计,充分使用GET/POST/PUT/DELETEHTTP Method,这样规范设计的接口会非常优雅。
  2. 同样的,我们默认开始使用v1版本。使用版本号做为良好的开发习惯,有利于未来接口的兼容性维护。
go
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项组件的配置,分别为:

  • serverWeb Server的配置。这里默认配置的监听地址为:8000,并启用了接口文档特性。
  • logger:默认日志组件的配置。这里的日志级别是所有日志都会打印,并且都会输出到标准输出。
  • xxxxxxxxxx package main​import ( "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到路由

image-20250621120036982

启动

gogogo

Released under the MIT License.