基于go语言gin框架的web项目骨架
2023/9/24 18:08:35
本文主要是介绍基于go语言gin框架的web项目骨架,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
该骨架每个组件之间可单独使用,组件之间松耦合,高内聚,组件的实现基于其他三方依赖包的封装。
目前该骨架实现了大多数的组件,比如事件
,中间件
,日志
,配置
,参数验证
,命令行
,定时任务
等功能,目前可以满足大多数开发需求,后续会持续维护更新功能。
github地址:https://github.com/czx-lab/skeleton
设置环境变量并下载项目依赖
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct go mod download
运行项目
go run ./cmd/main.go
项目编译打包运行
go build ./cmd/main.go // 编译 make build // 运行 make run // 编译与运行 make // 运行项目 ./main
项目目录结构说明
├─app │ ├─command ---> 命令行 │ ├─controller │ │ └─base.go ---> BaseController,主要定义了request参数验证器validator │ ├─event │ │ ├─entity ---> 事件实体目录 │ │ ├─listen ---> 事件监听执行脚本目录 │ │ └─event.go ---> 事件注册代码 │ │ │ ├─middleware ---> 中间件代码目录 │ ├─request ---> 请求参数校验代码目录 │ │ └─request.go ---> 参数验证器 │ └─task ---> 定时任务代码目录 │ └─task.go ---> 注册定时任务脚本 ├─cmd ---> 项目入口目录 │ └─cli ---> 项目命令行模式入口目录 ├─config │ └─config.yaml ---> 配置文件 ├─internal ---> 包含第三方包的封装 ├─router ---> 路由目录 │ └─router.go ├─storage ---> 日志、资源存储目录 │ └─logs └─test ---> 单元测试目录
基础功能
路由
该骨架的web框架是gin,所以路由定义可直接阅读Gin框架的文档。
在该骨架中定义注册路由需要在router
文件夹下面的router.go
文件中的func (*AppRouter) Add(server *gin.Engine)
方法定义注册:
server.GET("/foo", func(ctx *gin.Context) { ctx.String(http.StatusOK, "hello word!") })
也可以通过自己定义路由的定义注册,只需要实现github.com/czx-lab/skeleton/internal/server/router
下面的Interface
接口。如下示例:
在router目录下定义了一个CustomRouter
结构体,该结构体实现了Interface
接口
package router import ( "net/http" "github.com/czx-lab/skeleton/internal/server" "github.com/gin-gonic/gin" ) type CustomRouter struct { server server.HttpServer } func NewCustom(srv server.HttpServer) *CustomRouter { return &CustomRouter{ srv, } } func (*CustomRouter) Add(srv *gin.Engine) { srv.GET("/custom", func(ctx *gin.Context) { ctx.String(http.StatusOK, "custom router") }) }
需要注意的是,如果是自定义路由注册,需要修改项目
cmd
文件夹下面的main.go
入口文件,通过http.SetRouters(router.NewCustom(http))
注册给gin
中间件
定义中间件与gin
框架一样,该估计默认实现了panic异常的中间件,可以查看internal/server/middleware
文件夹中的exception.go
文件。
如果需要定义其他的中间件并加载注册,可以将定义好的中间件通过server.HttpServer
接口的SetMiddleware(middlewares ...middleware.Interface)
方法注册加载,
比如我们实现如下自定义全局中间件middleware/custom.go
:
type Custom struct{} func (c *Custom) Handle() gin.HandlerFunc { return func(ctx *gin.Context) { fmt.Println("Custom middleware exec...") } }
然后在定义路由的地方使用server.SetMiddleware(&middleware.Custom{})
注册中间件。
定义全局路由中间件可以参考router/router.go
中的New
方法。
如果是局部中间件,可以直接在具体的路由上注册,参考gin路由中间件的用法
日志
在该骨架中的日志是直接对go.uber.org/zap
的封装,使用时,直接通过全局变量variable.Log
访问写入日志,可直接使用zap支持的所有方法。
package demo import "github.com/czx-lab/skeleton/internal/variable" func Demo() { variable.Log.Info("info message") }
日志文件默认是以json
格式写入到storage/logs/system.log
里面
配置
配置项的定义直接在config/config.yaml
文件中定义,并且配置的读取写入是通过封装github.com/spf13/viper
实现,在该骨架中,只提供了如下一些获取配置的方法:
type ConfigInterface interface { Get(key string) any GetString(key string) string GetBool(key string) bool GetInt(key string) int GetInt32(key string) int32 GetInt64(key string) int64 GetFloat64(key string) float64 GetDuration(key string) time.Duration GetStringSlice(key string) []string }
需要注意的是,骨架中对配置项的获取做了缓存的处理,第一次加载是在文件中获取,后面每次回去都是在cache
中获取,目前cache
默认只支持memory
,骨架中也支持自定义cache
的方法,只需要实现config.CacheInterface
接口就可以,比如需要使用redis
作为配置缓存,可以通过下面的方式处理:
type ConfigRedisCache struct {} var _ config.CacheInterface = (*ConfigRedisCache)(nil) func (c *ConfigRedisCache) Get(key string) any { return nil } func (c *ConfigRedisCache) Set(key string, value any) bool { return true } func (c *ConfigRedisCache) Has(key string) bool { return true } func (c *ConfigRedisCache) FuzzyDelete(key string) { }
然后将ConfigRedisCache
结构体配置到config.Options
中,如下所示,修改internal/bootstrap/init.go
初始化配置的方法:
variable.Config, err := config.New(driver.New(), config.Options{ BasePath: './', Cache: &ConfigRedisCache{} })
config.yaml
基础配置如下:
# http配置 HttpServer: Port: ":8888" # 服务模式,和gin的gin.SetMode的值是一样的 Mode: "debug" # socket配置 Websocket: WriteReadBufferSize: 2048 HeartbeatFailMaxTimes: 4 PingPeriod: 20 ReadDeadline: 100 WriteDeadline: 35 PingMsg: "ping" # 数据库配置 Database: # 可以查看GORM相关的配置选项 Mysql: SlowThreshold: 5 LogLevel: 4 ConnMaxLifetime: 1 MaxIdleConn: 2 MaxOpenConn: 2 ConnMaxIdleTime: 12 Reade: - "root:root@tcp(192.168.1.4:3306)/test?charset=utf8mb4&loc=Local&parseTime=True" Write: "root:root@tcp(192.168.1.4:3306)/test?charset=utf8mb4&loc=Local&parseTime=True" # mongo数据库的基础配置 Mongo: Enable: false Uri: MinPoolSize: 10 MaxPoolSize: 20 Redis: Disabled: false Addr: "192.168.1.4:6379" Pwd: "" Db: 0 PoolSize: 20 MaxIdleConn: 30 MinIdleConn: 10 # 单位(秒) MaxLifeTime: 60 # 单位(分) MaxIdleTime: 30 # 定时任务 Crontab: Enable: true # 消息队列,使用rocketmq MQ: Enable: false Servers: - "127.0.0.1:9876" ConsumptionSize: 1 Retries: 1
事件机制
-
定义事件实体
在
app/event/entity
目录下定义一个事件实体,该实体实现了event.EventInterface
接口:package entity type DemoEvent struct {} func (d *DemoEvent) EventName() string { return "demo-event" } func (d *DemoEvent) GetData() any { return "demo param" }
-
定义事件监听
在
app/event/listen
目录中定义一个DemoEventListen
事件监听,并且该DemoEventListen
结构体必须要实现event.Interface
接口:package listen import ( "fmt" event2 "github.com/czx-lab/skeleton/app/event/entity" "github.com/czx-lab/skeleton/internal/event" ) type DemoEventListen struct { } func (*DemoEventListen) Listen() event.EventInterface { return &event2.DemoEvent{} } func (*DemoEventListen) Process(data any) (any, error) { return fmt.Sprintf("%v --> %s", data, "exec DemoEventListen.Process"), nil }
-
最后需要将事件进行注册,在
app/event/event.go
文件中的Init
方法内执行:variable.Event.Register(&listen.DemoEventListen{})
-
调用事件执行
variable.Event.Dispatch(&entity.DemoEvent{})
验证器
gin框架本身内置了validator
校验,骨架里面只是对其参数的校验做了统一的校验入口。
通过如下方式获取进行参数的校验,并设置中文错误提示:
type Param struct { Name int `binding:"required" form:"name" query:"name" json:"name"` } appRequest, err := AppRequest.New("zh") if err != nil { return } var data Param errMap := appRequest.Validator(ctx, &data) fmt.Println(errMap)
骨架里面已经实现了默认的参数校验,可以在app/request/request.go
文件中查看。并且在controller
目录中base.go
有一个Validate(ctx *gin.Context, param any)
方法,在其他controller中要进行参数校验的时候,只需要继承base
结构体,然后调用Validate
方法。
package controller import "github.com/gin-gonic/gin" type DemoController struct { base } type DemoRequest struct { Id int `binding:"required" form:"id" query:"id" json:"id"` } func (d *DemoController) Index(ctx *gin.Context) { var param DemoRequest if err := d.base.Validate(ctx, ¶m); err == nil { ctx.JSON(http.StatusOK, gin.H{"data": param}) } else { ctx.JSON(http.StatusBadRequest, gin.H{"message": err}) } }
验证规格参考
github.com/go-playground/validator
官方文档
命令行
基于github.com/spf13/cobra
封装
-
定义命令
在
app/command
目录中定义自己的命令,比如自定义一个输出success ok
的命令package command import ( "fmt" "github.com/spf13/cobra" ) type FooCommand struct {} func (f *FooCommand) Command() *cobra.Command { return &cobra.Command{ Use: "foo", Short: "命令使用简介.", Long: `命令介绍.`, Run: func(cmd *cobra.Command, args []string) { str, _ := cmd.Flags().GetString("name") fmt.Printf("success, %s", str) }, } } func (f *FooCommand) Flags(root *cobra.Command) { root.PersistentFlags().String("name", "", "命令参数") }
-
注册命令
需要在
cmd/cli/cli.go
中的main
方法内注册自定义命令。 -
执行命令
go run cmd/cli/cli.go foo --name ok
-
查看命令信息
go run cmd/cli/cli.go help // 或者 go run cmd/cli/cli.go foo --help
定时任务
定时是通过封装github.com/robfig/cron/v3
实现
-
定义定时任务方法
在
app/task
目录下定义执行方法,比如每一分钟打印success
字符package task import "fmt" type SuccessTask struct { } // 时间规则 func (s *SuccessTask) Rule() string { return "* * * * *" } func (s *SuccessTask) Execute() func() { return func() { fmt.Println("success") } }
-
加载定时任务
需要在
app/task/task.go
文件中的Tasks
方法内,加载自定义的任务,参考task目录下的task.go
文件
这篇关于基于go语言gin框架的web项目骨架的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23MongoDB身份认证机制揭秘!
- 2024-11-20MongoDB教程:从入门到实践详解
- 2024-11-17执行 Google Ads API 查询后返回的是空数组什么原因?-icode9专业技术文章分享
- 2024-11-17google广告数据不同经理账户下的凭证可以获取对方的api数据吗?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么实现同时向多个邮箱发送邮件?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么设置header 和 标签tag 呢?-icode9专业技术文章分享
- 2024-11-12Cargo deny安装指路
- 2024-11-02MongoDB项目实战:从入门到初级应用
- 2024-11-01随时随地一键转录,Google Cloud 新模型 Chirp 2 让语音识别更上一层楼
- 2024-10-25Google Cloud动手实验详解:如何在Cloud Run上开发无服务器应用