引言


1
2
3
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%s&loc=%s",
"root","123456","localhost","3306","utf8mb4","True","Local"
)

上面的行为是非常呆逼的,很生硬,而且一般来说不会吧具体的数值嵌入代码之中,又比如jwt.secretKey,因此就需要使用配置文件

YAML


一款标记性语言,一种较为人性化的数据序列化语言

基本语法

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 相同成绩的元素左对齐
  • ‘#’表示注释

这个文章中用到引用

1
2
3
4
xxxxxx:
xxxxxx: xxxxx
xxxxxx: xxxxx
xxxxxx: xxxxx

Viper


Viper是Go应用程序的完整配置解决方案

支持:

  • 默认配置
  • 从 JSON, TOML, YAML, HCL 和 Java 属性配置文件读取数据
  • 实时查看和重新读取配置文件(可选)
  • 从环境变量中读取
  • 从远程配置系统(etcd 或 Consul)读取数据并监听变化
  • 从命令行参数读取
  • 从 buffer 中读取
  • 设置显式值

作用:

  1. 以 JSON,TOML,YAML,HCL 或 Java 属性格式查找,加载和解组配置文件。
  2. 提供一种机制来为不同的配置选项设置默认值。
  3. 提供一种机制可以通过命令行标志指定的选项设置来覆盖值。
  4. 提供别名系统,轻松重命名参数,而不会破坏现有代码。
  5. 当用户提供命令行或配置文件与默认值相同时,可以轻松区分。

反正我知道你看了白看,Viper两个用处

  1. 从配置文件上读取数据
  2. 输出数据到应用程序

下面的代码实现就是围绕两个方面展开的

实现


为了实现这个配置文件的实现满足通用性,所以这个应用程序不能是简单的为了某个配置而配置的情况,要考虑到如果多个配置的实现。

​ 代码的整体思路还是将yaml格式的数据绑定到结构体全局变量中,然后使用全局变量到对应地点

下载依赖:

1
go get github.com/spf13/viper

结构

1
2
3
4
5
|-configs
|-config.yaml
|-init.go
|-model.go
|-setting.go

model.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package configs

import "github.com/spf13/viper"

var (
DbSettings *DatabaseSettings
JwtSettings *JWTSettings
)

//注意这边的vp也是放入到了某一个结构体之中
type Setting struct {
vp *viper.Viper
}

type DatabaseSettings struct {
Root string
Password string
Host string
Port int
Dbname string
Charset string
ParseTime string
Loc string
}

type JWTSettings struct {
Issuer string
Subject string
SecretKey string
}

logic实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//从锁定配置文件位置
func NewSetting ()(*Setting,error){
vp := viper.New()

vp.SetConfigName("config") //锁定配置文件的名字
vp.SetConfigPath("./configs") //锁定配置文件的路径
vp.SetConfigPath(".") //如果没有搜索到就从根目录开始搜索
vp.SetConfigType("yaml") //锁定配置文件的类型为yaml

err := vp.ReadInConfig() //开启监听
if err!=nil{
return nil , err
}

return &Setting{vp} , nil
}

// 转换yaml数据为结构体数据
func (s *Setting)ReadSection(str string , v interface{})error{
err := s.vp.Unmashal(str,v) //反序列化真的很神奇
if err!=nil{
return err
}

return nil
}

// 组装厂
func SetUpSettings() error{
settings , err := NewSetting()
if err!=nil{
return err
}
// 普遍化/可以使得多个配置转发到不同的结构体全局变量
err1 := settings.ReadSection("填入yaml顶格字段1",&DbSettings)
if err!=nil{
return err
}

err2 := settings.ReadSection("填入yaml顶格字段2",&JwtSettings)
if err2!=nil{
return err2
}
// ... 慢慢写

return nil
}

init

1
2
3
4
5
6
7
8
//封装完了
func Init() {
err := SetUpSettings()
if err != nil {
log.SugarLogger.Error(err)
return
}
}

使用


数据库中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func Connect() *gorm.DB {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%s&loc=%s",
configs.DbSettings.Root,
configs.DbSettings.Password,
configs.DbSettings.Host,
configs.DbSettings.Port,
configs.DbSettings.Dbname,
configs.DbSettings.Charset,
configs.DbSettings.ParseTime,
configs.DbSettings.Loc,
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.SugarLogger.Error(err)
return nil
}
fmt.Println("连接数据库成功")
return db
}

JWT:

1
2
3
4
5
6
7
8
9
10
11
12
tokenString, err := token.SignedString([]byte(configs.JwtSettings.SecretKey))
if err != nil {
return "", err
}
//-----------------------------------------------------------------------------------------------------------------
_, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
// 验证签名方法 HMAC-SHA56签名方法
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("Unexpected Signing Method")
}
return []byte(configs.JwtSettings.SecretKey), nil
})

报错(见证一下错误时刻)

1
2
3
4
5
{"level":"ERROR","ts":"2024-02-03T14:49:33.760+0800","caller":"configs/init.go:8","msg":"Config File \"configs\" Not Found in \"[F:\\\\LearningGo\\\\configs]\""}
{"level":"ERROR","ts":"2024-02-03T14:51:15.612+0800","caller":"configs/init.go:8","msg":"Config File \"configs\" Not Found in \"[F:\\\\LearningGo\\\\configs\\\\config.yaml]\""}
{"level":"ERROR","ts":"2024-02-03T14:52:21.998+0800","caller":"configs/init.go:8","msg":"Config File \"configs\" Not Found in \"[F:\\\\LearningGo\\\\config.yaml]\""}
{"level":"ERROR","ts":"2024-02-03T14:57:52.459+0800","caller":"configs/init.go:8","msg":"Config File \"configs\" Not Found in \"[F:\\\\LearningGo\\\\configs F:\\\\LearningGo]\""}
{"level":"ERROR","ts":"2024-02-03T14:59:18.138+0800","caller":"configs/init.go:8","msg":"Config File \"config\" Not Found in \"[F:\\\\LearningGo]\""}

总结


  1. 代码通用性而使用封装
  2. 配置文件的使用
  3. 结构体在数据转化很用有

文献:

编写公共组件 | Go 语言编程之旅 (eddycjy.com)

serendipity/internal/global/jwt/jwt.go at main · hduhelp/serendipity (github.com)

Echin-h/hahaha (github.com)