Config Validation
Validate a config struct at startup so the program fails fast instead of trickling NPEs hours later.
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 := "config invalid:\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
}Tips
- Make
Validate()your final gate for invariants that span sections, e.g.if cfg.Env == "production" && cfg.HTTP.Listen == "0.0.0.0". - Pair govalid with Viper / koanf / your loader of choice — load first, validate next. govalid only sees the final struct.
- For a single-pass dev experience, treat any error from
loadAndValidateas fatal so the binary never starts in a half-baked state.