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 := "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
}

ヒント

  • Validate() をセクション横断の不変条件の最終ゲートにします。 例:if cfg.Env == "production" && cfg.HTTP.Listen == "0.0.0.0"
  • govalid は Viper / koanf / 任意のローダーと組み合わせる前提で ——まずロード、次にバリデート。govalid は最終的な構造体だけを 見ます。
  • ワンパスの開発体験では、loadAndValidate のあらゆるエラーを致命 扱いにし、半完成状態で起動しないようにしましょう。

MIT ライセンスのもとで公開されています。