多年来,我们一直投入大量的个人时间和精力,与大家分享我们的知识。然而,我们现在需要你的帮助来维持这个博客的运行。你所要做的只是点击网站上的一个广告,否则它将由于托管等费用而不幸被关闭。谢谢你。
在这里,我们将创建一个名为config 的自定义结构标签,它将有两个字段,即kind|path ,只是为了玩玩。
我们的示例方案是关于处理应用程序的秘密和普通配置值。我们将覆盖 "秘密 "字段的值,并保留 "普通 "字段。这只是为了演示的目的。在现实生活中,你通常会使用 "路径 "值来定位内部/外部配置/秘密存储服务中的实际值,如AWS Secrets Manager/Parameter Store、HashiCorp Vault等的每个字段。这完全取决于你如何设计这个系统。这只是一个想法,所以请随意重构或采用它。虽然这是一个不同的主题,但你可以按照environment.service.***,environment/service/***,service/environment/*** 语法来表示 "路径 "或其他东西。语法也可以由你将使用的服务来强制执行。
config.go
package config
import (
"fmt"
"reflect"
)
// Bind iterates through all the fields in the config and operates only on
// custom "config" tag as long as it matches certain criteria.
func Bind(cfg interface{}) error {
configSource := reflect.ValueOf(cfg)
if configSource.Kind() != reflect.Ptr {
return fmt.Errorf("config must be a pointer")
}
configSource = configSource.Elem()
if configSource.Kind() != reflect.Struct {
return fmt.Errorf("config must be a struct")
}
configType := configSource.Type()
for i := 0; i < configSource.NumField(); i++ {
fieldTag, ok := configType.Field(i).Tag.Lookup("config")
if !ok {
continue
}
fieldName := configType.Field(i).Name
fieldValue := configSource.FieldByName(fieldName)
if !fieldValue.IsValid() {
continue
}
if !fieldValue.CanSet() {
continue
}
tagKind, tagPath := tag(fieldTag)
if tagKind == "" && tagPath == "" {
continue
}
if tagKind == tagSecret {
// This is just a random act. You should handle it as per your setup.
// Also add other type cases as well
switch configSource.Field(i).Kind() {
case reflect.String:
fieldValue.SetString("***")
case reflect.Int:
fieldValue.SetInt(000)
}
}
}
return nil
}
tag.go
package config
import (
"strings"
)
const (
tagPlain = "plain"
tagSecret = "secret"
)
// tag extracts kind and path values from the incoming tag value. Both values are
// must be non-empty string otherwise an empty string is returned for both.
func tag(tag string) (string, string) {
tagParts := strings.Split(tag, ",")
if len(tagParts) == 0 || len(tagParts) != 2 {
return "", ""
}
kindParts := strings.Split(tagParts[0], "=")
if len(kindParts) == 0 || len(kindParts) != 2 {
return "", ""
}
if kindParts[0] != "kind" || (kindParts[1] != tagPlain && kindParts[1] != tagSecret) {
return "", ""
}
pathParts := strings.Split(tagParts[1], "=")
if len(pathParts) == 0 || len(pathParts) != 2 {
return "", ""
}
if pathParts[0] != "path" || pathParts[1] == "" {
return "", ""
}
return kindParts[1], pathParts[1]
}
main.go
package main
import (
"fmt"
"log"
"time"
"you/config"
)
type Config struct {
Shutdown time.Duration
PrivateKey string `config:"kind=secret,path=common.ssh.private_key"`
Password string `config:"kind=secret,path=team.login.password"`
YearFound int `config:"kind=plain,path=team.year_found"`
}
func main() {
cfg := Config{
Shutdown: time.Minute,
PrivateKey: "prv",
Password: "psw",
YearFound: 2021,
}
fmt.Printf("ORIGINAL: %+v\n", cfg)
if err := config.Bind(&cfg); err != nil {
log.Fatalln(err)
}
fmt.Printf("MODIFIED: %+v\n", cfg)
}
测试
$ go run main.go
ORIGINAL: {Shutdown:1m0s PrivateKey:prv Password:psw YearFound:2021}
MODIFIED: {Shutdown:1m0s PrivateKey:*** Password:*** YearFound:2021}