Integration of Echo web framework with existing Gorilla Mux API - gorilla

I am wanting to move to the echo framework for my API due to an openapi package we wish to use (opai-codegen) However our current API is built via gorilla mux. Due to the size of the current codebase we need to run them both side by side.
So I am trying to work out how do I get gorilla mux and the echo framework to work together via the same http.Server
The gorilla mux API is created via:
router := mux.NewRouter().StrictSlash(true)
router.Handle("/..",...)
//etc ...
And then my echo API is created via:
echo := echo.New()
echo.Get("/..", ...)
// etc ...
However I can't get them to run with the same http.ListenAndServe
Love to know if there is any to make these two work together?
Thanks

This is what i can think of, Although you will need to move middle-wares to echo
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
// Echo instance
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
r := mux.NewRouter()
r.HandleFunc("/mux/", Hello).Methods("GET", "PUT").Name("mux")
r.HandleFunc("/muxp/", HelloP).Methods("POST").Name("muxp")
gorillaRouteNames := map[string]string{
"mux": "/mux/",
"muxp": "/muxp/",
}
// Routes
e.GET("/", hello)
// ro := e.Any("/mux", ehandler)
for name, url := range gorillaRouteNames {
route := r.GetRoute(name)
methods, _ := route.GetMethods()
e.Match(methods, url, echo.WrapHandler(route.GetHandler()))
fmt.Println(route.GetName())
}
// Start server
e.Logger.Fatal(e.Start(":1323"))
}
// Handler
func hello(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
}
func Hello(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "Hello world!")
}
func HelloP(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "Hello world By Post!")
}

Related

Blank page when using swaggo/http-swagger with julienschmidt/httprouter

I am making an api and used swaggo/swag to build a swagger interface. Previously, I used the net/http package, and everything was working fine.
I switched to julienschmidt/httprouter, but I don't manage to make the swagger interface work again. Here is my code
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
httpSwagger "github.com/swaggo/http-swagger"
)
func main() {
router := httprouter.New()
router.ServeFiles("/api/doc/static/*filepath", http.Dir("api/swagger/static"))
router.HandlerFunc(http.MethodGet, "/api/doc/index.html", swaggerHandler)
// router.HandlerFunc(http.MethodGet, "/api/doc", swaggerHandler)
fmt.Println("Server on port 8080")
log.Fatal(http.ListenAndServe(":8080", router))
}
func swaggerHandler(w http.ResponseWriter, r *http.Request) {
swaggerFileUrl := "http://localhost:8080/api/doc/static/swagger.json"
handler := httpSwagger.Handler(httpSwagger.URL(swaggerFileUrl))
handler.ServeHTTP(w, r)
}
I checked if swaggerFileUrl variable is correct, and I am able to access the json file with this url. The interface is a complete blank page titled "Swagger UI". Because the title is replaced, I am assuming, that something happened, but I don't know if the issue comes from httpSwagger or httprouter.
Edit: Issue is caused because javascript files loading the interface are not present. See this github issue
You can do it like this:
routes := httprouter.New()
routes.GET("/doc/:any", swaggerHandler)
func swaggerHandler(res http.ResponseWriter, req *http.Request, p httprouter.Params) {
httpSwagger.WrapHandler(res, req)
}
Do not forget import doc files
import (
_ "example.project/docs"
)

Pass a reference to a Redis instance to a Gorilla/Mux Handler

I'm trying to get my hands dirty while playing with some Gorilla/Mux and Go-Redis but I'm facing a little implementation problem here.
Essentially I have a project structured like the following:
Where redismanager.go handles the initialization of a Redis Client:
package redismanager
import (
"fmt"
"github.com/go-redis/redis"
)
func InitRedisClient() redis.Client {
client := redis.NewClient(&redis.Options{
Addr : "localhost:6379",
Password: "",
DB : 0, //default
})
pong, err := client.Ping().Result()
if( err != nil ){
fmt.Println("Cannot Initialize Redis Client ", err)
}
fmt.Println("Redis Client Successfully Initialized . . .", pong)
return *client
}
Where main.go calls redismanager.InitRedisClient and initializes mux.Handlers:
package main
import (
"github.com/gorilla/mux"
"github.com/go-redis/redis"
"net/http"
"fmt"
"log"
"encoding/json"
"io/ioutil"
"../redismanager"
"../api"
)
type RedisInstance struct {
RInstance *redis.Client
}
func main() {
//Initialize Redis Client
client := redismanager.InitRedisClient()
//Get current redis instance to get passed to different Gorilla-Mux Handlers
redisHandler := &RedisInstance{RInstance:&client}
//Initialize Router Handlers
r := mux.NewRouter()
r.HandleFunc("/todo", redisHandler.AddTodoHandler).
Methods("POST")
fmt.Println("Listening on port :8000 . . .")
// Bind to a port and pass our router in
log.Fatal(http.ListenAndServe(":8000", r))
}
Now, I can easily define and let work properly AddTodoHandler in the same file like:
func (c *RedisInstance) AddTodoHandler(w http.ResponseWriter, r *http.Request) {
. . . doSomething
}
But, to make things a bit more modular, I'm trying to move all of these RouteHandlers inside their respective files in api package. In order to make that, I need to pass a reference to redisHandler but I'm having some difficulties when trying to make that with an Handler inside api package.
For instance, If in the main I add:
r.HandleFunc("/todo/{id}", api.GetTodoHandler(&client)).
Methods("GET")
with gettodo.go
package api
import (
"net/http"
"github.com/gorilla/mux"
"fmt"
"encoding/json"
"github.com/go-redis/redis"
)
func GetTodoHandler(c *RedisInstance) func (w http.ResponseWriter, r *http.Request) {
func (w http.ResponseWriter, r *http.Request) {
. . . doSomething
}
}
It works nicely.
I'm still pretty new to Go and haven't found any cleaner solution to that even after several researches and reads.
Is my approach correct or are there any better ones?
Write a function that converts a function with the Redis instance argument to an HTTP handler:
func redisHandler(c *RedisInstance,
f func(c *RedisInstance, w http.ResponseWriter, r *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { f(c, w, r) })
}
Write your API handlers like this:
func AddTodoHandler(c *RedisInstance, w http.ResponseWriter, r *http.Request) {
...
}
Add to the mux like this:
r.Handler("/todo", redisHandler(client, api.AddTodoHandler)).Methods("POST")
where client is the Redis instance.
I would recommend using an App struct which initializes DB and Routes. And all Redis methods will be called inside.
e.g. type App struct{Routes *mux.Router, DB *DB_TYPE}
And which will have App.initializeRoutes method.
type App struct {
Router *mux.Router
DB *redis.NewClient
}
func (a *App) Run(addr string) {
log.Fatal(http.ListenAndServe(":8000", a.Router))
}
func (a *App) Initialize(addr, password string, db int) error {
// Connect postgres
db, err := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
DB: db,
})
if err != nil {
return err
}
// Ping to connection
err = db.Ping()
if err != nil {
return err
}
// Set db in Model
a.DB = db
a.Router = mux.NewRouter()
a.initializeRoutes()
return nil
}
func (a *App) initializeRoutes() {
a.Router.HandleFunc("/todo", a.AddTodoHandler).Methods("POST")
a.Router.HandleFunc("/todo/{id}", a.GetTodoHandler).Methods("GET")
}
// AddTodoHandler has access to DB, in your case Redis
// you can replace the steps for Redis.
func (a *App) AddTodoHandler() {
//has access to DB
a.DB
}
Hope you get the point, you can even extract out the Model work into a separate Struct and then pass it inside func's
r.HandleFunc("/todo/{id}", redisHandler.api.GetTodoHandler).Methods("GET")
Your redisHandler, as defined in main, has no api field, so this naturally doesn't compile.
If you re-defined your RedisInstance type in the api package, and you defined the handler methods on that type in the method-specific files, then you can initialize your redisHandler using that api.RedisInstance type and you can delete the main.RedisInstance type definition:
package main
import (
"github.com/gorilla/mux"
"github.com/go-redis/redis"
"net/http"
"fmt"
"log"
"encoding/json"
"io/ioutil"
"../redismanager"
"../api"
)
func main() {
//Initialize Redis Client
client := redismanager.InitRedisClient()
//Get current redis instance to get passed to different Gorilla-Mux Handlers
redisHandler := &api.RedisInstance{RInstance:&client}
//Initialize Router Handlers
r := mux.NewRouter()
r.HandleFunc("/todo", redisHandler.AddTodoHandler).Methods("POST")
r.HandleFunc("/todo/{id}", redisHandler.GetTodoHandler).Methods("GET")
fmt.Println("Listening on port :8000 . . .")
// Bind to a port and pass our router in
log.Fatal(http.ListenAndServe(":8000", r))
}

how to organize gorilla mux routes?

i am using Gorilla Mux for writing a REST API and i am having trouble organizing my routes, currently all of my routes are defined in the main.go file like this
//main.go
package main
import (
"NovAPI/routes"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/hello", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Hello")
})
router.HandleFunc("/user", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "User")
})
router.HandleFunc("/route2", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route2")
})
router.HandleFunc("/route3", func(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route3")
})
// route declarations continue like this
http.ListenAndServe(":1128", router)
}
so what i want to do is take out and split this route declaration into multiple files, how would i go about doing that? thanks in advance.
You can modularize your routers independently into different packages, and mount them on the main router
Elaborating just a little on the following issue, you can come up with this approach, that makes it quite scalable (and easier to test, to some degree)
/api/router.go
package api
import (
"net/http"
"github.com/gorilla/mux"
)
func Router() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/", home)
return router
}
func home(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello from API"))
}
/main.go
package main
import (
"log"
"net/http"
"strings"
"github.com/...yourPath.../api"
"github.com/...yourPath.../user"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/", home)
mount(router, "/api", api.Router())
mount(router, "/user", user.Router())
log.Fatal(http.ListenAndServe(":8080", router))
}
func mount(r *mux.Router, path string, handler http.Handler) {
r.PathPrefix(path).Handler(
http.StripPrefix(
strings.TrimSuffix(path, "/"),
handler,
),
)
}
func home(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Home"))
}
What about something like this ?
//main.go
package main
import (
"NovAPI/routes"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/hello", HelloHandler)
router.HandleFunc("/user", UserHandler)
router.HandleFunc("/route2", Route2Handler)
router.HandleFunc("/route3", Route3Handler)
// route declarations continue like this
http.ListenAndServe(":1128", router)
}
func HelloHandler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Hello")
}
func UserHandler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "User")
}
func Route2Handler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route2")
}
func Route3Handler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Route3")
}
That way you can put your handlers in other files, or even other packages.
If you endup with additionnal dependencies like a database, you can even avoid the need of the global var using a constructor trick:
//main.go
func main() {
db := sql.Open(…)
//...
router.HandleFunc("/hello", NewHelloHandler(db))
//...
}
func NewHelloHandler(db *sql.DB) func(http.ResponseWriter, *http.Request) {
return func(res http.ResponseWriter, req *http.Request) {
// db is in the local scope, and you can even inject it to test your
// handler
fmt.Fprintln(res, "Hello")
}
}
I like checking out other projects in github to grab ideas on how to do stuff, and for these cases I usually take a look first at the Docker repo. This is the way they do it:
For the system's routes, define all handlers in system_routes.go and then initialize those routes on a NewRouter function in system.go.
type systemRouter struct {
backend Backend
routes []router.Route
}
func NewRouter(b Backend) router.Router {
r := &systemRouter{
backend: b,
}
r.routes = []router.Route{
local.NewOptionsRoute("/", optionsHandler),
local.NewGetRoute("/_ping", pingHandler),
local.NewGetRoute("/events", r.getEvents),
local.NewGetRoute("/info", r.getInfo),
local.NewGetRoute("/version", r.getVersion),
local.NewPostRoute("/auth", r.postAuth),
}
return r
}
// Routes return all the API routes dedicated to the docker system.
func (s *systemRouter) Routes() []router.Route {
return s.routes
}
Notice that systemRouter implements the router.Router interface and the Routes function returns a []router.Route, and their handlers are defined as
func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error
instead of Go's standard http handler:
func(w http.ResponseWriter, r *http.Request)
So there's extra code of theirs to convert a Docker API Handler to a Go HTTP Handler at the makeHttpHandler function.
And finally, to add those routes to their mux router, on their server.go they implement several other functions to add middleware to their handlers.
If this is something that you think it's what you are looking for, then take your time to analyze the Docker code for their routes, and if you need me to elaborate more or if I missed anything, post a comment.
Since I am new to Go, I prefer a less verbose solution. In each module, we can create a Route function that expects a main route pointer and creates sub-routes to it. Our main.go file would be as follows
package main
import (
"net/http"
"github.com/user-name/repo-name/auth"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
auth.Router(r)
http.ListenAndServe(":8080", r)
}
then in auth module, we can create a route file
package auth
import "github.com/gorilla/mux"
func Router(r *mux.Router) {
routes := r.PathPrefix("/auth").Subrouter()
routes.HandleFunc("/register", Register)
}

How to create many http servers into one app?

I want create two http servers into one golang app. Example:
package main
import (
"io"
"net/http"
)
func helloOne(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world one!")
}
func helloTwo(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world two!")
}
func main() {
// how to create two http server instatce?
http.HandleFunc("/", helloOne)
http.HandleFunc("/", helloTwo)
go http.ListenAndServe(":8001", nil)
http.ListenAndServe(":8002", nil)
}
How to create two http server instance and add handlers for them?
You'll need to create separate http.ServeMux instances. Calling http.ListenAndServe(port, nil) uses the DefaultServeMux (i.e. shared). The docs for this are here: http://golang.org/pkg/net/http/#NewServeMux
Example:
func main() {
r1 := http.NewServeMux()
r1.HandleFunc("/", helloOne)
r2 := http.NewServeMux()
r2.HandleFunc("/", helloTwo)
go func() { log.Fatal(http.ListenAndServe(":8001", r1))}()
go func() { log.Fatal(http.ListenAndServe(":8002", r2))}()
select {}
}
Wrapping the servers with log.Fatal will cause the program to quit if one of the listeners doesn't function. If you wanted the program to stay up if one of the servers fails to start or crashes, you could err := http.ListenAndServe(port, mux) and handle the error another way.

Nested Gorilla Mux router does not work

Using code below, when I access /test2 it responds with 404 - not found. /test1 works correctly. Why is that? Is nesting not allowed despite the fact that routers implement http.Handler interface?
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mainRouter := mux.NewRouter()
subRouter := mux.NewRouter()
mainRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test1") })
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test2") })
mainRouter.Handle("/", subRouter)
http.ListenAndServe(":9999", mainRouter)
}
EDIT:
My main goal was to add some initial work which would be common for all routes in subRouter, and only for them. To be even more specific, I would like to use Negroni as my middleware orchiestrator.
On the Negroni website there is an example of adding middleware to the group of routes:
router := mux.NewRouter()
adminRoutes := mux.NewRouter()
// add admin routes here
Create a new negroni for the admin middleware
router.Handle("/admin", negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(adminRoutes),
))
Negroni basically executes ServeHTTP methods of every argument, since all of them implement http.Handler. It executes them in order, so router routes will be last.
I'm familiar with the concept of Subrouter in Mux, but AFAIK I can't use it in similar fashion as example above, in particular, I can't inject anything between mainRouter and its Subrouter. This is why nesting looks more flexible.
I know this question is somewhat old, but I have spent some time figuring out how handlers and matching work in go. You can see my experiment code here.
Basically, you can get the effect you want with code like this:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
mainRouter := mux.NewRouter()
subRouter := mux.NewRouter()
mainRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "test1")
})
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "test2")
})
// in mux, you need to register subrouter
// with the same path that the handlers in
// it are matching
mainRouter.Handle("/test2", subRouter)
// if your subrouter has handlers that match
// other sub paths - you also need to do this
mainRouter.Handle("/test2/{_dummy:.*}", subRouter)
http.ListenAndServe(":9999", mainRouter)
}
I hope this helps someone.
None of the previous answers given helped me achieve exactly what I was seeking out to do. I was trying to use negroni.Wrap() around a Subrouter with lots of routes. I believe that is what the original poster wanted.
Additional Context: I am deploying on Google App Engine, hence putting everything in the init() function.
package hello
import (
"fmt"
"net/http"
"github.com/codegangsta/negroni"
"github.com/gorilla/mux"
)
func init() {
// Create the "root" router, if you will...
r := mux.NewRouter().StrictSlash(true)
// Create your "Subrouter" dedicated to /api which will use the PathPrefix
apiRouter := mux.NewRouter().PathPrefix("/api").Subrouter().StrictSlash(true)
// This step is where we connect our "root" router and our "Subrouter" together.
r.PathPrefix("/api").Handler(negroni.New(
negroni.HandlerFunc(myMiddleware),
negroni.Wrap(apiRouter),
))
// Define "root" routes using r
r.HandleFunc(genHandleFunc("/", "root of site"))
r.HandleFunc(genHandleFunc("/home", "home"))
// Define "Subrouter" routes using apiRouter, prefix is /api
apiRouter.HandleFunc(genHandleFunc("/", "root of API, /api")) // Matches: /api
apiRouter.HandleFunc(genHandleFunc("/v1", "root of API V1, /api/v1")) // Matches: /api/v1
apiRouter.HandleFunc(genHandleFunc("/v1/resourceabc", "API V1 - resourceabc, /api/v1/resourceabc")) // Matches: /api/v1/resourceabc
/* Finally we pass our "root" router to the net/http library. The "root" router will contain all
of the routes for /api also.
*/
http.Handle("/", r)
}
// Silly function to quickly generate a HandleFunc
func genHandleFunc(p string, msg string) (path string, f func(http.ResponseWriter, *http.Request)) {
path = p
f = func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%v\n", msg)
}
return
}
func myMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
fmt.Fprintln(w, "Before doing work...")
next(w, r)
fmt.Fprintln(w, "After doing work...")
}
You wouldn't use two routers here anyway.
Gorilla Mux has the concept of a Subrouter, whereby you define the top level domain properties on the main router, then use the Subrouter instance to map the individual paths.
For example:
mainRouter := mux.NewRouter()
subRouter := mainRouter.PathPrefix("/").Subrouter()
subRouter.HandleFunc("/test1", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test1") })
subRouter.HandleFunc("/test2", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test2") })
mainRouter.Handle("/", mainRouter)
You can go even further than that though - for example, you may have another router at /test1 and a subrouter that matches anything further below that (say /test1/othertest).
Use full path in subroutes:
router := mux.NewRouter()
apiRoutes := mux.NewRouter()
apiRoutes.Handle("/api/auth", Auth)
router.PathPrefix("/api").Handler(negroni.New(
Middleware1,
Middleware2,
negroni.Wrap(apiRoutes),
))

Resources