Gin 实战 2:项目配置管理
端口、数据库、JWT 密钥不能写死在代码里。本章接入 Viper,把配置放到 configs/dev.yaml,启动时读取到 Go 结构体。
1. 安装 Viper
bash
go get github.com/spf13/viper2. 编写配置文件
创建 configs/dev.yaml:
yaml
server:
port: 8000
database:
host: 127.0.0.1
port: 3306
username: root
password: root
dbname: youlai_admin
security:
sessionType: jwt
jwt:
secretKey: "replace-with-a-long-secret-at-least-32-characters"
accessTokenTTL: 7200
refreshTokenTTL: 2592000本地开发默认读取 dev.yaml,后续部署时再增加 prod.yaml。
3. 定义配置结构体
创建 internal/common/config/config.go:
go
package config
// Config 是项目的总配置结构。
// mapstructure 用来声明 YAML 节点和 Go 字段之间的映射关系。
type Config struct {
Server ServerConfig `mapstructure:"server"` // 服务端口等 HTTP 配置
Database DatabaseConfig `mapstructure:"database"` // MySQL 连接配置
Security SecurityConfig `mapstructure:"security"` // 登录认证和 Token 配置
}
// ServerConfig 对应 configs/dev.yaml 中的 server 节点。
type ServerConfig struct {
Port int `mapstructure:"port"`
}
// DatabaseConfig 对应 database 节点。
// 这里只先定义连接必需字段,连接池参数会在数据库章节继续补充。
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbname"`
}
// SecurityConfig 对应 security 节点。
type SecurityConfig struct {
SessionType string `mapstructure:"sessionType"`
JWT JWTConfig `mapstructure:"jwt"`
}
// JWTConfig 对应 security.jwt 节点。
// accessTokenTTL 和 refreshTokenTTL 单位为秒。
type JWTConfig struct {
SecretKey string `mapstructure:"secretKey"`
AccessTokenTTL int `mapstructure:"accessTokenTTL"`
RefreshTokenTTL int `mapstructure:"refreshTokenTTL"`
}
// Cfg 保存应用启动后加载到内存中的配置。
// 后续数据库、JWT 初始化都会从这里读取配置。
var Cfg Config4. 加载配置
创建 internal/common/config/loader.go:
go
package config
import (
"fmt"
"os"
"github.com/spf13/viper"
)
// Load 负责读取 configs/{env}.yaml 并反序列化到 Cfg。
// 默认环境为 dev;设置 APP_ENV=prod 时会读取 configs/prod.yaml。
func Load() error {
// 1. 读取环境变量,决定加载哪个配置文件。
env := os.Getenv("APP_ENV")
if env == "" {
env = "dev"
}
// 2. 创建独立的 Viper 实例,避免污染全局默认实例。
v := viper.New()
// 3. 指定配置文件路径,例如 configs/dev.yaml。
v.SetConfigFile(fmt.Sprintf("configs/%s.yaml", env))
v.SetConfigType("yaml")
// 4. 读取配置文件内容。
if err := v.ReadInConfig(); err != nil {
return err
}
// 5. 按 mapstructure tag 映射到 Go 结构体。
return v.Unmarshal(&Cfg)
}5. main.go 使用配置
go
package main
import (
"fmt"
"log"
"github.com/gin-gonic/gin"
"youlai-gin/internal/common/config"
)
func main() {
// 1. 应用启动第一步先加载配置。
// 如果配置文件不存在或格式错误,直接终止启动。
if err := config.Load(); err != nil {
log.Fatalf("配置加载失败: %v", err)
}
// 2. 创建 Gin 实例。
// 这里先用 gin.New(),后续章节再逐步挂载日志、异常处理等中间件。
r := gin.New()
// 3. 临时保留一个 ping 接口,用来验证服务是否启动成功。
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "pong"})
})
// 4. 从配置中读取端口,拼成 Gin 监听地址,例如 :8000。
addr := fmt.Sprintf(":%d", config.Cfg.Server.Port)
// 5. 启动 HTTP 服务。
r.Run(addr)
}现在端口来自配置文件。下一章开始正式写第一个业务风格接口。
