dpstir0081 2018-04-11 16:45
浏览 10
已采纳

如何在Go中实现零散的配置文件验证

I try to configure a CLI app via config file (yaml). The app has several "components" (lets say a persistence layer, an integrated web server etc.). Those components are managed in subpackages to keep the code tidy. The configuration of those components is defined inside their packages and "merged" in a config package to a struct that represents the config file. See this code as a demo implementation:

package main

import (
    "errors"
    "fmt"

    yaml "gopkg.in/yaml.v2"
)

//
// This would be in package 'webserver'
// Only the Config part is shown, there would be a constructor and the implementation of
// the webserver as well...
//

// WebserverConfig holds all required configuration params
type WebserverConfig struct {
    Listen    string `yaml:"listen"`
    Autostart bool   `yaml:"autostart"`
}

// Validate checks if all fields make sense and returns a list of error string if problems
// are found
func (wsc WebserverConfig) Validate() (error, []string) {
    errs := []string{}
    if wsc.Listen == "" {
        errs = append(errs, "Field 'listen' must not be empty.")
    }
    if len(errs) > 0 {
        err := errors.New("Config of 'webserver' has errors")
        return err, errs
    }
    return nil, errs
}

// UnmarshalYAML implements a custom unmarshaller to make sure defaults are set
// to meaningful values
func (wsc *WebserverConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
    type WebserverConfigDefaulted WebserverConfig
    var defaults = WebserverConfigDefaulted{
        Listen:    ":8080",
        Autostart: true,
    }

    out := defaults
    err := unmarshal(&out)
    *wsc = WebserverConfig(out)
    return err
}

//
// This would be in package conf
//
//

// Config is the global configuration composed of all component configuration sections
type Config struct {
    Webserver WebserverConfig `yaml:"webserver"`
}

// Load reads the bytes into the global config
func Load(data string) (Config, error) {
    c := Config{}
    err := yaml.Unmarshal([]byte(data), &c)
    if err != nil {
        return c, fmt.Errorf("Error while unmarshalling configuration from yaml: %s", err.Error())
    }
    return c, nil
}

// Validate should call each individual component configurations validate function (if it exists)
func (c Config) Validate() error {
    // TODO: IMPLEMPNT...
    return nil
}

//
// This is finally in main
//
//

var yamlFile = `---
webserver:
  listen: ":9999" 
`

func main() {
    c, err := Load(yamlFile)
    if err != nil {
        fmt.Println(err)
    }
    err = c.Validate()
    if err != nil {
        fmt.Println(err)
    }
}

Find it on the playground here (does not run because of the dep to yaml): https://play.golang.org/p/i3EAZJP7aYz

QUESTION: At this point I have no idea how the global validation function func (c Config) Validate() error could be implemented to call all Validate funcs of the 'component configurations' (such as func (wsc WebserverConfig) Validate() (error, []string)). Any idea or hints?

  • 写回答

1条回答 默认 最新

  • duandun2136 2018-04-12 18:46
    关注

    I'd recommend calling c.Webserver.Validate() from c.Validate().

    The downside of this is you need to update c.Validate() every time you add a new config, which might be easy to forget. However, it is "simple, readable and explicit" (quoting from your comment).

    Alternatively, you could loop over every element of the config to see which ones match a Validater interface then call Validate on them. But, this would be much more complicated and more difficult to understand & debug. The usual recommendation is to avoid reflection unless absolutely necessary.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 如何绘制动力学系统的相图
  • ¥15 对接wps接口实现获取元数据
  • ¥20 给自己本科IT专业毕业的妹m找个实习工作
  • ¥15 用友U8:向一个无法连接的网络尝试了一个套接字操作,如何解决?
  • ¥30 我的代码按理说完成了模型的搭建、训练、验证测试等工作(标签-网络|关键词-变化检测)
  • ¥50 mac mini外接显示器 画质字体模糊
  • ¥15 TLS1.2协议通信解密
  • ¥40 图书信息管理系统程序编写
  • ¥20 Qcustomplot缩小曲线形状问题
  • ¥15 企业资源规划ERP沙盘模拟