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 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.
- Fast - see Benchmarks
 - URL parameters
 - Regex parameters
 - Routes groups
 - Reverse Routing
 - 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 -u 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 (
	"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))
}package main
import (
	"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))
}package main
import (
	"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))
}package main
import (
	"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/events <nil>
	params = make(map[string]string)
	params["owner"] = "xujiajun"
	params["repo"] = "xujiajun_repo"
	fmt.Println(mux.Generate(http.MethodGet, routeName2, params)) // /repos/xujiajun/xujiajun_repo <nil>
}package main
import (
	"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))
}package main
import (
	"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))
}package main
import (
	"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))
}package main
import (
	"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
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_]+}
if use default regex checks unless you know what you're doing
the benchmarks code for gorouter be found in the gorouter-bench repository.
go test -bench=.
- 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
 
- 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	    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
➜  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
- 
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 is good choice.
if you want a high performance router which not supports 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. Thanks to all contributors.
The gorouter is open-sourced software licensed under the MIT Licensed
This package is inspired by the following: