Viper 极简配置读取

Viper 极简配置读取指南

Viper 是 Go 语言中一个非常方便的配置库。如果你想从文件中读取配置,并将其映射到 Go 结构体以便于类型安全的调用,Viper 能轻松搞定。

1. 安装 Viper

首先,确保你的项目安装了 Viper

1
go get github.com/spf13/viper

2. 准备配置文件

Viper 支持多种配置文件格式,比如 YAML、JSON、TOML 等。我们以 YAML 格式为例,创建一个名为 config.yaml 的文件在你的项目根目录下:

1
2
3
4
5
6
7
8
9
10
# config.yaml
server:
port: 8080
name: MyWebApp
database:
host: localhost
user: admin
password: supersecret
# 这个 timeout 字段在结构体中会用到,如果配置文件没有,会用结构体的默认值
timeout_seconds: 10

3. 编写 Go 代码读取配置并绑定到结构体

下面是一个简单的 Go 程序,演示了如何使用 Viper 读取 config.yaml 文件中的配置,并将其绑定到一个 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package main

import (
"fmt"
"log" // 用于输出错误信息

"github.com/spf13/viper"
)

// Config 结构体映射整个配置文件。
// `mapstructure` 标签告诉 Viper 如何将配置键映射到结构体字段。
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
}

// ServerConfig 映射 'server' 配置部分。
type ServerConfig struct {
Port int `mapstructure:"port"`
Name string `mapstructure:"name"`
}

// DatabaseConfig 映射 'database' 配置部分。
type DatabaseConfig struct {
Host string `mapstructure:"host"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
TimeoutSeconds int `mapstructure:"timeout_seconds"` // 即使配置文件没有,结构体也有默认值
}

func main() {
// 1. 设置配置文件的名称 (不带扩展名)
viper.SetConfigName("config") // 对应 config.yaml, config.json 等

// 2. 设置配置文件的类型 (可选,但推荐明确指定)
viper.SetConfigType("yaml") // 明确告诉 Viper 配置文件是 YAML 格式

// 3. 添加查找配置文件的路径
// Viper 会按照添加的顺序查找,找到第一个就停止
viper.AddConfigPath(".") // 在当前目录查找
viper.AddConfigPath("./config") // 在当前目录下的 'config' 文件夹中查找 (如果你把 config.yaml 放在这里)

// 4. 读取配置文件
if err := viper.ReadInConfig(); err != nil {
// 如果配置文件没找到,这通常不是一个致命错误,
// 因为你可能想在生产环境通过环境变量或代码默认值提供配置。
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
fmt.Println("No config file found, continuing with defaults or environment variables.")
} else {
// 其他类型的错误(如文件损坏),是致命错误
log.Fatalf("Fatal error reading config file: %s \n", err)
}
} else {
fmt.Printf("Config file '%s' loaded successfully!\n", viper.ConfigFileUsed())
}

// 5. 绑定配置到结构体
// 定义一个 Config 结构体变量来接收配置
var appConfig Config

// 设置默认值到结构体字段(可选,但推荐)
// 如果配置文件中没有 `timeout_seconds`,这里设置的 10 会生效。
appConfig.Database.TimeoutSeconds = 10

if err := viper.Unmarshal(&appConfig); err != nil {
log.Fatalf("Unable to unmarshal config into struct: %s \n", err)
}

// 6. 通过结构体访问配置值 (推荐,类型安全,方便调用)
fmt.Println("\n--- 通过结构体访问配置 ---")
fmt.Printf("Server Name: %s\n", appConfig.Server.Name)
fmt.Printf("Server Port: %d\n", appConfig.Server.Port)
fmt.Printf("Database Host: %s\n", appConfig.Database.Host)
fmt.Printf("Database User: %s\n", appConfig.Database.User)
fmt.Printf("Database Password: %s\n", appConfig.Database.Password)
fmt.Printf("Database Timeout (seconds): %d\n", appConfig.Database.TimeoutSeconds)

// 还可以直接通过 Viper 实例获取值(适用于少量或不方便映射到结构体的配置)
fmt.Println("\n--- 直接通过 Viper.Get() 访问配置 ---")
// 注意:当使用 Unmarshal 后,结构体是主要的访问方式。
// 但 Viper 实例仍然可以获取任何已加载的配置。
fmt.Printf("Server Port (from Viper.GetInt): %d\n", viper.GetInt("server.port"))
}

4. 运行你的程序

  1. 将上面的 Go 代码保存为 main.go

  2. 确保 config.yaml 文件与 main.go 在同一目录下。

  3. 打开终端,进入该目录,然后运行:

    1
    go run main.go

你将看到程序成功读取 config.yaml,并将其内容绑定到 appConfig 结构体,然后通过结构体字段访问配置值。

结构体绑定小贴士:

  • mapstructure 标签: mapstructure:"key_name" 告诉 Viper 如何将配置文件中的 key_name 字段映射到 Go 结构体的对应字段。这是必不可少的。
  • 嵌套结构体: 对于配置文件中的嵌套配置(如 serverdatabase),Go 结构体也应使用嵌套结构体来匹配。
  • 默认值: 如果结构体字段是基本类型(如 int, string, bool),并且你在代码中给它赋了初始值(例如 TimeoutSeconds int = 10),那么在配置文件中没有这个键时,这个默认值就会被保留。如果配置文件有,则会被覆盖。