xujiajun/gorouter
is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.
I wanted a simple, fast router that has no unnecessary overhead using the standard library only, support regexp, following good practices and well tested code.
- Fast - see Benchmarks
- URL parameters
- Regex parameters
- Routes groups
- Custom NotFoundHandler
- Custom PanicHandler
- Middleware Chain Support
- Serve Static Files
- Pattern Rule Familiar
- HTTP Method Get、Post、Delete、Put、Patch Support
- No external dependencies (just Go stdlib)
- golang 1.8+
go get github.com/xujiajun/gorouter
package main
import (
"log"
"net/http"
"github.com/xujiajun/gorouter"
)
func main() {
mux := gorouter.New()
mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
package main
import (
"github.com/xujiajun/gorouter"
"log"
"net/http"
)
func main() {
mux := gorouter.New()
//url parameters match
mux.GET("/user/:id", func(w http.ResponseWriter, r *http.Request) {
//get one URL parameter
id := gorouter.GetParam(r, "id")
//get all URL parameters
//id := gorouter.GetAllParams(r)
//fmt.Println(id)
w.Write([]byte("match user/:id ! get id:" + id))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
package main
import (
"github.com/xujiajun/gorouter"
"log"
"net/http"
)
func main() {
mux := gorouter.New()
//url regex match
mux.GET("/user/{id:[0-9]+}", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("match user/{id:[0-9]+} !"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
package main
import (
"fmt"
"github.com/xujiajun/gorouter"
"log"
"net/http"
)
func usersHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "/api/users")
}
func main() {
mux := gorouter.New()
mux.Group("/api").GET("/users", usersHandler)
log.Fatal(http.ListenAndServe(":8181", mux))
}
package main
import (
"fmt"
"github.com/xujiajun/gorouter"
"log"
"net/http"
)
func notFoundFunc(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "404 page !!!")
}
func main() {
mux := gorouter.New()
mux.NotFoundFunc(notFoundFunc)
mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
package main
import (
"fmt"
"github.com/xujiajun/gorouter"
"log"
"net/http"
)
func main() {
mux := gorouter.New()
mux.PanicHandler = func(w http.ResponseWriter, req *http.Request, err interface{}) {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println("err from recover is :", err)
fmt.Fprint(w, "received a panic")
}
mux.GET("/panic", func(w http.ResponseWriter, r *http.Request) {
panic("panic")
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
package main
import (
"fmt"
"github.com/xujiajun/gorouter"
"log"
"net/http"
)
type statusRecorder struct {
http.ResponseWriter
status int
}
func (rec *statusRecorder) WriteHeader(code int) {
rec.status = code
rec.ResponseWriter.WriteHeader(code)
}
//https://upgear.io/blog/golang-tip-wrapping-http-response-writer-for-middleware/
func withStatusRecord(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rec := statusRecorder{w, http.StatusOK}
next.ServeHTTP(&rec, r)
log.Printf("response status: %v\n", rec.status)
}
}
func notFoundFunc(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "Not found page !")
}
func withLogging(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("Logged connection from %s", r.RemoteAddr)
next.ServeHTTP(w, r)
}
}
func withTracing(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("Tracing request for %s", r.RequestURI)
next.ServeHTTP(w, r)
}
}
func main() {
mux := gorouter.New()
mux.NotFoundFunc(notFoundFunc)
mux.Use(withLogging, withTracing, withStatusRecord)
mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
package main
import (
"github.com/xujiajun/gorouter"
"log"
"net/http"
"os"
)
//ServeFiles serve static resources
func ServeFiles(w http.ResponseWriter, r *http.Request) {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
dir := wd + "/examples/serveStaticFiles/files"
http.StripPrefix("/files/", http.FileServer(http.Dir(dir))).ServeHTTP(w, r)
}
func main() {
mux := gorouter.New()
mux.GET("/hi", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hi"))
})
//defined prefix
mux2 := mux.Group("/files")
//http://127.0.0.1:8181/files/demo.txt
//will match
mux2.GET("/{filename:[0-9a-zA-Z_.]+}", func(w http.ResponseWriter, r *http.Request) {
ServeFiles(w, r)
})
//http://127.0.0.1:8181/files/a/demo2.txt
//http://127.0.0.1:8181/files/a/demo.txt
//will match
mux2.GET("/{fileDir:[0-9a-zA-Z_.]+}/{filename:[0-9a-zA-Z_.]+}", func(w http.ResponseWriter, r *http.Request) {
ServeFiles(w, r)
})
log.Fatal(http.ListenAndServe(":8181", mux))
}
Detail see serveStaticFiles example
The syntax here is modeled after julienschmidt/httprouter and gorilla/mux
Syntax | Description | Example |
---|---|---|
:name |
named parameter | /user/:name |
{name:regexp} |
named with regexp parameter | /user/{name:[0-9a-zA-Z]+} |
:id |
named with regexp parameter | /user/:id |
And :id
is short for {id:[0-9]+}
, :name
are short for {name:[0-9a-zA-Z_]+}
go test -bench=.
- Go version 1.9.2
- OS: Mac OS X 10.13.3
- Architecture: x86_64
- 16 GB 2133 MHz LPDDR3
- beego/mux
- go-zoo/bone
- go-chi/chi
- julienschmidt/httprouter
- gorilla/mux
- trie-mux/mux
- xujiajun/gorouter
Thanks the author of httprouter: @julienschmidt give me advise about benchmark issues/24
Given some routing matching syntax differences, divide GithubAPI into two groups:
BenchmarkBeegoMuxRouterWithGithubAPI-8 10000 163941 ns/op 134752 B/op 1038 allocs/op
BenchmarkBoneRouterWithGithubAPI-8 500 2225313 ns/op 720160 B/op 8620 allocs/op
BenchmarkTrieMuxRouterWithGithubAPI-8 20000 93525 ns/op 65856 B/op 537 allocs/op
BenchmarkHttpRouterWithGithubAPI-8 50000 32473 ns/op 13792 B/op 167 allocs/op
BenchmarkGoRouter1WithGithubAPI-8 20000 61823 ns/op 13832 B/op 406 allocs/op
BenchmarkGoRouter2WithGithubAPI2-8 20000 64858 ns/op 13832 B/op 406 allocs/op
BenchmarkChiRouterWithGithubAPI2-8 10000 139495 ns/op 104435 B/op 1110 allocs/op
BenchmarkMuxRouterWithGithubAPI2-8 300 4781621 ns/op 61468 B/op 995 allocs/op
➜ gorouter git:(master) go test -bench=.
GithubAPI Routes: 203
GithubAPI2 Routes: 203
BeegoMuxRouter: 108144 Bytes
BoneRouter: 100992 Bytes
ChiRouter: 71512 Bytes
HttpRouter: 37464 Bytes
trie-mux: 133864 Bytes
MuxRouter: 1378384 Bytes
GoRouter1: 83824 Bytes
GoRouter2: 85584 Bytes
goos: darwin
goarch: amd64
pkg: github.com/xujiajun/gorouter
BenchmarkBeegoMuxRouterWithGithubAPI-8 10000 163941 ns/op 134752 B/op 1038 allocs/op
BenchmarkBoneRouterWithGithubAPI-8 500 2225313 ns/op 720160 B/op 8620 allocs/op
BenchmarkTrieMuxRouterWithGithubAPI-8 20000 93525 ns/op 65856 B/op 537 allocs/op
BenchmarkHttpRouterWithGithubAPI-8 50000 32473 ns/op 13792 B/op 167 allocs/op
BenchmarkGoRouter1WithGithubAPI-8 20000 61823 ns/op 13832 B/op 406 allocs/op
BenchmarkGoRouter2WithGithubAPI2-8 20000 64858 ns/op 13832 B/op 406 allocs/op
BenchmarkChiRouterWithGithubAPI2-8 10000 139495 ns/op 104435 B/op 1110 allocs/op
BenchmarkMuxRouterWithGithubAPI2-8 300 4781621 ns/op 61468 B/op 995 allocs/op
PASS
ok github.com/xujiajun/gorouter 15.013s
-
Performance (xujiajun/gorouter,julienschmidt/httprouter and teambition/trie-mux is fast)
-
Memory Consumption (xujiajun/gorouter and julienschmidt/httprouter is fewer)
-
Features (julienschmidt/httprouter not support regexp,but others support it)
if you want a high performance router which support regexp, maybe xujiajun/gorouter is good choice.
if you want a high performance router which not support regexp, maybe julienschmidt/httprouter is good choice.
In the end, as julienschmidt said performance can not be the (only) criterion for choosing a router. Play around a bit with some of the routers, and choose the one you like best.
If you'd like to help out with the project. You can put up a Pull Request.
The gorouter is open-sourced software licensed under the MIT Licensed
This package is inspired by the following: