Documentation ¶
Overview ¶
Package mux is a fast and safe HTTP request multiplexer.
The multiplexer in this package is capable of routing based on request method and a fixed rooted path (/favicon.ico) or subtree (/images/) which may include typed path parameters and wildcards.
serveMux := mux.New( mux.Handle(http.MethodGet, "/profile/{username string}", http.NotFoundHandler()), mux.HandleFunc(http.MethodGet, "/profile", http.RedirectHandler("/profile/me", http.StatusPermanentRedirect)), mux.Handle(http.MethodPost, "/logout", logoutHandler()), )
URL Parameters ¶
Routes registered on the multiplexer may contain variable path parameters that comprise an optional name, followed by a type.
/user/{id int}/edit
Valid types include:
int eg. -1, 1 (int64 in Go) uint eg. 0, 1 (uint64 in Go) float eg. 1, 1.123, -1.123 (float64 in Go) string eg. anything ({string} is the same as {}) path eg. files/123.png (must be the last path component)
All numeric types are 64 bits wide. Parameters of type "path" match the remainder of the input path and therefore may only appear as the final component of a route:
/file/{p path}
Two paths with different typed variable parameters (including static routes) in the same position are not allowed. Attempting to register any two of the following routes will panic:
/user/{a int}/new /user/{b int}/edit /user/{float}/edit /user/{b string}/edit /user/me
This is to prevent a common class of bug where a static route conflicts with a path parameter and it is not clear which should be selected. For example, if the route /users/new and /users/{username string} could be registered at the same time and someone attempts to register the user "new", it might make the new user page impossible to visit, or break the new users profile. Disallowing conflicting routes keeps things simple and eliminates this class of issues.
When a route is matched, the value of each named path parameter is stored on the request context. To retrieve the value of named path parameters from within a handler, the Param function can be used.
pinfo := mux.Param(req, "username") fmt.Println("Got username:", pinfo.Raw)
For more information, see the ParamInfo type and the examples.
Normalization ¶
It's common to normalize routes on HTTP servers. For example, a username may need to match the Username Case Mapped profile of PRECIS (RFC 8265), or the name of an identifier may need to always be lower cased. This can be tedious and error prone even if you have enough information to figure out what path components need to be replaced, and many HTTP routers don't even give you enough information to match path components to their original route parameters. To make this easier, this package provides the Path and WithParam functions. WithParam is used to attach a new context to the context tree with replacement values for existing route parameters, and Path is used to re-render the path from the original route using the request context. If the resulting path is different from req.URL.Path, a redirect can be issued or some other corrective action can be applied.
serveMux := mux.New( mux.HandleFunc(http.MethodGet, "/profile/{username string}", func(w http.ResponseWriter, r *http.Request) { username := mux.Param(r, "username") // golang.org/x/text/secure/precis normalized, err := precis.UsernameCaseMapped.String(username.Raw) if err != nil { … } if normalized != username.Raw { r = mux.WithParam(r, username.Name, normalized) newPath, err := mux.Path(r) if err != nil { … } http.Redirect(w, r, newPath, http.StatusPermanentRedirect) return } fmt.Fprintln(w, "The canonical username is:", username.Raw) }), )
For more information, see the examples.
Example (Normalization) ¶
package main import ( "fmt" "io" "net/http" "net/http/httptest" "os" "strings" "code.soquee.net/mux" ) func main() { serveMux := mux.New( mux.HandleFunc("GET", "/profile/{username string}/personal", func(w http.ResponseWriter, r *http.Request) { username := mux.Param(r, "username") // You probably want to use the Username Case Mapped profile from the // golang.org/x/text/secure/precis package instead. normalized := strings.ToLower(username.Raw) // If the username is not canonical, redirect. if normalized != username.Raw { r = mux.WithParam(r, username.Name, normalized) newPath, err := mux.Path(r) if err != nil { panic(fmt.Errorf("mux_test: error creating canonicalized path: %w", err)) } http.Redirect(w, r, newPath, http.StatusPermanentRedirect) return } // Show the users profile. fmt.Fprintf(w, "Profile for the user %q", username.Raw) }), ) server := httptest.NewServer(serveMux) defer server.Close() resp, err := http.Get(server.URL + "/profile/Me/personal") if err != nil { panic(err) } defer resp.Body.Close() io.Copy(os.Stdout, resp.Body) }
Output: Profile for the user "me"
Example (Path) ¶
package main import ( "crypto/sha256" "fmt" "io" "net/http" "net/http/httptest" "os" "code.soquee.net/mux" ) func main() { serveMux := mux.New( mux.HandleFunc("GET", "/sha256/{wildcard path}", func(w http.ResponseWriter, r *http.Request) { val := mux.Param(r, "wildcard") sum := sha256.Sum256([]byte(val.Raw)) fmt.Fprintf(w, "the hash of %q is %x", val.Raw, sum) }), ) server := httptest.NewServer(serveMux) resp, err := http.Get(server.URL + "/sha256/a/b") if err != nil { panic(err) } defer resp.Body.Close() io.Copy(os.Stdout, resp.Body) }
Output: the hash of "a/b" is c14cddc033f64b9dea80ea675cf280a015e672516090a5626781153dc68fea11
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Path ¶ added in v0.0.4
Path returns the request path by applying the route parameters found in the context to the route used to match the given request. This value may be different from r.URL.Path if some form of normalization has been applied to a route parameter, in which case the user may choose to issue a redirect to the canonical path.
func WithParam ¶ added in v0.0.4
WithParam returns a shallow copy of r with a new context that shadows the given route parameter. If the parameter does not exist, the original request is returned unaltered.
Because WithParam is used to normalize request parameters after the route has already been resolved, all replaced parameters are of type string.
Types ¶
type Option ¶
type Option func(*ServeMux)
Option is used to configure a ServeMux.
func Handle ¶
Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func HandleFunc ¶
func HandleFunc(method, r string, h http.HandlerFunc) Option
HandleFunc registers the handler for the given pattern. If a handler already exists for pattern, Handle panics.
func MethodNotAllowed ¶ added in v0.0.2
MethodNotAllowed sets the default handler to call when a path is matched to a route, but there is no handler registered for the specific method.
By default, http.Error with http.StatusMethodNotAllowed is used.
func NotFound ¶
NotFound sets the handler to use when a request does not have a registered route.
If the provided handler does not set the status code, it is set to 404 (Page Not Found) by default instead of 200. If the provided handler explicitly sets the status by calling "http.ResponseWriter".WriteHeader, that status code is used instead.
type ParamInfo ¶ added in v0.0.2
type ParamInfo struct { // The parsed value of the parameter (for example int64(10)) // If and only if no such parameter existed on the route, Value will be nil. Value interface{} // The raw value of the parameter (for example "10") Raw string // The name of the route component that the parameter was matched against (for // example "name" in "{name int}") Name string // Type type of the route component that the parameter was matched against // (for example "int" in "{name int}") Type string // contains filtered or unexported fields }
ParamInfo represents a route parameter and related metadata.
type ServeMux ¶
type ServeMux struct {
// contains filtered or unexported fields
}
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 pattern that most closely matches the URL.
func (*ServeMux) Handler ¶
Handler returns the handler to use for the given request, consulting r.URL.Path. It always returns a non-nil handler and request.
The path used is unchanged for CONNECT requests.
If there is no registered handler that applies to the request, Handler returns a page not found handler. If a new request is returned it uses a context that contains any route parameters that were matched against the request path.