https://github.com/xujiajun/gorouter
xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.
https://github.com/xujiajun/gorouter
go golang gorouter restful-api router
Last synced: 16 days ago
JSON representation
xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.
- Host: GitHub
- URL: https://github.com/xujiajun/gorouter
- Owner: xujiajun
- License: mit
- Created: 2018-01-29T09:28:28.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2019-09-27T07:07:43.000Z (over 5 years ago)
- Last Synced: 2025-03-28T07:05:36.665Z (23 days ago)
- Topics: go, golang, gorouter, restful-api, router
- Language: Go
- Homepage: https://xujiajun.cn/gorouter
- Size: 134 KB
- Stars: 531
- Watchers: 16
- Forks: 70
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-golang-repositories - gorouter
- awesome-go - gorouter - xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework. - ★ 332 (Web Frameworks)
- awesome-go-extra - gorouter - 01-29T09:28:28Z|2019-09-27T07:07:43Z| (Web Frameworks / Routers)
README
# gorouter [](https://godoc.org/github.com/xujiajun/gorouter)
[](https://goreportcard.com/report/github.com/xujiajun/gorouter) [](https://coveralls.io/github/xujiajun/gorouter?branch=master) [](https://raw.githubusercontent.com/xujiajun/gorouter/master/LICENSE) [](https://github.com/xujiajun/gorouter/releases/tag/v1.0.1) [](https://github.com/avelino/awesome-go#routers)
`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 and fast HTTP GO router, which supports regexp. I prefer to support regexp is because otherwise it will need the logic to check the URL parameter type, thus increasing the program complexity. So I did some searching on Github and found the wonderful `julienschmidt/httprouter`: it is very fast,unfortunately it does not support regexp. Later I found out about `gorilla/mux`: it is powerful as well,but a written benchmark shows me that it is somewhat slow. So I tried to develop a new router which both supports regexp and should be fast. Finally I did it and named `xujiajun/gorouter`. By the way, this is my first GO open source project. It may be the fastest GO HTTP router which supports regexp, and regarding its performance please refer to my latest [Benchmarks](#benchmarks).
## Features
* Fast - see [Benchmarks](#benchmarks)
* [URL parameters](#url-parameters)
* [Regex parameters](#regex-parameters)
* [Routes groups](#routes-groups)
* [Reverse Routing](#reverse-routing)
* [Custom NotFoundHandler](#custom-notfoundhandler)
* [Custom PanicHandler](#custom-panichandler)
* [Middleware Chain Support](#middlewares-chain)
* [Serve Static Files](#serve-static-files)
* [Pattern Rule Familiar](#pattern-rule)
* HTTP Method Get、Post、Delete、Put、Patch Support
* No external dependencies (just Go stdlib)## Requirements
* golang 1.8+
## Installation
```
go get -u github.com/xujiajun/gorouter
```## Usage
### Static routes
```golang
package mainimport (
"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
```golang
package mainimport (
"log"
"net/http""github.com/xujiajun/gorouter"
)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))
}
```### Regex Parameters
```golang
package mainimport (
"log"
"net/http""github.com/xujiajun/gorouter"
)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
```golang
package mainimport (
"fmt"
"log"
"net/http""github.com/xujiajun/gorouter"
)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))
}
```### Reverse Routing
```golang
package mainimport (
"fmt"
"net/http""github.com/xujiajun/gorouter"
)func main() {
mux := gorouter.New()routeName1 := "user_event"
mux.GETAndName("/users/:user/events", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/users/:user/events"))
}, routeName1)routeName2 := "repos_owner"
mux.GETAndName("/repos/{owner:\\w+}/{repo:\\w+}", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("/repos/{owner:\\w+}/{repo:\\w+}"))
}, routeName2)params := make(map[string]string)
params["user"] = "xujiajun"
fmt.Println(mux.Generate(http.MethodGet, routeName1, params)) // /users/xujiajun/eventsparams = make(map[string]string)
params["owner"] = "xujiajun"
params["repo"] = "xujiajun_repo"
fmt.Println(mux.Generate(http.MethodGet, routeName2, params)) // /repos/xujiajun/xujiajun_repo
}```
### Custom NotFoundHandler
```golang
package mainimport (
"fmt"
"log"
"net/http""github.com/xujiajun/gorouter"
)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
```golang
package mainimport (
"fmt"
"log"
"net/http""github.com/xujiajun/gorouter"
)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
```golang
package mainimport (
"fmt"
"log"
"net/http""github.com/xujiajun/gorouter"
)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
```golang
package mainimport (
"log"
"net/http"
"os"
"github.com/xujiajun/gorouter"
)//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](https://github.com/xujiajun/gorouter/blob/master/examples/serveStaticFiles/main.go)## Pattern Rule
The syntax here is modeled after [julienschmidt/httprouter](https://github.com/julienschmidt/httprouter) and [gorilla/mux](https://github.com/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_]+}`
> if use default regex checks unless you know what you're doing
## Benchmarksthe benchmarks code for gorouter be found in the [gorouter-bench](https://github.com/xujiajun/gorouter-bench) repository.
> go test -bench=.
### Benchmark System:
* Go Version : go1.11.2 darwin/amd64
* OS: Mac OS X 10.13.6
* Architecture: x86_64
* 16 GB 2133 MHz LPDDR3
* CPU: 3.1 GHz Intel Core i7### Tested routers:
* [beego/mux](https://github.com/beego/mux)
* [go-zoo/bone](https://github.com/go-zoo/bone)
* [go-chi/chi](https://github.com/go-chi/chi)
* [julienschmidt/httprouter](https://github.com/julienschmidt/httprouter)
* [gorilla/mux](https://github.com/gorilla/mux)
* [trie-mux/mux](https://github.com/teambition/trie-mux)
* [xujiajun/gorouter](https://github.com/xujiajun/gorouter)Thanks the author of httprouter: [@julienschmidt](https://github.com/julienschmidt) give me advise about benchmark [issues/24](https://github.com/xujiajun/gorouter/issues/24)
## Result:
Given some routing matching syntax differences, divide GithubAPI into two groups:
### Using GithubAPI Result:
```
BenchmarkBeegoMuxRouterWithGithubAPI-8 10000 142398 ns/op 134752 B/op 1038 allocs/op
BenchmarkBoneRouterWithGithubAPI-8 1000 2104486 ns/op 720160 B/op 8620 allocs/op
BenchmarkTrieMuxRouterWithGithubAPI-8 20000 80845 ns/op 65856 B/op 537 allocs/op
BenchmarkHttpRouterWithGithubAPI-8 50000 30169 ns/op 13792 B/op 167 allocs/op
BenchmarkGoRouter1WithGithubAPI-8 30000 57793 ns/op 13832 B/op 406 allocs/op```
### Using GithubAPI2 Result:```
BenchmarkGoRouter2WithGithubAPI2-8 30000 57613 ns/op 13832 B/op 406 allocs/op
BenchmarkChiRouterWithGithubAPI2-8 10000 143224 ns/op 104436 B/op 1110 allocs/op
BenchmarkMuxRouterWithGithubAPI2-8 300 4450731 ns/op 61463 B/op 995 allocs/op
```### All togther Result:
```
➜ gorouter git:(master) go test -bench=.
GithubAPI Routes: 203
GithubAPI2 Routes: 203
BeegoMuxRouter: 111072 Bytes
BoneRouter: 100992 Bytes
ChiRouter: 71512 Bytes
HttpRouter: 37016 Bytes
trie-mux: 131128 Bytes
MuxRouter: 1378496 Bytes
GoRouter1: 83824 Bytes
GoRouter2: 85584 Bytes
goos: darwin
goarch: amd64
pkg: github.com/xujiajun/gorouter
BenchmarkBeegoMuxRouterWithGithubAPI-8 10000 142398 ns/op 134752 B/op 1038 allocs/op
BenchmarkBoneRouterWithGithubAPI-8 1000 2104486 ns/op 720160 B/op 8620 allocs/op
BenchmarkTrieMuxRouterWithGithubAPI-8 20000 80845 ns/op 65856 B/op 537 allocs/op
BenchmarkHttpRouterWithGithubAPI-8 50000 30169 ns/op 13792 B/op 167 allocs/op
BenchmarkGoRouter1WithGithubAPI-8 30000 57793 ns/op 13832 B/op 406 allocs/op
BenchmarkGoRouter2WithGithubAPI2-8 30000 57613 ns/op 13832 B/op 406 allocs/op
BenchmarkChiRouterWithGithubAPI2-8 10000 143224 ns/op 104436 B/op 1110 allocs/op
BenchmarkMuxRouterWithGithubAPI2-8 300 4450731 ns/op 61463 B/op 995 allocs/op
PASS
ok github.com/xujiajun/gorouter 15.918s```
### Conclusions:
* Performance (xujiajun/gorouter,julienschmidt/httprouter and teambition/trie-mux are fast)
* Memory Consumption (xujiajun/gorouter and julienschmidt/httprouter are fewer)
* Features (julienschmidt/httprouter not supports regexp,but others support it)
> if you want a high performance router which supports regexp, maybe [xujiajun/gorouter](https://github.com/xujiajun/gorouter) is good choice.
> if you want a high performance router which not supports regexp, maybe [julienschmidt/httprouter](https://github.com/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.`
## Contributing
If you'd like to help out with the project. You can put up a Pull Request. Thanks to all [contributors](https://github.com/xujiajun/gorouter/graphs/contributors).
## Author
* [xujiajun](https://github.com/xujiajun)
## License
The gorouter is open-sourced software licensed under the [MIT Licensed](http://www.opensource.org/licenses/MIT)
## Acknowledgements
This package is inspired by the following:
* [httprouter](https://github.com/julienschmidt/httprouter)
* [bone](https://github.com/go-zoo/bone)
* [trie-mux](https://github.com/teambition/trie-mux)
* [alien](https://github.com/gernest/alien)