I come from JavaScript which has first class function support. For example you can:
Just a brainteaser with recursive function definition for chaining middlewares in a web app.
First, the toolbox:
func MakeChain() (Chain, http.Handler) {
nop := http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {})
var list []Middleware
var final http.Handler = nop
var f Chain
f = func(m Middleware) Chain {
if m != nil {
list = append(list, m)
} else {
for i := len(list) - 1; i >= 0; i-- {
mid := list[i]
if mid == nil {
continue
}
if next := mid(final); next != nil {
final = next
} else {
final = nop
}
}
if final == nil {
final = nop
}
return nil
}
return f
}
return f, final
}
type (
Middleware func(http.Handler) http.Handler
Chain func(Middleware) Chain
)
As you see type Chain
is a function that returns another function of the same type Chain
(How first class is that!).
Now some tests to see it in action:
func TestDummy(t *testing.T) {
c, final := MakeChain()
c(mw1(`OK!`))(mw2(t, `OK!`))(nil)
log.Println(final)
w1 := httptest.NewRecorder()
r1, err := http.NewRequest("GET", "/api/v1", nil)
if err != nil {
t.Fatal(err)
}
final.ServeHTTP(w1, r1)
}
func mw2(t *testing.T, expectedState string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
val := r.Context().Value(contextKey("state"))
sval := fmt.Sprintf("%v", val)
assert.Equal(t, sval, expectedState)
})
}
}
func mw1(initialState string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), contextKey("state"), initialState)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
type contextKey string
Again, this was just a brainteaser to show we can use first class functions in Go in different ways. Personally I use chi nowadays as router and for handling middlewares.