golang环境变量解析与结构体映射插件库env的使用

Golang环境变量解析与结构体映射插件库env的使用

env是一个用于Go的环境变量工具包,它提供了简单的函数来获取和设置环境变量,包括支持将环境变量解组到结构体中,支持嵌套结构、默认值和必填字段。

功能特性

  • 基本获取/设置:简单的获取、设置和取消设置环境变量的函数
  • 类型转换:将环境变量作为不同类型(int、bool、float)获取的函数
  • 回退值:支持在环境变量未设置时使用回退值
  • 解组:使用结构体标签将环境变量加载到结构体中
  • 嵌套结构体:支持嵌套结构体前缀来分组环境变量

安装

go get github.com/syntaqx/env

基本使用

package main

import (
    "fmt"
    "github.com/syntaqx/env"
)

func main() {
    port := env.GetWithFallback("PORT", "8080")
    fmt.Printf("Port: %s\n", port)

    // 假设HOSTS的值是一个逗号分隔的字符串列表
    // 例如: some-host:8000,another-host:8000
    hosts, err := env.GetStringSliceWithFallback("HOSTS", []string{"fallback-host-1:8000", "fallback-host-2:8000"})
    if err != nil {
        fmt.Printf("Error getting hosts: %v\n", err)
    } else {
        fmt.Printf("Hosts: %v\n", hosts)
    }
}

解组到结构体

Unmarshal函数允许你基于结构体标签将环境变量加载到结构体中。你可以使用defaultfallback作为回退值,使用required来强制要求必须设置环境变量。

package main

import (
    "fmt"
    "log"

    "github.com/syntaqx/env"
)

type DatabaseConfig struct {
    Host     string `env:"DATABASE_HOST,default=localhost"`
    Port     int    `env:"DATABASE_PORT|DB_PORT,fallback=3306"`
    Username string `env:"DATABASE_USERNAME,default=root"`
    Password string `env:"DATABASE_PASSWORD,required"`
    Database string `env:"DATABASE_NAME"`
}

type Config struct {
    Debug    bool           `env:"DEBUG"`
    Port     string         `env:"PORT,default=8080"`
    Database DatabaseConfig
}

func main() {
    var cfg Config

    // 设置示例环境变量
    _ = env.Set("DEBUG", "true")
    _ = env.Set("PORT", "9090")
    _ = env.Set("DATABASE_HOST", "dbhost")
    _ = env.Set("DATABASE_PORT", "5432")
    _ = env.Set("DATABASE_USERNAME", "admin")
    _ = env.Set("DATABASE_PASSWORD", "secret")
    _ = env.Set("DATABASE_NAME", "mydb")

    if err := env.Unmarshal(&cfg); err != nil {
        log.Fatalf("Error unmarshalling config: %v", err)
    }

    fmt.Printf("Config: %+v\n", cfg)
}

嵌套结构体前缀

你可以使用嵌套前缀来分组环境变量。这允许你在多个地方重用相同的结构体,而不必担心环境变量冲突。

package main

import (
    "fmt"
    "log"

    "github.com/syntaqx/env"
)

type DatabaseConfig struct {
    Host     string `env:"HOST,default=localhost"`
    Port     int    `env:"PORT,fallback=3306"`
    Username string `env:"USERNAME,default=root"`
    Password string `env:"PASSWORD,required"`
    Database string `env:"NAME"`
}

type Config struct {
    Debug         bool           `env:"DEBUG"`
    Port          string         `env:"PORT,default=8080"`
    ReadDatabase  DatabaseConfig `env:"READ_DATABASE"`
    WriteDatabase DatabaseConfig `env:"WRITE_DATABASE"`
}

func main() {
    var cfg Config

    // 设置示例环境变量
    _ = env.Set("DEBUG", "true")
    _ = env.Set("PORT", "9090")
    _ = env.Set("READ_DATABASE_HOST", "read-dbhost")
    _ = env.Set("READ_DATABASE_PORT", "5432")
    _ = env.Set("READ_DATABASE_USERNAME", "read-admin")
    _ = env.Set("READ_DATABASE_PASSWORD", "read-secret")
    _ = env.Set("READ_DATABASE_NAME", "read-mydb")
    _ = env.Set("WRITE_DATABASE_HOST", "write-dbhost")
    _ = env.Set("WRITE_DATABASE_PORT", "5432")
    _ = env.Set("WRITE_DATABASE_USERNAME", "write-admin")
    _ = env.Set("WRITE_DATABASE_PASSWORD", "write-secret")
    _ = env.Set("WRITE_DATABASE_NAME", "write-mydb")

    if err := env.Unmarshal(&cfg); err != nil {
        log.Fatalf("Error unmarshalling config: %v", err)
    }

    fmt.Printf("Config: %+v\n", cfg)
}

切片类型默认值

当使用切片类型时,如果你声明单个值作为默认值,可以像平常一样使用default标签:

type Config struct {
    Hosts []string `env:"HOSTS,default=localhost"`
}

但是如果你想声明多个值作为默认值,必须将值括在方括号中:

type Config struct {
    Hosts []string `env:"HOSTS,default=[localhost,localhost2]"`
}

代码中的默认值

你也可以通过在代码中初始化结构体数据来定义默认值,然后再由env.Unmarshal填充。但是,定义为结构体标签的默认值将优先于代码中定义的默认值。

type Config struct {
    Username string `env:"USERNAME,default=admin"`
    Password string `env:"PASSWORD"`
}

cfg := Config{
    Username: "test",
    Password: "password123",
}

if err := env.Unmarshal(&cfg); err != nil {
    log.Fatalf("Error unmarshalling config: %v", err)
}

// { Username: "admin", Password: "password123" }

从文件读取

file标签选项可用于指示变量的值应从文件中加载。文件的路径由变量的值给出。

echo "password123" > /run/secrets/password
type Config struct {
    Username string `env:"USERNAME"`
    Password string `env:"PASSWORD,file"`
}

cfg := Config{
    Username: "test",
    Password: "/run/secrets/password",
}

if err := env.Unmarshal(&cfg); err != nil {
    log.Fatalf("Error unmarshalling config: %v", err)
}

// { "Username": "test", "Password": "password123" }

扩展变量

expand标签选项可用于指示变量的值应在设置前扩展(以${var}$var格式)。

type Config struct {
    Username string `env:"USERNAME,expand"`
    Password string `env:"PASSWORD,expand"`
}

这与default标签选项配合使用效果很好:

type Config struct {
    Address string `env:"ADDRESS,expand,default=${HOST}:${PORT}"`
}

此外,默认值可以从其他结构体字段引用。允许你在环境变量未设置时链接默认值,而不是回退到空值:

type Config struct {
    Host string `env:"HOST,default=localhost"`
    Port string `env:"PORT,default=8080"`
    Address string `env:"ADDRESS,expand,default=${HOST}:${PORT}"`
}

贡献

欢迎提出问题或为项目做贡献。贡献总是受欢迎的!

许可证

本项目采用MIT许可证。


更多关于golang环境变量解析与结构体映射插件库env的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang环境变量解析与结构体映射插件库env的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang环境变量解析与结构体映射插件库env使用指南

env是一个流行的Go语言库,用于将环境变量映射到结构体字段,简化配置管理。下面详细介绍其使用方法。

安装

go get github.com/caarlos0/env/v7

基本用法

简单示例

package main

import (
	"fmt"
	"log"
	"github.com/caarlos0/env/v7"
)

type config struct {
	Port     int    `env:"PORT" envDefault:"3000"`
	LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
	Debug    bool   `env:"DEBUG" envDefault:"false"`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		log.Fatal(err)
	}
	
	fmt.Printf("Port: %d\n", cfg.Port)
	fmt.Printf("LogLevel: %s\n", cfg.LogLevel)
	fmt.Printf("Debug: %v\n", cfg.Debug)
}

支持的字段类型

env支持多种数据类型:

  • 基本类型:string, bool, int, int8, int16, int32, int64
  • 无符号整数:uint, uint8, uint16, uint32, uint64
  • 浮点数:float32, float64
  • 时间类型:time.Duration, time.Time
  • 切片:[]string, []int, []float64
  • 自定义类型(需实现encoding.TextUnmarshaler接口)

高级功能

嵌套结构体

type DatabaseConfig struct {
	Host     string `env:"DB_HOST" envDefault:"localhost"`
	Port     int    `env:"DB_PORT" envDefault:"5432"`
	Username string `env:"DB_USER" envDefault:"postgres"`
	Password string `env:"DB_PASS" envDefault:""`
}

type AppConfig struct {
	Port     int           `env:"PORT" envDefault:"8080"`
	Database DatabaseConfig
	Timeout  time.Duration `env:"TIMEOUT" envDefault:"5s"`
}

func main() {
	cfg := AppConfig{}
	if err := env.Parse(&cfg); err != nil {
		log.Fatal(err)
	}
	
	fmt.Printf("Database Host: %s\n", cfg.Database.Host)
}

切片类型

type config struct {
	Hosts    []string `env:"HOSTS" envSeparator:":"`
	Ports    []int    `env:"PORTS" envSeparator:","`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		log.Fatal(err)
	}
	
	// 设置环境变量 HOSTS=host1:host2:host3
	// 设置环境变量 PORTS=8080,8081,8082
	fmt.Printf("Hosts: %v\n", cfg.Hosts)
	fmt.Printf("Ports: %v\n", cfg.Ports)
}

自定义解析器

type URL struct {
	Scheme string
	Host   string
	Path   string
}

func (u *URL) UnmarshalText(text []byte) error {
	parsed, err := url.Parse(string(text))
	if err != nil {
		return err
	}
	u.Scheme = parsed.Scheme
	u.Host = parsed.Host
	u.Path = parsed.Path
	return nil
}

type config struct {
	APIEndpoint URL `env:"API_ENDPOINT" envDefault:"https://api.example.com/v1"`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		log.Fatal(err)
	}
	
	fmt.Printf("API Endpoint: %s://%s%s\n", cfg.APIEndpoint.Scheme, cfg.APIEndpoint.Host, cfg.APIEndpoint.Path)
}

必填字段验证

type config struct {
	APIKey string `env:"API_KEY,required"`
	Env    string `env:"ENV" envDefault:"development"`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		log.Fatal(err) // 如果API_KEY未设置会报错
	}
}

前缀处理

type config struct {
	Host string `env:"HOST" envDefault:"localhost"`
	Port int    `env:"PORT" envDefault:"8080"`
}

func main() {
	os.Setenv("APP_HOST", "example.com")
	os.Setenv("APP_PORT", "3000")
	
	cfg := config{}
	opts := env.Options{Prefix: "APP_"}
	
	if err := env.ParseWithOptions(&cfg, opts); err != nil {
		log.Fatal(err)
	}
	
	fmt.Printf("Host: %s, Port: %d\n", cfg.Host, cfg.Port)
	// 输出: Host: example.com, Port: 3000
}

最佳实践

  1. 集中管理配置:将所有配置放在一个结构体中,便于管理
  2. 提供默认值:为所有非必填字段提供合理的默认值
  3. 环境区分:使用前缀区分不同环境的配置
  4. 敏感信息:不要将密码等敏感信息硬编码,使用环境变量或配置中心
  5. 验证配置:在应用启动时验证关键配置项

完整示例

package main

import (
	"fmt"
	"log"
	"net/url"
	"os"
	"time"
	
	"github.com/caarlos0/env/v7"
)

type DatabaseConfig struct {
	Host     string `env:"DB_HOST" envDefault:"localhost"`
	Port     int    `env:"DB_PORT" envDefault:"5432"`
	Username string `env:"DB_USER" envDefault:"postgres"`
	Password string `env:"DB_PASS,required"`
	Name     string `env:"DB_NAME" envDefault:"appdb"`
}

type RedisConfig struct {
	Host string `env:"REDIS_HOST" envDefault:"localhost"`
	Port int    `env:"REDIS_PORT" envDefault:"6379"`
}

type AppConfig struct {
	Env        string        `env:"ENV" envDefault:"development"`
	Port       int           `env:"PORT" envDefault:"8080"`
	Debug      bool          `env:"DEBUG" envDefault:"false"`
	Timeout    time.Duration `env:"TIMEOUT" envDefault:"30s"`
	Database   DatabaseConfig
	Redis      RedisConfig
	AllowedIPs []string `env:"ALLOWED_IPS" envSeparator:"," envDefault:"127.0.0.1,::1"`
}

func main() {
	// 模拟设置环境变量
	os.Setenv("DB_PASS", "securepassword")
	os.Setenv("ALLOWED_IPS", "192.168.1.1,192.168.1.2,10.0.0.1")
	
	cfg := AppConfig{}
	if err := env.Parse(&cfg); err != nil {
		log.Fatalf("Failed to parse config: %v", err)
	}
	
	fmt.Printf("Environment: %s\n", cfg.Env)
	fmt.Printf("Server Port: %d\n", cfg.Port)
	fmt.Printf("Debug Mode: %v\n", cfg.Debug)
	fmt.Printf("Timeout: %v\n", cfg.Timeout)
	fmt.Printf("Database: %s@%s:%d/%s\n", 
		cfg.Database.Username, 
		cfg.Database.Host, 
		cfg.Database.Port, 
		cfg.Database.Name)
	fmt.Printf("Redis: %s:%d\n", cfg.Redis.Host, cfg.Redis.Port)
	fmt.Printf("Allowed IPs: %v\n", cfg.AllowedIPs)
}

env库简化了Go应用程序的配置管理,通过结构体标签与环境变量映射,使配置更加清晰和类型安全。它支持丰富的特性,能满足大多数应用的配置需求。

回到顶部