1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-21 11:57:02 +00: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:
Gerasimos (Makis) Maropoulos
2018-08-23 06:30:12 +03:00
parent 01b5f6089d
commit b019a281eb
28 changed files with 478 additions and 242 deletions

View File

@@ -110,9 +110,9 @@ app := iris.New()
users := app.Party("/users", myAuthMiddlewareHandler)
// http://localhost:8080/users/42/profile
users.Get("/{id:int}/profile", userProfileHandler)
users.Get("/{id:uint64}/profile", userProfileHandler)
// 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).
@@ -124,13 +124,13 @@ app.PartyFunc("/users", func(users iris.Party) {
users.Use(myAuthMiddlewareHandler)
// http://localhost:8080/users/42/profile
users.Get("/{id:int}/profile", userProfileHandler)
users.Get("/{id:uint64}/profile", userProfileHandler)
// 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
@@ -154,21 +154,27 @@ Standard macro types for route path parameters
string type
anything
+------------------------+
| {param:int} |
+------------------------+
+-------------------------------+
| {param:number} or {param:int} |
+-------------------------------+
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
only numbers (0-9)
-9223372036854775808 to 9223372036854775807
+------------------------+
| {param:boolean} |
| {param:uint64} |
+------------------------+
uint64 type
0 to 18446744073709551615
+---------------------------------+
| {param:bool} or {param:boolean} |
+---------------------------------+
bool type
only "1" or "t" or "T" or "TRUE" or "true" or "True"
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
```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
// -> 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`.
```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 {
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"))
}) // 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
// "minValue" = the argument of the function
// 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.
app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
// a user requests a path which contains the :number macro type with the min(...) macro parameter function.
app.Macros().Number.RegisterFunc("min", func(minValue int) func(string) bool {
// do anything before serve here [...]
// at this case we don't need to do anything
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
// 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.
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,
// the validaton already happened.
id, _ := ctx.Params().GetInt("id")
id, _ := ctx.Params().GetUint64("id")
ctx.Writef("Hello id: %d", id)
})
// 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) {
id, _ := ctx.Params().GetInt("id")
friendid, _ := ctx.Params().GetInt("friendid")
app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) {
id, _ := ctx.Params().GetUint64("id")
friendid, _ := ctx.Params().GetUint64("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.
// 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.
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"))
@@ -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()`.
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
`ctx.Values()`.
Path parameter's values can be retrieved from `ctx.Params()`,
context's local storage that can be used to communicate between handlers and middleware(s) can be stored to `ctx.Values()`.
# Routing and reverse lookups

View File

@@ -29,12 +29,12 @@ func main() {
app.Get("/donate", donateHandler, donateFinishHandler)
// Pssst, don't forget dynamic-path example for more "magic"!
app.Get("/api/users/{userid:int min(1)}", func(ctx iris.Context) {
userID, err := ctx.Params().GetInt("userid")
app.Get("/api/users/{userid:uint64 min(1)}", func(ctx iris.Context) {
userID, err := ctx.Params().GetUint64("userid")
if err != nil {
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)
return
}
@@ -103,7 +103,7 @@ func main() {
ctx.Writef("All users")
})
// 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"))
})
}

View File

@@ -14,15 +14,14 @@ func main() {
// 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.
// 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)
// 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,
// 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,
// We call them "macros" for shortcut.
// How? It calculates its needs and if not any special regexp needed then it just
@@ -36,21 +35,27 @@ func main() {
// string type
// anything
//
// +------------------------+
// | {param:int} |
// +------------------------+
// +-------------------------------+
// | {param:int} or {param:number} |
// +-------------------------------+
// 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
// only numbers (0-9)
// -9223372036854775808 to 9223372036854775807
//
// +------------------------+
// | {param:boolean} |
// | {param:uint64} |
// +------------------------+
// uint64 type
// 0 to 18446744073709551615
//
// +---------------------------------+
// | {param:bool} or {param:boolean} |
// +---------------------------------+
// bool type
// only "1" or "t" or "T" or "TRUE" or "true" or "True"
// 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!.
//
// 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.
// })
@@ -107,12 +112,12 @@ func main() {
ctx.Writef("Hello %s", ctx.Params().Get("name"))
}) // 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
// "minValue" = the argument of the function
// 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.
app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
// a user requests a path which contains the :number macro type with the min(...) macro parameter function.
app.Macros().Number.RegisterFunc("min", func(minValue int) func(string) bool {
// do anything before serve here [...]
// at this case we don't need to do anything
return func(paramValue string) bool {
@@ -127,7 +132,7 @@ func main() {
// http://localhost:8080/profile/id>=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.
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,
// the validaton already happened.
id, _ := ctx.Params().GetInt("id")
@@ -135,8 +140,8 @@ func main() {
})
// 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) {
id, _ := ctx.Params().GetInt("id")
app.Get("/profile/{id:number min(1)}/friends/{friendid:number min(1) else 504}", func(ctx iris.Context) {
id, _ := ctx.Params().GetInt("id") // or GetUint64.
friendid, _ := ctx.Params().GetInt("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.
@@ -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.
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"))
})
@@ -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".
// 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()`.
// 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
// `ctx.Values()`.
// Path parameter's values can be retrieved from `ctx.Params()`,
// context's local storage that can be used to communicate between handlers and middleware(s) can be stored to `ctx.Values()`.
app.Run(iris.Addr(":8080"))
}

View File

@@ -35,25 +35,25 @@ func registerGamesRoutes(app *iris.Application) {
{ // braces are optional of course, it's just a style of code
// "GET" method
games.Get("/{gameID:int}/clans", h)
games.Get("/{gameID:int}/clans/clan/{clanPublicID:int}", h)
games.Get("/{gameID:int}/clans/search", h)
games.Get("/{gameID:uint64}/clans", h)
games.Get("/{gameID:uint64}/clans/clan/{clanPublicID:uint64}", h)
games.Get("/{gameID:uint64}/clans/search", h)
// "PUT" method
games.Put("/{gameID:int}/players/{clanPublicID:int}", h)
games.Put("/{gameID:int}/clans/clan/{clanPublicID:int}", h)
games.Put("/{gameID:uint64}/players/{clanPublicID:uint64}", h)
games.Put("/{gameID:uint64}/clans/clan/{clanPublicID:uint64}", h)
// remember: "clanPublicID" should not be changed to other routes with the same prefix.
// "POST" method
games.Post("/{gameID:int}/clans", h)
games.Post("/{gameID:int}/players", h)
games.Post("/{gameID:int}/clans/{clanPublicID:int}/leave", h)
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application", h)
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application/{action}", h) // {action} == {action:string}
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation", h)
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation/{action}", h)
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/delete", h)
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/promote", h)
games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/demote", h)
games.Post("/{gameID:uint64}/clans", h)
games.Post("/{gameID:uint64}/players", h)
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/leave", h)
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application", h)
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/application/{action}", h) // {action} == {action:string}
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation", h)
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/invitation/{action}", h)
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/delete", h)
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/promote", h)
games.Post("/{gameID:uint64}/clans/{clanPublicID:uint64}/memberships/demote", h)
gamesCh := games.Party("/challenge")
{

View File

@@ -68,8 +68,8 @@ func main() {
// GET: http://localhost:8080/users/42
// **/users/42 and /users/help works after iris version 7.0.5**
usersRoutes.Get("/{id:int}", func(ctx iris.Context) {
id, _ := ctx.Params().GetInt("id")
usersRoutes.Get("/{id:uint64}", func(ctx iris.Context) {
id, _ := ctx.Params().GetUint64("id")
ctx.Writef("get user by id: %d", id)
})
@@ -80,15 +80,15 @@ func main() {
})
// PUT: http://localhost:8080/users
usersRoutes.Put("/{id:int}", func(ctx iris.Context) {
id, _ := ctx.Params().GetInt("id") // or .Get to get its string represatantion.
usersRoutes.Put("/{id:uint64}", func(ctx iris.Context) {
id, _ := ctx.Params().GetUint64("id") // or .Get to get its string represatantion.
username := ctx.PostValue("username")
ctx.Writef("update user for id= %d and new username= %s", id, username)
})
// DELETE: http://localhost:8080/users/42
usersRoutes.Delete("/{id:int}", func(ctx iris.Context) {
id, _ := ctx.Params().GetInt("id")
usersRoutes.Delete("/{id:uint64}", func(ctx iris.Context) {
id, _ := ctx.Params().GetUint64("id")
ctx.Writef("delete user by id: %d", id)
})