iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
Software Development

Go Clean Architecture API 開發全攻略系列 第 3

設定管理哲學:使用 Viper 與環境變數打造靈活的 Config

  • 分享至 

  • xImage
  •  

寫在最前面,最重要的先說

.env 檔案放置的都是 資料庫密碼, API 金鑰 等等不應該外洩的內容,
務必務必要保證不要被提交到 git 或任何一種版本控制管理系統

務必將 .env 檔案加入到 .gitignore 中

本篇目標

在應用程式開發中,設定管理是一個重要的環節。

良好的設定管理能夠讓應用程式在不同環境(如開發、測試、生產)下靈活運行,
同時也能確保敏感資訊(如資料庫密碼、API 金鑰)不會被 寫在 程式碼中。

為什麼需要設定管理

在 Go 語言裡面,獲取環境變數是一個簡單的動作

environment := os.Getenv("ENVIRONMENT")

那為什麼不要直接使用 os.Getenv() 就好呢,
因為這種做法在大型應用程式中可能會導致一些問題:

  1. 重複代碼:每次需要獲取環境變數時,都必須寫一遍 os.Getenv(),這會導致代碼重複,增加維護成本。

  2. 錯誤處理os.Getenv() 只會返回一個字串,如果環境變數不存在,則返回空字串。這使得錯誤處理變得困難,開發者需要額外的邏輯來處理缺失的環境變數。

  3. 測試困難:在單元測試中,模擬環境變數的存在與否變得更加複雜,這可能會導致測試不穩定。

為了解決這些問題,我們可以使用集中式的配置管理工具,來統一管理應用程式的配置。這樣不僅可以減少重複代碼,還能提供更好的錯誤處理和測試支持。

第一步:定義 Config 類型 (config_types.go)

internal/config/config_types.go 中定義我們的 config 會有哪些內容:

// internal/config/config_type.go

type Config struct {
	Server     ServerConfig
	MySQL      DatabaseConfig
	Redis      RedisConfig
	Logger     LoggerConfig
	Token      TokenConfig
	APIDocAuth APIDocAuth
}

type ServerConfig struct {
	Environment string
	Port        string
}

....

第二步:實作載入邏輯 (config.go)

internal/config/config.go 中實作載入的 function
這裡會讀取 .env 跟讀取環境變數

// internal/config/config.go
func LoadConfig() (*Config, error) {
	v := viper.New()

	// 啟用自動讀取環境變數, 用於 Server 部署
	v.AutomaticEnv()

	// 設定讀取 .env 檔案,用於本地開發使用
	v.SetConfigName(".env")
	v.SetConfigType("env")
	v.AddConfigPath(".")

	err := v.ReadInConfig()
	if err != nil {
		// 如果錯誤是指定的 config 檔案不存在,則不處理
		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
			return nil, fmt.Errorf("error reading config file: %w", err)
		}
	}

	var cfg Config

	cfg.Server.Environment = v.GetString("SERVER_ENVIRONMENT")
	cfg.Server.Port = v.GetString("SERVER_PORT")

    ....

	return &cfg, nil
}

到這裡看起來,會覺得 os.Getenv() 跟 viper.GetString()
沒什麼差異
但實際上,viper.GetString() 會先查詢已載入的設定檔(如 .env),再查詢環境變數。
這樣就達到,有環境變數,就使用環境變數,否則有給 .env 就使用 .env 的內容
這時候,不論是在 server,還是在本地開發,都不用調整程式碼

為什麼同時支援 .env 檔案與環境變數?

同時支援 .env 檔案與環境變數能夠兼顧開發與部署的需求:

  • 開發便利性:在本地開發時,使用 .env 檔案可以讓每位開發者根據自身環境快速調整設定,且不必將敏感資訊提交到版本控制。
  • 部署安全性與彈性:在生產或測試環境,通常會透過環境變數來管理設定,這樣可以避免敏感資訊外洩,並且方便在不同平台(如 Docker、Kubernetes)自動化配置。
  • 一致性:這種設計確保專案在不同環境下都能以一致的方式載入設定,減少環境差異造成的問題。

讓我們的專案 運行起來

我們已經實現了讀取 .env 跟環境變數
接下來我們就實現 main.go
讓專案可以運行起來

// cmd/api/main.go
func main() {
	config, err := config.LoadConfig()
	if err != nil {
		log.Fatalf("failed to load config: %v", err)
	}

	// 這裡先只把 config 印出,後續會使用到
	log.Println(config)
}

gogogo

go run ./cmd/api/main.go

🎉🎉🎉🎉🎉

總結

在本篇文章中,我們探討了 Go 語言中的設定管理問題,並介紹了 Viper 作為解決方案。透過 Viper,
我們能夠輕鬆地管理應用程式的配置,並在不同環境中保持一致性。
最終,我們實現了一個靈活的設定管理模組,能夠支援 .env 檔案與環境變數的雙重來源,提升了應用程式的可維護性與安全性。

===
以上程式碼的完整內容可以到 Github 觀看


上一篇
架構選擇:我們為何在 Go 專案中採用六角形架構
下一篇
日誌系統設計:整合 slog 實現高效能的結構化日誌
系列文
Go Clean Architecture API 開發全攻略4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言