Skip to content

配置校验

启动时校验配置结构体,让程序快速失败,而不是几个小时后才出现莫名 其妙的 NPE。

go
package main

import (
    "fmt"
    "log"

    "github.com/wuhan005/govalid"
)

type Database struct {
    Host     string `valid:"required" label:"数据库地址"`
    Port     int    `valid:"required;min:1;max:65535" label:"端口"`
    Username string `valid:"required" label:"用户"`
    Password string `valid:"required" label:"密码"`
    SSLMode  string `valid:"list:disable,allow,prefer,require" label:"SSL 模式"`
}

type HTTPServer struct {
    Listen string `valid:"required" label:"监听地址"`
    Port   int    `valid:"min:1;max:65535" label:"HTTP 端口"`
}

type Config struct {
    Env      string   `valid:"required;list:dev,staging,production" label:"环境"`
    Database Database
    HTTP     HTTPServer
    Admins   []string `valid:"required;minlen:1" label:"管理员邮箱"`
}

func loadAndValidate() (*Config, error) {
    cfg := &Config{
        Env: "production",
        Database: Database{
            Host:     "db.internal",
            Port:     5432,
            Username: "app",
            SSLMode:  "require",
        },
        HTTP: HTTPServer{Listen: "0.0.0.0", Port: 8080},
        Admins: []string{"ops@example.com"},
    }

    if errs, ok := govalid.Check(cfg); !ok {
        msg := "配置非法:\n"
        for _, e := range errs {
            msg += fmt.Sprintf("  - %s\n", e.Error())
        }
        return nil, fmt.Errorf(msg)
    }
    return cfg, nil
}

func main() {
    cfg, err := loadAndValidate()
    if err != nil {
        log.Fatal(err)
    }
    _ = cfg
}

小贴士

  • Validate() 成为跨小节不变量的最后一道闸门,例如 if cfg.Env == "production" && cfg.HTTP.Listen == "0.0.0.0"
  • govalid 与 Viper / koanf / 任何你喜欢的 loader 配合使用——先 加载,再校验。govalid 只看最终的 struct。
  • 单步开发体验里,把 loadAndValidate 的任何错误都视为致命错误, 这样二进制永远不会以半成品状态启动。

基于 MIT 协议发布。