mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
New ':int64' and ':uint64' route path parameters - and - support the new uint64 for MVC (int64 was already supported there) - and - add ctx.Params().GetUint64 (GetInt64 was already there) - and - make the ':int or :number' to accept negative numbers with no digit limit (at low level) and rename the 'app.Macros().Int.RegisterFunc' to 'Number.RegisterFunc' because number can be any type of number not only standard go type limited - and - add alias for ':boolean' -> ':bool'. Finally, Update the examples but not the version yet, I have to provide a good README table to explain the end-developers how they can benefit by those changes and why the breaking change (which is to accept negative numbers via ':int') is for their own good and how they can make their own macro functions so they do not depend on the Iris builtn macro funcs only. More to come tomorrow, stay tuned
Former-commit-id: 3601abfc89478185afec3594375080778214283e
This commit is contained in:
parent
01b5f6089d
commit
b019a281eb
|
@ -145,6 +145,14 @@ func main() {
|
||||||
ctx.Writef("Hello %s", name)
|
ctx.Writef("Hello %s", name)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This handler will match /users/42
|
||||||
|
// but will not match /users/-1 because uint should be bigger than zero
|
||||||
|
// neither /users or /users/.
|
||||||
|
app.Get("/users/{id:uint64}", func(ctx iris.Context) {
|
||||||
|
id, _ := ctx.Params().GetUint64("id")
|
||||||
|
ctx.Writef("User with ID: %d", id)
|
||||||
|
})
|
||||||
|
|
||||||
// However, this one will match /user/john/ and also /user/john/send.
|
// However, this one will match /user/john/ and also /user/john/send.
|
||||||
app.Post("/user/{name:string}/{action:path}", func(ctx iris.Context) {
|
app.Post("/user/{name:string}/{action:path}", func(ctx iris.Context) {
|
||||||
name := ctx.Params().Get("name")
|
name := ctx.Params().Get("name")
|
||||||
|
|
|
@ -104,7 +104,7 @@ Structuring depends on your own needs. We can't tell you how to design your own
|
||||||
|
|
||||||
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context
|
### Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context
|
||||||
|
|
||||||
* `app.Get("{userid:int min(1)}", myHandler)`
|
* `app.Get("{userid:number min(1)}", myHandler)`
|
||||||
* `app.Post("{asset:path}", myHandler)`
|
* `app.Post("{asset:path}", myHandler)`
|
||||||
* `app.Put("{custom:string regexp([a-z]+)}", myHandler)`
|
* `app.Put("{custom:string regexp([a-z]+)}", myHandler)`
|
||||||
|
|
||||||
|
@ -128,10 +128,10 @@ app.Get("/profile/me", userHandler)
|
||||||
|
|
||||||
// Matches all GET requests prefixed with /users/
|
// Matches all GET requests prefixed with /users/
|
||||||
// and followed by a number which should be equal or bigger than 1
|
// and followed by a number which should be equal or bigger than 1
|
||||||
app.Get("/user/{userid:int min(1)}", getUserHandler)
|
app.Get("/user/{userid:number min(1)}", getUserHandler)
|
||||||
// Matches all requests DELETE prefixed with /users/
|
// Matches all requests DELETE prefixed with /users/
|
||||||
// and following by a number which should be equal or bigger than 1
|
// and following by a number which should be equal or bigger than 1
|
||||||
app.Delete("/user/{userid:int min(1)}", deleteUserHandler)
|
app.Delete("/user/{userid:number min(1)}", deleteUserHandler)
|
||||||
|
|
||||||
// Matches all GET requests except "/", "/about", anything starts with "/assets/" etc...
|
// Matches all GET requests except "/", "/about", anything starts with "/assets/" etc...
|
||||||
// because it does not conflict with the rest of the routes.
|
// because it does not conflict with the rest of the routes.
|
||||||
|
|
|
@ -63,7 +63,7 @@ Iris 是个底层框架, 对 MVC 模式有很好的支持,但不限制文件
|
||||||
|
|
||||||
### 路由、路由分组、路径动态参数、路由参数处理宏 、 自定义上下文
|
### 路由、路由分组、路径动态参数、路由参数处理宏 、 自定义上下文
|
||||||
|
|
||||||
* `app.Get("{userid:int min(1)}", myHandler)`
|
* `app.Get("{userid:number min(1)}", myHandler)`
|
||||||
* `app.Post("{asset:path}", myHandler)`
|
* `app.Post("{asset:path}", myHandler)`
|
||||||
* `app.Put("{custom:string regexp([a-z]+)}", myHandler)`
|
* `app.Put("{custom:string regexp([a-z]+)}", myHandler)`
|
||||||
|
|
||||||
|
@ -87,10 +87,10 @@ app.Get("/profile/me", userHandler)
|
||||||
|
|
||||||
// 匹配所有前缀为 /users/ 的 GET 请求
|
// 匹配所有前缀为 /users/ 的 GET 请求
|
||||||
// 参数为数字,且 >= 1
|
// 参数为数字,且 >= 1
|
||||||
app.Get("/user/{userid:int min(1)}", getUserHandler)
|
app.Get("/user/{userid:number min(1)}", getUserHandler)
|
||||||
// 匹配所有前缀为 /users/ 的 DELETE 请求
|
// 匹配所有前缀为 /users/ 的 DELETE 请求
|
||||||
// 参数为数字,且 >= 1
|
// 参数为数字,且 >= 1
|
||||||
app.Delete("/user/{userid:int min(1)}", deleteUserHandler)
|
app.Delete("/user/{userid:number min(1)}", deleteUserHandler)
|
||||||
|
|
||||||
// 匹配所有 GET 请求,除了 "/", "/about", 或其他以 "/assets/" 开头
|
// 匹配所有 GET 请求,除了 "/", "/about", 或其他以 "/assets/" 开头
|
||||||
// 因为它不会与其他路线冲突。
|
// 因为它不会与其他路线冲突。
|
||||||
|
|
|
@ -68,7 +68,7 @@ func main() {
|
||||||
usersRoutes := app.Party("/users", logThisMiddleware)
|
usersRoutes := app.Party("/users", logThisMiddleware)
|
||||||
{
|
{
|
||||||
// Method GET: http://localhost:8080/users/42
|
// Method GET: http://localhost:8080/users/42
|
||||||
usersRoutes.Get("/{id:int min(1)}", getUserByID)
|
usersRoutes.Get("/{id:number min(1)}", getUserByID)
|
||||||
// Method POST: http://localhost:8080/users/create
|
// Method POST: http://localhost:8080/users/create
|
||||||
usersRoutes.Post("/create", createUser)
|
usersRoutes.Post("/create", createUser)
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,9 +110,9 @@ app := iris.New()
|
||||||
users := app.Party("/users", myAuthMiddlewareHandler)
|
users := app.Party("/users", myAuthMiddlewareHandler)
|
||||||
|
|
||||||
// http://localhost:8080/users/42/profile
|
// http://localhost:8080/users/42/profile
|
||||||
users.Get("/{id:int}/profile", userProfileHandler)
|
users.Get("/{id:uint64}/profile", userProfileHandler)
|
||||||
// http://localhost:8080/users/messages/1
|
// http://localhost:8080/users/messages/1
|
||||||
users.Get("/inbox/{id:int}", userMessageHandler)
|
users.Get("/inbox/{id:uint64}", userMessageHandler)
|
||||||
```
|
```
|
||||||
|
|
||||||
The same could be also written using a function which accepts the child router(the Party).
|
The same could be also written using a function which accepts the child router(the Party).
|
||||||
|
@ -124,13 +124,13 @@ app.PartyFunc("/users", func(users iris.Party) {
|
||||||
users.Use(myAuthMiddlewareHandler)
|
users.Use(myAuthMiddlewareHandler)
|
||||||
|
|
||||||
// http://localhost:8080/users/42/profile
|
// http://localhost:8080/users/42/profile
|
||||||
users.Get("/{id:int}/profile", userProfileHandler)
|
users.Get("/{id:uint64}/profile", userProfileHandler)
|
||||||
// http://localhost:8080/users/messages/1
|
// http://localhost:8080/users/messages/1
|
||||||
users.Get("/inbox/{id:int}", userMessageHandler)
|
users.Get("/inbox/{id:uint64}", userMessageHandler)
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
> `id:int` is a (typed) dynamic path parameter, learn more by scrolling down.
|
> `id:uint` is a (typed) dynamic path parameter, learn more by scrolling down.
|
||||||
|
|
||||||
# Dynamic Path Parameters
|
# Dynamic Path Parameters
|
||||||
|
|
||||||
|
@ -154,21 +154,27 @@ Standard macro types for route path parameters
|
||||||
string type
|
string type
|
||||||
anything
|
anything
|
||||||
|
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
| {param:int} |
|
| {param:number} or {param:int} |
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
int type
|
int type
|
||||||
only numbers (0-9)
|
both positive and negative numbers, any number of digits (ctx.Params().GetInt will limit the digits based on the host arch)
|
||||||
|
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
| {param:long} |
|
| {param:long} or {param:int64} |
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
int64 type
|
int64 type
|
||||||
only numbers (0-9)
|
-9223372036854775808 to 9223372036854775807
|
||||||
|
|
||||||
+------------------------+
|
+------------------------+
|
||||||
| {param:boolean} |
|
| {param:uint64} |
|
||||||
+------------------------+
|
+------------------------+
|
||||||
|
uint64 type
|
||||||
|
0 to 18446744073709551615
|
||||||
|
|
||||||
|
+---------------------------------+
|
||||||
|
| {param:bool} or {param:boolean} |
|
||||||
|
+---------------------------------+
|
||||||
bool type
|
bool type
|
||||||
only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
||||||
or "0" or "f" or "F" or "FALSE" or "false" or "False"
|
or "0" or "f" or "F" or "FALSE" or "false" or "False"
|
||||||
|
@ -209,7 +215,7 @@ you are able to register your own too!.
|
||||||
Register a named path parameter function
|
Register a named path parameter function
|
||||||
|
|
||||||
```go
|
```go
|
||||||
app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool {
|
app.Macros().Number.RegisterFunc("min", func(argument int) func(paramValue string) bool {
|
||||||
// [...]
|
// [...]
|
||||||
return true
|
return true
|
||||||
// -> true means valid, false means invalid fire 404 or if "else 500" is appended to the macro syntax then internal server error.
|
// -> true means valid, false means invalid fire 404 or if "else 500" is appended to the macro syntax then internal server error.
|
||||||
|
@ -219,7 +225,10 @@ app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string)
|
||||||
At the `func(argument ...)` you can have any standard type, it will be validated before the server starts so don't care about any performance cost there, the only thing it runs at serve time is the returning `func(paramValue string) bool`.
|
At the `func(argument ...)` you can have any standard type, it will be validated before the server starts so don't care about any performance cost there, the only thing it runs at serve time is the returning `func(paramValue string) bool`.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
{param:string equal(iris)} , "iris" will be the argument here:
|
{param:string equal(iris)}
|
||||||
|
```
|
||||||
|
The "iris" will be the argument here:
|
||||||
|
```go
|
||||||
app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
|
app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
|
||||||
return func(paramValue string){ return argument == paramValue }
|
return func(paramValue string){ return argument == paramValue }
|
||||||
})
|
})
|
||||||
|
@ -234,12 +243,12 @@ app.Get("/username/{name}", func(ctx iris.Context) {
|
||||||
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
||||||
}) // type is missing = {name:string}
|
}) // type is missing = {name:string}
|
||||||
|
|
||||||
// Let's register our first macro attached to int macro type.
|
// Let's register our first macro attached to number macro type.
|
||||||
// "min" = the function
|
// "min" = the function
|
||||||
// "minValue" = the argument of the function
|
// "minValue" = the argument of the function
|
||||||
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
||||||
// a user requests a path which contains the :int macro type with the min(...) macro parameter function.
|
// a user requests a path which contains the :number macro type with the min(...) macro parameter function.
|
||||||
app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
|
app.Macros().Number.RegisterFunc("min", func(minValue int) func(string) bool {
|
||||||
// do anything before serve here [...]
|
// do anything before serve here [...]
|
||||||
// at this case we don't need to do anything
|
// at this case we don't need to do anything
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
|
@ -254,21 +263,21 @@ app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
|
||||||
// http://localhost:8080/profile/id>=1
|
// http://localhost:8080/profile/id>=1
|
||||||
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
||||||
// macro parameter functions are optional of course.
|
// macro parameter functions are optional of course.
|
||||||
app.Get("/profile/{id:int min(1)}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(1)}", func(ctx iris.Context) {
|
||||||
// second parameter is the error but it will always nil because we use macros,
|
// second parameter is the error but it will always nil because we use macros,
|
||||||
// the validaton already happened.
|
// the validaton already happened.
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetUint64("id")
|
||||||
ctx.Writef("Hello id: %d", id)
|
ctx.Writef("Hello id: %d", id)
|
||||||
})
|
})
|
||||||
|
|
||||||
// to change the error code per route's macro evaluator:
|
// to change the error code per route's macro evaluator:
|
||||||
app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetUint64("id")
|
||||||
friendid, _ := ctx.Params().GetInt("friendid")
|
friendid, _ := ctx.Params().GetUint64("friendid")
|
||||||
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
||||||
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
||||||
|
|
||||||
// http://localhost:8080/game/a-zA-Z/level/0-9
|
// http://localhost:8080/game/a-zA-Z/level/42
|
||||||
// remember, alphabetical is lowercase or uppercase letters only.
|
// remember, alphabetical is lowercase or uppercase letters only.
|
||||||
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
|
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
|
||||||
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
|
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
|
||||||
|
@ -297,12 +306,10 @@ app.Run(iris.Addr(":8080"))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A **path parameter name should contain only alphabetical letters. Symbols like '_' and numbers are NOT allowed**.
|
A path parameter name should contain only alphabetical letters or digits. Symbols like '_' are NOT allowed.
|
||||||
|
|
||||||
Last, do not confuse `ctx.Params()` with `ctx.Values()`.
|
Last, do not confuse `ctx.Params()` with `ctx.Values()`.
|
||||||
Path parameter's values goes to `ctx.Params()` and context's local storage
|
Path parameter's values can be retrieved from `ctx.Params()`,
|
||||||
that can be used to communicate between handlers and middleware(s) goes to
|
context's local storage that can be used to communicate between handlers and middleware(s) can be stored to `ctx.Values()`.
|
||||||
`ctx.Values()`.
|
|
||||||
|
|
||||||
# Routing and reverse lookups
|
# Routing and reverse lookups
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,12 @@ func main() {
|
||||||
app.Get("/donate", donateHandler, donateFinishHandler)
|
app.Get("/donate", donateHandler, donateFinishHandler)
|
||||||
|
|
||||||
// Pssst, don't forget dynamic-path example for more "magic"!
|
// Pssst, don't forget dynamic-path example for more "magic"!
|
||||||
app.Get("/api/users/{userid:int min(1)}", func(ctx iris.Context) {
|
app.Get("/api/users/{userid:uint64 min(1)}", func(ctx iris.Context) {
|
||||||
userID, err := ctx.Params().GetInt("userid")
|
userID, err := ctx.Params().GetUint64("userid")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Writef("error while trying to parse userid parameter," +
|
ctx.Writef("error while trying to parse userid parameter," +
|
||||||
"this will never happen if :int is being used because if it's not integer it will fire Not Found automatically.")
|
"this will never happen if :uint64 is being used because if it's not a valid uint64 it will fire Not Found automatically.")
|
||||||
ctx.StatusCode(iris.StatusBadRequest)
|
ctx.StatusCode(iris.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ func main() {
|
||||||
ctx.Writef("All users")
|
ctx.Writef("All users")
|
||||||
})
|
})
|
||||||
// http://v1.localhost:8080/api/users/42
|
// http://v1.localhost:8080/api/users/42
|
||||||
usersAPI.Get("/{userid:int}", func(ctx iris.Context) {
|
usersAPI.Get("/{userid:number}", func(ctx iris.Context) {
|
||||||
ctx.Writef("user with id: %s", ctx.Params().Get("userid"))
|
ctx.Writef("user with id: %s", ctx.Params().Get("userid"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,15 +14,14 @@ func main() {
|
||||||
// we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path
|
// we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path
|
||||||
// with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros.
|
// with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros.
|
||||||
|
|
||||||
// iris, like net/http std package registers route's handlers
|
// Iris, like net/http std package registers route's handlers
|
||||||
// by a Handler, the iris' type of handler is just a func(ctx iris.Context)
|
// by a Handler, the iris' type of handler is just a func(ctx iris.Context)
|
||||||
// where context comes from github.com/kataras/iris/context.
|
// where context comes from github.com/kataras/iris/context.
|
||||||
// Until go 1.9 you will have to import that package too, after go 1.9 this will be not be necessary.
|
|
||||||
//
|
//
|
||||||
// iris has the easiest and the most powerful routing process you have ever meet.
|
// Iris has the easiest and the most powerful routing process you have ever meet.
|
||||||
//
|
//
|
||||||
// At the same time,
|
// At the same time,
|
||||||
// iris has its own interpeter(yes like a programming language)
|
// Iris has its own interpeter(yes like a programming language)
|
||||||
// for route's path syntax and their dynamic path parameters parsing and evaluation,
|
// for route's path syntax and their dynamic path parameters parsing and evaluation,
|
||||||
// We call them "macros" for shortcut.
|
// We call them "macros" for shortcut.
|
||||||
// How? It calculates its needs and if not any special regexp needed then it just
|
// How? It calculates its needs and if not any special regexp needed then it just
|
||||||
|
@ -36,21 +35,27 @@ func main() {
|
||||||
// string type
|
// string type
|
||||||
// anything
|
// anything
|
||||||
//
|
//
|
||||||
// +------------------------+
|
// +-------------------------------+
|
||||||
// | {param:int} |
|
// | {param:int} or {param:number} |
|
||||||
// +------------------------+
|
// +-------------------------------+
|
||||||
// int type
|
// int type
|
||||||
// only numbers (0-9)
|
// both positive and negative numbers, any number of digits (ctx.Params().GetInt will limit the digits based on the host arch)
|
||||||
//
|
//
|
||||||
// +------------------------+
|
// +-------------------------------+
|
||||||
// | {param:long} |
|
// | {param:int64} or {param:long} |
|
||||||
// +------------------------+
|
// +-------------------------------+
|
||||||
// int64 type
|
// int64 type
|
||||||
// only numbers (0-9)
|
// -9223372036854775808 to 9223372036854775807
|
||||||
//
|
//
|
||||||
// +------------------------+
|
// +------------------------+
|
||||||
// | {param:boolean} |
|
// | {param:uint64} |
|
||||||
// +------------------------+
|
// +------------------------+
|
||||||
|
// uint64 type
|
||||||
|
// 0 to 18446744073709551615
|
||||||
|
//
|
||||||
|
// +---------------------------------+
|
||||||
|
// | {param:bool} or {param:boolean} |
|
||||||
|
// +---------------------------------+
|
||||||
// bool type
|
// bool type
|
||||||
// only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
// only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
||||||
// or "0" or "f" or "F" or "FALSE" or "false" or "False"
|
// or "0" or "f" or "F" or "FALSE" or "false" or "False"
|
||||||
|
@ -89,7 +94,7 @@ func main() {
|
||||||
// you are able to register your own too!.
|
// you are able to register your own too!.
|
||||||
//
|
//
|
||||||
// Register a named path parameter function:
|
// Register a named path parameter function:
|
||||||
// app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool {
|
// app.Macros().Number.RegisterFunc("min", func(argument int) func(paramValue string) bool {
|
||||||
// [...]
|
// [...]
|
||||||
// return true/false -> true means valid.
|
// return true/false -> true means valid.
|
||||||
// })
|
// })
|
||||||
|
@ -107,12 +112,12 @@ func main() {
|
||||||
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
||||||
}) // type is missing = {name:string}
|
}) // type is missing = {name:string}
|
||||||
|
|
||||||
// Let's register our first macro attached to int macro type.
|
// Let's register our first macro attached to number macro type.
|
||||||
// "min" = the function
|
// "min" = the function
|
||||||
// "minValue" = the argument of the function
|
// "minValue" = the argument of the function
|
||||||
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
||||||
// a user requests a path which contains the :int macro type with the min(...) macro parameter function.
|
// a user requests a path which contains the :number macro type with the min(...) macro parameter function.
|
||||||
app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
|
app.Macros().Number.RegisterFunc("min", func(minValue int) func(string) bool {
|
||||||
// do anything before serve here [...]
|
// do anything before serve here [...]
|
||||||
// at this case we don't need to do anything
|
// at this case we don't need to do anything
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
|
@ -127,7 +132,7 @@ func main() {
|
||||||
// http://localhost:8080/profile/id>=1
|
// http://localhost:8080/profile/id>=1
|
||||||
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
||||||
// macro parameter functions are optional of course.
|
// macro parameter functions are optional of course.
|
||||||
app.Get("/profile/{id:int min(1)}", func(ctx iris.Context) {
|
app.Get("/profile/{id:number min(1)}", func(ctx iris.Context) {
|
||||||
// second parameter is the error but it will always nil because we use macros,
|
// second parameter is the error but it will always nil because we use macros,
|
||||||
// the validaton already happened.
|
// the validaton already happened.
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetInt("id")
|
||||||
|
@ -135,8 +140,8 @@ func main() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// to change the error code per route's macro evaluator:
|
// to change the error code per route's macro evaluator:
|
||||||
app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx iris.Context) {
|
app.Get("/profile/{id:number min(1)}/friends/{friendid:number min(1) else 504}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetInt("id") // or GetUint64.
|
||||||
friendid, _ := ctx.Params().GetInt("friendid")
|
friendid, _ := ctx.Params().GetInt("friendid")
|
||||||
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
||||||
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
||||||
|
@ -159,9 +164,9 @@ func main() {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
// http://localhost:8080/game/a-zA-Z/level/0-9
|
// http://localhost:8080/game/a-zA-Z/level/42
|
||||||
// remember, alphabetical is lowercase or uppercase letters only.
|
// remember, alphabetical is lowercase or uppercase letters only.
|
||||||
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
|
app.Get("/game/{name:alphabetical}/level/{level:number}", func(ctx iris.Context) {
|
||||||
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
|
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -197,10 +202,9 @@ func main() {
|
||||||
// if "/mypath/{myparam:path}" then the parameter has two names, one is the "*" and the other is the user-defined "myparam".
|
// if "/mypath/{myparam:path}" then the parameter has two names, one is the "*" and the other is the user-defined "myparam".
|
||||||
|
|
||||||
// WARNING:
|
// WARNING:
|
||||||
// A path parameter name should contain only alphabetical letters. Symbols like '_' and numbers are NOT allowed.
|
// A path parameter name should contain only alphabetical letters or digits. Symbols like '_' are NOT allowed.
|
||||||
// Last, do not confuse `ctx.Params()` with `ctx.Values()`.
|
// Last, do not confuse `ctx.Params()` with `ctx.Values()`.
|
||||||
// Path parameter's values goes to `ctx.Params()` and context's local storage
|
// Path parameter's values can be retrieved from `ctx.Params()`,
|
||||||
// that can be used to communicate between handlers and middleware(s) goes to
|
// context's local storage that can be used to communicate between handlers and middleware(s) can be stored to `ctx.Values()`.
|
||||||
// `ctx.Values()`.
|
|
||||||
app.Run(iris.Addr(":8080"))
|
app.Run(iris.Addr(":8080"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,25 +35,25 @@ func registerGamesRoutes(app *iris.Application) {
|
||||||
{ // braces are optional of course, it's just a style of code
|
{ // braces are optional of course, it's just a style of code
|
||||||
|
|
||||||
// "GET" method
|
// "GET" method
|
||||||
games.Get("/{gameID:int}/clans", h)
|
games.Get("/{gameID:uint64}/clans", h)
|
||||||
games.Get("/{gameID:int}/clans/clan/{clanPublicID:int}", h)
|
games.Get("/{gameID:uint64}/clans/clan/{clanPublicID:uint64}", h)
|
||||||
games.Get("/{gameID:int}/clans/search", h)
|
games.Get("/{gameID:uint64}/clans/search", h)
|
||||||
|
|
||||||
// "PUT" method
|
// "PUT" method
|
||||||
games.Put("/{gameID:int}/players/{clanPublicID:int}", h)
|
games.Put("/{gameID:uint64}/players/{clanPublicID:uint64}", h)
|
||||||
games.Put("/{gameID:int}/clans/clan/{clanPublicID:int}", h)
|
games.Put("/{gameID:uint64}/clans/clan/{clanPublicID:uint64}", h)
|
||||||
// remember: "clanPublicID" should not be changed to other routes with the same prefix.
|
// remember: "clanPublicID" should not be changed to other routes with the same prefix.
|
||||||
// "POST" method
|
// "POST" method
|
||||||
games.Post("/{gameID:int}/clans", h)
|
games.Post("/{gameID:uint64}/clans", h)
|
||||||
games.Post("/{gameID:int}/players", h)
|
games.Post("/{gameID:uint64}/players", h)
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/leave", h)
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/leave", h)
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application", h)
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application", h)
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application/{action}", h) // {action} == {action:string}
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application/{action}", h) // {action} == {action:string}
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation", h)
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation", h)
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation/{action}", h)
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation/{action}", h)
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/delete", h)
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/delete", h)
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/promote", h)
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/promote", h)
|
||||||
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/demote", h)
|
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/demote", h)
|
||||||
|
|
||||||
gamesCh := games.Party("/challenge")
|
gamesCh := games.Party("/challenge")
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,8 +68,8 @@ func main() {
|
||||||
|
|
||||||
// GET: http://localhost:8080/users/42
|
// GET: http://localhost:8080/users/42
|
||||||
// **/users/42 and /users/help works after iris version 7.0.5**
|
// **/users/42 and /users/help works after iris version 7.0.5**
|
||||||
usersRoutes.Get("/{id:int}", func(ctx iris.Context) {
|
usersRoutes.Get("/{id:uint64}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetUint64("id")
|
||||||
ctx.Writef("get user by id: %d", id)
|
ctx.Writef("get user by id: %d", id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -80,15 +80,15 @@ func main() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// PUT: http://localhost:8080/users
|
// PUT: http://localhost:8080/users
|
||||||
usersRoutes.Put("/{id:int}", func(ctx iris.Context) {
|
usersRoutes.Put("/{id:uint64}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id") // or .Get to get its string represatantion.
|
id, _ := ctx.Params().GetUint64("id") // or .Get to get its string represatantion.
|
||||||
username := ctx.PostValue("username")
|
username := ctx.PostValue("username")
|
||||||
ctx.Writef("update user for id= %d and new username= %s", id, username)
|
ctx.Writef("update user for id= %d and new username= %s", id, username)
|
||||||
})
|
})
|
||||||
|
|
||||||
// DELETE: http://localhost:8080/users/42
|
// DELETE: http://localhost:8080/users/42
|
||||||
usersRoutes.Delete("/{id:int}", func(ctx iris.Context) {
|
usersRoutes.Delete("/{id:uint64}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetUint64("id")
|
||||||
ctx.Writef("delete user by id: %d", id)
|
ctx.Writef("delete user by id: %d", id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,11 @@ func newApp() *iris.Application {
|
||||||
|
|
||||||
app.PartyFunc("/api/users", func(r iris.Party) {
|
app.PartyFunc("/api/users", func(r iris.Party) {
|
||||||
r.Get("/", info)
|
r.Get("/", info)
|
||||||
r.Get("/{id:int}", info)
|
r.Get("/{id:uint64}", info)
|
||||||
|
|
||||||
r.Post("/", info)
|
r.Post("/", info)
|
||||||
|
|
||||||
r.Put("/{id:int}", info)
|
r.Put("/{id:uint64}", info)
|
||||||
}) /* <- same as:
|
}) /* <- same as:
|
||||||
usersAPI := app.Party("/api/users")
|
usersAPI := app.Party("/api/users")
|
||||||
{ // those brackets are just syntactic-sugar things.
|
{ // those brackets are just syntactic-sugar things.
|
||||||
|
|
|
@ -25,7 +25,7 @@ type RouteReadOnly interface {
|
||||||
|
|
||||||
// StaticPath returns the static part of the original, registered route path.
|
// StaticPath returns the static part of the original, registered route path.
|
||||||
// if /user/{id} it will return /user
|
// if /user/{id} it will return /user
|
||||||
// if /user/{id}/friend/{friendid:int} it will return /user too
|
// if /user/{id}/friend/{friendid:uint64} it will return /user too
|
||||||
// if /assets/{filepath:path} it will return /assets.
|
// if /assets/{filepath:path} it will return /assets.
|
||||||
StaticPath() string
|
StaticPath() string
|
||||||
|
|
||||||
|
|
|
@ -270,10 +270,10 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
||||||
// otherwise use `Party` which can handle many paths with different handlers and middlewares.
|
// otherwise use `Party` which can handle many paths with different handlers and middlewares.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// app.HandleMany("GET", "/user /user/{id:int} /user/me", genericUserHandler)
|
// app.HandleMany("GET", "/user /user/{id:uint64} /user/me", genericUserHandler)
|
||||||
// At the other side, with `Handle` we've had to write:
|
// At the other side, with `Handle` we've had to write:
|
||||||
// app.Handle("GET", "/user", userHandler)
|
// app.Handle("GET", "/user", userHandler)
|
||||||
// app.Handle("GET", "/user/{id:int}", userByIDHandler)
|
// app.Handle("GET", "/user/{id:uint64}", userByIDHandler)
|
||||||
// app.Handle("GET", "/user/me", userMeHandler)
|
// app.Handle("GET", "/user/me", userMeHandler)
|
||||||
//
|
//
|
||||||
// This method is used behind the scenes at the `Controller` function
|
// This method is used behind the scenes at the `Controller` function
|
||||||
|
|
|
@ -56,7 +56,7 @@ func genPaths(routesLength, minCharLength, maxCharLength int) []string {
|
||||||
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
|
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
|
||||||
b.WriteString("/{name:string}/") // sugar.
|
b.WriteString("/{name:string}/") // sugar.
|
||||||
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
|
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
|
||||||
b.WriteString("/{age:int}/end")
|
b.WriteString("/{age:number}/end")
|
||||||
paths[i] = b.String()
|
paths[i] = b.String()
|
||||||
|
|
||||||
b.Reset()
|
b.Reset()
|
||||||
|
|
|
@ -35,8 +35,9 @@ func registerBuiltinsMacroFuncs(out *macro.Map) {
|
||||||
//
|
//
|
||||||
// these can be overridden by the user, later on.
|
// these can be overridden by the user, later on.
|
||||||
registerStringMacroFuncs(out.String)
|
registerStringMacroFuncs(out.String)
|
||||||
registerIntMacroFuncs(out.Int)
|
registerNumberMacroFuncs(out.Number)
|
||||||
registerIntMacroFuncs(out.Long)
|
registerInt64MacroFuncs(out.Int64)
|
||||||
|
registerUint64MacroFuncs(out.Uint64)
|
||||||
registerAlphabeticalMacroFuncs(out.Alphabetical)
|
registerAlphabeticalMacroFuncs(out.Alphabetical)
|
||||||
registerFileMacroFuncs(out.File)
|
registerFileMacroFuncs(out.File)
|
||||||
registerPathMacroFuncs(out.Path)
|
registerPathMacroFuncs(out.Path)
|
||||||
|
@ -87,9 +88,9 @@ func registerStringMacroFuncs(out *macro.Macro) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int
|
// Number
|
||||||
// only numbers (0-9)
|
// positive and negative numbers, number of digits depends on the arch.
|
||||||
func registerIntMacroFuncs(out *macro.Macro) {
|
func registerNumberMacroFuncs(out *macro.Macro) {
|
||||||
// checks if the param value's int representation is
|
// checks if the param value's int representation is
|
||||||
// bigger or equal than 'min'
|
// bigger or equal than 'min'
|
||||||
out.RegisterFunc("min", func(min int) macro.EvaluatorFunc {
|
out.RegisterFunc("min", func(min int) macro.EvaluatorFunc {
|
||||||
|
@ -131,6 +132,94 @@ func registerIntMacroFuncs(out *macro.Macro) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Int64
|
||||||
|
// -9223372036854775808 to 9223372036854775807.
|
||||||
|
func registerInt64MacroFuncs(out *macro.Macro) {
|
||||||
|
// checks if the param value's int64 representation is
|
||||||
|
// bigger or equal than 'min'
|
||||||
|
out.RegisterFunc("min", func(min int64) macro.EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n >= min
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// checks if the param value's int64 representation is
|
||||||
|
// smaller or equal than 'max'
|
||||||
|
out.RegisterFunc("max", func(max int64) macro.EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n <= max
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// checks if the param value's int64 representation is
|
||||||
|
// between min and max, including 'min' and 'max'
|
||||||
|
out.RegisterFunc("range", func(min, max int64) macro.EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < min || n > max {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64
|
||||||
|
// 0 to 18446744073709551615.
|
||||||
|
func registerUint64MacroFuncs(out *macro.Macro) {
|
||||||
|
// checks if the param value's uint64 representation is
|
||||||
|
// bigger or equal than 'min'
|
||||||
|
out.RegisterFunc("min", func(min uint64) macro.EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n >= min
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// checks if the param value's uint64 representation is
|
||||||
|
// smaller or equal than 'max'
|
||||||
|
out.RegisterFunc("max", func(max uint64) macro.EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n <= max
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// checks if the param value's uint64 representation is
|
||||||
|
// between min and max, including 'min' and 'max'
|
||||||
|
out.RegisterFunc("range", func(min, max uint64) macro.EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < min || n > max {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Alphabetical
|
// Alphabetical
|
||||||
// letters only (upper or lowercase)
|
// letters only (upper or lowercase)
|
||||||
func registerAlphabeticalMacroFuncs(out *macro.Macro) {
|
func registerAlphabeticalMacroFuncs(out *macro.Macro) {
|
||||||
|
|
|
@ -16,20 +16,27 @@ const (
|
||||||
// ParamTypeString is the string type.
|
// ParamTypeString is the string type.
|
||||||
// If parameter type is missing then it defaults to String type.
|
// If parameter type is missing then it defaults to String type.
|
||||||
// Allows anything
|
// Allows anything
|
||||||
// Declaration: /mypath/{myparam:string} or /mypath{myparam}
|
// Declaration: /mypath/{myparam:string} or {myparam}
|
||||||
ParamTypeString
|
ParamTypeString
|
||||||
// ParamTypeInt is the integer, a number type.
|
|
||||||
// Allows only positive numbers (0-9)
|
// ParamTypeNumber is the integer, a number type.
|
||||||
// Declaration: /mypath/{myparam:int}
|
// Allows both positive and negative numbers, any number of digits.
|
||||||
ParamTypeInt
|
// Declaration: /mypath/{myparam:number} or {myparam:int} for backwards-compatibility
|
||||||
// ParamTypeLong is the integer, a number type.
|
ParamTypeNumber
|
||||||
// Allows only positive numbers (0-9)
|
|
||||||
// Declaration: /mypath/{myparam:long}
|
// ParamTypeInt64 is a number type.
|
||||||
ParamTypeLong
|
// Allows only -9223372036854775808 to 9223372036854775807.
|
||||||
|
// Declaration: /mypath/{myparam:int64} or {myparam:long}
|
||||||
|
ParamTypeInt64
|
||||||
|
// ParamTypeUint64 a number type.
|
||||||
|
// Allows only 0 to 18446744073709551615.
|
||||||
|
// Declaration: /mypath/{myparam:uint64}
|
||||||
|
ParamTypeUint64
|
||||||
|
|
||||||
// ParamTypeBoolean is the bool type.
|
// ParamTypeBoolean is the bool type.
|
||||||
// Allows only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
// Allows only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
||||||
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
|
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
|
||||||
// Declaration: /mypath/{myparam:boolean}
|
// Declaration: /mypath/{myparam:bool} or {myparam:boolean}
|
||||||
ParamTypeBoolean
|
ParamTypeBoolean
|
||||||
// ParamTypeAlphabetical is the alphabetical/letter type type.
|
// ParamTypeAlphabetical is the alphabetical/letter type type.
|
||||||
// Allows letters only (upper or lowercase)
|
// Allows letters only (upper or lowercase)
|
||||||
|
@ -79,10 +86,12 @@ func (pt ParamType) Kind() reflect.Kind {
|
||||||
fallthrough
|
fallthrough
|
||||||
case ParamTypeString:
|
case ParamTypeString:
|
||||||
return reflect.String
|
return reflect.String
|
||||||
case ParamTypeInt:
|
case ParamTypeNumber:
|
||||||
return reflect.Int
|
return reflect.Int
|
||||||
case ParamTypeLong:
|
case ParamTypeInt64:
|
||||||
return reflect.Int64
|
return reflect.Int64
|
||||||
|
case ParamTypeUint64:
|
||||||
|
return reflect.Uint64
|
||||||
case ParamTypeBoolean:
|
case ParamTypeBoolean:
|
||||||
return reflect.Bool
|
return reflect.Bool
|
||||||
}
|
}
|
||||||
|
@ -99,6 +108,8 @@ func ValidKind(k reflect.Kind) bool {
|
||||||
fallthrough
|
fallthrough
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
case reflect.Uint64:
|
||||||
|
fallthrough
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
|
@ -113,10 +124,17 @@ func (pt ParamType) Assignable(k reflect.Kind) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramTypes = map[string]ParamType{
|
var paramTypes = map[string]ParamType{
|
||||||
"string": ParamTypeString,
|
"string": ParamTypeString,
|
||||||
"int": ParamTypeInt,
|
|
||||||
"long": ParamTypeLong,
|
"number": ParamTypeNumber,
|
||||||
"boolean": ParamTypeBoolean,
|
"int": ParamTypeNumber, // same as number.
|
||||||
|
"long": ParamTypeInt64,
|
||||||
|
"int64": ParamTypeInt64, // same as long.
|
||||||
|
"uint64": ParamTypeUint64,
|
||||||
|
|
||||||
|
"boolean": ParamTypeBoolean,
|
||||||
|
"bool": ParamTypeBoolean, // same as boolean.
|
||||||
|
|
||||||
"alphabetical": ParamTypeAlphabetical,
|
"alphabetical": ParamTypeAlphabetical,
|
||||||
"file": ParamTypeFile,
|
"file": ParamTypeFile,
|
||||||
"path": ParamTypePath,
|
"path": ParamTypePath,
|
||||||
|
@ -131,8 +149,10 @@ var paramTypes = map[string]ParamType{
|
||||||
// representation of a parameter type.
|
// representation of a parameter type.
|
||||||
// Available:
|
// Available:
|
||||||
// "string"
|
// "string"
|
||||||
// "int"
|
// "number" or "int"
|
||||||
// "long"
|
// "long" or "int64"
|
||||||
|
// "uint64"
|
||||||
|
// "boolean" or "bool"
|
||||||
// "alphabetical"
|
// "alphabetical"
|
||||||
// "file"
|
// "file"
|
||||||
// "path"
|
// "path"
|
||||||
|
@ -149,17 +169,20 @@ func LookupParamType(ident string) ParamType {
|
||||||
// make sure that caller resolves these types before this call.
|
// make sure that caller resolves these types before this call.
|
||||||
//
|
//
|
||||||
// string matches to string
|
// string matches to string
|
||||||
// int matches to int
|
// int matches to int/number
|
||||||
// int64 matches to long
|
// int64 matches to int64/long
|
||||||
// bool matches to boolean
|
// uint64 matches to uint64
|
||||||
|
// bool matches to bool/boolean
|
||||||
func LookupParamTypeFromStd(goType string) ParamType {
|
func LookupParamTypeFromStd(goType string) ParamType {
|
||||||
switch goType {
|
switch goType {
|
||||||
case "string":
|
case "string":
|
||||||
return ParamTypeString
|
return ParamTypeString
|
||||||
case "int":
|
case "int":
|
||||||
return ParamTypeInt
|
return ParamTypeNumber
|
||||||
case "int64":
|
case "int64":
|
||||||
return ParamTypeLong
|
return ParamTypeInt64
|
||||||
|
case "uint64":
|
||||||
|
return ParamTypeUint64
|
||||||
case "bool":
|
case "bool":
|
||||||
return ParamTypeBoolean
|
return ParamTypeBoolean
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -179,7 +179,7 @@ func (l *Lexer) skipWhitespace() {
|
||||||
|
|
||||||
func (l *Lexer) readIdentifier() string {
|
func (l *Lexer) readIdentifier() string {
|
||||||
pos := l.pos
|
pos := l.pos
|
||||||
for isLetter(l.ch) {
|
for isLetter(l.ch) || isDigit(l.ch) {
|
||||||
l.readChar()
|
l.readChar()
|
||||||
}
|
}
|
||||||
return l.input[pos:l.pos]
|
return l.input[pos:l.pos]
|
||||||
|
|
|
@ -7,27 +7,27 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNextToken(t *testing.T) {
|
func TestNextToken(t *testing.T) {
|
||||||
input := `{id:int min(1) max(5) else 404}`
|
input := `{id:number min(1) max(5) else 404}`
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
expectedType token.Type
|
expectedType token.Type
|
||||||
expectedLiteral string
|
expectedLiteral string
|
||||||
}{
|
}{
|
||||||
{token.LBRACE, "{"}, // 0
|
{token.LBRACE, "{"}, // 0
|
||||||
{token.IDENT, "id"}, // 1
|
{token.IDENT, "id"}, // 1
|
||||||
{token.COLON, ":"}, // 2
|
{token.COLON, ":"}, // 2
|
||||||
{token.IDENT, "int"}, // 3
|
{token.IDENT, "number"}, // 3
|
||||||
{token.IDENT, "min"}, // 4
|
{token.IDENT, "min"}, // 4
|
||||||
{token.LPAREN, "("}, // 5
|
{token.LPAREN, "("}, // 5
|
||||||
{token.INT, "1"}, // 6
|
{token.INT, "1"}, // 6
|
||||||
{token.RPAREN, ")"}, // 7
|
{token.RPAREN, ")"}, // 7
|
||||||
{token.IDENT, "max"}, // 8
|
{token.IDENT, "max"}, // 8
|
||||||
{token.LPAREN, "("}, // 9
|
{token.LPAREN, "("}, // 9
|
||||||
{token.INT, "5"}, // 10
|
{token.INT, "5"}, // 10
|
||||||
{token.RPAREN, ")"}, // 11
|
{token.RPAREN, ")"}, // 11
|
||||||
{token.ELSE, "else"}, // 12
|
{token.ELSE, "else"}, // 12
|
||||||
{token.INT, "404"}, // 13
|
{token.INT, "404"}, // 13
|
||||||
{token.RBRACE, "}"}, // 14
|
{token.RBRACE, "}"}, // 14
|
||||||
}
|
}
|
||||||
|
|
||||||
l := New(input)
|
l := New(input)
|
||||||
|
|
|
@ -120,11 +120,11 @@ func (p *ParamParser) Parse() (*ast.ParamStatement, error) {
|
||||||
|
|
||||||
switch t.Type {
|
switch t.Type {
|
||||||
case token.LBRACE:
|
case token.LBRACE:
|
||||||
// name, alphabetical and _, param names are not allowed to contain any number.
|
// can accept only letter or number only.
|
||||||
nextTok := l.NextToken()
|
nextTok := l.NextToken()
|
||||||
stmt.Name = nextTok.Literal
|
stmt.Name = nextTok.Literal
|
||||||
case token.COLON:
|
case token.COLON:
|
||||||
// type
|
// type can accept both letters and numbers but not symbols ofc.
|
||||||
nextTok := l.NextToken()
|
nextTok := l.NextToken()
|
||||||
paramType := ast.LookupParamType(nextTok.Literal)
|
paramType := ast.LookupParamType(nextTok.Literal)
|
||||||
if paramType == ast.ParamTypeUnExpected {
|
if paramType == ast.ParamTypeUnExpected {
|
||||||
|
|
|
@ -30,7 +30,7 @@ func TestParseParamError(t *testing.T) {
|
||||||
//
|
//
|
||||||
|
|
||||||
// success
|
// success
|
||||||
input2 := "{id:int range(1,5) else 404}"
|
input2 := "{id:uint64 range(1,5) else 404}"
|
||||||
p.Reset(input2)
|
p.Reset(input2)
|
||||||
_, err = p.Parse()
|
_, err = p.Parse()
|
||||||
|
|
||||||
|
@ -47,9 +47,9 @@ func TestParseParam(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{true,
|
{true,
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{id:int min(1) max(5) else 404}",
|
Src: "{id:number min(1) max(5) else 404}",
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: ast.ParamTypeInt,
|
Type: ast.ParamTypeNumber,
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "min",
|
Name: "min",
|
||||||
|
@ -63,9 +63,9 @@ func TestParseParam(t *testing.T) {
|
||||||
|
|
||||||
{true,
|
{true,
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{id:int range(1,5)}",
|
Src: "{id:number range(1,5)}",
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: ast.ParamTypeInt,
|
Type: ast.ParamTypeNumber,
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "range",
|
Name: "range",
|
||||||
|
@ -106,18 +106,18 @@ func TestParseParam(t *testing.T) {
|
||||||
Type: ast.ParamTypeUnExpected,
|
Type: ast.ParamTypeUnExpected,
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 5
|
}}, // 5
|
||||||
{false, // false because it will give an error of unexpeced token type with value 2
|
{true,
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{myparam2}",
|
Src: "{myparam2}",
|
||||||
Name: "myparam", // expected "myparam" because we don't allow integers to the parameter names.
|
Name: "myparam2", // we now allow integers to the parameter names.
|
||||||
Type: ast.ParamTypeString,
|
Type: ast.ParamTypeString,
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 6
|
}}, // 6
|
||||||
{true,
|
{true,
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{id:int even()}", // test param funcs without any arguments (LPAREN peek for RPAREN)
|
Src: "{id:number even()}", // test param funcs without any arguments (LPAREN peek for RPAREN)
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: ast.ParamTypeInt,
|
Type: ast.ParamTypeNumber,
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "even"},
|
Name: "even"},
|
||||||
|
@ -126,18 +126,32 @@ func TestParseParam(t *testing.T) {
|
||||||
}}, // 7
|
}}, // 7
|
||||||
{true,
|
{true,
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{id:long else 404}",
|
Src: "{id:int64 else 404}",
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: ast.ParamTypeLong,
|
Type: ast.ParamTypeInt64,
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 8
|
}}, // 8
|
||||||
{true,
|
{true,
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{has:boolean else 404}",
|
Src: "{id:long else 404}", // backwards-compatible test.
|
||||||
|
Name: "id",
|
||||||
|
Type: ast.ParamTypeInt64,
|
||||||
|
ErrorCode: 404,
|
||||||
|
}}, // 9
|
||||||
|
{true,
|
||||||
|
ast.ParamStatement{
|
||||||
|
Src: "{has:bool else 404}",
|
||||||
Name: "has",
|
Name: "has",
|
||||||
Type: ast.ParamTypeBoolean,
|
Type: ast.ParamTypeBoolean,
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 9
|
}}, // 10
|
||||||
|
{true,
|
||||||
|
ast.ParamStatement{
|
||||||
|
Src: "{has:boolean else 404}", // backwards-compatible test.
|
||||||
|
Name: "has",
|
||||||
|
Type: ast.ParamTypeBoolean,
|
||||||
|
ErrorCode: 404,
|
||||||
|
}}, // 11
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,11 +181,11 @@ func TestParse(t *testing.T) {
|
||||||
valid bool
|
valid bool
|
||||||
expectedStatements []ast.ParamStatement
|
expectedStatements []ast.ParamStatement
|
||||||
}{
|
}{
|
||||||
{"/api/users/{id:int min(1) max(5) else 404}", true,
|
{"/api/users/{id:number min(1) max(5) else 404}", true,
|
||||||
[]ast.ParamStatement{{
|
[]ast.ParamStatement{{
|
||||||
Src: "{id:int min(1) max(5) else 404}",
|
Src: "{id:number min(1) max(5) else 404}",
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: ast.ParamTypeInt,
|
Type: ast.ParamTypeNumber,
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "min",
|
Name: "min",
|
||||||
|
@ -183,11 +197,11 @@ func TestParse(t *testing.T) {
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
},
|
},
|
||||||
}}, // 0
|
}}, // 0
|
||||||
{"/admin/{id:int range(1,5)}", true,
|
{"/admin/{id:uint64 range(1,5)}", true,
|
||||||
[]ast.ParamStatement{{
|
[]ast.ParamStatement{{
|
||||||
Src: "{id:int range(1,5)}",
|
Src: "{id:uint64 range(1,5)}", // test alternative (backwards-compatibility) "int"
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: ast.ParamTypeInt,
|
Type: ast.ParamTypeUint64,
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
{
|
{
|
||||||
Name: "range",
|
Name: "range",
|
||||||
|
@ -233,10 +247,10 @@ func TestParse(t *testing.T) {
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
},
|
},
|
||||||
}}, // 5
|
}}, // 5
|
||||||
{"/p2/{myparam2}", false, // false because it will give an error of unexpeced token type with value 2
|
{"/p2/{myparam2}", true,
|
||||||
[]ast.ParamStatement{{
|
[]ast.ParamStatement{{
|
||||||
Src: "{myparam2}",
|
Src: "{myparam2}",
|
||||||
Name: "myparam", // expected "myparam" because we don't allow integers to the parameter names.
|
Name: "myparam2", // we now allow integers to the parameter names.
|
||||||
Type: ast.ParamTypeString,
|
Type: ast.ParamTypeString,
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,8 +13,8 @@ type Token struct {
|
||||||
|
|
||||||
// /about/{fullname:alphabetical}
|
// /about/{fullname:alphabetical}
|
||||||
// /profile/{anySpecialName:string}
|
// /profile/{anySpecialName:string}
|
||||||
// {id:int range(1,5) else 404}
|
// {id:uint64 range(1,5) else 404}
|
||||||
// /admin/{id:int eq(1) else 402}
|
// /admin/{id:number eq(1) else 402}
|
||||||
// /file/{filepath:file else 405}
|
// /file/{filepath:file else 405}
|
||||||
const (
|
const (
|
||||||
EOF = iota // 0
|
EOF = iota // 0
|
||||||
|
|
|
@ -214,14 +214,17 @@ type Map struct {
|
||||||
// string type
|
// string type
|
||||||
// anything
|
// anything
|
||||||
String *Macro
|
String *Macro
|
||||||
// uint type
|
|
||||||
// only positive numbers (+0-9)
|
// int type
|
||||||
// it could be uint/uint32 but we keep int for simplicity
|
// both positive and negative numbers, any number of digits.
|
||||||
Int *Macro
|
Number *Macro
|
||||||
// long an int64 type
|
// int64 as int64 type
|
||||||
// only positive numbers (+0-9)
|
// -9223372036854775808 to 9223372036854775807.
|
||||||
// it could be uint64 but we keep int64 for simplicity
|
Int64 *Macro
|
||||||
Long *Macro
|
// uint64 as uint64 type
|
||||||
|
// 0 to 18446744073709551615.
|
||||||
|
Uint64 *Macro
|
||||||
|
|
||||||
// boolean as bool type
|
// boolean as bool type
|
||||||
// a string which is "1" or "t" or "T" or "TRUE" or "true" or "True"
|
// a string which is "1" or "t" or "T" or "TRUE" or "true" or "True"
|
||||||
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
|
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
|
||||||
|
@ -247,11 +250,26 @@ type Map struct {
|
||||||
//
|
//
|
||||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
||||||
func NewMap() *Map {
|
func NewMap() *Map {
|
||||||
|
simpleNumberEvalutator := MustNewEvaluatorFromRegexp("^-?[0-9]+$")
|
||||||
return &Map{
|
return &Map{
|
||||||
// it allows everything, so no need for a regexp here.
|
// it allows everything, so no need for a regexp here.
|
||||||
String: newMacro(func(string) bool { return true }),
|
String: newMacro(func(string) bool { return true }),
|
||||||
Int: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")),
|
Number: newMacro(simpleNumberEvalutator), //"^(-?0\\.[0-9]*[1-9]+[0-9]*$)|(^-?[1-9]+[0-9]*((\\.[0-9]*[1-9]+[0-9]*$)|(\\.[0-9]+)))|(^-?[1-9]+[0-9]*$)|(^0$){1}")), //("^-?[0-9]+$")),
|
||||||
Long: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")),
|
Int64: newMacro(func(paramValue string) bool {
|
||||||
|
if !simpleNumberEvalutator(paramValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
// if err == strconv.ErrRange...
|
||||||
|
return err == nil
|
||||||
|
}), //("^-[1-9]|-?[1-9][0-9]{1,14}|-?1000000000000000|-?10000000000000000|-?100000000000000000|-?[1-9]000000000000000000|-?9[0-2]00000000000000000|-?92[0-2]0000000000000000|-?922[0-3]000000000000000|-?9223[0-3]00000000000000|-?92233[0-7]0000000000000|-?922337[0-2]000000000000|-?92233720[0-3]0000000000|-?922337203[0-6]000000000|-?9223372036[0-8]00000000|-?92233720368[0-5]0000000|-?922337203685[0-4]000000|-?9223372036854[0-7]00000|-?92233720368547[0-7]0000|-?922337203685477[0-5]000|-?922337203685477[56]000|[0-9]$")),
|
||||||
|
Uint64: newMacro(func(paramValue string) bool {
|
||||||
|
if !simpleNumberEvalutator(paramValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
return err == nil
|
||||||
|
}), //("^[0-9]|[1-9][0-9]{1,14}|1000000000000000|10000000000000000|100000000000000000|1000000000000000000|1[0-8]000000000000000000|18[0-4]00000000000000000|184[0-4]0000000000000000|1844[0-6]000000000000000|18446[0-7]00000000000000|184467[0-4]0000000000000|1844674[0-4]000000000000|184467440[0-7]0000000000|1844674407[0-3]000000000|18446744073[0-7]00000000|1844674407370000000[0-9]|18446744073709[0-5]00000|184467440737095[0-5]0000|1844674407370955[0-2]000$")),
|
||||||
Boolean: newMacro(func(paramValue string) bool {
|
Boolean: newMacro(func(paramValue string) bool {
|
||||||
// a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$
|
// a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$
|
||||||
// in this case.
|
// in this case.
|
||||||
|
@ -270,14 +288,16 @@ func NewMap() *Map {
|
||||||
|
|
||||||
// Lookup returns the specific Macro from the map
|
// Lookup returns the specific Macro from the map
|
||||||
// based on the parameter type.
|
// based on the parameter type.
|
||||||
// i.e if ast.ParamTypeInt then it will return the m.Int.
|
// i.e if ast.ParamTypeNumber then it will return the m.Number.
|
||||||
// Returns the m.String if not matched.
|
// Returns the m.String if not matched.
|
||||||
func (m *Map) Lookup(typ ast.ParamType) *Macro {
|
func (m *Map) Lookup(typ ast.ParamType) *Macro {
|
||||||
switch typ {
|
switch typ {
|
||||||
case ast.ParamTypeInt:
|
case ast.ParamTypeNumber:
|
||||||
return m.Int
|
return m.Number
|
||||||
case ast.ParamTypeLong:
|
case ast.ParamTypeInt64:
|
||||||
return m.Long
|
return m.Int64
|
||||||
|
case ast.ParamTypeUint64:
|
||||||
|
return m.Uint64
|
||||||
case ast.ParamTypeBoolean:
|
case ast.ParamTypeBoolean:
|
||||||
return m.Boolean
|
return m.Boolean
|
||||||
case ast.ParamTypeAlphabetical:
|
case ast.ParamTypeAlphabetical:
|
||||||
|
|
|
@ -64,9 +64,9 @@ func TestGoodParamFuncName(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEvaluatorRaw(macroEvaluator *Macro, input string, pass bool, i int, t *testing.T) {
|
func testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, pass bool, i int) {
|
||||||
if got := macroEvaluator.Evaluator(input); pass != got {
|
if got := macroEvaluator.Evaluator(input); pass != got {
|
||||||
t.Fatalf("tests[%d] - expecting %v but got %v", i, pass, got)
|
t.Fatalf("%s - tests[%d] - expecting %v but got %v", t.Name(), i, pass, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,26 +86,86 @@ func TestStringEvaluatorRaw(t *testing.T) {
|
||||||
} // 0
|
} // 0
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(f.String, tt.input, tt.pass, i, t)
|
testEvaluatorRaw(t, f.String, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntEvaluatorRaw(t *testing.T) {
|
func TestNumberEvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
f := NewMap()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
}{
|
}{
|
||||||
{false, "astring"}, // 0
|
{false, "astring"}, // 0
|
||||||
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||||
{true, "32321"}, // 2
|
{true, "32321"}, // 2
|
||||||
{false, "main.css"}, // 3
|
{true, "18446744073709551615"}, // 3
|
||||||
{false, "/assets/main.css"}, // 4
|
{true, "-18446744073709551615"}, // 4
|
||||||
|
{true, "-18446744073709553213213213213213121615"}, // 5
|
||||||
|
{false, "42 18446744073709551615"}, // 6
|
||||||
|
{false, "--42"}, // 7
|
||||||
|
{false, "+42"}, // 9
|
||||||
|
{false, "main.css"}, // 9
|
||||||
|
{false, "/assets/main.css"}, // 10
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(f.Int, tt.input, tt.pass, i, t)
|
testEvaluatorRaw(t, f.Number, tt.input, tt.pass, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInt64EvaluatorRaw(t *testing.T) {
|
||||||
|
f := NewMap()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
pass bool
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{false, "astring"}, // 0
|
||||||
|
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||||
|
{false, "18446744073709551615"}, // 2
|
||||||
|
{false, "92233720368547758079223372036854775807"}, // 3
|
||||||
|
{false, "9223372036854775808 9223372036854775808"}, // 4
|
||||||
|
{false, "main.css"}, // 5
|
||||||
|
{false, "/assets/main.css"}, // 6
|
||||||
|
{true, "9223372036854775807"}, // 7
|
||||||
|
{true, "-9223372036854775808"}, // 8
|
||||||
|
{true, "-0"}, // 9
|
||||||
|
{true, "1"}, // 10
|
||||||
|
{true, "-042"}, // 11
|
||||||
|
{true, "142"}, // 12
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
testEvaluatorRaw(t, f.Int64, tt.input, tt.pass, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUint64EvaluatorRaw(t *testing.T) {
|
||||||
|
f := NewMap()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
pass bool
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{false, "astring"}, // 0
|
||||||
|
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||||
|
{false, "-9223372036854775808"}, // 2
|
||||||
|
{false, "main.css"}, // 3
|
||||||
|
{false, "/assets/main.css"}, // 4
|
||||||
|
{false, "92233720368547758079223372036854775807"}, // 5
|
||||||
|
{false, "9223372036854775808 9223372036854775808"}, // 6
|
||||||
|
{false, "-1"}, // 7
|
||||||
|
{false, "-0"}, // 8
|
||||||
|
{false, "+1"}, // 9
|
||||||
|
{true, "18446744073709551615"}, // 10
|
||||||
|
{true, "9223372036854775807"}, // 11
|
||||||
|
{true, "0"}, // 12
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
testEvaluatorRaw(t, f.Uint64, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +184,7 @@ func TestAlphabeticalEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(f.Alphabetical, tt.input, tt.pass, i, t)
|
testEvaluatorRaw(t, f.Alphabetical, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +203,7 @@ func TestFileEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(f.File, tt.input, tt.pass, i, t)
|
testEvaluatorRaw(t, f.File, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +223,7 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range pathTests {
|
for i, tt := range pathTests {
|
||||||
testEvaluatorRaw(f.Path, tt.input, tt.pass, i, t)
|
testEvaluatorRaw(t, f.Path, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,5 +242,5 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||||
|
|
||||||
// // p.Params = append(p.)
|
// // p.Params = append(p.)
|
||||||
|
|
||||||
// testEvaluatorRaw(m.String, p.Src, false, 0, t)
|
// testEvaluatorRaw(t, m.String, p.Src, false, 0)
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -110,10 +110,10 @@ type Party interface {
|
||||||
// otherwise use `Party` which can handle many paths with different handlers and middlewares.
|
// otherwise use `Party` which can handle many paths with different handlers and middlewares.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// app.HandleMany(iris.MethodGet, "/user /user/{id:int} /user/me", userHandler)
|
// app.HandleMany(iris.MethodGet, "/user /user/{id:uint64} /user/me", userHandler)
|
||||||
// At the other side, with `Handle` we've had to write:
|
// At the other side, with `Handle` we've had to write:
|
||||||
// app.Handle(iris.MethodGet, "/user", userHandler)
|
// app.Handle(iris.MethodGet, "/user", userHandler)
|
||||||
// app.Handle(iris.MethodGet, "/user/{id:int}", userHandler)
|
// app.Handle(iris.MethodGet, "/user/{id:uint64}", userHandler)
|
||||||
// app.Handle(iris.MethodGet, "/user/me", userHandler)
|
// app.Handle(iris.MethodGet, "/user/me", userHandler)
|
||||||
//
|
//
|
||||||
// This method is used behind the scenes at the `Controller` function
|
// This method is used behind the scenes at the `Controller` function
|
||||||
|
|
|
@ -27,8 +27,8 @@ func TestCleanPath(t *testing.T) {
|
||||||
"/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}"},
|
"/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}"},
|
||||||
{"/single_no_params",
|
{"/single_no_params",
|
||||||
"/single_no_params"},
|
"/single_no_params"},
|
||||||
{"/single/{id:int}",
|
{"/single/{id:uint64}",
|
||||||
"/single/{id:int}"},
|
"/single/{id:uint64}"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
@ -45,14 +45,16 @@ func TestSplitPath(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{"/v2/stores/{id:string format(uuid)} /v3",
|
{"/v2/stores/{id:string format(uuid)} /v3",
|
||||||
[]string{"/v2/stores/{id:string format(uuid)}", "/v3"}},
|
[]string{"/v2/stores/{id:string format(uuid)}", "/v3"}},
|
||||||
{"/user/{id:int} /admin/{id:int}",
|
{"/user/{id:uint64} /admin/{id:uint64}",
|
||||||
[]string{"/user/{id:int}", "/admin/{id:int}"}},
|
[]string{"/user/{id:uint64}", "/admin/{id:uint64}"}},
|
||||||
|
{"/users/{id:int} /admins/{id:int64}",
|
||||||
|
[]string{"/users/{id:int}", "/admins/{id:int64}"}},
|
||||||
{"/user /admin",
|
{"/user /admin",
|
||||||
[]string{"/user", "/admin"}},
|
[]string{"/user", "/admin"}},
|
||||||
{"/single_no_params",
|
{"/single_no_params",
|
||||||
[]string{"/single_no_params"}},
|
[]string{"/single_no_params"}},
|
||||||
{"/single/{id:int}",
|
{"/single/{id:number}",
|
||||||
[]string{"/single/{id:int}"}},
|
[]string{"/single/{id:number}"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
equalSlice := func(s1 []string, s2 []string) bool {
|
equalSlice := func(s1 []string, s2 []string) bool {
|
||||||
|
|
|
@ -16,7 +16,7 @@ type Route struct {
|
||||||
Method string `json:"method"` // "GET"
|
Method string `json:"method"` // "GET"
|
||||||
methodBckp string // if Method changed to something else (which is possible at runtime as well, via RefreshRouter) then this field will be filled with the old one.
|
methodBckp string // if Method changed to something else (which is possible at runtime as well, via RefreshRouter) then this field will be filled with the old one.
|
||||||
Subdomain string `json:"subdomain"` // "admin."
|
Subdomain string `json:"subdomain"` // "admin."
|
||||||
tmpl *macro.Template // Tmpl().Src: "/api/user/{id:int}"
|
tmpl *macro.Template // Tmpl().Src: "/api/user/{id:uint64}"
|
||||||
// temp storage, they're appended to the Handlers on build.
|
// temp storage, they're appended to the Handlers on build.
|
||||||
// Execution happens before Handlers, can be empty.
|
// Execution happens before Handlers, can be empty.
|
||||||
beginHandlers context.Handlers
|
beginHandlers context.Handlers
|
||||||
|
@ -198,7 +198,7 @@ func formatPath(path string) string {
|
||||||
|
|
||||||
// StaticPath returns the static part of the original, registered route path.
|
// StaticPath returns the static part of the original, registered route path.
|
||||||
// if /user/{id} it will return /user
|
// if /user/{id} it will return /user
|
||||||
// if /user/{id}/friend/{friendid:int} it will return /user too
|
// if /user/{id}/friend/{friendid:uint64} it will return /user too
|
||||||
// if /assets/{filepath:path} it will return /assets.
|
// if /assets/{filepath:path} it will return /assets.
|
||||||
func (r Route) StaticPath() string {
|
func (r Route) StaticPath() string {
|
||||||
src := r.tmpl.Src
|
src := r.tmpl.Src
|
||||||
|
|
69
doc.go
69
doc.go
|
@ -119,7 +119,7 @@ Example code:
|
||||||
usersRoutes := app.Party("/users", logThisMiddleware)
|
usersRoutes := app.Party("/users", logThisMiddleware)
|
||||||
{
|
{
|
||||||
// Method GET: http://localhost:8080/users/42
|
// Method GET: http://localhost:8080/users/42
|
||||||
usersRoutes.Get("/{id:int min(1)}", getUserByID)
|
usersRoutes.Get("/{id:uint64 min(1)}", getUserByID)
|
||||||
// Method POST: http://localhost:8080/users/create
|
// Method POST: http://localhost:8080/users/create
|
||||||
usersRoutes.Post("/create", createUser)
|
usersRoutes.Post("/create", createUser)
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ Example code:
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserByID(ctx iris.Context) {
|
func getUserByID(ctx iris.Context) {
|
||||||
userID := ctx.Params().Get("id") // Or convert directly using: .Values().GetInt/GetInt64 etc...
|
userID := ctx.Params().Get("id") // Or convert directly using: .Values().GetInt/GetUint64/GetInt64 etc...
|
||||||
// your own db fetch here instead of user :=...
|
// your own db fetch here instead of user :=...
|
||||||
user := User{Username: "username" + userID}
|
user := User{Username: "username" + userID}
|
||||||
|
|
||||||
|
@ -489,9 +489,9 @@ Example code:
|
||||||
users := app.Party("/users", myAuthMiddlewareHandler)
|
users := app.Party("/users", myAuthMiddlewareHandler)
|
||||||
|
|
||||||
// http://myhost.com/users/42/profile
|
// http://myhost.com/users/42/profile
|
||||||
users.Get("/{id:int}/profile", userProfileHandler)
|
users.Get("/{id:uint64}/profile", userProfileHandler)
|
||||||
// http://myhost.com/users/messages/1
|
// http://myhost.com/users/messages/1
|
||||||
users.Get("/inbox/{id:int}", userMessageHandler)
|
users.Get("/inbox/{id:number}", userMessageHandler)
|
||||||
|
|
||||||
|
|
||||||
Custom HTTP Errors
|
Custom HTTP Errors
|
||||||
|
@ -548,12 +548,12 @@ Example code:
|
||||||
app.Get("/donate", donateHandler, donateFinishHandler)
|
app.Get("/donate", donateHandler, donateFinishHandler)
|
||||||
|
|
||||||
// Pssst, don't forget dynamic-path example for more "magic"!
|
// Pssst, don't forget dynamic-path example for more "magic"!
|
||||||
app.Get("/api/users/{userid:int min(1)}", func(ctx iris.Context) {
|
app.Get("/api/users/{userid:uint64 min(1)}", func(ctx iris.Context) {
|
||||||
userID, err := ctx.Params().GetInt("userid")
|
userID, err := ctx.Params().GetUint64("userid")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Writef("error while trying to parse userid parameter," +
|
ctx.Writef("error while trying to parse userid parameter," +
|
||||||
"this will never happen if :int is being used because if it's not integer it will fire Not Found automatically.")
|
"this will never happen if :number is being used because if it's not integer it will fire Not Found automatically.")
|
||||||
ctx.StatusCode(iris.StatusBadRequest)
|
ctx.StatusCode(iris.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -622,8 +622,8 @@ Example code:
|
||||||
ctx.Writef("All users")
|
ctx.Writef("All users")
|
||||||
})
|
})
|
||||||
// http://v1.localhost:8080/api/users/42
|
// http://v1.localhost:8080/api/users/42
|
||||||
usersAPI.Get("/{userid:int}", func(ctx iris.Context) {
|
usersAPI.Get("/{userid:uint64}", func(ctx iris.Context) {
|
||||||
ctx.Writef("user with id: %s", ctx.Params().Get("userid"))
|
ctx.Writef("user with id: %s", ctx.Params().GetUint64("userid"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -711,21 +711,27 @@ Standard macro types for parameters:
|
||||||
string type
|
string type
|
||||||
anything
|
anything
|
||||||
|
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
| {param:int} |
|
| {param:number} or {param:int} |
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
int type
|
int type
|
||||||
only numbers (0-9)
|
both positive and negative numbers, any number of digits (ctx.Params().GetInt will limit the digits based on the host arch)
|
||||||
|
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
| {param:long} |
|
| {param:long} or {param:int64} |
|
||||||
+------------------------+
|
+-------------------------------+
|
||||||
int64 type
|
int64 type
|
||||||
only numbers (0-9)
|
-9223372036854775808 to 9223372036854775807
|
||||||
|
|
||||||
+------------------------+
|
+------------------------+
|
||||||
| {param:boolean} |
|
| {param:uint64} |
|
||||||
+------------------------+
|
+------------------------+
|
||||||
|
uint64 type
|
||||||
|
0 to 18446744073709551615
|
||||||
|
|
||||||
|
+---------------------------------+
|
||||||
|
| {param:bool} or {param:boolean} |
|
||||||
|
+---------------------------------+
|
||||||
bool type
|
bool type
|
||||||
only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
||||||
or "0" or "f" or "F" or "FALSE" or "false" or "False"
|
or "0" or "f" or "F" or "FALSE" or "false" or "False"
|
||||||
|
@ -761,7 +767,7 @@ If a function not found on that type then the "string"'s types functions are bei
|
||||||
i.e:
|
i.e:
|
||||||
|
|
||||||
|
|
||||||
{param:int min(3)}
|
{param:number min(3)}
|
||||||
|
|
||||||
|
|
||||||
Besides the fact that iris provides the basic types and some default "macro funcs"
|
Besides the fact that iris provides the basic types and some default "macro funcs"
|
||||||
|
@ -770,7 +776,7 @@ you are able to register your own too!.
|
||||||
Register a named path parameter function:
|
Register a named path parameter function:
|
||||||
|
|
||||||
|
|
||||||
app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool {
|
app.Macros().Number.RegisterFunc("min", func(argument int) func(paramValue string) bool {
|
||||||
[...]
|
[...]
|
||||||
return true/false -> true means valid.
|
return true/false -> true means valid.
|
||||||
})
|
})
|
||||||
|
@ -792,12 +798,12 @@ Example Code:
|
||||||
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
ctx.Writef("Hello %s", ctx.Params().Get("name"))
|
||||||
}) // type is missing = {name:string}
|
}) // type is missing = {name:string}
|
||||||
|
|
||||||
// Let's register our first macro attached to int macro type.
|
// Let's register our first macro attached to number macro type.
|
||||||
// "min" = the function
|
// "min" = the function
|
||||||
// "minValue" = the argument of the function
|
// "minValue" = the argument of the function
|
||||||
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
||||||
// a user requests a path which contains the :int macro type with the min(...) macro parameter function.
|
// a user requests a path which contains the number macro type with the min(...) macro parameter function.
|
||||||
app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
|
app.Macros().Number.RegisterFunc("min", func(minValue int) func(string) bool {
|
||||||
// do anything before serve here [...]
|
// do anything before serve here [...]
|
||||||
// at this case we don't need to do anything
|
// at this case we don't need to do anything
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
|
@ -812,21 +818,21 @@ Example Code:
|
||||||
// http://localhost:8080/profile/id>=1
|
// http://localhost:8080/profile/id>=1
|
||||||
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
|
||||||
// macro parameter functions are optional of course.
|
// macro parameter functions are optional of course.
|
||||||
app.Get("/profile/{id:int min(1)}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(1)}", func(ctx iris.Context) {
|
||||||
// second parameter is the error but it will always nil because we use macros,
|
// second parameter is the error but it will always nil because we use macros,
|
||||||
// the validaton already happened.
|
// the validaton already happened.
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetUint64("id")
|
||||||
ctx.Writef("Hello id: %d", id)
|
ctx.Writef("Hello id: %d", id)
|
||||||
})
|
})
|
||||||
|
|
||||||
// to change the error code per route's macro evaluator:
|
// to change the error code per route's macro evaluator:
|
||||||
app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id, _ := ctx.Params().GetUint64("id")
|
||||||
friendid, _ := ctx.Params().GetInt("friendid")
|
friendid, _ := ctx.Params().GetUint64("friendid")
|
||||||
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
||||||
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
||||||
|
|
||||||
// http://localhost:8080/game/a-zA-Z/level/0-9
|
// http://localhost:8080/game/a-zA-Z/level/42
|
||||||
// remember, alphabetical is lowercase or uppercase letters only.
|
// remember, alphabetical is lowercase or uppercase letters only.
|
||||||
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
|
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
|
||||||
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
|
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
|
||||||
|
@ -855,11 +861,6 @@ Example Code:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed.
|
|
||||||
If route failed to be registered, the app will panic without any warnings
|
|
||||||
if you didn't catch the second return value(error) on .Handle/.Get....
|
|
||||||
|
|
||||||
Last, do not confuse ctx.Values() with ctx.Params().
|
Last, do not confuse ctx.Values() with ctx.Params().
|
||||||
Path parameter's values goes to ctx.Params() and context's local storage
|
Path parameter's values goes to ctx.Params() and context's local storage
|
||||||
that can be used to communicate between handlers and middleware(s) goes to
|
that can be used to communicate between handlers and middleware(s) goes to
|
||||||
|
|
|
@ -365,7 +365,8 @@ func (c *testControllerRelPathFromFunc) EndRequest(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testControllerRelPathFromFunc) Get() {}
|
func (c *testControllerRelPathFromFunc) Get() {}
|
||||||
func (c *testControllerRelPathFromFunc) GetBy(int64) {}
|
func (c *testControllerRelPathFromFunc) GetBy(uint64) {}
|
||||||
|
func (c *testControllerRelPathFromFunc) GetRatioBy(int64) {}
|
||||||
func (c *testControllerRelPathFromFunc) GetAnythingByWildcard(string) {}
|
func (c *testControllerRelPathFromFunc) GetAnythingByWildcard(string) {}
|
||||||
|
|
||||||
func (c *testControllerRelPathFromFunc) GetLogin() {}
|
func (c *testControllerRelPathFromFunc) GetLogin() {}
|
||||||
|
@ -388,8 +389,10 @@ func TestControllerRelPathFromFunc(t *testing.T) {
|
||||||
e.GET("/").Expect().Status(iris.StatusOK).
|
e.GET("/").Expect().Status(iris.StatusOK).
|
||||||
Body().Equal("GET:/")
|
Body().Equal("GET:/")
|
||||||
|
|
||||||
e.GET("/42").Expect().Status(iris.StatusOK).
|
e.GET("/18446744073709551615").Expect().Status(iris.StatusOK).
|
||||||
Body().Equal("GET:/42")
|
Body().Equal("GET:/18446744073709551615")
|
||||||
|
e.GET("/ratio/-42").Expect().Status(iris.StatusOK).
|
||||||
|
Body().Equal("GET:/ratio/-42")
|
||||||
e.GET("/something/true").Expect().Status(iris.StatusOK).
|
e.GET("/something/true").Expect().Status(iris.StatusOK).
|
||||||
Body().Equal("GET:/something/true")
|
Body().Equal("GET:/something/true")
|
||||||
e.GET("/something/false").Expect().Status(iris.StatusOK).
|
e.GET("/something/false").Expect().Status(iris.StatusOK).
|
||||||
|
|
|
@ -37,16 +37,21 @@ func makeFuncParamGetter(paramType ast.ParamType, paramName string) reflect.Valu
|
||||||
var fn interface{}
|
var fn interface{}
|
||||||
|
|
||||||
switch paramType {
|
switch paramType {
|
||||||
case ast.ParamTypeInt:
|
case ast.ParamTypeNumber:
|
||||||
fn = func(ctx context.Context) int {
|
fn = func(ctx context.Context) int {
|
||||||
v, _ := ctx.Params().GetInt(paramName)
|
v, _ := ctx.Params().GetInt(paramName)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
case ast.ParamTypeLong:
|
case ast.ParamTypeInt64:
|
||||||
fn = func(ctx context.Context) int64 {
|
fn = func(ctx context.Context) int64 {
|
||||||
v, _ := ctx.Params().GetInt64(paramName)
|
v, _ := ctx.Params().GetInt64(paramName)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
case ast.ParamTypeUint64:
|
||||||
|
fn = func(ctx context.Context) uint64 {
|
||||||
|
v, _ := ctx.Params().GetUint64(paramName)
|
||||||
|
return v
|
||||||
|
}
|
||||||
case ast.ParamTypeBoolean:
|
case ast.ParamTypeBoolean:
|
||||||
fn = func(ctx context.Context) bool {
|
fn = func(ctx context.Context) bool {
|
||||||
v, _ := ctx.Params().GetBool(paramName)
|
v, _ := ctx.Params().GetBool(paramName)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user