Viper
ที่อยู่ repository: spf13/viper: Go configuration with fangs (github.com)
ที่อยู่เอกสาร: spf13/viper: Go configuration with fangs (github.com)
TIP
กำลังมีการอภิปรายเกี่ยวกับการเปลี่ยนผ่านไปยัง Viper2 ผู้ที่สนใจสามารถดูได้ที่: Viper2
การติดตั้ง
go get github.com/spf13/viperบทนำ
Viper เป็นโซลูชันการกำหนดไฟล์การกำหนดค่าที่สมบูรณ์สำหรับแอปพลิเคชัน Go สามารถจัดการความต้องการและรูปแบบการกำหนดค่าได้เกือบทุกประเภท ช่วยในการจัดการไฟล์การกำหนดค่าของโปรเจกต์ และมีคุณสมบัติพิเศษดังนี้:
- การตั้งค่าค่าเริ่มต้น
- รองรับรูปแบบ JSON, TOML, YAML, HCL, envfile, Java properties
- รองรับตรวจสอบและโหลดไฟล์การกำหนดค่าใหม่แบบเรียลไทม์
- รองรับอ่านจากตัวแปรสภาพแวดล้อม
- รองรับอ่านการกำหนดค่าจากระบบการกำหนดค่าระยะไกลและตรวจสอบการเปลี่ยนแปลง
- รองรับอ่านจากเครื่องหมายคำสั่ง
- รองรับอ่านจากบัฟเฟอร์
- รองรับแสดงค่าที่ตั้งไว้
ทางอย่างเป็นทางการระบุว่า Viper สามารถตอบสนองความต้องการการกำหนดค่าของแอปพลิเคชันทั้งหมด ผู้พัฒนาเพียงมุ่งเน้นไปที่การสร้างแอปพลิเคชัน ให้ Viper รับผิดชอบการจัดการการกำหนดค่า โปรเจกต์ที่มีชื่อเสียงหลายแห่งใช้ Viper
DANGER
Viper ไม่รับผิดชอบการเข้ารหัสและถอดรหัสไฟล์การกำหนดค่า กล่าวคือจะไม่ทำการประมวลผลความปลอดภัยใดๆ กับไฟล์การกำหนดค่า
ลำดับการอ่าน
Viper ใช้ลำดับความสำคัญต่อไปนี้ในการอ่านการกำหนดค่า:
- ค่าที่ตั้งไว้ชัดเจน
- เครื่องหมายคำสั่ง
- ตัวแปรสภาพแวดล้อม
- ไฟล์การกำหนดค่า
- ที่เก็บคีย์-ค่า
- ค่าเริ่มต้น
TIP
คีย์ในการกำหนดค่าของ Viper ไม่แยกแยะตัวพิมพ์ใหญ่-เล็ก การอภิปรายในอนาคตอาจทำให้เป็นตัวเลือก
ค่าเริ่มต้น
ระบบการกำหนดค่าที่ดีควรสนับสนุนการตั้งค่าค่าเริ่มต้น แม้บางครั้งอาจไม่จำเป็น แต่จะมีประโยชน์มากเมื่อไม่ได้ตั้งค่าไฟล์การกำหนดค่า ด้านล่างนี้เป็นตัวอย่าง
viper.SetDefault("filePath","./dir/img/usr")
viper.SetDefault("root","123456")อ่านไฟล์การกำหนดค่า
Viper ต้องการการกำหนดค่าน้อยมากเพื่อรู้ว่าต้องค้นหาไฟล์การกำหนดค่าที่ไหน Viper รองรับ JSON, TOML, YAML, HCL, INI, envfile และไฟล์ JavaProperties Viper สามารถค้นหาหลายพาธได้ แต่ปัจจุบันอินสแตนซ์ Viper เดียวรองรับไฟล์การกำหนดค่าเดียวเท่านั้น Viper ไม่ได้กำหนดพาธการค้นหาเป็นค่าเริ่มต้น ทิ้งการตัดสินใจเริ่มต้นไว้ให้แอปพลิเคชัน
ด้านล่างนี้เป็นตัวอย่างการใช้ Viper อ่านไฟล์การกำหนดค่า ไม่จำเป็นต้องระบุพาธแบบเต็ม แต่ควรให้ไฟล์การกำหนดค่าอย่างน้อยหนึ่งไฟล์เมื่อใช้งาน
func TestReadConfigFile(t *testing.T) {
viper.SetConfigName("config.yml") // อ่านไฟล์การกำหนดค่าชื่อ config ไม่ได้ตั้งนามสกุลไฟล์เฉพาะ
viper.SetConfigType("yaml") // เมื่อไม่ได้ตั้งนามสกุลไฟล์เฉพาะ ต้องระบุประเภทไฟล์
viper.AddConfigPath("./") // ค้นหาในโฟลเดอร์ปัจจุบัน
viper.AddConfigPath("$HOME/") // ใช้ตัวแปร
viper.AddConfigPath(".") // ค้นหาในไดเรกทอรีทำงาน
err := viper.ReadInConfig() // อ่านการกำหนดค่า
if err != nil {
log.Fatalln(err)
}
}ยังสามารถจัดการกรณีที่ไม่พบไฟล์การกำหนดค่าแยกต่างหาก
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// ไม่พบไฟล์การกำหนดค่า
} else {
// ข้อผิดพลาดประเภทอื่น
}
}ต่อไปนี้เป็นฟังก์ชันทั้งหมดสำหรับเข้าถึงการกำหนดค่า
Get(key string) : interface{}GetBool(key string) : boolGetFloat64(key string) : float64GetInt(key string) : intGetIntSlice(key string) : []intGetString(key string) : stringGetStringMap(key string) : map[string]interface{}GetStringMapString(key string) : map[string]stringGetStringSlice(key string) : []stringGetTime(key string) : time.TimeGetDuration(key string) : time.DurationIsSet(key string) : boolAllSettings() : map[string]interface{}
เมื่อเข้าถึงการกำหนดค่าแบบซ้อน ใช้ตัวคั่น . เช่น:
{
"server":{
"database":{
"url": "mysql...."
}
}
}สามารถเข้าถึงแบบซ้อนได้ด้วย GetString("server.database.url")
เขียนไฟล์การกำหนดค่า
Viper ให้ชุดฟังก์ชันเพื่อให้นักพัฒนาสามารถเขียนการกำหนดค่าที่เก็บในรันไทม์ลงในไฟล์การกำหนดค่า
// WriteConfig เขียนการกำหนดค่าลงในไฟล์การกำหนดค่าเดิม หากไม่มีจะเกิดข้อผิดพลาด หากมีจะทับ
func WriteConfig() error { return v.WriteConfig() }
// SafeWriteConfig เขียนการกำหนดค่าอย่างปลอดภัยลงในไฟล์การกำหนดค่าเดิม หากไม่มีจะเขียน หากมีจะไม่ทับ
func SafeWriteConfig() error { return v.SafeWriteConfig() }
// WriteConfigAs เขียนการกำหนดค่าปัจจุบันลงในไฟล์ที่ระบุ หากไฟล์ไม่มีจะเกิดข้อผิดพลาด หากมีจะทับการกำหนดค่าเดิม
func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) }
// SafeWriteConfigAs หากไฟล์ที่ระบุมีอยู่ จะไม่ทับไฟล์การกำหนดค่าเดิม หากไฟล์มีอยู่จะเกิดข้อผิดพลาด
func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) }ด้านล่างนี้เป็นตัวอย่างบางตัว:
func TestWritingConfig(t *testing.T) {
viper.WriteConfig() // เขียนการกำหนดค่าลงในไฟล์การกำหนดค่าเดิม ไฟล์การกำหนดค่าเหล่านี้ควรถูกกำหนดไว้ล่วงหน้าด้วย 'viper.AddConfigPath()' และ 'viper.SetConfigName'
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // เนื่องจากไฟล์ที่ระบุมีอยู่ จะเกิดข้อผิดพลาด
viper.SafeWriteConfigAs("/path/to/my/.other_config")
}ตรวจสอบและโหลดการกำหนดค่าใหม่
Viper ช่วยให้แอปพลิเคชันอ่านไฟล์การกำหนดค่าแบบไดนามิกในรันไทม์ กล่าวคือไม่ต้องรีสตาร์ทแอปพลิเคชันก็สามารถทำให้การกำหนดค่าที่อัปเดตมีผล และไม่พลาดทุกรายละเอียดที่เปลี่ยนแปลง เพียงบอกอินสแตนซ์ Viper ให้ตรวจสอบการเปลี่ยนแปลงการกำหนดค่า หรือสามารถให้ฟังก์ชันแก่ viper เพื่อเรียกใช้ฟังก์ชันนั้นเมื่อมีการเปลี่ยนแปลง
func TestWatchingConfig(t *testing.T) {
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("ไฟล์การกำหนดค่าถูกเปลี่ยนแปลง:", e.Name)
})
viper.WatchConfig()
}นามแฝง
func TestAliases(t *testing.T) {
viper.RegisterAlias("a", "b")
viper.Set("a", 1)
viper.Set("b", 2) // จะทับการกำหนดค่าของ a
fmt.Println(viper.GetInt("a"))
}แยกโครงสร้างย่อย
ได้กล่าวไปแล้วว่าใช้ตัวคั่น . เพื่อเข้าถึงการกำหนดค่าแบบซ้อน ที่จริงแล้วยังสามารถใช้ฟังก์ชัน viper.Sub() เพื่อแยกโครงสร้างย่อยได้ ค่าที่ส่งกลับเป็นอินสแตนซ์ Viper ดังตัวอย่างต่อไปนี้:
cache:
cache1:
max-items: 100
item-size: 64
cache2:
max-items: 200
item-size: 80cache1Config := viper.Sub("cache.cache1")
if cache1Config == nil { // หากไม่มีอยู่จะส่งคืน nil
panic("การกำหนดค่า cache1 ไม่มีอยู่")
}ตั้งค่าตัวคั่นแบบซ้อน
เมื่อต้องการให้คีย์ที่ระบุมี . จำเป็นต้องระบุตัวคั่นอื่นด้วยตนเองเพื่อป้องกันการแยกวิเคราะห์ผิดพลาด เช่น:
viper.KeyDelimiter("/") // ตั้งค่าตัวคั่นเป็น /การแยกวิเคราะห์อนุกรม
Viper ให้ฟังก์ชันสองฟังก์ชันเพื่อแยกวิเคราะห์การกำหนดค่าเป็นโครงสร้างหรือ map รองรับโครงสร้างแบบซ้อน:
Unmarshal(rawVal interface{}) : errorUnmarshalKey(key string, rawVal interface{}) : error
type config struct {
Port int
Name string
PathMap string `mapstructure:"path_map"`
}
var C config
err := viper.Unmarshal(&C)
if err != nil {
t.Fatalf("ไม่สามารถแยกวิเคราะห์อนุกรมเป็นโครงสร้าง, %v", err)
}การสร้างอนุกรม
สร้างอนุกรมการกำหนดค่าปัจจุบันเป็นสตริงในรูปแบบเฉพาะเพื่อเก็บในไฟล์การกำหนดค่า โดยปกติรองรับ JSON, TOML, YAML, HCL, envfile, Java properties
TIP
Viper รองรับรูปแบบการสร้างอนุกรมแบบกำหนดเอง Decoding custom formats with Viper - Márk Sági-Kazár (sagikazarmark.hu)
import (
yaml "gopkg.in/yaml.v2"
// ...
)
func yamlStringSettings() string {
c := viper.AllSettings()
bs, err := yaml.Marshal(c)
if err != nil {
log.Fatalf("ไม่สามารถสร้างอนุกรมการกำหนดค่าเป็น YAML: %v", err)
}
return string(bs)
}หลายอินสแตนซ์
โดยปกติแล้วใช้อินสแตนซ์ทั่วโลกที่ Viper ให้มาก็เพียงพอแล้ว แต่เนื่องจากอินสแตนซ์เดียวสามารถแมปกับไฟล์การกำหนดค่าเดียวได้ สามารถสร้างหลายอินสแตนซ์เพื่อดำเนินการมากขึ้น เช่น:
x := viper.New()
y := viper.New()
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")
//...