From b3bc30c5aff0053f2229fb37839756a8383f65b1 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Fri, 24 Aug 2018 14:41:53 +0300 Subject: [PATCH] add a table on the README for the param types, macro funcs and do it yourself section Former-commit-id: eca9418779371c014d3bf3bca88430055841da4f --- README.md | 101 +++++++++++++++++++++++++ _examples/routing/dynamic-path/main.go | 46 +++++++++-- mvc/controller_test.go | 6 +- 3 files changed, 145 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9e1c4f54..eb800cee 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,107 @@ func main() { ### Parameters in path +| Param Type | Go Type | Validation | Retrieve Helper | +| -----------------|------|-------------|------| +| `:string` | string | anything | `Params().Get` | +| `:number` | uint, uint8, uint16, uint32, uint64, int, int8, int32, int64 | positive or negative number, any number of digits | `Params().GetInt/Int64`...| +| `:int64` | int64 | -9223372036854775808 to 9223372036854775807 | `Params().GetInt64` | +| `:uint8` | uint8 | 0 to 255 | `Params().GetUint8` | +| `:uint64` | uint64 | 0 to 18446744073709551615 | `Params().GetUint64` | +| `:bool` | bool | "1" or "t" or "T" or "TRUE" or "true" or "True" or "0" or "f" or "F" or "FALSE" or "false" or "False" | `Params().GetBool` | +| `:alphabetical` | string | lowercase or uppercase letters | `Params().Get` | +| `:file` | string | lowercase or uppercase letters, numbers, underscore (_), dash (-), point (.) and no spaces or other special characters that are not valid for filenames | `Params().Get` | +| `:path` | string | anything, can be separated by slashes (path segments) but should be the last part of the route path | `Params().Get` | + +**Usage**: + +```go +app.Get("/users/{id:uint64}", func(ctx iris.Context){ + id, _ := ctx.Params().GetUint64("id") + // [...] +}) +``` + +| Built'n Func | Param Types | +| -----------|---------------| +| `regexp`(expr string) | :string | +| `prefix`(prefix string) | :string | +| `suffix`(suffix string) | :string | +| `contains`(s string) | :string | +| `min`(minValue int or int8 or int16 or int32 or int64 or uint8 or uint16 or uint32 or uint64 or float32 or float64) | :string(char length), :number, :int64, :uint8, :uint64 | +| `max`(maxValue int or int8 or int16 or int32 or int64 or uint8 or uint16 or uint32 or uint64 or float32 or float64) | :string(char length), :number, :int64, :uint8, :uint64 | +| `range`(minValue, maxValue int or int8 or int16 or int32 or int64 or uint8 or uint16 or uint32 or uint64 or float32 or float64) | :number, :int64, :uint8, :uint64 | + +**Usage**: + +```go +app.Get("/profile/{name:alphabetical max(255)}", func(ctx iris.Context){ + name := ctx.Params().Get("name") + // len(name) <=255 otherwise this route will fire 404 Not Found + // and this handler will not be executed at all. +}) +``` + +**Do It Yourself**: + +The `RegisterFunc` can accept any function that returns a `func(paramValue string) bool`. +Or just a `func(string) bool`. +If the validation fails then it will fire `404` or whatever status code the `else` keyword has. + +```go +latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$" +latLonRegex, _ := regexp.Compile(latLonExpr) + +// Register your custom argument-less macro function to the :string param type. +// MatchString is a type of func(string) bool, so we use it as it is. +app.Macros().String.RegisterFunc("coordinate", latLonRegex.MatchString) + +app.Get("/coordinates/{lat:string coordinate() else 400}/{lon:string coordinate() else 400}", func(ctx iris.Context) { + ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon")) +}) +``` + +Register your custom macro function which accepts two int arguments. + +```go + +app.Macros().String.RegisterFunc("range", func(minLength, maxLength int) func(string) bool { + return func(paramValue string) bool { + return len(paramValue) >= minLength && len(paramValue) <= maxLength + } +}) + +app.Get("/limitchar/{name:string range(1,200)}", func(ctx iris.Context) { + name := ctx.Params().Get("name") + ctx.Writef(`Hello %s | the name should be between 1 and 200 characters length + otherwise this handler will not be executed`, name) +}) +``` + +Register your custom macro function which accepts a slice of strings `[...,...]`. + +```go +app.Macros().String.RegisterFunc("has", func(validNames []string) func(string) bool { + return func(paramValue string) bool { + for _, validName := range validNames { + if validName == paramValue { + return true + } + } + + return false + } +}) + +app.Get("/static_validation/{name:string has([kataras,gerasimos,maropoulos]}", func(ctx iris.Context) { + name := ctx.Params().Get("name") + ctx.Writef(`Hello %s | the name should be "kataras" or "gerasimos" or "maropoulos" + otherwise this handler will not be executed`, name) +}) +``` + +**Example Code**: + ```go func main() { app := iris.Default() diff --git a/_examples/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go index 462634c7..926b4af1 100644 --- a/_examples/routing/dynamic-path/main.go +++ b/_examples/routing/dynamic-path/main.go @@ -159,17 +159,17 @@ func main() { ctx.Writef("age selected: %d", age) }) - // Another example using a custom regexp and any custom logic. + // Another example using a custom regexp or any custom logic. + + // Register your custom argument-less macro function to the :string param type. latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$" latLonRegex, err := regexp.Compile(latLonExpr) if err != nil { panic(err) } - app.Macros().String.RegisterFunc("coordinate", func() func(paramName string) (ok bool) { - // MatchString is a type of func(string) bool, so we can return that as it's. - return latLonRegex.MatchString - }) + // MatchString is a type of func(string) bool, so we use it as it is. + app.Macros().String.RegisterFunc("coordinate", latLonRegex.MatchString) app.Get("/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}", func(ctx iris.Context) { ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon")) @@ -177,6 +177,42 @@ func main() { // + // Another one is by using a custom body. + app.Macros().String.RegisterFunc("range", func(minLength, maxLength int) func(string) bool { + return func(paramValue string) bool { + return len(paramValue) >= minLength && len(paramValue) <= maxLength + } + }) + + app.Get("/limitchar/{name:string range(1,200)}", func(ctx iris.Context) { + name := ctx.Params().Get("name") + ctx.Writef(`Hello %s | the name should be between 1 and 200 characters length + otherwise this handler will not be executed`, name) + }) + + // + + // Register your custom macro function which accepts a slice of strings `[...,...]`. + app.Macros().String.RegisterFunc("has", func(validNames []string) func(string) bool { + return func(paramValue string) bool { + for _, validName := range validNames { + if validName == paramValue { + return true + } + } + + return false + } + }) + + app.Get("/static_validation/{name:string has([kataras,gerasimos,maropoulos]}", func(ctx iris.Context) { + name := ctx.Params().Get("name") + ctx.Writef(`Hello %s | the name should be "kataras" or "gerasimos" or "maropoulos" + otherwise this handler will not be executed`, name) + }) + + // + // http://localhost:8080/game/a-zA-Z/level/42 // remember, alphabetical is lowercase or uppercase letters only. app.Get("/game/{name:alphabetical}/level/{level:number}", func(ctx iris.Context) { diff --git a/mvc/controller_test.go b/mvc/controller_test.go index 525c2103..f367d0ea 100644 --- a/mvc/controller_test.go +++ b/mvc/controller_test.go @@ -367,7 +367,7 @@ func (c *testControllerRelPathFromFunc) EndRequest(ctx context.Context) { func (c *testControllerRelPathFromFunc) Get() {} func (c *testControllerRelPathFromFunc) GetBy(uint64) {} func (c *testControllerRelPathFromFunc) GetUint8RatioBy(uint8) {} -func (c *testControllerRelPathFromFunc) GetUint64RatioBy(int64) {} +func (c *testControllerRelPathFromFunc) GetInt64RatioBy(int64) {} func (c *testControllerRelPathFromFunc) GetAnythingByWildcard(string) {} func (c *testControllerRelPathFromFunc) GetLogin() {} @@ -395,8 +395,8 @@ func TestControllerRelPathFromFunc(t *testing.T) { e.GET("/uint8/ratio/255").Expect().Status(iris.StatusOK). Body().Equal("GET:/uint8/ratio/255") e.GET("/uint8/ratio/256").Expect().Status(iris.StatusNotFound) - e.GET("/uint64/ratio/-42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/uint64/ratio/-42") + e.GET("/int64/ratio/-42").Expect().Status(iris.StatusOK). + Body().Equal("GET:/int64/ratio/-42") e.GET("/something/true").Expect().Status(iris.StatusOK). Body().Equal("GET:/something/true") e.GET("/something/false").Expect().Status(iris.StatusOK).