Simple Golang HTTP router
Bellt Package implements a request router with the aim of managing controller actions based on fixed and parameterized routes.
The project so far has the following functionalities:
- Standard definition of route "/health", in order to prepare the service developed with bellt to act as microservice.
- Providing the creation of parameterized routes, simple or segmented (groups).
- All requests can be made through fixed patterns, querystrings and parameters.
- Obtaining the requisition parameters in the controller functions.
To get Bellt
go get -u github.com/GuilhermeCaruso/bellt
dep ensure -add github.com/GuilhermeCaruso/bellt
govendor fetch github.com/GuilhermeCaruso/bellt
To initialize our router
var router = bellt.NewRouter()
package main
import (
"fmt"
"log"
"net/http"
"github.com/GuilhermeCaruso/bellt"
)
func main() {
router := bellt.NewRouter()
log.Fatal(http.ListenAndServe(":8080", nil))
}
HandleFunc function responsible for initializing a common route or built through the Router. All non-grouped routes must be initialized by this method.
/*
[path] - Endpoint string
[handlerFunc] - Function that will be called on the request
[methods] - Slice for endpoint methods ("GET", "POST", "PUT", "DELETE")
*/
router.HandleFunc(path, handlerFunc, methods)
package main
import (
"fmt"
"log"
"net/http"
"github.com/GuilhermeCaruso/bellt"
)
func main() {
router := bellt.NewRouter()
router.HandleFunc("/bellt", belltHandler, "GET")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func belltHandle(w http.ResponseWriter, r *http.Request){
w.WriteHeader(http.StatusOK)
w.Write([]byte("Simple Golang HTTP router")
}
HandleGroup is responsible for creating a group of routes. The main path can be set for all other routes.
/*
[mainPath] - Main route used in all subr-outes
[subHandleFunc] - SubHandleFunc function responsiblefor initializing a common route or
built through the Router. All grouped routes must be initialized by this method
*/
router.HandleGroup(mainPath, ...SubHandleFunc)
SubHandleFunc is responsible for initializing a common or built route. Its use must be made within the scope of the HandleGroup method, where the main path will be declared.
/*
[path] - Endpoint string
[handlerFunc] - Function that will be called on the request
[methods] - Slice for endpoint methods ("GET", "POST", "PUT", "DELETE")
*/
router.SubHandleFunc(path, handlerFunc, methods)
package main
import (
"fmt"
"log"
"net/http"
"github.com/GuilhermeCaruso/bellt"
)
func main() {
router := bellt.NewRouter()
router.HandleGroup("/api",
router.SubHandleFunc("/bellt", belltHandle, "GET"),
router.SubHandleFunc("/check", checkHandle, "GET"),
)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func belltHandle(w http.ResponseWriter, r *http.Request){
w.WriteHeader(http.StatusOK)
w.Write([]byte("Simple Golang HTTP router")
}
func checkHandle(w http.ResponseWriter, r *http.Request){
w.WriteHeader(http.StatusOK)
w.Write([]byte("Ok!")
}
The declaration of middlewares in HandleFunc or SubHandleFunc should be done using the Use method
/*
handlerFunc - Function that will be called on the request
middlewareList - Slice of middleware that will be used in the request (Middleware)
*/
bellt.Use(handlerFunc, ...middlewareList)
The middleware type has a following signature
type Middleware func(http.HandlerFunc) http.HandlerFunc
Applying middlewares to routes
package main
import (
"fmt"
"log"
"net/http"
"github.com/GuilhermeCaruso/bellt"
)
func main() {
router := bellt.NewRouter()
router.HandleFunc("/hello", bellt.Use(
exampleHandler,
middlewareOne,
middlewareTwo,
), "GET")
router.HandleGroup("/api",
router.SubHandleFunc("/hello", bellt.Use(
exampleHandler,
middlewareOne,
middlewareTwo,
), "GET"),
)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func exampleHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`Hello Middleware!`))
}
func middlewareOne(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Step One")
next.ServeHTTP(w, r)
}
}
func middlewareTwo(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Step Two")
next.ServeHTTP(w, r)
}
}
Route parameters must be passed using {}
as scope limiter
router.HandleFunc("/hello/{name}", handlerFunc, "GET")
router.HandleGroup("/api",
SubHandleFunc("/item/{id}", handlerFunc, "GET")
)
RouteVariables used to capture and store parameters passed to built routes.
Need to pass the *Request of the HandlerFunc used in the HandleFunc method.
/*
r = *Request of the HandlerFunc
*/
rv := bellt.RouteVariables(r)
The declaration must be made within the HandlerFunc
func exampleHandler(w http.ResponseWriter, r *http.Request) {
rv := bellt.RouteVariables(r)
/*[...]*/
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello!"))
}
GetVar returns the parameter value of the route
/*
r = *Request of the HandlerFunc
param = Parameter name string
*/
rv := bellt.RouteVariables(r)
rv.GetVar(param)
func exampleHandler(w http.ResponseWriter, r *http.Request) {
rv := bellt.RouteVariables(r)
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf(`Hello %v gopher!`, rv.GetVar("color")))))
}
The complete implementation of parameterized routes should look like this:
package main
import (
"fmt"
"log"
"net/http"
"github.com/GuilhermeCaruso/bellt"
)
func main() {
router := bellt.NewRouter()
router.HandleFunc("/contact/{id}/{user}", exampleHandler, "GET")
router.HandleGroup("/api",
router.SubHandleFunc("/check/{id}/{user}", exampleHandler, "GET"),
)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func exampleHandler(w http.ResponseWriter, r *http.Request) {
rv := bellt.RouteVariables(r)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf(`{"id": %v, "user": %v}`, rv.GetVar("user"), rv.GetVar("id"))))
}
package main
import (
"fmt"
"log"
"net/http"
"github.com/GuilhermeCaruso/bellt"
)
func main() {
router := bellt.NewRouter()
router.HandleFunc("/contact/{id}/{user}", bellt.Use(
exampleHandler,
middlewareOne,
middlewareTwo,
), "GET")
router.HandleFunc("/contact", bellt.Use(
exampleNewHandler,
middlewareOne,
middlewareTwo,
), "GET")
router.HandleGroup("/api",
router.SubHandleFunc("/check", bellt.Use(
exampleNewHandler,
middlewareOne,
middlewareTwo,
), "GET"),
router.SubHandleFunc("/check/{id}/{user}", bellt.Use(
exampleHandler,
middlewareOne,
middlewareTwo,
), "GET"),
)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func exampleHandler(w http.ResponseWriter, r *http.Request) {
rv := bellt.RouteVariables(r)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf(`{"id": %v, "user": %v}`, rv.GetVar("user"), rv.GetVar("id"))))
}
func exampleNewHandler(w http.ResponseWriter, r *http.Request) {
rv := bellt.RouteVariables(r)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"msg": "Works"}`))
}
func middlewareOne(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Step One")
next.ServeHTTP(w, r)
}
}
func middlewareTwo(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Step Two")
next.ServeHTTP(w, r)
}
}
Applying requisition performance tests, the following results were obtained, showing the initial potency of the Bellt package.
Guilherme Caruso @guicaruso_ on twitter
Guilherme Caruso - Cabify- GolangSP Meetup 2 - 21/03/2019 - São Paulo /Brazil
Slides - Construindo Rotas Parametrizadas em GO
Video - GolangSP Meetup 2
MIT licensed. See the LICENSE file for details.