feat: load explicit configuration references and remove implicit env overlays

This commit is contained in:
2026-09-26 16:14:54 +08:00
parent 4bf6e88cfe
commit d598b2ebee
94 changed files with 1355 additions and 1759 deletions

View File

@@ -10,11 +10,13 @@ DEMO_CLIENT_TIMEOUT=500ms DEMO_MAX_ITEMS=0 go run ./examples/configuration
DEMO_MAX_ITEMS=wrong go run ./examples/configuration
```
前三次都输出 `timeout=2s maxItems=100 allowed=[json xml]`。环境覆盖允许显式零值。
前三次都输出 `timeout=2s maxItems=100 allowed=[json xml]`。文件显式引用允许零值。
最后一条输出定位 `maxItems` / `DEMO_MAX_ITEMS` 的安全错误,不打印输入原值。
也可 `-config /path/to/app.yaml` 读取自己的文件。示例没有建立网络连接。
`Config` 定义字段,`envDefault` 定义缺省值,`Validate` 用 `config.Invalid` 返回字段与公开原因。
`Config` 定义字段,`Default()` 定义初始值,`Validate` 用 `config.Invalid` 返回字段与公开原因。
文件中的 `${NAME}` 必填,`${NAME:-value}` 支持空/未设置的备用值,`$${NAME}` 保留字面量;字符串不裁剪,也不二次展开。没有出现在文件引用中的变量不会被读取。
`newLoader` 额外注册 `.custom`(演示复用 JSON decoder);同一个 `load` 可读取所有格式。
JSON/YAML/XML 直接可用,无需注册。新增格式只修改解析器注册,不改业务结构或校验。

View File

@@ -1,5 +1 @@
{
"client": {"endpoint": "https://example.com", "timeout": "2s"},
"maxItems": 100,
"allowed": ["json", "xml"]
}
{"client":{"endpoint":"https://${DEMO_HOST:-example.com}","timeout":"${DEMO_CLIENT_TIMEOUT:-2s}","token":"${DEMO_CLIENT_TOKEN:-}"},"maxItems":"${DEMO_MAX_ITEMS:-100}","allowed":["json","xml"]}

View File

@@ -1,8 +1 @@
<config>
<client>
<endpoint>https://example.com</endpoint>
<timeout>2s</timeout>
</client>
<maxItems>100</maxItems>
<allowed type="array"><item>json</item><item>xml</item></allowed>
</config>
<config><client><endpoint>https://${DEMO_HOST:-example.com}</endpoint><timeout>${DEMO_CLIENT_TIMEOUT:-2s}</timeout><token>${DEMO_CLIENT_TOKEN:-}</token></client><maxItems>${DEMO_MAX_ITEMS:-100}</maxItems><allowed type="array"><item>json</item><item>xml</item></allowed></config>

View File

@@ -1,5 +1,6 @@
client:
endpoint: https://example.com
timeout: 2s
maxItems: 100
endpoint: "https://${DEMO_HOST:-example.com}"
timeout: "${DEMO_CLIENT_TIMEOUT:-2s}"
token: "${DEMO_CLIENT_TOKEN:-}"
maxItems: "${DEMO_MAX_ITEMS:-100}"
allowed: [json, xml]

View File

@@ -17,16 +17,18 @@ var examples embed.FS
type ClientConfig struct {
Endpoint string `json:"endpoint"`
Timeout time.Duration `json:"timeout" envDefault:"3s"`
// This field is deliberately unavailable to configuration files.
Token string `json:"-" env:"DEMO_CLIENT_TOKEN,secret"`
Timeout time.Duration `json:"timeout"`
// The file chooses a fixed value or an explicit environment reference.
Token string `json:"token"`
}
type Config struct {
Client ClientConfig `json:"client"`
MaxItems int `json:"maxItems" envDefault:"20"`
MaxItems int `json:"maxItems"`
Allowed []string `json:"allowed"`
}
func Default() Config { return Config{Client: ClientConfig{Timeout: 3 * time.Second}, MaxItems: 20} }
func (c Config) Validate() error {
if c.Client.Timeout <= 0 {
return config.Invalid("client.timeout", "must be positive")
@@ -48,7 +50,7 @@ func load(path string) (Config, error) {
if err != nil {
return Config{}, err
}
return config.LoadProject(path, Config{}, config.EnvOptions{Prefix: "DEMO_", Empty: "reject"}, loader)
return config.LoadProject(path, Default(), loader)
}
func run() error {
path := flag.String("config", "", "external configuration file")