本章节主要的内容是对go-admin中的一些有趣编码进行分析,为本身之后提供一些借鉴mysql
使用cobra[眼镜蛇]完成强壮cli的工具,确保稳定。git
使用cli的方式启动项目的好处显而易见,能够在进行配置的自定义化,而不是固定的使用某个配置文件中的信息。在一些须要频繁更换命令参数的场景下尤其有效。github
cobra的使用有一个默认的规定,即新建一个cmd
文件夹,基于这个文件夹定义本身的命令结构web
一、小型项目sql
cmd 文件夹数据库
-- root.go 根命令服务器
-- version.go 版本命令【子命令】app
二、中/大型项目框架
cmd 文件夹函数
--version 文件夹
--server.go
--config 文件夹
--server.go
-- root.go 入口指令
不一样的项目选用不一样的方式进行命令的定义。
// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间) quit := make(chan os.Signal) signal.Notify(quit, os.Interrupt) <-quit fmt.Printf("%s Shutdown Server ... \r\n", tools.GetCurrentTimeStr()) if err := srv.Shutdown(ctx); err != nil { log.Fatal("Server Shutdown:", err) } log.Info("Server exiting")
信号监听,当遇到ctrl+c的时候当前服务结束,打印结束日志
viper的使用,配置文件路径,获取viper根类
//数据库配置 cfgDatabase = viper.Sub("settings.database") DatabaseConfig = InitDatabase(cfgDatabase) //应用程序配置 cfgApplication = viper.Sub("settings.application") ApplicationConfig = InitApplication(cfgApplication)
其余的内容与此方式相似
viper.Sub获取对应分类的内容,而后使用自定义的Init***函数初使化类
/tools/config文件夹中定义实体类,对应config.yml中的分类
举例:application.go对应图二的application分类内容,其余的也是一样意思
此目录下的内容,package包名为config。config下的配置信息使用大写表示【对外暴露】。若是其余地方要调用,引用包后使用config.分类名称.参数
便可获得配置文件的内容
配置文件 dirver:mysql,定义不一样的数据库字符串,程序启动时case 链接字符串,对不一样的数据库类型作不一样的配置
数据库日志开关做为单独配置区分,若是开启,数据库日志单独启用
如今程序的数据库.go都须要实现接口以下
type Database interface { Setup() Open(conn string, cfg *gorm.Config) (db *gorm.DB, err error) GetConnect() string GetDriver() string }
interface定义接口,其余的go文件实现接口-使用不一样的开源数据库驱动
casbin
轻量级开源访问控制框架,采用了元模型的设计思想,支持多种经典的访问控制方案,如基于角色的访问控制 RBAC、基于属性的访问控制 ABAC 等
策略文件
// Initialize the model from a string. var text = ` [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && (keyMatch2(r.obj, p.obj) || keyMatch(r.obj, p.obj)) && (r.act == p.act || p.act == "*") `
使用 github.com/casbin/gorm-adapter/v3
做为casbin的数据库适配器便可
gin一次请求共用一个数据库实例
r.Use()是gin的中间件扩展方法,WithContextDb方法说明的是,若是每次请求到来会打开一次数据库连接,得到数据库连接实例,将实例返回后做为参数传递给方法。若是有此实例,c.Set()方法执行,将此实例保存到gin执行上下文中。
使用一个异常捕获方法recover()补获未知晓的异常
若是异常消息符合定义的规则,打印后返回给前台。
若是无异常,正常执行下一个定义的中间件
r.Use(CustomError)
func CustomError(c *gin.Context) { defer func() { if err := recover(); err != nil { if c.IsAborted() { c.Status(200) } switch errStr := err.(type) { case string: p := strings.Split(errStr, "#") if len(p) == 3 && p[0] == "CustomError" { statusCode, e := strconv.Atoi(p[1]) if e != nil { break } c.Status(statusCode) fmt.Println( time.Now().Format("2006-01-02 15:04:05"), "[ERROR]", c.Request.Method, c.Request.URL, statusCode, c.Request.RequestURI, c.ClientIP(), p[2], ) c.JSON(http.StatusOK, gin.H{ "code": statusCode, "msg": p[2], }) } default: panic(err) } } }() c.Next() }
从配置文件中获取内容,定义addr
获得对应的web执行引擎,当前使用gin
启动一个协程,判断若是ssl,使用TlS方法。不然使用正常模式
srv := &http.Server{ Addr: config.ApplicationConfig.Host + ":" + config.ApplicationConfig.Port, Handler: global.Cfg.GetEngine(), } go func() { // 服务链接 if config.SslConfig.Enable { if err := srv.ListenAndServeTLS(config.SslConfig.Pem, config.SslConfig.KeyStr); err != nil && err != http.ErrServerClosed { log.Fatal("listen: ", err) } } else { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatal("listen: ", err) } } }()