0

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
}
Scott Deerwester
  • 3,503
  • 4
  • 33
  • 56
  • I don't think this is possible to do without a `map[string]http.Handler`. – Ainar-G Nov 11 '16 at 15:05
  • Possible duplicate of [Call all functions with special prefix or suffix in Golang](http://stackoverflow.com/questions/37384473/call-all-functions-with-special-prefix-or-suffix-in-golang). – icza Nov 11 '16 at 15:20

2 Answers2

1

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.

Matthias247
  • 9,836
  • 1
  • 20
  • 29
1

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)
jussius
  • 3,114
  • 15
  • 21