[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# Viper Env Binding and Flag Binding
|
||||
|
||||
## The binding interaction model
|
||||
|
||||
Three settings control how viper maps environment variables to keys. They must be set together:
|
||||
|
||||
```go
|
||||
viper.SetEnvPrefix("MYAPP") // adds MYAPP_ prefix
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) // database.host → MYAPP_DATABASE_HOST
|
||||
viper.AutomaticEnv() // activates auto-binding
|
||||
```
|
||||
|
||||
Call these before any `ReadInConfig` or `viper.Get*` call — typically in a root command's `PersistentPreRunE` or in `init()`.
|
||||
|
||||
## AutomaticEnv vs BindEnv
|
||||
|
||||
| Method | Behavior |
|
||||
| --- | --- |
|
||||
| `AutomaticEnv()` | Every key is automatically mapped to its env equivalent (with prefix and replacer applied) |
|
||||
| `BindEnv(key, envVars...)` | Only the specified key is bound, to the specified env var name(s) |
|
||||
|
||||
Use `AutomaticEnv` for the common case. Use `BindEnv` when you need to bind to an env var with a name that doesn't follow your prefix/replacer convention (e.g., third-party env vars like `GOOGLE_APPLICATION_CREDENTIALS`).
|
||||
|
||||
```go
|
||||
// Bind a specific non-prefixed env var
|
||||
viper.BindEnv("google.credentials", "GOOGLE_APPLICATION_CREDENTIALS")
|
||||
```
|
||||
|
||||
## SetEnvKeyReplacer in depth
|
||||
|
||||
Viper keys use `.` as separator for nested values. Env vars cannot contain dots. The replacer maps between them.
|
||||
|
||||
```go
|
||||
// Config file:
|
||||
// database:
|
||||
// host: localhost
|
||||
// max_conn: 25
|
||||
|
||||
// Without replacer:
|
||||
viper.SetEnvPrefix("MYAPP")
|
||||
viper.AutomaticEnv()
|
||||
viper.GetString("database.host") // looks for MYAPP_DATABASE.HOST — no match
|
||||
|
||||
// With replacer:
|
||||
viper.SetEnvPrefix("MYAPP")
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
viper.GetString("database.host") // looks for MYAPP_DATABASE_HOST — matches
|
||||
```
|
||||
|
||||
The replacer operates on the viper key **before** prepending the prefix, so the lookup chain is: `database.host` → replace `.` with `_` → `database_host` → prepend prefix → `MYAPP_DATABASE_HOST`.
|
||||
|
||||
## AllowEmptyEnv
|
||||
|
||||
By default, viper ignores env vars set to the empty string — the empty string is treated as "not set" and viper continues down the precedence stack. Override this behavior:
|
||||
|
||||
```go
|
||||
viper.AllowEmptyEnv(true)
|
||||
// now MYAPP_PORT="" → viper.GetInt("port") == 0, not the default
|
||||
```
|
||||
|
||||
## Flag binding
|
||||
|
||||
Bind a pflag after defining it:
|
||||
|
||||
```go
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().Int("port", 8080, "listen port")
|
||||
viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port"))
|
||||
}
|
||||
```
|
||||
|
||||
Bind an entire flag set:
|
||||
|
||||
```go
|
||||
viper.BindPFlags(rootCmd.PersistentFlags())
|
||||
```
|
||||
|
||||
**Timing rule:** Bind flags in `init()` or in `PersistentPreRunE`. The binding call must happen before `Execute()` parses flags — specifically, before any `viper.Get*` call on a flag-backed key. Binding after `Execute()` causes the flag's `Changed` state to be unknown, so viper may not promote the flag value to the correct precedence layer.
|
||||
|
||||
## How pflag binding interacts with precedence
|
||||
|
||||
Viper checks `flag.Changed` (whether the user explicitly passed the flag). This is how it distinguishes between "flag default" (low priority) and "flag explicitly set" (high priority):
|
||||
|
||||
- `flag.Changed == false` (flag has its default): viper treats the flag as not present and falls through to env/file/default.
|
||||
- `flag.Changed == true` (flag was provided on the command line): viper treats the flag value as the highest-priority source.
|
||||
|
||||
This means `viper.GetInt("port")` correctly returns the flag value when `--port 9090` is passed, and falls back to env `MYAPP_PORT` or config file `port: 8080` otherwise.
|
||||
|
||||
## Debugging binding
|
||||
|
||||
Print all resolved values to verify your binding is correct:
|
||||
|
||||
```go
|
||||
fmt.Println(viper.AllSettings())
|
||||
// map[database:map[host:localhost max_conn:25] port:8080]
|
||||
```
|
||||
|
||||
Check env var resolution:
|
||||
|
||||
```go
|
||||
os.Setenv("MYAPP_PORT", "9090")
|
||||
viper.AutomaticEnv()
|
||||
fmt.Println(viper.GetInt("port")) // 9090
|
||||
```
|
||||
Reference in New Issue
Block a user