I would like to put all my routes in a routes.go file. Currently I have a class here:
cmd-> src -> main.go
Where I have this line of code:
func startServer(port string, services Services, stop chan os.Signal, psFact *ps.Factory, logger log.Logger) *http.Server {
router := mux.NewRouter()
apiHandler.RegisterRoutes(router)
router.HandleFunc
srv := &http.Server{Addr: fmt.Sprintf(":%v", port), Handler: router}
go func() {
if err := srv.ListenAndServe(); err != nil {
if strings.HasPrefix(err.Error(), "listen tcp :5002: bind") {
stop <- syscall.SIGTERM
}
log.Log.Error("error shutting down server", zap.Error(err))
}
}()
return srv
}
I would like to throw all my router.HandleFuncs in a separate file called routes.go in the cmd ->src -> routes.go, but am having trouble figuring out how I can do that. Currently, what I have is this:
package main
import "net/http"
func (services Services) routes() {
}
But how can I call these routes.go from my main.go?
I am not sure if I get it... but try something like the following:
$ tree
.
└── cmd
└── src
├── main.go
└── routes.go
2 directories, 2 files
// $ cat cmd/src/main.go
package main
func main() {
println("hello world")
muxRouter := "SuperRouter"
myService := &MyService{}
myService.routes(muxRouter)
}
// $ cat cmd/src/routes.go
package main
import "fmt"
// MyService ...
type MyService struct {
// deps
}
// routes ...
func (ms *MyService) routes(router string) { //
// register here your endpoints & handlers
fmt.Printf("routes - router: %s & GetServices: %s
", router, ms.GetServices())
}
// GetServices ...
func (ms *MyService) GetServices() string {
return "baz"
}
$ go run cmd/src/*.go
hello world
routes - router: SuperRouter & GetServices: baz
Best,
The actual problem here is that your service knows best what it can provide and your router does not. Since your Services methods are handler functions anyway, you can delegate the setup of the router to the Services. In the example below this is done in BaseRoutes
.
Then, all you have to do is to get a router and let the instance of your Service modify the behavior of said router.
This does not prevent you from setting up addition routes, say for providing an HTMl GUI, as shown by the usage of HelloRoute
as a variadic arg in NewRouter
.
At the end of the day, you hand your Services instance and the additional routes you might want (this is optional!) over to the NewRouter
wrapper and can use the result as usual.
NOTE: I did not do any error handling in the Services, as this highly depends on what you actually wanna do. Please put proper though into error handling anyway.
// Services are a dummy for the actual implementation.
// However, using an interface might well make sense if you have multiple Services.
type Services interface {
GetServices(w http.ResponseWriter, r *http.Request)
GetAssetStatus(w http.ResponseWriter, r *http.Request)
UpdateEncoderConfigForLid(w http.ResponseWriter, r *http.Request)
BaseRoutes(router *mux.Router) error
}
// MyServices is dummy implementation of Services.
type MyServices struct {
}
// GetServices implements the Services interface for MyServices
func (s MyServices) GetServices(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("GetServices"))
}
// GetAssetStatus implements the Services interface for MyServices
func (s MyServices) GetAssetStatus(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("GetAssetStatus"))
}
// UpdateEncoderConfigForLid implements the Services interface for MyServices
func (s MyServices) UpdateEncoderConfigForLid(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("UpdateEncoderConfigForLid"))
}
// BaseRoutes will set up the routes
func (s *MyServices) BaseRoutes(router *mux.Router) error {
router.HandleFunc("/api/services", s.GetServices).Methods(http.MethodGet)
router.HandleFunc("/api/services/status", s.GetAssetStatus).Methods(http.MethodGet)
router.HandleFunc("/api/services/{lid}/encoders", s.UpdateEncoderConfigForLid).Methods(http.MethodPut)
return nil
}
// HelloRoute is an example for an additional route you might want to set up
func HelloRoute(router *mux.Router) error {
router.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
return nil
}
type stdRoute func(router *mux.Router) error
// NewRouter wraps the setup of your service routes and additional routes
// We use the Services interface here to make it more generic.
func NewRouter(s Services, additionalRoutes ...stdRoute) *mux.Router {
r := mux.NewRouter()
var err error
if err = s.BaseRoutes(r); err != nil {
log.Printf("setting up service routes: %s => HANDLE THIS!", err)
}
for _, route := range additionalRoutes {
if err = route(r); err != nil {
log.Printf("setting up additional routes: %s => HANDLE THIS!", err)
}
}
return r
}
// Of course, if you have additional services, you can use variadic args again.
func web(s Services) {
router := NewRouter(s, HelloRoute)
log.Fatal(http.ListenAndServe(":8080", router))
}