How to combine mux (gorilla) in Go? - go

I have the following code in which I have two mux, one for unauthenticated routes (r) and another for authenticated routes (chain). I want to combine these two and serve.
package main
import (
"ekart.com/authentication_service/models"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/justinas/alice"
)
func main() {
var JSON_SECRET []byte = []byte("TZWfMhBEuyzsc2e52wQQaDxfBriktka9GR4BhBtH9PhXi6XHwyaaaUyxBc-yd1RtYKsiFAGXZcBJuQ6ML5UGBEEx72Qhw6Q-192msUDwMHxT3Scz5wWnk--Bp8wotvu7FS2-v88cvC52e1lfG8mr60dGu7kg-jzcRa5cDTfR4KMQIAD6lO1H3J6f48u46gLjQtzOLPH9yjx0mqVtGWmaizGMQE7NdrhHH5ZlMuuj-A6lZRjf2VZKxUiFFWmfnVMhPVh-wpOybMaFzhUjm-RWXQ-E6cCeI-sBzcu5ZJ8aZVnYPc1Inc5RJ9R5rKQblctxHt5QCYlxiFHb63aO36ZS0Q")
var DSN string = "root:toor#tcp(localhost:3306)/ekart_auth?charset=utf8&parseTime=True&loc=Local"
app := Config{secret: JSON_SECRET}
app.NewDatabaseConnection(DSN)
app.DB.AutoMigrate(&models.User{})
r := mux.NewRouter()
r.HandleFunc("/login", app.Login).Methods("POST")
r.HandleFunc("/register", app.Register).Methods("POST")
authmux := mux.NewRouter()
authmux.HandleFunc("/onlyauth", app.OnlyIfAuthenticated).Methods("GET")
chain := alice.New(app.Authenticate).Then(authmux)
log.Print(fmt.Sprintf("Starting Server on port %d", 8080))
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", 8080), r))
}
I have tried this way but that didn't work.
Also I tried from here
s := r.PathPrefix("/auth").Subrouter()
s.HandleFunc("/", chain.ServeHTTP)
But this also doesn't work.
It would be better if the answer doesn't require me to create a subpath but ok if I have to do that.

You can combine both r and chain routers by using the Use method of the mux package. You can use Use to add a middleware to a route, and since chain has a middleware app.Authenticate using alice.New, you can add it to the r router as a middleware.
func main() {
// ...
r := mux.NewRouter()
r.HandleFunc("/login", app.Login).Methods("POST")
r.HandleFunc("/register", app.Register).Methods("POST")
authmux := mux.NewRouter()
authmux.HandleFunc("/onlyauth", app.OnlyIfAuthenticated).Methods("GET")
chain := alice.New(app.Authenticate).Then(authmux)
r.Use(app.Authenticate)
r.PathPrefix("/auth").Handler(chain)
log.Print(fmt.Sprintf("Starting Server on port %d", 8080))
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", 8080), r))
}
Now all routes under /auth will have the app.Authenticate middleware applied to them.

Related

Integration of Echo web framework with existing Gorilla Mux API

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!")
}

Sharing a gorilla/mux router across packages

I'm having some issues with implementing a slight MVC design with gorilla/mux.
The layout of the modules is as follows:
main.go
-- controllers
---- base.controller.go
---- example.controller.go
-- models
---- base.model.go
---- example.controller.go
All the files in controllers is in the controllers package, same with models and then the main.go is the main package.
Currently I'm just trying to get the Base Controller to be able to be shared with the main package which is working, although it's throwing some errors when trying to implement routes. The build is not throwing any errors, but the routes are not available. If I implement the Walk function in the Gorilla/Mux documentation to print out all the registered routes for the mux.Router then it gives me this error:
&{%!!(MISSING)s(*mux.Router=&{ [0xc4200901b0] map[] true
false false false}) %!!(MISSING)s(http.HandlerFunc=0xc8df0)
[%!!(MISSING)s(*mux.routeRegexp=&{/ false false true false
0xc420095360 / [] []})] %!!(MISSING)s(*mux.routeRegexpGroup=&{
0xc420016240 []}) %!!(MISSING)s(bool=true) %!!(MISSING)s(bool=false)
%!!(MISSING)s(bool=false) %!!(MISSING)s(bool=false)
%!!(MISSING)s(mux.BuildVarsFunc=)}
The reasoning for the global var V1Router *mux.Router is firstly to access it in the main package and also to create subrouters in the other controllers.
I am fairly new to Go, but I'm trying my best to learn the best practices! Any help would be greatly appreciated!
Example code below:
base.controllers.go
package controllers
import (
"fmt"
"bytes"
"net/http"
"github.com/gorilla/mux"
)
var V1Router *mux.Router
func init () {
V1Router = mux.NewRouter()
V1Router.StrictSlash(true)
V1Router.HandleFunc("/", BaseHandler)
}
// Base route to access the API Documentation.
func BaseHandler (w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, Gophers!")
}
main.go
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
In response to the comments, I tried this solution with the same result:
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
r := mux.NewRouter()
r.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Gorilla mux.Router is supposed to be used to create mapping between a set of predefined rules (e.g. host, path, protocol, scheme, etc...) and it's handler (http.Handler or http.HandlerFunc). Gorilla mux can be used to replace standard server mux. If you combine gorilla/mux with built in http server mux as your original question, i.e.
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
what actually happen when a client access /v1 is controllers.V1Router will be called with request path /v1 passed to V1Router1. In the controllers.V1Router, you defined that / will be handled by BaseHandler. However, since incoming request path is /v1, it won't match to your routing table. If you want to define sub routing, you can do as follows (this is what I mean in first comment):
func main () {
r := mux.NewRouter()
v1 := r.PathPrefix("/v1").Subrouter()
controllers.RegisterHandlers(v1)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Then in the controllers (base.controllers.go) define
//Register handlers and it's sub router
func RegisterHandlers(r *mux.Router) {
//base handler, i.e. /v1
r.StrictSlash(true)
r.HandleFunc("/", BaseHandler)
//example sub-router, i.e. /v1/example
ex := r.PathPrefix("/example").Subrouter()
ex.HandleFunc("/", ExampleHandler)
//other handlers...
}

Put the http.Handler in martini

How do I integrate just like http.FileServer with martini?
` package main
import (
"github.com/go-martini/martini"
"net/http"
)
func main() {
m := martini.Classic()
//http.Handle("/", http.FileServer(http.Dir("."))) //It doesn't work!
m.Run()
}`
I believe the FileServer isn't used directly in Martini: see issues/20:
Unfortunately The fileserver middleware throws a 404 if there is no match, which means we will need to roll our own
Hence PR 26 and commit a945713 in static.go that you can see in static_test.go
m := New()
r := NewRouter()
m.Use(Static(currentRoot))
m.Action(r.Handle)

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),
))

Profiling Go web application built with Gorilla's mux with net/http/pprof

I have a relatively big web application written in Go that uses Gorilla's mux for routing. I recently realised that my web application is quite slow and I would like to profile the web application.
After reading about it, it seems that net/http/pprof is what I need. But I can't make it run with mux; even in the case of the most trivial web application.
Does anyone knows how to make that work?
Here is an example of a trivial code that does not work (i.e. nothing is served at /debug).
package main
import (
"fmt"
"github.com/gorilla/mux"
"math"
"net/http"
)
import _ "net/http/pprof"
func SayHello(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 1000000; i++ {
math.Pow(36, 89)
}
fmt.Fprint(w, "Hello!")
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/hello", SayHello)
http.ListenAndServe(":6060", r)
}
My preferred method for this is to just let net/http/pprof register itself to http.DefaultServeMux, and then pass all requests starting with /debug/pprof/ along:
package main
import (
"net/http"
_ "net/http/pprof"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
if err := http.ListenAndServe(":6060", router); err != nil {
panic(err)
}
}
I find that this approach is a lot more stable than one that depends on the implementation of a hidden initialization method, and also guarantees that you didn't miss anything.
user983716 - Thanks for your question and solution!
I was not able to use the links from the web index (http://[my-server]/debug/pprof), until I added a few lines to your solution, like so:
...
func AttachProfiler(router *mux.Router) {
router.HandleFunc("/debug/pprof/", pprof.Index)
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
// Manually add support for paths linked to by index page at /debug/pprof/
router.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
router.Handle("/debug/pprof/heap", pprof.Handler("heap"))
router.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
router.Handle("/debug/pprof/block", pprof.Handler("block"))
}
...
If anyone has the same problem, I hope this helps!
Sorry for that question. The answer is in the init() function of pprof. One just need to add 4 functions from pprof to the mux router. Here is the fixed code from above.
package main
import (
"fmt"
"github.com/gorilla/mux"
"math"
"net/http"
)
import "net/http/pprof"
func AttachProfiler(router *mux.Router) {
router.HandleFunc("/debug/pprof/", pprof.Index)
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
}
func SayHello(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 1000000; i++ {
math.Pow(36, 89)
}
fmt.Fprint(w, "Hello!")
}
func main() {
r := mux.NewRouter()
AttachProfiler(r)
r.HandleFunc("/hello", SayHello)
http.ListenAndServe(":6060", r)
}
Previous examples not really work on my side.
To use pprof in an existing golang project with gorrila/mux, try to add :
...previous code
func main() {
r := mux.NewRouter()
r.HandleFunc("/hello", SayHello)
go func() {
log.Fatal(http.ListenAndServe(":6061", http.DefaultServeMux))
}()
http.ListenAndServe(":6060", r)
}
then go to http://localhost:6061/debug/pprof/
I did something else, I added another native http server on a different port and it just works out of the box
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
log.Println(http.ListenAndServe(":6060", nil))
}()
log.Fatalln(http.ListenAndServe(":8080", route.Handlers()))
}
Now the pprof endpoint is at :
http://localhost:6060/debug/pprof/ and the applcation is running on port :8080
Just so:
r := mux.NewRouter()
r.PathPrefix("/debug").Handler(http.DefaultServeMux)
Im using https://github.com/julienschmidt/httprouter but i just got this answer from google search.
That's what i did
router := httprouter.New()
router.Handler("GET", "/debug/pprof/profile", http.DefaultServeMux)
router.Handler("GET", "/debug/pprof/heap", http.DefaultServeMux)
I only need this two routes.
This answer is combine of #damien and #user983716 answers.
The following should work:
import (
"net/http"
_ "net/http/pprof"
)
myrouter.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)

Resources