Here is the complete example from my current reading material "Hands-On Restful Web Services With Go" from Packt.
func filterContentType(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Currently in the check content type middleware")
// Filtering requests by MIME type
if r.Header.Get("Content-type") != "application/json" {
w.WriteHeader(http.StatusUnsupportedMediaType)
w.Write([]byte("415 - Unsupported Media Type. Please send JSON"))
return
}
handler.ServeHTTP(w, r)
})
}
func setServerTimeCookie(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Setting cookie to every API response
cookie := http.Cookie{Name: "ServerTimeUTC", Value: strconv.FormatInt(time.Now().Unix(), 10)}
http.SetCookie(w, &cookie)
log.Println("Currently in the set server time middleware")
handler.ServeHTTP(w, r)
})
}
func handle(w http.ResponseWriter, r *http.Request) {
// Check if method is POST
if r.Method == "POST" {
var tempCity city
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&tempCity)
if err != nil {
panic(err)
}
defer r.Body.Close()
// Your resource creation logic goes here. For now it is plain print to console
log.Printf("Got %s city with area of %d sq miles!\n", tempCity.Name, tempCity.Area)
// Tell everything is fine
w.WriteHeader(http.StatusOK)
w.Write([]byte("201 - Created"))
} else {
// Say method not allowed
w.WriteHeader(http.StatusMethodNotAllowed)
w.Write([]byte("405 - Method Not Allowed"))
}
}
func main() {
originalHandler := http.HandlerFunc(handle)
http.Handle("/city", filterContentType(setServerTimeCookie(originalHandler))) // !
http.ListenAndServe(":8000", nil)
}
This program simply consists of the main function and 3 other functions, their logic is arbitrary and just copied from my book's example.
At bottom, where I've commented with "!", filterContentType
is using an argument that itself is a function (setServerTimeCookie
), and it looks like it's being invoked with originalHandler
as its argument.
However when this code is run, the order of execution is:
filterContentType
2.setServerTimeCookie
3.originalHandler
This is counterintuitive to what I understand about using functions as arguments. I assumed that setServerTimeCookie
would be the first to execute but that's not the case; it's behaving like an uninvoked function.
This leads to my question, what is causing setServerTimeCookie
to defer its execution despite the syntax suggesting it's being invoked as filterContentType
's argument?
I attempted to simplify things for my own understanding:
func main() {
one(two(three))
}
func one(f func()) {
fmt.Println("ONE\n")
f()
}
func two(f func()) {
fmt.Println("TWO\n")
f()
}
func three(){
fmt.Println("THREE\n")
}
This code does not build, I'm left with the error:
two(three) used as value
-which tells me that two
is being invoked, unlike the book's example.
What's the difference and again, why doesn't the book's example invoke setServerTimeCookie
first? My only assumption is that it has something to do with the implementation of http.HandlerFunc
so maybe I should start there.
Any insight to fast-forward my understanding would be greatly appreciated.
CodePudding user response:
This doesn't compile because two(three)
does not return a value.
I assume you want to return a function closure in this case, so to fix:
func two(f func()) func() {
return func() {
fmt.Println("TWO\n")
f()
}
}
https://go.dev/play/p/vBrAO6nwy4X
Circling back to your question about setServerTimeCookie
and it's use of return http.HandlerFunc(fn)
. Looking at the source for http.HandlerFunc reveals it's actually a type definition - and NOT a conventional function call. It's actual IMHO the most powerful and underrated four lines of code in the go
standard library:
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
By creating this value of http.HandlerFunc
, it's implicitly a http.Handler
, since it provides the ServeHTTP
method. This therefore allows this method to be called upon request - which is exactly what a webservice is designed to do: the underlying function f
will be invoked when the handler is invoked.
CodePudding user response:
Because in the expression one(two(three))
function two
is not passed as function reference. Instead function two
is called with the argument tree
, which not not what function one
expects