1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-06 19:47:05 +00:00
Former-commit-id: fe6305deed00e170bf4d39a12c0644fe686e0a24
This commit is contained in:
Gerasimos (Makis) Maropoulos
2018-10-21 19:20:05 +03:00
parent dbba631df4
commit 3962710d3d
109 changed files with 4383 additions and 2658 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
@@ -152,23 +152,71 @@ Standard macro types for route path parameters
| {param:string} |
+------------------------+
string type
anything
anything (single path segmnent)
+------------------------+
| {param:int} |
+------------------------+
+-------------------------------+
| {param:int} |
+-------------------------------+
int type
only numbers (0-9)
-9223372036854775808 to 9223372036854775807 (x64) or -2147483648 to 2147483647 (x32), depends on the host arch
+------------------------+
| {param:long} |
| {param:int8} |
+------------------------+
int8 type
-128 to 127
+------------------------+
| {param:int16} |
+------------------------+
int16 type
-32768 to 32767
+------------------------+
| {param:int32} |
+------------------------+
int32 type
-2147483648 to 2147483647
+------------------------+
| {param:int64} |
+------------------------+
int64 type
only numbers (0-9)
-9223372036854775808 to 9223372036854775807
+------------------------+
| {param:boolean} |
| {param:uint} |
+------------------------+
uint type
0 to 18446744073709551615 (x64) or 0 to 4294967295 (x32)
+------------------------+
| {param:uint8} |
+------------------------+
uint8 type
0 to 255
+------------------------+
| {param:uint16} |
+------------------------+
uint16 type
0 to 65535
+------------------------+
| {param:uint32} |
+------------------------+
uint32 type
0 to 4294967295
+------------------------+
| {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"
@@ -194,8 +242,8 @@ no spaces ! or other character
| {param:path} |
+------------------------+
path type
anything, should be the last part, more than one path segment,
i.e: /path1/path2/path3 , ctx.Params().Get("param") == "/path1/path2/path3"
anything, should be the last part, can be more than one path segment,
i.e: "/test/*param" and request: "/test/path1/path2/path3" , ctx.Params().Get("param") == "path1/path2/path3"
```
If type is missing then parameter's type is defaulted to string, so
@@ -209,18 +257,24 @@ 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().Get("int").RegisterFunc("min", func(argument int) func(paramValue int) 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.
return func(paramValue int) bool {
// -> true means valid, false means invalid fire 404
// or if "else 500" is appended to the macro syntax then internal server error.
return true
}
})
```
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 <T>) bool`.
```go
{param:string equal(iris)} , "iris" will be the argument here:
app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
{param:string equal(iris)}
```
The "iris" will be the argument here:
```go
app.Macros().Get("string").RegisterFunc("equal", func(argument string) func(paramValue string) bool {
return func(paramValue string){ return argument == paramValue }
})
```
@@ -237,38 +291,34 @@ app.Get("/username/{name}", func(ctx iris.Context) {
// Let's register our first macro attached to int 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
// func(<T>) 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 {
app.Macros().Get("int").RegisterFunc("min", func(minValue int) func(int) bool {
// do anything before serve here [...]
// at this case we don't need to do anything
return func(paramValue string) bool {
n, err := strconv.Atoi(paramValue)
if err != nil {
return false
}
return n >= minValue
return func(paramValue int) bool {
return paramValue >= minValue
}
})
// 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 +347,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
@@ -776,6 +824,32 @@ type Context interface {
// IsStopped checks and returns true if the current position of the Context is 255,
// means that the StopExecution() was called.
IsStopped() bool
// OnConnectionClose registers the "cb" function which will fire (on its own goroutine, no need to be registered goroutine by the end-dev)
// when the underlying connection has gone away.
//
// This mechanism can be used to cancel long operations on the server
// if the client has disconnected before the response is ready.
//
// It depends on the `http#CloseNotify`.
// CloseNotify may wait to notify until Request.Body has been
// fully read.
//
// After the main Handler has returned, there is no guarantee
// that the channel receives a value.
//
// Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported).
// The "cb" will not fire for sure if the output value is false.
//
// Note that you can register only one callback for the entire request handler chain/per route.
//
// Look the `ResponseWriter#CloseNotifier` for more.
OnConnectionClose(fnGoroutine func()) bool
// OnClose registers the callback function "cb" to the underline connection closing event using the `Context#OnConnectionClose`
// and also in the end of the request handler using the `ResponseWriter#SetBeforeFlush`.
// Note that you can register only one callback for the entire request handler chain/per route.
//
// Look the `Context#OnConnectionClose` and `ResponseWriter#SetBeforeFlush` for more.
OnClose(cb func())
// +------------------------------------------------------------+
// | Current "user/request" storage |
@@ -855,8 +929,12 @@ type Context interface {
//
// Keep note that this checks the "User-Agent" request header.
IsMobile() bool
// GetReferrer extracts and returns the information from the "Referer" header as specified
// in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
// or by the URL query parameter "referer".
GetReferrer() Referrer
// +------------------------------------------------------------+
// | Response Headers helpers |
// | Headers helpers |
// +------------------------------------------------------------+
// Header adds a header to the response writer.
@@ -867,16 +945,18 @@ type Context interface {
// GetContentType returns the response writer's header value of "Content-Type"
// which may, setted before with the 'ContentType'.
GetContentType() string
// GetContentType returns the request's header value of "Content-Type".
GetContentTypeRequested() string
// GetContentLength returns the request's header value of "Content-Length".
// Returns 0 if header was unable to be found or its value was not a valid number.
GetContentLength() int64
// StatusCode sets the status code header to the response.
// Look .GetStatusCode too.
// Look .`GetStatusCode` too.
StatusCode(statusCode int)
// GetStatusCode returns the current status code of the response.
// Look StatusCode too.
// Look `StatusCode` too.
GetStatusCode() int
// Redirect sends a redirect response to the client
@@ -911,6 +991,9 @@ type Context interface {
// URLParamIntDefault returns the url query parameter as int value from a request,
// if not found or parse failed then "def" is returned.
URLParamIntDefault(name string, def int) int
// URLParamInt32Default returns the url query parameter as int32 value from a request,
// if not found or parse failed then "def" is returned.
URLParamInt32Default(name string, def int32) int32
// URLParamInt64 returns the url query parameter as int64 value from a request,
// returns -1 and an error if parse failed.
URLParamInt64(name string) (int64, error)
@@ -1058,6 +1141,10 @@ type Context interface {
// Examples of usage: context.ReadJSON, context.ReadXML.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-custom-via-unmarshaler/main.go
//
// UnmarshalBody does not check about gzipped data.
// Do not rely on compressed data incoming to your server. The main reason is: https://en.wikipedia.org/wiki/Zip_bomb
// However you are still free to read the `ctx.Request().Body io.Reader` manually.
UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error
// ReadJSON reads JSON from request's body and binds it to a pointer of a value of any json-valid type.
//
@@ -1068,7 +1155,8 @@ type Context interface {
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-xml/main.go
ReadXML(xmlObjectPtr interface{}) error
// ReadForm binds the formObject with the form data
// it supports any kind of struct.
// it supports any kind of type, including custom structs.
// It will return nothing if request data are empty.
//
// Example: https://github.com/kataras/iris/blob/master/_examples/http_request/read-form/main.go
ReadForm(formObjectPtr interface{}) error

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
}

View File

@@ -0,0 +1,103 @@
package main
import (
"strings"
"github.com/kataras/iris"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router"
)
/* A Router should contain all three of the following methods:
- HandleRequest should handle the request based on the Context.
HandleRequest(ctx context.Context)
- Build should builds the handler, it's being called on router's BuildRouter.
Build(provider router.RoutesProvider) error
- RouteExists reports whether a particular route exists.
RouteExists(ctx context.Context, method, path string) bool
For a more detailed, complete and useful example
you can take a look at the iris' router itself which is located at:
https://github.com/kataras/iris/tree/master/core/router/handler.go
which completes this exact interface, the `router#RequestHandler`.
*/
type customRouter struct {
// a copy of routes (safer because you will not be able to alter a route on serve-time without a `app.RefreshRouter` call):
// []router.Route
// or just expect the whole routes provider:
provider router.RoutesProvider
}
// HandleRequest a silly example which finds routes based only on the first part of the requested path
// which must be a static one as well, the rest goes to fill the parameters.
func (r *customRouter) HandleRequest(ctx context.Context) {
path := ctx.Path()
ctx.Application().Logger().Infof("Requested resource path: %s", path)
parts := strings.Split(path, "/")[1:]
staticPath := "/" + parts[0]
for _, route := range r.provider.GetRoutes() {
if strings.HasPrefix(route.Path, staticPath) && route.Method == ctx.Method() {
paramParts := parts[1:]
for _, paramValue := range paramParts {
for _, p := range route.Tmpl().Params {
ctx.Params().Set(p.Name, paramValue)
}
}
ctx.SetCurrentRouteName(route.Name)
ctx.Do(route.Handlers)
return
}
}
// if nothing found...
ctx.StatusCode(iris.StatusNotFound)
}
func (r *customRouter) Build(provider router.RoutesProvider) error {
for _, route := range provider.GetRoutes() {
// do any necessary validation or conversations based on your custom logic here
// but always run the "BuildHandlers" for each registered route.
route.BuildHandlers()
// [...] r.routes = append(r.routes, *route)
}
r.provider = provider
return nil
}
func (r *customRouter) RouteExists(ctx context.Context, method, path string) bool {
// [...]
return false
}
func main() {
app := iris.New()
// In case you are wondering, the parameter types and macros like "{param:string $func()}" still work inside
// your custom router if you fetch by the Route's Handler
// because they are middlewares under the hood, so you don't have to implement the logic of handling them manually,
// though you have to match what requested path is what route and fill the ctx.Params(), this is the work of your custom router.
app.Get("/hello/{name}", func(ctx context.Context) {
name := ctx.Params().Get("name")
ctx.Writef("Hello %s\n", name)
})
app.Get("/cs/{num:uint64 min(10) else 400}", func(ctx context.Context) {
num := ctx.Params().GetUint64Default("num", 0)
ctx.Writef("num is: %d\n", num)
})
// To replace the existing router with a customized one by using the iris/context.Context
// you have to use the `app.BuildRouter` method before `app.Run` and after the routes registered.
// You should pass your custom router's instance as the second input arg, which must completes the `router#RequestHandler`
// interface as shown above.
//
// To see how you can build something even more low-level without direct iris' context support (you can do that manually as well)
// navigate to the "custom-wrapper" example instead.
myCustomRouter := new(customRouter)
app.BuildRouter(app.ContextPool, myCustomRouter, app.APIBuilder, true)
app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
}

View File

@@ -2,7 +2,6 @@ package main
import (
"regexp"
"strconv"
"github.com/kataras/iris"
)
@@ -14,15 +13,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 +34,34 @@ func main() {
// string type
// anything
//
// +------------------------+
// | {param:int} |
// +------------------------+
// +-------------------------------+
// | {param:int} 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:int64} or {param:long} |
// +-------------------------------+
// int64 type
// only numbers (0-9)
// -9223372036854775808 to 9223372036854775807
//
// +------------------------+
// | {param:boolean} |
// | {param:uint8} |
// +------------------------+
// uint8 type
// 0 to 255
//
//
// +------------------------+
// | {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 +100,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,51 +118,52 @@ 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 uint64 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 {
// do anything before serve here [...]
// at this case we don't need to do anything
return func(paramValue string) bool {
n, err := strconv.Atoi(paramValue)
if err != nil {
return false
}
return n >= minValue
// func(uint64) bool = our func's evaluator, this executes in serve time when
// a user requests a path which contains the :uint64 macro parameter type with the min(...) macro parameter function.
app.Macros().Get("uint64").RegisterFunc("min", func(minValue uint64) func(uint64) bool {
// type of "paramValue" should match the type of the internal macro's evaluator function, which in this case is "uint64".
return func(paramValue uint64) bool {
return paramValue >= minValue
}
})
// http://localhost:8080/profile/id>=1
// http://localhost:8080/profile/id>=20
// 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(20)}", 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().GetUint64Default("id", 0)
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().GetUint64Default("id", 0)
friendid := ctx.Params().GetUint64Default("friendid", 0)
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.
// Another example using a custom regexp and any custom logic.
// :uint8 0 to 255.
app.Get("/ages/{age:uint8 else 400}", func(ctx iris.Context) {
age, _ := ctx.Params().GetUint8("age")
ctx.Writef("age selected: %d", age)
})
// Another example using a custom regexp or any custom logic.
// Register your custom argument-less macro function to the :string param type.
latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$"
latLonRegex, err := regexp.Compile(latLonExpr)
if err != nil {
panic(err)
}
app.Macros().String.RegisterFunc("coordinate", func() func(paramName string) (ok bool) {
// MatchString is a type of func(string) bool, so we can return that as it's.
return latLonRegex.MatchString
})
// MatchString is a type of func(string) bool, so we use it as it is.
app.Macros().Get("string").RegisterFunc("coordinate", latLonRegex.MatchString)
app.Get("/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}", func(ctx iris.Context) {
ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon"))
@@ -159,7 +171,43 @@ func main() {
//
// http://localhost:8080/game/a-zA-Z/level/0-9
// Another one is by using a custom body.
app.Macros().Get("string").RegisterFunc("range", func(minLength, maxLength int) func(string) bool {
return func(paramValue string) bool {
return len(paramValue) >= minLength && len(paramValue) <= maxLength
}
})
app.Get("/limitchar/{name:string range(1,200)}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
ctx.Writef(`Hello %s | the name should be between 1 and 200 characters length
otherwise this handler will not be executed`, name)
})
//
// Register your custom macro function which accepts a slice of strings `[...,...]`.
app.Macros().Get("string").RegisterFunc("has", func(validNames []string) func(string) bool {
return func(paramValue string) bool {
for _, validName := range validNames {
if validName == paramValue {
return true
}
}
return false
}
})
app.Get("/static_validation/{name:string has([kataras,gerasimos,maropoulos]}", func(ctx iris.Context) {
name := ctx.Params().Get("name")
ctx.Writef(`Hello %s | the name should be "kataras" or "gerasimos" or "maropoulos"
otherwise this handler will not be executed`, name)
})
//
// http://localhost:8080/game/a-zA-Z/level/42
// remember, alphabetical is lowercase or uppercase letters only.
app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
@@ -197,10 +245,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

@@ -0,0 +1,76 @@
// Package main shows how you can register a custom parameter type and macro functions that belongs to it.
package main
import (
"fmt"
"reflect"
"sort"
"strings"
"github.com/kataras/iris"
"github.com/kataras/iris/context"
"github.com/kataras/iris/hero"
)
func main() {
app := iris.New()
app.Logger().SetLevel("debug")
app.Macros().Register("slice", "", false, true, func(paramValue string) (interface{}, bool) {
return strings.Split(paramValue, "/"), true
}).RegisterFunc("contains", func(expectedItems []string) func(paramValue []string) bool {
sort.Strings(expectedItems)
return func(paramValue []string) bool {
if len(paramValue) != len(expectedItems) {
return false
}
sort.Strings(paramValue)
for i := 0; i < len(paramValue); i++ {
if paramValue[i] != expectedItems[i] {
return false
}
}
return true
}
})
// In order to use your new param type inside MVC controller's function input argument or a hero function input argument
// you have to tell the Iris what type it is, the `ValueRaw` of the parameter is the same type
// as you defined it above with the func(paramValue string) (interface{}, bool).
// The new value and its type(from string to your new custom type) it is stored only once now,
// you don't have to do any conversions for simple cases like this.
context.ParamResolvers[reflect.TypeOf([]string{})] = func(paramIndex int) interface{} {
return func(ctx context.Context) []string {
// When you want to retrieve a parameter with a value type that it is not supported by-default, such as ctx.Params().GetInt
// then you can use the `GetEntry` or `GetEntryAt` and cast its underline `ValueRaw` to the desired type.
// The type should be the same as the macro's evaluator function (last argument on the Macros#Register) return value.
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.([]string)
}
}
/*
http://localhost:8080/test_slice_hero/myvaluei1/myavlue2 ->
myparam's value (a trailing path parameter type) is: []string{"myvaluei1", "myavlue2"}
*/
app.Get("/test_slice_hero/{myparam:slice}", hero.Handler(func(myparam []string) string {
return fmt.Sprintf("myparam's value (a trailing path parameter type) is: %#v\n", myparam)
}))
/*
http://localhost:8080/test_slice_contains/notcontains1/value2 ->
(404) Not Found
http://localhost:8080/test_slice_contains/value1/value2 ->
myparam's value (a trailing path parameter type) is: []string{"value1", "value2"}
*/
app.Get("/test_slice_contains/{myparam:slice contains([value1,value2])}", func(ctx context.Context) {
// When it is not a built'n function available to retrieve your value with the type you want, such as ctx.Params().GetInt
// then you can use the `GetEntry.ValueRaw` to get the real value, which is set-ed by your macro above.
myparam := ctx.Params().GetEntry("myparam").ValueRaw.([]string)
ctx.Writef("myparam's value (a trailing path parameter type) is: %#v\n", myparam)
})
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)
})