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)
}
Related
I am creating a module which will provide a http.Handler that can be attached to any running server.
My module has a lot of bundled handlerFuncs for handling internal requests:
func NewHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/one", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("one"))
})
mux.HandleFunc("/two", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("two"))
})
return mux
}
This handler can be attached to a custom path:
package main
import (
"net/http"
"github.com/someuser/someproject"
)
func main() {
http.Handle("/path", someproject.NewHandler())
http.ListenAndServe(":8080", nil)
}
On request invocation to http://localhost:8080/path/one I get 404. What am I doing wrong?
I'm coming from node express, and I was able to pass in as many middleware as possible, for example: routes.use('/*', ensureAuth, logImportant, ... n);
How can I do something similar when using r.GET("/", HomeIndex)?
Am I forced to do something like EnsureAuth(HomeIndex)? Because I can get that to work. Unfortunately, I'm not sure what would be a good way to add as many middlewares as I want without chaining functions together.
Is there a more elegant way so I could somehow use variadic type function to do r.GET("/", applyMiddleware(HomeIndex, m1, m2, m3, m4)? I'm trying that out right now, but I feel like there's a better way to do this.
I've looked at the httprouter issues page, can't find anything :(
Thanks!
Here is an example how I did it:
package main
import (
"context"
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
"github.com/justinas/alice"
)
// m1 is middleware 1
func m1(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//do something with m1
log.Println("m1 start here")
next.ServeHTTP(w, r)
log.Println("m1 end here")
})
}
// m2 is middleware 2
func m2(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//do something with m2
log.Println("m2 start here")
next.ServeHTTP(w, r)
log.Println("m2 end here")
})
}
func index(w http.ResponseWriter, r *http.Request) {
// get httprouter.Params from request context
ps := r.Context().Value("params").(httprouter.Params)
fmt.Fprintf(w, "Hello, %s", ps.ByName("name"))
}
// wrapper wraps http.Handler and returns httprouter.Handle
func wrapper(next http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
//pass httprouter.Params to request context
ctx := context.WithValue(r.Context(), "params", ps)
//call next middleware with new context
next.ServeHTTP(w, r.WithContext(ctx))
}
}
func main() {
router := httprouter.New()
chain := alice.New(m1, m2)
//need to wrap http.Handler to be compatible with httprouter.Handle
router.GET("/user/:name", wrapper(chain.ThenFunc(index)))
log.Fatal(http.ListenAndServe(":9000", router))
}
Link to code (you can't run it from play.golang.org though): https://play.golang.org/p/BOCt97xcoY
I am trying to write a simple http MiddleWare handler that will process an http response. Unfortunately, it does not work and I cannot figure out what mistake I am making. Any/all help is appreciated!
I am using Go Gorilla mux router
Here are illustrative parts of the code:
import (
"fmt"
"log"
"github.com/gorilla/mux"
)
:
func Start() {
router := mux.NewRouter()
router.HandleFunc("/", myHandler)
:
log.Fatal(http.ListenAndServe(":8088", Middleware(router)))
}
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "myHandler called")
}
func Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
neww := NewProcessor(w)
h.ServeHTTP(neww, r)
})
}
type Processor struct {
http.ResponseWriter
}
func (r *Processor) Write(b []byte) (int, error) {
fmt.Printf("******* Processor writing...")
log.Print(string(b)) // log it out
return r.Write(b) // pass it to the original ResponseWriter
}
func NewProcessor(w http.ResponseWriter) http.ResponseWriter {
fmt.Printf("******* Creating new Processor...")
return &Processor{ResponseWriter: w}
}
The output I get is listed below (extra logging text omitted for clarity):
******* Creating new Processor
myHandler called
However, notice the message "******* Processor writing..." was not displayed, suggesting that the "Write" function did not get called.
What changes need to be made to allow the "Write" function to be called?
return r.Write(b) caused an infinite loop of calls to the Processor's Write() method. Replacing it with return r.ResponseWriter.Write(b) fixed the bug.
Here is the corrected code:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", myHandler)
log.Fatal(http.ListenAndServe(":8088", Middleware(mux)))
}
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "myHandler called")
}
func Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
new := NewProcessor(w)
h.ServeHTTP(new, r)
})
}
type Processor struct {
http.ResponseWriter
}
func (r *Processor) Write(b []byte) (int, error) {
log.Print("******* Processor writing...")
log.Print(string(b)) // log it out
return r.ResponseWriter.Write(b) // pass it to the original ResponseWriter
}
func NewProcessor(w http.ResponseWriter) http.ResponseWriter {
log.Print("******* Creating new Processor...")
return &Processor{ResponseWriter: w}
}
Output:
2016/07/21 22:59:08 ******* Creating new Processor...
2016/07/21 22:59:08 ******* Processor writing...
2016/07/21 22:59:08 myHandler called
I'm reasonably new to golang and am trying to do work out the best way to do this idiomatically.
I have an array of routes I am statically defining and passing to gorilla/mux. I am wrapping each handler function with something to time the request and handle panics (mainly so I could understand how the wrapping worked).
I want them each to be able to have access to a 'context' - a struct that's going to be one-per-http-server, which might have things like database handles, config etc. What I don't want to do is use a static global variable.
The way I'm currently doing it I can give the wrappers access to the context structure, but I can't see how to get this into the actual handler, as it wants that to be an http.HandlerFunc. I thought what I could do is convert http.HandlerFunc into a type of my own that was a receiver for Context (and do similarly for the wrappers, but (after much playing about) I couldn't then get Handler() to accept this.
I can't help but think I'm missing something obvious here. Code below.
package main
import (
"fmt"
"github.com/gorilla/mux"
"html"
"log"
"net/http"
"time"
)
type Route struct {
Name string
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
type Context struct {
route *Route
// imagine other stuff here, like database handles, config etc.
}
type Routes []Route
var routes = Routes{
Route{
"Index",
"GET",
"/",
index,
},
// imagine lots more routes here
}
func wrapLogger(inner http.Handler, context *Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner.ServeHTTP(w, r)
log.Printf(
"%s\t%s\t%s\t%s",
r.Method,
r.RequestURI,
context.route.Name,
time.Since(start),
)
})
}
func wrapPanic(inner http.Handler, context *Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("panic caught: %+v", err)
http.Error(w, http.StatusText(500), 500)
}
}()
inner.ServeHTTP(w, r)
})
}
func newRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
// the context object is created here
context := Context {
&route,
// imagine more stuff here
}
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(wrapLogger(wrapPanic(route.HandlerFunc, &context), &context))
}
return router
}
func index(w http.ResponseWriter, r *http.Request) {
// I want this function to be able to have access to 'context'
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}
func main() {
fmt.Print("Starting\n");
router := newRouter()
log.Fatal(http.ListenAndServe("127.0.0.1:8080", router))
}
Here's a way to do it, but it seems pretty horrible. I can't help but think there must be some better way to do it - perhaps to subclass (?) http.Handler.
package main
import (
"fmt"
"github.com/gorilla/mux"
"html"
"log"
"net/http"
"time"
)
type Route struct {
Name string
Method string
Pattern string
HandlerFunc ContextHandlerFunc
}
type Context struct {
route *Route
secret string
}
type ContextHandlerFunc func(c *Context, w http.ResponseWriter, r *http.Request)
type Routes []Route
var routes = Routes{
Route{
"Index",
"GET",
"/",
index,
},
}
func wrapLogger(inner ContextHandlerFunc) ContextHandlerFunc {
return func(c *Context, w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner(c, w, r)
log.Printf(
"%s\t%s\t%s\t%s",
r.Method,
r.RequestURI,
c.route.Name,
time.Since(start),
)
}
}
func wrapPanic(inner ContextHandlerFunc) ContextHandlerFunc {
return func(c *Context, w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("panic caught: %+v", err)
http.Error(w, http.StatusText(500), 500)
}
}()
inner(c, w, r)
}
}
func newRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
context := Context{
&route,
"test",
}
router.Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
wrapLogger(wrapPanic(route.HandlerFunc))(&context, w, r)
})
}
return router
}
func index(c *Context, w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q secret is %s\n", html.EscapeString(r.URL.Path), c.secret)
}
func main() {
fmt.Print("Starting\n")
router := newRouter()
log.Fatal(http.ListenAndServe("127.0.0.1:8080", router))
}
I am learning Go and currently in the middle of a nearly identical problem, and this is how I've dealt with it:
First, I think you missed an important detail: There are no global variables in Go. The widest scope you can have for a variable is package scope. The only true globals in Go are predeclared identifiers like true and false (and you can't change these or make your own).
So, it's perfectly fine to set a variable scoped to package main to hold context for your program. Coming from a C/C++ background this took me a little time to get used to. Since the variables are package scoped, they do not suffer from the problems of global variables. If something in another package needs such a variable, you will have to pass it explicitly.
Don't be afraid to use package variables when it makes sense. This can help you reduce complexity in your program, and in a lot of cases make your custom handlers much simpler (where calling http.HandlerFunc() and passing a closure will suffice).
Such a simple handler might look like this:
func simpleHandler(c Context, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// FIXME Do something with our context
next.ServeHTTP(w, r)
})
}
and be used by:
r = mux.NewRouter()
http.Handle("/", simpleHandler(c, r))
If your needs are more complex, you may need to implement your own http.Handler. Remember that an http.Handler is just an interface which implements ServeHTTP(w http.ResponseWriter, r *http.Request).
This is untested but should get you about 95% of the way there:
package main
import (
"net/http"
)
type complicatedHandler struct {
h http.Handler
opts ComplicatedOptions
}
type ComplicatedOptions struct {
// FIXME All of the variables you want to set for this handler
}
func (m complicatedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// FIXME Do stuff before serving page
// Call the next handler
m.h.ServeHTTP(w, r)
// FIXME Do stuff after serving page
}
func ComplicatedHandler(o ComplicatedOptions) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return complicatedHandler{h, o}
}
}
To use it:
r := mux.NewRouter()
// FIXME: Add routes to the mux
opts := ComplicatedOptions{/* FIXME */}
myHandler := ComplicatedHandler(opts)
http.Handle("/", myHandler(r))
For a more developed handler example see basicAuth in goji/httpauth, from which this example was shamelessly ripped off.
Some further reading:
A Recap of Request Handling
Making and Using HTTP Middleware
justinas/alice (for chaining lots of handlers)
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),
))