I need to replace an existing API interface with one that considers an incoming Auth token and also issues a machine-to-machine token for the outgoing service calls.
In summary, this is an API using gorilla/mux routing framework and I'm just adding endpoints to a mux.NewRouter(). Nothing fancy...yet ;).
I have been trying a few different patterns, but the one that seems most appealing is the Adapter Interface derived by Mat Ryer in https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81 and https://go-talks.appspot.com/github.com/matryer/golanguk/building-apis.slide#30
In summary with no direct logic, I have done the following and get stuck in an infinite loop when I run the endpoint from Postman.
{"level":"info","msg":"New Relic Checkpoint!!! /endpoint","time":"2019-08-19T14:28:27-05:00"} {"level":"info","msg":"Security Checkpoint!!! /endpoint","time":"2019-08-19T14:28:27-05:00"} {"level":"info","msg":"Header Checkpoint!!! /endpoint","time":"2019-08-19T14:28:27-05:00"} {"level":"info","msg":"New Relic Checkpoint!!! /endpoint","time":"2019-08-19T14:28:27-05:00"} {"level":"info","msg":"Security Checkpoint!!! /endpoint","time":"2019-08-19T14:28:27-05:00"} {"level":"info","msg":"Header Checkpoint!!! /endpoint","time":"2019-08-19T14:28:27-05:00"}
r.Handle(endpoint.Path(), Adapt(r, NewRelicAdapter(endpoint), SecurityAdapter(endpoint), WithHeader(endpoint)))
the Adapter Interface is established as follows...
type Adapter func(http.Handler) http.Handler
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
for _, adapter := range reverseAdapters(adapters) {
h = adapter(h)
}
return h
}
func NewRelicAdapter(endpoint rest.Endpoint) Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Infof("New Relic Checkpoint!!! %v", endpoint.Path())
h.ServeHTTP(w, r)
})
}
}
func SecurityAdapter(endpoint rest.Endpoint) Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Infof("Security Checkpoint!!! %v", endpoint.Path())
h.ServeHTTP(w, r)
})
}
}
func WithHeader(endpoint rest.Endpoint) Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Infof("Header Checkpoint!!! %v", endpoint.Path())
h.ServeHTTP(w, r)
})
}
}
func reverseAdapters(adapters []Adapter) []Adapter {
for i := 0; i < len(adapters)/2; i++ {
j := len(adapters) - i - 1
adapters[i], adapters[j] = adapters[j], adapters[i]
}
return adapters
}
I would really appreciate knowing why this loops before I get into the details on what each adapter would be doing.
I think this is because your chain of handlers ends with r, in effect becoming:
r.Handle(path, r)
You have to have an actual handler somewhere to handle the call, so your setup should look like:
r.Handle(path, Adapt(theHandler, adapter1, adapter2, adapter3))
However, since you said you're using gorilla/mux, there is another way you can to this. Take a look at Router.Use