当前位置 博文首页 > happlyfox:go-admin在线开发平台学习-3[细节解析]

    happlyfox:go-admin在线开发平台学习-3[细节解析]

    作者:happlyfox 时间:2021-02-18 18:32

    本章节主要的内容是对go-admin中的一些有趣编码进行分析,为自己以后提供一些借鉴

    使用cli方式启动项目

    使用cobra[眼镜蛇]完成强壮cli的工具,确保稳定。

    使用cli的方式启动项目的好处显而易见,可以在进行配置的自定义化,而不是固定的使用某个配置文件中的信息。在一些需要频繁更换命令参数的场景下尤为有效。

    cobra的使用有一个默认的规定,即新建一个cmd文件夹,基于这个文件夹定义自己的命令结构

    1、小型项目

    cmd 文件夹

    -- root.go 根命令

    -- version.go 版本命令【子命令】

    2、中/大型项目

    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的使用,配置文件路径,获取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分类内容,其他的也是同样意思

    image-20210218155728868

    image-20210218155808880

    此目录下的内容,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
    }
    

    image-20210218161101421

    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

    gin一次请求共用一个数据库实例

    image-20210218162726796

    image-20210218162715014

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

    web服务运行区分ssl和正常

    从配置文件中获取内容,定义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)
    			}
    		}
    	}()
    
    bk
    下一篇:没有了