gorouter:簡單高效能的 Go router
gorouter
xujiajun/gorouter
is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.
Motivation
I wanted a simple, fast router that has no unnecessary overhead using the standard library only, following good practices and well tested code.
Features
- Custom NotFoundHandler
- Middleware Chain Support
- Pattern Rule Familiar
- HTTP Method Get、Post、Delete、Put、Patch Support
- No external dependencies (just Go stdlib)
Requirements
- golang 1.8+
Installation
go get github.com/xujiajun/gorouter
Usage
Static routes
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)) }
URL Parameters
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) { w.Write([]byte("match user/:id !")) }) log.Fatal(http.ListenAndServe(":8181", mux)) }
Regex Parameters
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)) }
Routes Groups
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)) }
Custom NotFoundHandler
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)) }
Custom PanicHandler
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)) }
Middlewares Chain
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)) }
Serve static files
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 ofollow,noindex" target="_blank">serveStaticFiles example
Pattern Rule
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_]+}
Benchmarks
go test -bench=.
Benchmark System:
Tested routers:
Result:
➜gorouter git:(master) ✗ go test -bench=. GithubAPI Routes: 203 GithubAPI2 Routes: 203 HttpRouter: 37464 Bytes GoRouter: 83616 Bytes trie-mux: 135096 Bytes MuxRouter: 1324192 Bytes goos: darwin goarch: amd64 pkg: github.com/xujiajun/gorouter BenchmarkTrieMuxRouter-810000692179 ns/op1086465 B/op2975 allocs/op BenchmarkHttpRouter-810000627134 ns/op1034366 B/op2604 allocs/op BenchmarkGoRouter-810000630895 ns/op1034415 B/op2843 allocs/op BenchmarkMuxRouter-8100006396340 ns/op1272876 B/op4691 allocs/op PASS okgithub.com/xujiajun/gorouter83.503s
Conclusions:
-
Performance (xujiajun/gorouter ≈ julienschmidt/httprouter > teambition/trie-mux > gorilla/mux)
-
Memory Consumption (xujiajun/gorouter ≈ julienschmidt/httprouter < teambition/trie-mux < gorilla/mux)
-
Features (xujiajun/gorouter, gorilla/mux and teambition/trie-mux support regexp, But julienschmidt/httprouter not support)
if you want a performance router which support regexp, maybe xujiajun/gorouter is good choice.
Contributing
If you'd like to help out with the project. You can put up a Pull Request.
Author
License
The gorouter is open-sourced software licensed under the IT" rel="nofollow,noindex" target="_blank">MIT Licensed
Acknowledgements
This package is inspired by the following: