Listing handlers
For a directory contains a combination of function and method handlers you’ll get matching number of listers for receiver types inside the helpers file. Look at your helpers file now for the related section. Notice the return types mention a symbol declared inside the gohandlers package. You need to get the package in addition to installing CLI prior to compiling your code.
package handlers
import "github.com/ufukty/gohandlers/pkg/gohandlers"
func ListHandlers() map[string]gohandlers.HandlerInfo
func (u *User) ListHandlers() map[string]gohandlers.HandlerInfo
func (p *Pets) ListHandlers() map[string]gohandlers.HandlerInfo
Inside each lister there is only one statement, that constructs a map
literal and returns to the caller. The map literal is assigned handler names and meta data as keys and values. So you can use it for registering routes into the http.ServeMux
or the router of your choice. Examine the content of a very usual lister:
func (pe *Pets) ListHandlers() map[string]gohandlers.HandlerInfo {
return map[string]gohandlers.HandlerInfo{
"Create": {Method: "POST", Path: "/create", Ref: pe.Create},
"Delete": {Method: "DELETE", Path: "/pets/{id}", Ref: pe.Delete},
"Get": {Method: "GET", Path: "/pets/{id}", Ref: pe.Get},
"List": {Method: "GET", Path: "/pets", Ref: pe.List},
}
}
Using listers
Listers are functions that return a map
of handler names and handler meta data. The meta data contains path, method and a pointer to the method. Using listers, user can automate route registration to the ServeMux
with help of a simple loop such as:
func main() {
pets := pets.NewPetsHandler()
s := http.NewServeMux()
for name, meta := range pets.ListHandlers() {
pattern := fmt.Sprintf("%s %s", meta.Method, meta.Path)
fmt.Println("registering", name, "as", pattern)
s.HandleFunc(pattern, meta.Ref)
}
log.Println("Starting server on :8080...")
http.ListenAndServe(":8080", s)
}
go run .
registering Create as POST /create
registering Delete as DELETE /pets/{id}
registering Get as GET /pets/{id}
registering List as GET /pets
Using handler listers is great to eliminate missing route issues due to the outdated registration file. Just, make sure build command is in your build pipeline.
Fun fact
You might be thinking yourself “Why the helpers file doesn’t declare the meta data struct itself instead of importing from a package?”. The reason is that when you have handlers spread across the different directories of your project, such as in a microservices project, you’ll produce multiple helpers file. If each declares its HandlerInfo
you would be disabled to implement a project-wide function that processes an HandlerInfo
value. Such as a function that takes a lister, router and logger and register routes to router after logging for easier debugging. This is the case because Go interfaces only lets you to generalize types by the list of common methods, and not fields.
package receptionist
import (
"net/http"
"github.com/ufukty/gohandlers/pkg/gohandlers"
)
func Register(l *slog.Logger, m *http.ServeMux, h map[string]gohandlers.HandlerInfo)
Of course, a function as such is only a simplification. You might have better reasons to declare a lister-accepting function.