Pure Go's stdlib, idiomatic, fast, simple, net/http compatible, context compatible, http router/mux
Your existent http.Handler works with NO CHANGES with this package. And now they can get route parameters! The parameters are stored on the new Context object inside http.Request since Go 1.7.
- Go 1.7+
package main
import (
"github.com/leonelquinteros/router"
"net/http"
)
func sayHello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello " + router.Param(r, "name")))
}
func main() {
// Create route
r := router.New("/")
r.Add("/hello/:name", http.HandlerFunc(sayHello))
s := &http.Server{
Addr: ":8080",
Handler: router.Build(r),
}
s.ListenAndServe()
}
Your handlers are plain simply Go's stdlib http.Handler objects.
import "net/http"
type MyHandler struct {}
func (mh MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world!")
}
And, if you're used to use the http.HandleFunc approach,
you're still covered by the net/http
package with http.HandlerFunc.
import "net/http"
func MyHandlerFunc(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world!")
}
h := http.HandlerFunc(MyHandlerFunc)
No surprises, no changes in the function signature when you want to receive route parameters, nothing.
Routes are handled by a router object that can group several routes under a single prefix.
Then, multiple routers can join into a single dispatcher that acts as a replacement for http.Server.Handler
.
import (
"github.com/leonelquinteros/router"
"net/http"
)
func sayHello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello " + router.Param(r, "name")))
}
func main() {
// Create first router for routes starting at `/v1`
r1 := router.New("/v1")
r1.Add("/hello/:name", http.HandlerFunc(sayHello))
// Create second router for routes starting at `/v2`
r2 := router.New("/v2")
r2.Add("/hello/:name", http.HandlerFunc(sayHello))
// Create http.Server and dispatch both routers
s := &http.Server{
Addr: ":8080",
Handler: router.Build(r1, r2), // router.Build creates the dispatcher object
}
s.ListenAndServe()
}
Since Go 1.7, the context package is included on the stdlib, and with it, the http.Request object now has a Context included on its definition that allow us to pass context related values (just like our parameters) across the life of our request. Bella Vista Router uses this new feature to keep it compatible with existent (and future) net/http handlers.
Your routes can hold parameters by defining a route part starting with :
.
So, if you want to receive a parameter called id
at the end of your /user
route, you can define and consume as follows
import (
"github.com/leonelquinteros/router"
"net/http"
)
func getUser(w http.ResponseWriter, r *http.Request) {
id := router.Param(r, "id")
// Do something with that id
// ...
}
func main() {
// Create route
r := router.New("/")
r.Add("/user/:id", http.HandlerFunc(getUser))
s := &http.Server{
Addr: ":8080",
Handler: router.Build(r),
}
s.ListenAndServe()
}
Middleware type is a function that takes an http.Handler object and returns another http.Handler object to be executed. It has the following signature:
type Middleware func(http.Handler) http.Handler
It can be used to wrap handlers at handler level, router level and/or dispatcher level so different request flows can be architected:
import (
"context"
"github.com/leonelquinteros/router"
"net/http"
)
// Middleware to set content type
func mContentType(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Set header
r.Header().Set("Content-Type", "text/plain")
// Continue flow
next.ServeHTTP(w, r)
})
}
// Handler that says hello
func hSayHello(w http.ResponseWriter, r *http.Request) {
r.Write([]byte("Hello!"))
}
func main() {
// Create route
r := router.New("/")
// Use middleware at router level
r.Wrap(mContentType)
// Route to handler
r.Add("/hello", http.HandlerFunc(hSayHello))
// Get dispatcher
d := router.Build(r)
s := &http.Server{
Addr: ":8080",
Handler: d,
}
s.ListenAndServe()
}