Henrique Dias
Henrique Dias

Reputation: 193

Go: use different handlers or inject variables into request Context?

I have an Handler like this:

type handler struct {
    Services *domain.Services
    Config *domain.Config
}

And then, a lot of new types (they can be twenty or more), like this:

type Handler1 handler
type Handler2 handler

And each one has a ServeHTTP method. And I use this so they can access the Services and Config variables.

They are being used in routes like this:

r.Handle("/login", &h.Handler1{
    Services: s,
    Config: c,
})

My question is: should I create all of this structs or just create a function that injects the Services and Config into the request Context and then I access them using r.Context().Value()?

I thought about doing this:

func handler1(w http.ResponseWriter, r *http.Request) {
    s, c := r.Context().Value("services"), r.Context().Value("config")
    // My code
}

r.HandleFunc("/login", inject(handler1, s, c))

What's the best/recommended?

Upvotes: 1

Views: 529

Answers (2)

baloo
baloo

Reputation: 7755

As an alternative to creating all these handler types, you can have functions which return other functions (http.HandlerFunc). This way, you will create a closure and can access the parameters when the request arrives in the handler. For example:

package main

import (
    "fmt"
    "net/http"
)

func SomeHandler(conf SomeConfig, service SomeService) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "foobar config: %q", conf)
    }
}

func main() {
    // TODO: initialise your conf & service
    http.HandleFunc("/somepath", SomeHandler(conf, service))
}

Upvotes: 2

Mikhail Kochegarov
Mikhail Kochegarov

Reputation: 334

You probably could create some kind of Router which will provide ServeHTTP and do mapping between your real handlers and route paths.

Something like this:

package main

import "net/http"

type Router struct {
    routes map[string]func(rw http.ResponseWriter, r *http.Request)
}

func NewRouter() *Router {
    var r Router
    r.routes = make(map[string]func(rw http.ResponseWriter, r *http.Request))
    return &r
}

func (router *Router) addRoute(path string, f func(rw http.ResponseWriter, r *http.Request)) {
    router.routes[path] = f
}

func (router *Router) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
    for route, serveHTTP := range router.routes {
        if route == r.URL.Path {
            serveHTTP(rw, r)
            return
        }
    }
    rw.WriteHeader(http.StatusNotFound)
}

func teapot(rw http.ResponseWriter, r *http.Request) {
    rw.WriteHeader(http.StatusTeapot)
}

func ok(rw http.ResponseWriter, r *http.Request) {
    rw.WriteHeader(http.StatusOK)
}

func main() {
    r := NewRouter()
    r.addRoute("/teapot", teapot)
    r.addRoute("/ok", ok)

    http.ListenAndServe("localhost:8080", r)
}

Upvotes: 0

Related Questions