Skip to content

Latest commit

 

History

History
212 lines (157 loc) · 6.81 KB

README.md

File metadata and controls

212 lines (157 loc) · 6.81 KB

gowww router GoDoc Build Coverage Go Report

Package router provides a lightning fast HTTP router.

Features

  • 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

Installing

  1. Get package:

    go get -u github.com/gowww/router
  2. Import it in your code:

    import "github.com/gowww/router"

Usage

  1. Make a new router:

    rt := router.New()
  2. 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.

  3. Give the router to the server:

    http.ListenAndServe(":8080", rt)

Parameters

Named

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)
}))

Regular expressions

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)
}))

Wildcard

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)
}))

Static files

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"))))

Custom "not found" handler

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.