Skip to content

Gin 实战 2:项目配置管理

端口、数据库、JWT 密钥不能写死在代码里。本章接入 Viper,把配置放到 configs/dev.yaml,启动时读取到 Go 结构体。

1. 安装 Viper

bash
go get github.com/spf13/viper

2. 编写配置文件

创建 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 Config

4. 加载配置

创建 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)
}

现在端口来自配置文件。下一章开始正式写第一个业务风格接口。

基于 MIT 许可发布 · 如需部署协助或二开定制,请查看 支持与合作