httpz is based on httpz.ServeMux
ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the most specific pattern. — go.dev
Basic Route Matching
- Exact match, without a trailing
/
:
func main() {
mux := httpz.NewServeMux()
mux.Get("/about", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "/about")
return nil
})
http.ListenAndServe(":8080", mux)
}
- GET http://localhost:8080/about, matches “/about” response: “/about”
- GET http://localhost:8080/about/, does not match response: “404 page not found”
- GET http://localhost:8080/about/foo, does not match response: “404 page not found”
/
as a suffix
Consider the following example, which matches all GET requests starting with /about
:
func main() {
mux := httpz.NewServeMux()
mux.Get("/about/", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "/about/")
return nil
})
http.ListenAndServe(":8080", mux)
}
- GET http://localhost:8080/about/, matches “/about/” response: “/about/”
- GET http://localhost:8080/about/foo, matches “/about/” response: “/about/”
Note that accessing GET /about
will automatically redirect to GET /about/
- GET http://localhost:8080/about -> redirects to
/about/
If GET /about
is registered, it will not redirect to GET /about/
, because GET /about
is more specific.
func main() {
mux := httpz.NewServeMux()
mux.Get("/about/", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "/about/")
return nil
})
mux.Get("/about", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "/about")
return nil
})
http.ListenAndServe(":8080", mux)
}
- GET http://localhost:8080/about matches “/about”, response “/about”
Matching Rules
Consider the following example. If both GET /about/foo
and GET /about/
are registered, which controller will GET /about/foo
match?
- GET /about/foo exact match
- GET /about/ matches all URLs with the
/about/
prefix
func main() {
mux := httpz.NewServeMux()
mux.Get("/about/foo", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "/about/foo")
return nil
})
mux.Get("/about/", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "/about/")
return nil
})
http.ListenAndServe(":8080", mux)
}
- GET http://localhost:8080/about/foo matches “/about/foo”, response “/about/foo”
Because /about/foo
is more specific. The routing matching rule is that more specific patterns take precedence.
Path Cleaning
Extra /
or .
in the URL will be automatically removed, and it will redirect to the clean path.
For example:
- GET /about//foo redirects to GET /about/foo
- GET /about/../foo redirects to GET /about/foo
Host Matching
// Matches all requests for the host example.com
mux.GET("example.com/", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "Welcome to example.com!")
return nil
})
// Matches requests for all hosts
mux.GET("/", func(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "API data endpoint")
return nil
})
The pattern format is [HOST]/[PATH]
. When the HOST is omitted, it matches all hosts.
GET example.com/
will matchexample.com/
, not/
, becauseexample.com/
is more specific.
Wildcard Matching
- Path parameters are set using
{name}
mux.Get("/user/{id}", func(w http.ResponseWriter, r *http.Request) error {
// Get path parameter
id := r.PathValue("id")
fmt.Fprintf(w, "id = %s", id)
return nil
})
- GET /user/1 responds with “id = 1”
- GET /user/2 responds with “id = 2”
- Ellipsis
...
mux.Get("/user/{ids...}", func(w http.ResponseWriter, r *http.Request) error {
// Get path parameter
id := r.PathValue("id")
fmt.Fprintf(w, "ids = %s", id)
return nil
})
- GET /user/foo/bar responds with “ids = foo/bar”
- GET /user/foo responds with “ids = foo”
- GET /user/ responds with “ids = ”