当前位置: 首页 > news >正文

GoFrame框架学习笔记

朋友,你来晚了,不过没关系,我们的故事从现在开始,我们准备了一些使用指南,助你快速了解 GoFram

https://goframe.org/

快速开始

go版本限制

为保证框架的稳定性和安全性,GoFrame框架要求的最低的基础Go语言版本通常会比最新的Go语言版本低1~3个版本。

当前最新框架版本要求的最低Go语言版本:

golang版本 >= 1.20

安装

初始化go.mod:

go mod init hello

下载框架最新版本:

go get -u -v github.com/gogf/gf/v2

运行

我们尝试运行以下代码:

main.go
package mainimport ("fmt""github.com/gogf/gf/v2"
)func main() {fmt.Println("Hello GoFrame:", gf.VERSION)
}

Hello World

新建main.go文件

main.go
package mainimport ("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()
}

配置go mod并安装依赖

go mod init main
go mod tidy
  • 任何时候,您都可以通过 g.Server() 方法获得一个默认的 Server 对象,该方法采用单例模式设计, 也就是说,多次调用该方法,返回的是同一个 Server 对象。其中的g组件是框架提供的一个耦合组件,封装和初始化一些常用的组件对象,为业务项目提供便捷化的使用方式。
  • 通过Server对象的BindHandler方法绑定路由以及路由函数。在本示例中,我们绑定了/路由,并指定路由函数返回Hello World。
  • 在路由函数中,输入参数为当前请求对象r *ghttp.Request,该对象包含当前请求的上下文信息。在本示例中,我们通过r.Response返回对象直接Write返回结果信息。
    通过SetPort方法设置当前Server监听端口。在本示例中,我们监听8000端口,如果在没有设置端口的情况下,它默认会监听一个随机的端口。
  • 通过 Run() 方法阻塞执行 Server 的监听运行。

获取请求

直接获取请求参数

package mainimport ("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()
}

在GoFrame框架中,获取参数非常便捷。在本示例中,我们通过r.Get方法获取客户端提交的参数,该方法能够获取所有HTTP Method提交的参数, 比如Query String/Form/Body等,其内部将会根据客户端提交的类型自动识别解析,比如支持自动识别参数格式例如json/xml等。该方法的定义如下:

func (r *Request) Get(key string, def ...interface{}) *gvar.Var

可以看到,Get方法接受两个参数,第一个为参数名称,第二个参数为非必须参数,表示默认值。返回结果为一个*gvar.Var对象,该对象为GoFrame框架 提供的运行时泛型对象,开发者可以根据业务场景需要将参数转换为各种类型。

绑定结构参数与校验

我们定义一个请求的数据结构来接收客户端提交的参数信息:

type HelloReq struct {
Name string // 名称
Age int // 年龄
}

太棒了,看起来我们既可以对参数进行注释描述,也能确定参数的类型,不再需要参数名称硬编码。

package mainimport ("github.com/gogf/gf/v2/frame/g""github.com/gogf/gf/v2/net/ghttp"
)type HelloReq struct {Name string // 名称Age  int    // 年龄
}func main() {s := g.Server()s.BindHandler("/", func(r *ghttp.Request) {var req HelloReqif err := r.Parse(&req); err != nil {r.Response.Write(err.Error())return}if req.Name == "" {r.Response.Write("name should not be empty")return}if req.Age <= 0 {r.Response.Write("invalid age value")return}r.Response.Writef("Hello %s! Your Age is %d",req.Name,req.Age,)})s.SetPort(8000)s.Run()
}

parse方法没区分get与post

在本示例中:

我们通过r.Parse方法将请求参数映射到请求对象上,随后可以通过对象的方式来使用参数。 r.Parse方法支持自动解析客户端提交参数,并赋值到指定对象上。 内部有固定的名称映射逻辑,您将在开发手册的类型转换组件中详细了解到,这里不作过多介绍。
同时,我们在本示例中增加了校验逻辑,Name及Age参数不能为空。

规范路由——便捷操作

为了简化路由注册方式,避免一些繁琐的参数处理细节, 让开发者将精力聚焦于业务逻辑本身,GoFrame框架提供了规范化的路由注册方式。 规范化的路由注册方式,我们为了见名知意,便命名为了规范路由。

数据结构定义

在规范路由中,我们同样定义一个请求的数据结构来接收客户端提交的参数信息,但同时需要定义一个返回对象。 目的是为了未来返回参数扩展的需要,以及未来标准化接口文档生成的需要。

type HelloReq struct {g.Meta `path:"/" method:"get"`Name   string `v:"required" dc:"姓名"`Age    int    `v:"required" dc:"年龄"`
}
type HelloRes struct {}

简要介绍:

在请求对象中,我们多了一个g.Meta对象的引用,并给定了一些结构体标签。该对象为元数据对象,用于给结构体嵌入 一些定义的标签信息。例如在本示例中:

  • path:表示注册的路由地址。
  • method:表示注册绑定的HTTP Method。

在属性中同样出现两个新的标签名称:
v:表示校验规则,为valid的缩写,用于自动校验该参数。这里使用v:"required"表示该参数为必需参数,如果客户端未传递该参数时,服务端将会校验失败。
dc:表示参数描述信息,为description的缩写,用于描述该参数的含义。
信息
在开发手册的对应章节中,有关于全部标签信息以及校验组件的详细讲解,这里只需要了解其作用即可,不做过多介绍。

路由对象管理

为了更好地管理路由注册,特别是接口比较多的场景下,如果手动一一去配置路由与路由函数关系太过于繁琐。 我们通过对象化的形式来封装路由函数,通过对象化封装的方式来简化我们的路由管理。 我们定义一个路由对象如下:

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
}

发现多了一个req的参数

我们定义了一个Hello对象,该对象用于封装路由函数,其所有定义的公开方法都将被作为路由函数进行注册。
可以看到该路由对象的Say方法的路由函数的定义方式,相比较于func(ghttp.Request)的路由函数定义方式,更符合业务逻辑函数的定义风格。
在路由回调方法Say中,我们通过g.RequestFromCtx方法从ctx获取原始的
ghttp.Request请求对象,用于自定义返回内容数据。

完整示例代码

我们调整我们前面的Web Server程序如下:

main.go

package mainimport ("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 main() {s := g.Server()s.Group("/", func(group *ghttp.RouterGroup) {group.Bind(new(Hello),)})s.SetPort(8000)s.Run()
}

在本示例中:

通过s.Group的分组路由方式定义一组路由注册,在其回调方法中注册的所有路由,都会带有其定义的分组路由前缀/。
通过group.Bind方法注册路由对象,该方法将会遍历路由对象的所有公开方法,读取方法的输入输出结构体定义,并对其执行路由注册。

运行后,我们访问 http://127.0.0.1:8000/?name=john&age=18 可以看到,页面输出结果符合预期。

中间件试用

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

func Middleware(r *ghttp.Request) {// 前置中间件处理逻辑 r.Middleware.Next()// 后置中间件处理逻辑
}

在中间件中执行完成处理逻辑后,使用 r.Middleware.Next() 方法进一步执行下一个流程; 如果这个时候直接退出不调用 r.Middleware.Next() 方法的话,将会退出后续的执行流程(例如可以用于请求的鉴权处理)。

优雅返回json

package mainimport ("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 stringres = 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()
}

在中间件中:
通过r.GetHandlerResponse()方法获取路由函数的执行结果,即路由函数返回的第一个结果参数*HelloRes
通过r.GetError()获取路由函数的执行状态,即路由函数返回的第二个结果参数error,如果该结果不为nil,表示该路由函数执行产生了错误。
通过r.Response.WriteJson将结果整合到统一的返回数据结构Response,并编码为json格式返回给调用端。

生成接口文档

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:"返回结果"`
}

可以看到,基于前面章节的接口数据结构定义,我们在其g.Meta中增加了两个标签:

tags: 该接口属于哪个分类,或者接口模块。
summary: 接口描述。

项目脚手架

框架提供的开发工具为开发者提供了便捷的开发指令简化开发工作,提供了例如工程脚手架、代码自动生成、工具及框架更新等实用命令。工具下载地址: https://github.com/gogf/gf/releases

具体教程 看https://goframe.org/quick/scaffold-index

创建项目模板

gf init demo -u

该命令创建一个工程脚手架目录,项目名称是 demo,其中的 -u 参数用户指定是否更新项目中使用的 goframe 框架为最新版本。 GoFrame框架有独特的项目工程结构

此外还可以 根据数据库表 自动生成entity、do、dao层 以及接口文档等

项目结构、流转流程、规范(如dao和service层如何写)

https://goframe.org/docs/design/project-structure
这个写得很好 建议反复阅读

我自己写的内容

快速开始

首先生成dao、do、entity

使用cli 默认生成

生成控制器目录文件

如果你想RESTFUL方式通过下面的路径调用接口

GET 'http://127.0.0.1:8000/user/1'

那么首先,在api/接口名 下面新建文件

api/user/v1/user.gotype 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"`
}

其中,v1代表版本1 方便日后维护 修改版本,但是v1不体现在客户端的访问链接中

然后使用cli工具生成controller代码
会自动生成下面代码
/Users/john/Temp/demo/为项目根目录

$ make ctrl
generated: /Users/john/Temp/demo/api/user/user.go
generated: /Users/john/Temp/demo/internal/controller/user/user.go
generated: /Users/john/Temp/demo/internal/controller/user/user_new.go
generated: /Users/john/Temp/demo/internal/controller/user/user_v1_get_one.go
接口抽象文件/api/user/user.go——一般无需关注

其中 /api/user/user.go
定义了api interface,用于保证控制器实现的接口完整性,避免controller缺失部分接口实现的问题。由于GoFrame是一款严谨的开发框架,这种细节控制得比较好,至于该特性开发者使用与否,可以根据自身场景和需要来选择。

api/user/user.go
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================package userimport ("context""demo/api/user/v1"
)type IUserV1 interface {Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error)Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error)Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error)GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error)GetList(ctx context.Context, req *v1.GetListReq) (res *v1.GetListRes, err error)
}
路由对象管理user.go、user_new.go——一般无需关注

用于管理控制器的初始化,以及一些控制内部使用的数据结构、常量定义。

generated: /项目根目录/internal/controller/user/user.go
generated: /项目根目录/internal/controller/user/user_new.go

其中internal/controller/user/user.go是一个空的源码文件,可用于定义一些控制器内部使用的数据结构、常量等内容。

internal/controller/user/user.go
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================package user

另一个internal/controller/user/user_new.go文件是自动生成的路由对象创建文件。

internal/controller/user/user_new.go
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================package userimport ("demo/api/user"
)type ControllerV1 struct{}func NewV1() user.IUserV1 {return &ControllerV1{}
}

这两个文件都只会生成一次,随后开发者可以随意修改、扩展。

如果后续我们需要定义v2接口,make ctrl命令会类似生成type ControllerV2 struct{}结构体定义,以及func NewV2() user.IUserV2初始化方法。

controller路由实际实现代码——需要重点关注

用于具体的api接口实现的代码文件。默认情况下,会按照一个api接口一个源码文件的形式生成代码。当然,也可以控制按照api文件定义的接口聚合生成到对应的一个源码文件中。具体的命令介绍及配置请参考章节 接口规范-gen ctrl——https://goframe.org/docs/cli/gen-ctrl

generated: /项目根目录/internal/controller/user/user_v1_get_one.go

自动为我们生成的代码可能如下 :

package userimport ("context""demo/api/user/v1""demo/internal/dao"
)func (c *ControllerV1) GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error) {return nil, gerror.NewCode(gcode.CodeNotImplemented)
}

然后在此基础上修改即可,比如:

package userimport ("context""demo/api/user/v1""demo/internal/dao"
)func (c *ControllerV1) GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error) {res = &v1.GetOneRes{}err = dao.User.Ctx(ctx).WherePri(req.Id).Scan(&res.User)return
}

数据查询接口中,我们使用了Scan方法,该方法可以将查询到的单条数据表记录智能地映射到结构体对象上。大家需要注意这里的&res.User中的User属性对象其实是没有初始化的,其值为nil。如果查询到了数据,Scan方法会对其做初始化并赋值,如果查询不到数据,那么Scan方法什么都不会做,其值还是nil

配置文件

工具配置 hack/config.yaml
在前面的章节我们已经有过介绍。这个配置文件主要是本地开发时候使用,当cli脚手架工具执行时会自动读取其中的配置内容。

示例源码:https://github.com/gogf/quick-demo/blob/main/hack/config.yaml

业务配置 manifest/config/config.yaml
主要维护业务项目的组件配置信息、业务模块配置,完全由开发者自行维护。在程序启动时会读取该配置文件。该业务 默认的脚手架项目模板提供的业务配置如下:

manifest/config/config.yaml

# https://goframe.org/docs/web/server-config-file-template
server:address:     ":8000"openapiPath: "/api.json"swaggerPath: "/swagger"# https://goframe.org/docs/core/glog-config
logger:level : "all"stdout: true# https://goframe.org/docs/core/gdb-config-file
database:default:link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"

默认提供了3项组件的配置,分别为:

server:Web Server的配置。这里默认配置的监听地址为:8000,并启用了接口文档特性。
logger:默认日志组件的配置。这里的日志级别是所有日志都会打印,并且都会输出到标准输出。
database:数据库组件的配置。这只是一个模板,需要我们根据实际情况修改链接地址。
每一项组件配置的注释上提供了官网文档的配置参考链接。我们这里需要修改数据库配置中的链接信息,为我们真实可用的链接信息。关于数据库的配置详细介绍,感兴趣请参考章节:ORM使用配置-配置文件

示例源码:https://github.com/gogf/quick-demo/blob/main/manifest/config/config.yaml

添加路由注册

添加我们新填写的api到路由非常简单,如下:,在分组路由的group.Bind方法中,通过user.NewV1()添加我们的路由对象即可。

在这里插入图片描述

其他

在脚手架项目模板main.go的import中有一段_ “demo/internal/packed”,表示GoFrame框架的资源管理,这是一个高级特性。该特性可以将任何资源打包进二进制文件,这样我们在发布的时候,仅需要发布一个二进制文件即可。我们这里没有用到该特性,因此大家了解即可,感兴趣可以后续查阅开发手册相关章节。

http://www.dtcms.com/a/439259.html

相关文章:

  • 东莞 网站建设淄博网站建设公司哪家好
  • 未备案运行网站2022年最新国际军事新闻
  • 门户网站集群建设域名注册服务商
  • MSF后渗透(提权)
  • 优秀的摄影作品网站企业管理课程视频
  • SNP亮相2025德莱维数字技术行业峰会
  • 中文人名生成器中文姓名姓氏名字称呼日本人名翻译人名英文人名可用于中文分词人名实体识别
  • 【Svelte】加载数据实现响应式的正确方式
  • 出售自己的网站Add-ons wordpress
  • 网络安全相关的专业术语
  • 帝国cms影视网站模板宁波网站制作哪家强
  • (一)算法
  • 23ICPC济南站补题
  • 商务网站建设ppt模板培训网站排名
  • 南阳市宛城区建设局网站设计本质
  • nacos使用指南
  • 中山AI搜索哪家好?GEO优化与传统SEO深度解析
  • MySQL优化----非查询SQL优化
  • 影视网站代理徐州市经济技术开发区建设局网站
  • 基于单片机的三相逆变电源设计
  • Python基础入门例程92-NP92 公式计算器
  • MyCat实现分库分表
  • 宿迁网站建设公司宣传海报制作
  • 中铁中基建设集团网站品牌形象网站源码
  • 中医院网站源码传智黑马培训机构
  • python全栈(基础篇)——day01:环境准备(python解释器安装+pycharm专业版的下载+vscode编辑器+汉化设置)
  • CodeForces Round 1054(div.3)C题
  • 南宁网站建设制作定制seo搜索引擎优化招聘
  • 3.java常用类知识点
  • 费马小定理证明