在Go中运行时访问符号

huangapple go评论113阅读模式
英文:

Runtime access to symbols in Go

问题

在使用Go语言编写Web服务器时,我想在运行时解引用符号,以便能够从配置文件中确定要调用的函数,类似于下面示例中对虚构的"eval"函数的调用。这将允许我从处理程序库中选择处理程序,并仅使用配置文件部署新的服务器。在Go中有没有办法实现这一点?

config.json

{ "url": "/api/apple", "handler": "Apple", "method": "get" }
{ "url": "/api/banana", "handler": "Banana", "method": "get" }

play.go

package main

import (
    "github.com/gorilla/mux"
    "net/http"
    "encoding/json"
    "log"
)

type ConfigEntry struct {
    URL     string `json:"url"`
    Method  string `json:"method"`
    Handler string `json:"handler"`
}

func main() {
    ifp, err := os.Open("config.json")

    if err != nil {
        log.Fatal(err)
    }

    dec := json.NewDecoder(ifp)
    r := mux.NewRouter()

    for {
        var config ConfigEntry

        if err = dec.Decode(&config); err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        }

        r.HandleFunc(config.URL, eval(config.Handler+"Handler")).Methods(config.Method)
    }

    http.Handle("/", r)
    http.ListenAndServe(8080, nil)
}

func AppleHandler(w http.ResponseWriter, r *http.Request) (status int, err error) {
    w.Write("Apples!\n")
    return http.StatusOK, nil
}

func BananaHandler(w http.ResponseWriter, r *http.Request) (status int, err error) {
    w.Write("Bananas!\n")
    return http.StatusOK, nil
}
英文:

In writing a Web server in Go, I'd like to be able to dereference symbols at runtime, to allow me to figure out which functions to call from a configuration file, something like the call to the fictional "eval" function in the example below. That would allow me to select handlers from a library of handlers, and to deploy a new server with just a config file. Is there any way to accomplish this in Go?

config.json

{ "url": "/api/apple", "handler": "Apple", "method": "get" }
{ "url": "/api/banana", "handler": "Banana", "method": "get" }

play.go

package main

import (
    "github.com/gorilla/mux"
    "net/http"
    "encoding/json"
    "log"
)

type ConfigEntry struct {
    URL string `json:"url"`
    Method string `json:"method"`
    Handler string `json:"handler"`
}

func main() {
    ifp, err := os.Open("config.json")

    if err != nil {
	    log.Fatal(err)
    }

    dec := json.NewDecoder(ifp)
    r := mux.NewRouter()

    for {
	    var config ConfigEntry

	    if err = dec.Decode(&m); err == io.EOF {
		    break
	    } else if err != nil {
		    log.Fatal(err)
	    }

	    r.HandleFunc(config.URL, eval(config.Handler + "Handler")).Methods(config.Method)
    }

    http.Handle("/", r)
    http.ListenAndServe(8080, nil)
}

func AppleHandler(w http.ResponseWriter, r *http.Request) (status int, err error) {
    w.Write("Apples!\n")
    return http.StatusOK, nil
}

func BananaHandler(w http.ResponseWriter, r *http.Request) (status int, err error) {
    w.Write("Bananas!\n")
    return http.StatusOK, nil
}

答案1

得分: 1

在运行时,可以使用reflect包以有限的方式访问对象。然而,它不允许你在一个包中搜索所有适用的独立函数。如果它们都是已知结构类型/值上的方法,那么这是可能的。

作为替代方案,你可以使用map[string]func(...)来存储所有处理程序,在启动时(在init()期间)进行初始化,并从中获取处理程序。但这与现有的HTTP多路复用器所做的事情基本上是一样的。

英文:

There's some limited way to access things during runtime with the reflect package. However it doesn't allow you to search for all suitable standalone functions in a package. It would be possible if they are all methods on a known struct type/value.

As an alternative your given example you could simply use a map[string]func(...) to store all handlers, initialize it at startup (during init()) and fetch the handlers from there. But that also more or less what the existing http muxes are doing.

答案2

得分: 1

在Go语言中没有像eval这样的函数,这是一件好事,因为这样的函数非常危险。

你可以通过在配置文件中将处理程序字符串映射到代码中的处理函数来实现:

var handlers = map[string]func(http.ResponseWriter, *http.Request) (int, error){
    "Apple": AppleHandler,
    "Banana": BananaHandler,
}

然后,你可以通过以下方式简单地注册这些处理程序:

handler, ok := handlers[config.Handler]
if !ok {
    log.Fatal(fmt.Errorf("Handler not found"))
}
r.HandleFunc(config.URL, handler).Methods(config.Method)
英文:

There's nothing like eval in Go, which is a good thing since things like that are very dangerous.

What you can do is have a map mapping the handler strings in your config file to the handler functions in your code:

var handlers = map[string]func(http.ResponseWriter, *http.Request) (int, error){
        "Apple": AppleHandler,
        "Banana": BananaHandler,
}

Then you can register those handlers by simply doing:

handler, ok := handlers[config.Handler]
if !ok {
        log.Fatal(fmt.Errorf("Handler not found"))
}
r.HandleFunc(config.URL, handler).Methods(config.Method)

huangapple
  • 本文由 发表于 2016年11月11日 22:47:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/40550441.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定