From 93dc859164e8675b2c6d06c59a9c9e1b98fa9e9a Mon Sep 17 00:00:00 2001 From: Peter Kieltyka Date: Thu, 31 Mar 2016 09:47:50 -0400 Subject: [PATCH] Update README and examples --- README.md | 50 +++++++++++++++++---------------- _examples/rest/main.go | 6 ++-- _examples/rest/render/render.go | 49 -------------------------------- _examples/simple/main.go | 2 +- mux_test.go | 2 +- 5 files changed, 31 insertions(+), 78 deletions(-) delete mode 100644 _examples/rest/render/render.go diff --git a/README.md b/README.md index 6bb1e87e..7cfab653 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,12 @@ scaled very well. ## Features -* Lightweight - cloc`d in ~600 LOC for the chi router -* Fast - yes, see [benchmarks](#benchmarks) -* Expressive routing - middlewares, inline middleware groups/chains, and subrouter mounting -* Request context control (value chaining, deadlines and timeouts) - built on `net/context` -* Robust (tested, used in production) +* **Lightweight** - cloc'd in <1000 LOC for the chi router +* **Fast** - yes, see [benchmarks](#benchmarks) +* **Zero allocations** - no GC pressure during routing +* **Designed for modular/composable APIs** - middlewares, inline middleware groups/chains, and subrouter mounting +* **Context control** - built on `net/context` with value chaining, deadlines and timeouts +* **Robust** - tested / used in production ## Router design @@ -122,7 +123,7 @@ func StdHandler(w http.ResponseWriter, r *http.Request) { ```go // net/context HTTP request handler func CtxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { - userID := chi.URLParams(ctx)["userID"] // from a route like /users/:userID + userID := chi.URLParam(ctx, "userID") // from a route like /users/:userID key := ctx.Value("key").(string) w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key))) } @@ -200,7 +201,7 @@ func main() { func ArticleCtx(next chi.Handler) chi.Handler { return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - articleID := chi.URLParams(ctx)["articleID"] + articleID := chi.URLParam(ctx, "articleID") article, err := dbGetArticle(articleID) if err != nil { http.Error(w, http.StatusText(404), 404) @@ -284,11 +285,24 @@ See discussions: The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark -The results as of Nov. 6, 2015 - https://gist.github.com/pkieltyka/505b07b09f5c63e36ef5 - -Note: by design, chi allocates new routing URLParams map for each request, as opposed -to reusing URLParams from a pool. - +```shell +BenchmarkChi_Param 10000000 181 ns/op 0 B/op 0 allocs/op +BenchmarkChi_Param5 3000000 570 ns/op 0 B/op 0 allocs/op +BenchmarkChi_Param20 1000000 2057 ns/op 0 B/op 0 allocs/op +BenchmarkChi_ParamWrite 5000000 245 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GithubStatic 5000000 250 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GithubParam 2000000 589 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GithubAll 10000 102664 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GPlusStatic 10000000 161 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GPlusParam 5000000 291 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GPlus2Params 5000000 393 ns/op 0 B/op 0 allocs/op +BenchmarkChi_GPlusAll 300000 4335 ns/op 0 B/op 0 allocs/op +BenchmarkChi_ParseStatic 10000000 162 ns/op 0 B/op 0 allocs/op +BenchmarkChi_ParseParam 10000000 227 ns/op 0 B/op 0 allocs/op +BenchmarkChi_Parse2Params 5000000 327 ns/op 0 B/op 0 allocs/op +BenchmarkChi_ParseAll 200000 7368 ns/op 0 B/op 0 allocs/op +BenchmarkChi_StaticAll 30000 57990 ns/op 0 B/op 0 allocs/op +``` ## Credits @@ -298,18 +312,6 @@ to reusing URLParams from a pool. * Armon Dadgar for https://github.com/armon/go-radix * Contributions: [@VojtechVitek](https://github.com/VojtechVitek) - -## TODO - -* Mux options - * Trailing slash? - * Case insensitive paths? - * GET for HEAD requests (auto fallback)? -* Register error handler (500's), ServerError() handler? -* HTTP2 example - * both http 1.1 and http2 automatically.. just turn it on :) -* Regexp support in router "/:id([0-9]+)" or "#id^[0-9]+$" or .. - We'll be more than happy to see [your contributions](./CONTRIBUTING.md)! ## License diff --git a/_examples/rest/main.go b/_examples/rest/main.go index 5e584ea0..34afb6c2 100644 --- a/_examples/rest/main.go +++ b/_examples/rest/main.go @@ -9,8 +9,8 @@ import ( "time" "github.com/pressly/chi" - "github.com/pressly/chi/_examples/rest/render" "github.com/pressly/chi/middleware" + "github.com/pressly/chi/render" "golang.org/x/net/context" ) @@ -113,7 +113,7 @@ type Article struct { func ArticleCtx(next chi.Handler) chi.Handler { return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - articleID := chi.URLParams(ctx)["articleID"] + articleID := chi.URLParam(ctx, "articleID") article, err := dbGetArticle(articleID) if err != nil { http.Error(w, http.StatusText(404), 404) @@ -229,7 +229,7 @@ func adminRouter() http.Handler { // or chi.Router { w.Write([]byte("admin: list accounts..")) }) r.Get("/users/:userId", func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - w.Write([]byte(fmt.Sprintf("admin: view user id %v", chi.URLParams(ctx)["userId"]))) + w.Write([]byte(fmt.Sprintf("admin: view user id %v", chi.URLParam(ctx, "userId")))) }) return r } diff --git a/_examples/rest/render/render.go b/_examples/rest/render/render.go deleted file mode 100644 index 2535818a..00000000 --- a/_examples/rest/render/render.go +++ /dev/null @@ -1,49 +0,0 @@ -// Responder as a wrapper of https://github.com/unrolled/render -// -- -// This is a good base to work off and extend for your own uses, or build your own. -// For example, adding pagination to JSON() where you add a "Link" header that is -// a cursor URL to the next page of results (check how GitHubb does it in their API docs). -// The power is yours. -package render - -import ( - "net/http" - - renderer "github.com/unrolled/render" -) - -var ( - Renderer *renderer.Render -) - -func init() { - Renderer = renderer.New() -} - -func Render(w http.ResponseWriter, e renderer.Engine, data interface{}) error { - return Renderer.Render(w, e, data) -} - -func Data(w http.ResponseWriter, status int, v []byte) error { - return Renderer.Data(w, status, v) -} - -func HTML(w http.ResponseWriter, status int, name string, binding interface{}, htmlOpt ...renderer.HTMLOptions) error { - return Renderer.HTML(w, status, name, binding, htmlOpt...) -} - -func JSON(w http.ResponseWriter, status int, v interface{}) error { - return Renderer.JSON(w, status, v) -} - -func JSONP(w http.ResponseWriter, status int, callback string, v interface{}) error { - return Renderer.JSONP(w, status, callback, v) -} - -func Text(w http.ResponseWriter, status int, v string) error { - return Renderer.Text(w, status, v) -} - -func XML(w http.ResponseWriter, status int, v interface{}) error { - return Renderer.XML(w, status, v) -} diff --git a/_examples/simple/main.go b/_examples/simple/main.go index f6cb00b2..cc6bec83 100644 --- a/_examples/simple/main.go +++ b/_examples/simple/main.go @@ -108,7 +108,7 @@ func createAccount(w http.ResponseWriter, r *http.Request) { } func getAccount(ctx context.Context, w http.ResponseWriter, r *http.Request) { - accountID := chi.URLParams(ctx)["accountID"] + accountID := chi.URLParam(ctx, "accountID") account := ctx.Value("account").(string) w.Write([]byte(fmt.Sprintf("get account id:%s details:%s", accountID, account))) } diff --git a/mux_test.go b/mux_test.go index 7491a346..a2e8ac0c 100644 --- a/mux_test.go +++ b/mux_test.go @@ -80,7 +80,7 @@ func TestMux(t *testing.T) { _ = pingAll2 pingOne := func(ctx context.Context, w http.ResponseWriter, r *http.Request) { - idParam := URLParam(ctx, "id") // from outside: chi.URLParams(ctx) + idParam := URLParam(ctx, "id") w.WriteHeader(200) w.Write([]byte(fmt.Sprintf("ping one id: %s", idParam)))