Package router provides a lightning fast HTTP router.
- Extreme performance: sub-microsecond routing in most cases
- Full compatibility with the http.Handler interface
- Generic: no magic methods, bring your own handlers
- Path parameters, regular expressions and wildcards
- Smart prioritized routes
- Zero memory allocations during serving (but for parameters)
- Respecting the principle of least surprise
- Tested and used in production
-
Get package:
go get -u github.com/gowww/router
-
Import it in your code:
import "github.com/gowww/router"
-
Make a new router:
rt := router.New()
-
Make a route:
rt.Handle("GET", "/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello") }))
Remember that HTTP methods are case-sensitive and uppercase by convention (RFC 7231 4.1).
So you can directly use the built-in shortcuts for standard HTTP methods: Router.Get, Router.Post, Router.Put, Router.Patch and Router.Delete. -
Give the router to the server:
http.ListenAndServe(":8080", rt)
A named parameter begins with :
and matches any value until the next /
in path.
To retrieve the value (stored in request's context), ask Parameter.
It will return the value as a string (empty if the parameter doesn't exist).
Example, with a parameter id
:
rt.Get("/users/:id", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := router.Parameter(r, "id")
fmt.Fprintf(w, "Page of user #%s", id)
}))
No surprise
A parameter can be used on the same level as a static route, without conflict:
rt.Get("/users/all", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "All users page")
}))
rt.Get("/users/:id", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := router.Parameter(r, "id")
fmt.Fprintf(w, "Page of user #%s", id)
}))
If a parameter must match an exact pattern (digits only, for example), you can also set a regular expression constraint just after the parameter name and another :
:
rt.Get(`/users/:id:^\d+$`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := router.Parameter(r, "id")
fmt.Fprintf(w, "Page of user #%s", id)
}))
If you don't need to retrieve the parameter value but only use a regular expression, you can omit the parameter name:
rt.Get(`/shows/::^prison-break-s06-.+`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Prison Break S06 — Coming soon…")
}))
Don't forget that regular expressions can significantly reduce performance.
No surprise
A parameter with a regular expression can be used on the same level as a simple parameter, without conflict:
rt.Get(`/users/:id:^\d+$`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := router.Parameter(r, "id")
fmt.Fprintf(w, "Page of user #%s", id)
}))
rt.Get("/users/:name", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
name := router.Parameter(r, "name")
fmt.Fprintf(w, "Page of %s", name)
}))
A trailing slash in a route path is significant.
It behaves like a wildcard by matching the beginning of the request path and keeping the rest as a parameter value, under *
:
rt.Get("/files/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
filepath := router.Parameter(r, "*")
fmt.Fprintf(w, "Get file %s", filepath)
}))
Note that a trailing slash in a request path is always trimmed and the client redirected.
For example, a request for /files/
will be redirected to /files
and will never match a /files/
route.
In other words, /files
and /files/
are two different routes.
No surprise
Deeper route paths with the same prefix as the wildcard will take precedence, without conflict:
// Will match:
// /files/one
// /files/two
// ...
rt.Get("/files/:name", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {kv
name := router.Parameter(r, "name")
fmt.Fprintf(w, "Get root file #%s", name)
}))
// Will match:
// /files/one/...
// /files/two/...
// ...
rt.Get("/files/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
filepath := router.Parameter(r, "*")
fmt.Fprintf(w, "Get file %s", filepath)
}))
// Will match:
// /files/movies/one
// /files/movies/two
// ...
rt.Get("/files/movies/:name", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
name := router.Parameter(r, "name")
fmt.Fprintf(w, "Get movie #%s", name)
}))
For serving static files, like for other routes, just bring your own handler.
Example, with the standard net/http.FileServer:
rt.Get("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
When a request cannot be matched with a route, a 404 error with an empty body is sent by default.
But you can set your own "not found" handler (and send an HTML page, for example):
rt.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
})
Note that is this case, it's up to you to set the correct status code (normally 404) for the response.