mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
Update to Version 8.4.2 | Read HISTORY.md
https://github.com/kataras/iris/blob/master/HISTORY.md#fr-15-september-2017--v842 Former-commit-id: 0ee4cc1d93ef7f26e5d402fdfbe07062aff5b08c
This commit is contained in:
parent
1c512619c7
commit
69b5327ecc
42
HISTORY.md
42
HISTORY.md
|
@ -18,6 +18,48 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
|
||||||
|
|
||||||
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
|
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
|
||||||
|
|
||||||
|
# Fr, 15 September 2017 | v8.4.2
|
||||||
|
|
||||||
|
## MVC
|
||||||
|
|
||||||
|
Support more than one dynamic method function receivers.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/kataras/iris"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
app.Controller("/user", new(UserController))
|
||||||
|
app.Run(iris.Addr("localhost:8080"))
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserController struct { iris.Controller }
|
||||||
|
|
||||||
|
// Maps to GET /user
|
||||||
|
// Request example: http://localhost:8080/user
|
||||||
|
// as usual.
|
||||||
|
func (c *UserController) Get() {
|
||||||
|
c.Text = "hello from /user"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps to GET /user/{paramfirst:long}
|
||||||
|
// Request example: http://localhost:8080/user/42
|
||||||
|
// as usual.
|
||||||
|
func (c *UserController) GetBy(userID int64) {
|
||||||
|
c.Ctx.Writef("hello user with id: %d", userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW:
|
||||||
|
// Maps to GET /user/{paramfirst:long}/business/{paramsecond:long}
|
||||||
|
// Request example: http://localhost:8080/user/42/business/93
|
||||||
|
func (c *UserController) GetByBusinessBy(userID int64, businessID int64) {
|
||||||
|
c.Ctx.Writef("fetch a business id: %d that user with id: %d owns, may make your db query faster",
|
||||||
|
businessID, userID)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Th, 07 September 2017 | v8.4.1
|
# Th, 07 September 2017 | v8.4.1
|
||||||
|
|
||||||
## Routing
|
## Routing
|
||||||
|
|
|
@ -38,7 +38,7 @@ Iris may have reached version 8, but we're not stopping there. We have many feat
|
||||||
### 📑 Table of contents
|
### 📑 Table of contents
|
||||||
|
|
||||||
* [Installation](#-installation)
|
* [Installation](#-installation)
|
||||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#th-07-september-2017--v841)
|
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#fr-15-september-2017--v842)
|
||||||
* [Learn](#-learn)
|
* [Learn](#-learn)
|
||||||
* [HTTP Listening](_examples/#http-listening)
|
* [HTTP Listening](_examples/#http-listening)
|
||||||
* [Configuration](_examples/#configuration)
|
* [Configuration](_examples/#configuration)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
8.4.1:https://github.com/kataras/iris/blob/master/HISTORY.md#th-07-september-2017--v841
|
8.4.2:https://github.com/kataras/iris/blob/master/HISTORY.md#fr-15-september-2017--v842
|
|
@ -98,6 +98,11 @@ func main() {
|
||||||
// GET: http://any_thing_here.localhost:8080
|
// GET: http://any_thing_here.localhost:8080
|
||||||
dynamicSubdomainRoutes.Get("/", info)
|
dynamicSubdomainRoutes.Get("/", info)
|
||||||
|
|
||||||
|
app.Delete("/something", func(ctx iris.Context) {
|
||||||
|
name := ctx.URLParam("name")
|
||||||
|
ctx.Writef(name)
|
||||||
|
})
|
||||||
|
|
||||||
// GET: http://localhost:8080/
|
// GET: http://localhost:8080/
|
||||||
// GET: http://localhost:8080/profile/anyusername
|
// GET: http://localhost:8080/profile/anyusername
|
||||||
// GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here
|
// GET: http://localhost:8080/profile/anyusername/backups/any/number/of/paths/here
|
||||||
|
@ -108,13 +113,12 @@ func main() {
|
||||||
// POST: http://localhost:8080/users
|
// POST: http://localhost:8080/users
|
||||||
// PUT: http://localhost:8080/users
|
// PUT: http://localhost:8080/users
|
||||||
// DELETE: http://localhost:8080/users/42
|
// DELETE: http://localhost:8080/users/42
|
||||||
|
// DELETE: http://localhost:8080/something?name=iris
|
||||||
|
|
||||||
// GET: http://admin.localhost:8080
|
// GET: http://admin.localhost:8080
|
||||||
// GET: http://admin.localhost:8080/settings
|
// GET: http://admin.localhost:8080/settings
|
||||||
// GET: http://any_thing_here.localhost:8080
|
// GET: http://any_thing_here.localhost:8080
|
||||||
if err := app.Run(iris.Addr(":8080")); err != nil {
|
app.Run(iris.Addr(":8080"))
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func info(ctx iris.Context) {
|
func info(ctx iris.Context) {
|
||||||
|
|
BIN
_examples/routing/overview/public/images/favicon.ico
Normal file
BIN
_examples/routing/overview/public/images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
5
doc.go
5
doc.go
|
@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
|
||||||
|
|
||||||
Current Version
|
Current Version
|
||||||
|
|
||||||
8.4.0
|
8.4.2
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
|
||||||
|
@ -820,6 +820,8 @@ and it adds its logic to its `BeginRequest`. Source file: https://github.com/kat
|
||||||
|
|
||||||
Read access to the current route via the `Route` field.
|
Read access to the current route via the `Route` field.
|
||||||
|
|
||||||
|
Support for more than one input arguments (map to dynamic request path parameters).
|
||||||
|
|
||||||
Register one or more relative paths and able to get path parameters, i.e
|
Register one or more relative paths and able to get path parameters, i.e
|
||||||
|
|
||||||
If `app.Controller("/user", new(user.Controller))`
|
If `app.Controller("/user", new(user.Controller))`
|
||||||
|
@ -843,6 +845,7 @@ Register one or more relative paths and able to get path parameters, i.e
|
||||||
If `app.Controller("/equality", new(profile.Equality))`
|
If `app.Controller("/equality", new(profile.Equality))`
|
||||||
|
|
||||||
- `func(*Controller) GetBy(is bool)` - `GET:/equality/{param:boolean}`
|
- `func(*Controller) GetBy(is bool)` - `GET:/equality/{param:boolean}`
|
||||||
|
- `func(*Controller) GetByOtherBy(is bool, otherID int64)` - `GET:/equality/{paramfirst:boolean}/other/{paramsecond:long}`
|
||||||
|
|
||||||
Supported types for method functions receivers: int, int64, bool and string.
|
Supported types for method functions receivers: int, int64, bool and string.
|
||||||
|
|
||||||
|
|
2
iris.go
2
iris.go
|
@ -32,7 +32,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Version is the current version number of the Iris Web Framework.
|
// Version is the current version number of the Iris Web Framework.
|
||||||
Version = "8.4.1"
|
Version = "8.4.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP status codes as registered with IANA.
|
// HTTP status codes as registered with IANA.
|
||||||
|
|
|
@ -141,7 +141,7 @@ func (t TController) HandlerOf(methodFunc methodfunc.MethodFunc) context.Handler
|
||||||
// the most important, execute the specific function
|
// the most important, execute the specific function
|
||||||
// from the controller that is responsible to handle
|
// from the controller that is responsible to handle
|
||||||
// this request, by method and path.
|
// this request, by method and path.
|
||||||
handleRequest(ctx, c.Method(methodFunc.Index).Interface())
|
handleRequest(ctx, c.Method(methodFunc.Index))
|
||||||
// if had models, set them after the end-developer's handler.
|
// if had models, set them after the end-developer's handler.
|
||||||
if hasModels {
|
if hasModels {
|
||||||
t.modelController.Handle(ctx, c)
|
t.modelController.Handle(ctx, c)
|
||||||
|
|
|
@ -1,68 +1,62 @@
|
||||||
package methodfunc
|
package methodfunc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FuncCaller is responsible to call the controller's function
|
// buildMethodCall builds the method caller.
|
||||||
// which is responsible
|
// We have repeated code here but it's the only way
|
||||||
// for that request for this http method.
|
// to support more than one input arguments without performance cost compared to previous implementation.
|
||||||
type FuncCaller interface {
|
// so it's hard-coded written to check the length of input args and their types.
|
||||||
// MethodCall fires the actual handler.
|
func buildMethodCall(a *ast) func(ctx context.Context, f reflect.Value) {
|
||||||
// The "ctx" is the current context, helps us to get any path parameter's values.
|
// if accepts one or more parameters.
|
||||||
//
|
if a.dynamic {
|
||||||
// The "f" is the controller's function which is responsible
|
// if one function input argument then call the function
|
||||||
// for that request for this http method.
|
// by "casting" (faster).
|
||||||
// That function can accept one parameter.
|
if l := len(a.paramKeys); l == 1 {
|
||||||
//
|
paramType := a.paramTypes[0]
|
||||||
// The default callers (and the only one for now)
|
paramKey := a.paramKeys[0]
|
||||||
// are pre-calculated by the framework.
|
|
||||||
MethodCall(ctx context.Context, f interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type callerFunc func(ctx context.Context, f interface{})
|
if paramType == paramTypeInt {
|
||||||
|
return func(ctx context.Context, f reflect.Value) {
|
||||||
func (c callerFunc) MethodCall(ctx context.Context, f interface{}) {
|
v, _ := ctx.Params().GetInt(paramKey)
|
||||||
c(ctx, f)
|
f.Interface().(func(int))(v)
|
||||||
}
|
|
||||||
|
|
||||||
func resolveCaller(p pathInfo) callerFunc {
|
|
||||||
// if it's standard `Get`, `Post` without parameters.
|
|
||||||
if p.ParamType == "" {
|
|
||||||
return func(ctx context.Context, f interface{}) {
|
|
||||||
f.(func())()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember,
|
if paramType == paramTypeLong {
|
||||||
// the router already checks for the correct type,
|
return func(ctx context.Context, f reflect.Value) {
|
||||||
// we did pre-calculate everything
|
v, _ := ctx.Params().GetInt64(paramKey)
|
||||||
// and now we will pre-calculate the method caller itself as well.
|
f.Interface().(func(int64))(v)
|
||||||
|
}
|
||||||
|
|
||||||
if p.ParamType == paramTypeInt {
|
}
|
||||||
return func(ctx context.Context, f interface{}) {
|
|
||||||
paramValue, _ := ctx.Params().GetInt(paramName)
|
if paramType == paramTypeBoolean {
|
||||||
f.(func(int))(paramValue)
|
return func(ctx context.Context, f reflect.Value) {
|
||||||
|
v, _ := ctx.Params().GetBool(paramKey)
|
||||||
|
f.Interface().(func(bool))(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.ParamType == paramTypeLong {
|
// string, path...
|
||||||
return func(ctx context.Context, f interface{}) {
|
return func(ctx context.Context, f reflect.Value) {
|
||||||
paramValue, _ := ctx.Params().GetInt64(paramName)
|
f.Interface().(func(string))(ctx.Params().Get(paramKey))
|
||||||
f.(func(int64))(paramValue)
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if func input arguments are more than one then
|
||||||
|
// use the Call method (slower).
|
||||||
|
return func(ctx context.Context, f reflect.Value) {
|
||||||
|
f.Call(a.paramValues(ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.ParamType == paramTypeBoolean {
|
// if it's static without any receivers then just call it.
|
||||||
return func(ctx context.Context, f interface{}) {
|
return func(ctx context.Context, f reflect.Value) {
|
||||||
paramValue, _ := ctx.Params().GetBool(paramName)
|
f.Interface().(func())()
|
||||||
f.(func(bool))(paramValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// else it's string or path, both of them are simple strings.
|
|
||||||
return func(ctx context.Context, f interface{}) {
|
|
||||||
paramValue := ctx.Params().Get(paramName)
|
|
||||||
f.(func(string))(paramValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
89
mvc/activator/methodfunc/func_lexer.go
Normal file
89
mvc/activator/methodfunc/func_lexer.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package methodfunc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tokenBy = "By"
|
||||||
|
tokenWildcard = "Wildcard" // should be followed by "By",
|
||||||
|
)
|
||||||
|
|
||||||
|
// word lexer, not characters.
|
||||||
|
type lexer struct {
|
||||||
|
words []string
|
||||||
|
cur int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLexer(s string) *lexer {
|
||||||
|
l := new(lexer)
|
||||||
|
l.reset(s)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lexer) reset(trailing string) {
|
||||||
|
l.cur = -1
|
||||||
|
var words []string
|
||||||
|
if trailing != "" {
|
||||||
|
end := len(trailing)
|
||||||
|
start := -1
|
||||||
|
|
||||||
|
for i, n := 0, end; i < n; i++ {
|
||||||
|
c := rune(trailing[i])
|
||||||
|
if unicode.IsUpper(c) {
|
||||||
|
// it doesn't count the last uppercase
|
||||||
|
if start != -1 {
|
||||||
|
end = i
|
||||||
|
words = append(words, trailing[start:end])
|
||||||
|
}
|
||||||
|
start = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
end = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if end > 0 && len(trailing) >= end {
|
||||||
|
words = append(words, trailing[start:end])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.words = words
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lexer) next() (w string) {
|
||||||
|
cur := l.cur + 1
|
||||||
|
|
||||||
|
if w = l.peek(cur); w != "" {
|
||||||
|
l.cur++
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lexer) skip() {
|
||||||
|
if cur := l.cur + 1; cur < len(l.words) {
|
||||||
|
l.cur = cur
|
||||||
|
} else {
|
||||||
|
l.cur = len(l.words) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lexer) peek(idx int) string {
|
||||||
|
if idx < len(l.words) {
|
||||||
|
return l.words[idx]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lexer) peekNext() (w string) {
|
||||||
|
return l.peek(l.cur + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lexer) peekPrev() (w string) {
|
||||||
|
if l.cur > 0 {
|
||||||
|
cur := l.cur - 1
|
||||||
|
w = l.words[cur]
|
||||||
|
}
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
162
mvc/activator/methodfunc/func_parser.go
Normal file
162
mvc/activator/methodfunc/func_parser.go
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package methodfunc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var posWords = map[int]string{
|
||||||
|
0: "",
|
||||||
|
1: "first",
|
||||||
|
2: "second",
|
||||||
|
3: "third",
|
||||||
|
4: "forth",
|
||||||
|
5: "five",
|
||||||
|
6: "sixth",
|
||||||
|
7: "seventh",
|
||||||
|
8: "eighth",
|
||||||
|
9: "ninth",
|
||||||
|
}
|
||||||
|
|
||||||
|
func genParamKey(argIdx int) string {
|
||||||
|
return "param" + posWords[argIdx] // paramfirst, paramsecond...
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
paramTypeInt = "int"
|
||||||
|
paramTypeLong = "long"
|
||||||
|
paramTypeBoolean = "boolean"
|
||||||
|
paramTypeString = "string"
|
||||||
|
paramTypePath = "path"
|
||||||
|
)
|
||||||
|
|
||||||
|
var macroTypes = map[string]string{
|
||||||
|
"int": paramTypeInt,
|
||||||
|
"int64": paramTypeLong,
|
||||||
|
"bool": paramTypeBoolean,
|
||||||
|
"string": paramTypeString,
|
||||||
|
// there is "path" param type but it's being captured "on-air"
|
||||||
|
// "file" param type is not supported by the current implementation, yet
|
||||||
|
// but if someone ask for it I'll implement it, it's easy.
|
||||||
|
}
|
||||||
|
|
||||||
|
type funcParser struct {
|
||||||
|
info FuncInfo
|
||||||
|
lexer *lexer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncParser(info FuncInfo) *funcParser {
|
||||||
|
return &funcParser{
|
||||||
|
info: info,
|
||||||
|
lexer: newLexer(info.Trailing),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *funcParser) parse() (*ast, error) {
|
||||||
|
a := new(ast)
|
||||||
|
funcArgPos := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
w := p.lexer.next()
|
||||||
|
if w == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if w == tokenBy {
|
||||||
|
typ := p.info.Type
|
||||||
|
funcArgPos++ // starting with 1 because in typ.NumIn() the first is the struct receiver.
|
||||||
|
|
||||||
|
if p.lexer.peekPrev() == tokenBy || typ.NumIn() == 1 { // ByBy, then act this second By like a path
|
||||||
|
a.relPath += "/" + strings.ToLower(w)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.NumIn() <= funcArgPos {
|
||||||
|
return nil, errors.New("keyword 'By' found but length of input receivers are not match for " +
|
||||||
|
p.info.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
paramKey = genParamKey(funcArgPos) // paramfirst, paramsecond...
|
||||||
|
paramType = paramTypeString // default string
|
||||||
|
)
|
||||||
|
|
||||||
|
// string, int...
|
||||||
|
goType := typ.In(funcArgPos).Name()
|
||||||
|
|
||||||
|
if p.lexer.peekNext() == tokenWildcard {
|
||||||
|
p.lexer.skip() // skip the Wildcard word.
|
||||||
|
paramType = paramTypePath
|
||||||
|
} else if pType, ok := macroTypes[goType]; ok {
|
||||||
|
// it's not wildcard, so check base on our available macro types.
|
||||||
|
paramType = pType
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("invalid syntax for " + p.info.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.paramKeys = append(a.paramKeys, paramKey)
|
||||||
|
a.paramTypes = append(a.paramTypes, paramType)
|
||||||
|
// /{paramfirst:path}, /{paramfirst:long}...
|
||||||
|
a.relPath += fmt.Sprintf("/{%s:%s}", paramKey, paramType)
|
||||||
|
a.dynamic = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
a.relPath += "/" + strings.ToLower(w)
|
||||||
|
}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ast struct {
|
||||||
|
paramKeys []string // paramfirst, paramsecond... [0]
|
||||||
|
paramTypes []string // string, int, long, path... [0]
|
||||||
|
relPath string
|
||||||
|
dynamic bool // when paramKeys (and paramTypes, are equal) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// moved to func_caller#buildMethodcall, it's bigger and with repeated code
|
||||||
|
// than this, below function but it's faster.
|
||||||
|
// func (a *ast) MethodCall(ctx context.Context, f reflect.Value) {
|
||||||
|
// if a.dynamic {
|
||||||
|
// f.Call(a.paramValues(ctx))
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// f.Interface().(func())()
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (a *ast) paramValues(ctx context.Context) []reflect.Value {
|
||||||
|
l := len(a.paramKeys)
|
||||||
|
values := make([]reflect.Value, l, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
paramKey := a.paramKeys[i]
|
||||||
|
paramType := a.paramTypes[i]
|
||||||
|
values[i] = getParamValueFromType(ctx, paramType, paramKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func getParamValueFromType(ctx context.Context, paramType string, paramKey string) reflect.Value {
|
||||||
|
if paramType == paramTypeInt {
|
||||||
|
v, _ := ctx.Params().GetInt(paramKey)
|
||||||
|
return reflect.ValueOf(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramType == paramTypeLong {
|
||||||
|
v, _ := ctx.Params().GetInt64(paramKey)
|
||||||
|
return reflect.ValueOf(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramType == paramTypeBoolean {
|
||||||
|
v, _ := ctx.Params().GetBool(paramKey)
|
||||||
|
return reflect.ValueOf(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// string, path...
|
||||||
|
return reflect.ValueOf(ctx.Params().Get(paramKey))
|
||||||
|
}
|
|
@ -1,121 +0,0 @@
|
||||||
package methodfunc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
by = "By"
|
|
||||||
wildcard = "Wildcard"
|
|
||||||
paramName = "param"
|
|
||||||
)
|
|
||||||
|
|
||||||
type pathInfo struct {
|
|
||||||
GoParamType string
|
|
||||||
ParamType string
|
|
||||||
RelPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
paramTypeInt = "int"
|
|
||||||
paramTypeLong = "long"
|
|
||||||
paramTypeBoolean = "boolean"
|
|
||||||
paramTypeString = "string"
|
|
||||||
paramTypePath = "path"
|
|
||||||
)
|
|
||||||
|
|
||||||
var macroTypes = map[string]string{
|
|
||||||
"int": paramTypeInt,
|
|
||||||
"int64": paramTypeLong,
|
|
||||||
"bool": paramTypeBoolean,
|
|
||||||
"string": paramTypeString,
|
|
||||||
// there is "path" param type but it's being captured "on-air"
|
|
||||||
// "file" param type is not supported by the current implementation, yet
|
|
||||||
// but if someone ask for it I'll implement it, it's easy.
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveRelativePath(info FuncInfo) (p pathInfo, ok bool) {
|
|
||||||
if info.Trailing == "" {
|
|
||||||
// it's valid
|
|
||||||
// it's just don't have a relative path,
|
|
||||||
// therefore p.RelPath will be empty, as we want.
|
|
||||||
return p, true
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
typ = info.Type
|
|
||||||
tr = info.Trailing
|
|
||||||
relPath = resolvePathFromFunc(tr)
|
|
||||||
|
|
||||||
goType, paramType string
|
|
||||||
)
|
|
||||||
|
|
||||||
byKeywordIdx := strings.LastIndex(tr, by)
|
|
||||||
if byKeywordIdx != -1 && typ.NumIn() == 2 { // first is the struct receiver.
|
|
||||||
funcPath := tr[0:byKeywordIdx] // remove the "By"
|
|
||||||
goType = typ.In(1).Name()
|
|
||||||
afterBy := byKeywordIdx + len(by)
|
|
||||||
if len(tr) > afterBy {
|
|
||||||
if tr[afterBy:] == wildcard {
|
|
||||||
paramType = paramTypePath
|
|
||||||
} else {
|
|
||||||
// invalid syntax
|
|
||||||
return p, false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// it's not wildcard, so check base on our available macro types.
|
|
||||||
if paramType, ok = macroTypes[goType]; !ok {
|
|
||||||
// ivalid type
|
|
||||||
return p, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// int, int64, bool and string are supported.
|
|
||||||
// as there is no way to get the parameter name
|
|
||||||
// we will use the "param" everywhere.
|
|
||||||
suffix := fmt.Sprintf("/{%s:%s}", paramName, paramType)
|
|
||||||
relPath = resolvePathFromFunc(funcPath) + suffix
|
|
||||||
}
|
|
||||||
|
|
||||||
// if GetSomething/PostSomething/PutSomething...
|
|
||||||
// we will not check for "Something" because we could
|
|
||||||
// occur unexpected behaviors to the existing users
|
|
||||||
// who using exported functions for controller's internal
|
|
||||||
// functionalities and not for serving a request path.
|
|
||||||
|
|
||||||
return pathInfo{
|
|
||||||
GoParamType: goType,
|
|
||||||
ParamType: paramType,
|
|
||||||
RelPath: relPath,
|
|
||||||
}, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolvePathFromFunc(funcName string) string {
|
|
||||||
end := len(funcName)
|
|
||||||
start := -1
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
|
|
||||||
for i, n := 0, end; i < n; i++ {
|
|
||||||
c := rune(funcName[i])
|
|
||||||
if unicode.IsUpper(c) {
|
|
||||||
// it doesn't count the last uppercase
|
|
||||||
if start != -1 {
|
|
||||||
end = i
|
|
||||||
s := "/" + strings.ToLower(funcName[start:end])
|
|
||||||
buf.WriteString(s)
|
|
||||||
}
|
|
||||||
start = i
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
end = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if end > 0 && len(funcName) >= end {
|
|
||||||
buf.WriteString("/" + strings.ToLower(funcName[start:end]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
|
@ -2,12 +2,24 @@ package methodfunc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/kataras/golog"
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MethodFunc the handler function.
|
// MethodFunc the handler function.
|
||||||
type MethodFunc struct {
|
type MethodFunc struct {
|
||||||
FuncInfo
|
FuncInfo
|
||||||
FuncCaller
|
// MethodCall fires the actual handler.
|
||||||
|
// The "ctx" is the current context, helps us to get any path parameter's values.
|
||||||
|
//
|
||||||
|
// The "f" is the controller's function which is responsible
|
||||||
|
// for that request for this http method.
|
||||||
|
// That function can accept one parameter.
|
||||||
|
//
|
||||||
|
// The default callers (and the only one for now)
|
||||||
|
// are pre-calculated by the framework.
|
||||||
|
MethodCall func(ctx context.Context, f reflect.Value)
|
||||||
RelPath string
|
RelPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,15 +29,16 @@ type MethodFunc struct {
|
||||||
func Resolve(typ reflect.Type) (methodFuncs []MethodFunc) {
|
func Resolve(typ reflect.Type) (methodFuncs []MethodFunc) {
|
||||||
infos := fetchInfos(typ)
|
infos := fetchInfos(typ)
|
||||||
for _, info := range infos {
|
for _, info := range infos {
|
||||||
p, ok := resolveRelativePath(info)
|
parser := newFuncParser(info)
|
||||||
if !ok {
|
a, err := parser.parse()
|
||||||
|
if err != nil {
|
||||||
|
golog.Errorf("MVC: %s\n", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
caller := resolveCaller(p)
|
|
||||||
methodFunc := MethodFunc{
|
methodFunc := MethodFunc{
|
||||||
RelPath: p.RelPath,
|
RelPath: a.relPath,
|
||||||
FuncInfo: info,
|
FuncInfo: info,
|
||||||
FuncCaller: caller,
|
MethodCall: buildMethodCall(a),
|
||||||
}
|
}
|
||||||
|
|
||||||
methodFuncs = append(methodFuncs, methodFunc)
|
methodFuncs = append(methodFuncs, methodFunc)
|
||||||
|
|
|
@ -429,6 +429,7 @@ func TestControllerInsideControllerRecursively(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
|
||||||
app.Controller("/user/{username}", new(testCtrl0),
|
app.Controller("/user/{username}", new(testCtrl0),
|
||||||
&testBindType{title: title})
|
&testBindType{title: title})
|
||||||
|
|
||||||
|
@ -455,6 +456,7 @@ func (c *testControllerRelPathFromFunc) GetAdminLogin() {}
|
||||||
|
|
||||||
func (c *testControllerRelPathFromFunc) PutSomethingIntoThis() {}
|
func (c *testControllerRelPathFromFunc) PutSomethingIntoThis() {}
|
||||||
func (c *testControllerRelPathFromFunc) GetSomethingBy(bool) {}
|
func (c *testControllerRelPathFromFunc) GetSomethingBy(bool) {}
|
||||||
|
func (c *testControllerRelPathFromFunc) GetSomethingByElseThisBy(bool, int) {} // two input arguments
|
||||||
|
|
||||||
func TestControllerRelPathFromFunc(t *testing.T) {
|
func TestControllerRelPathFromFunc(t *testing.T) {
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
|
@ -472,6 +474,8 @@ func TestControllerRelPathFromFunc(t *testing.T) {
|
||||||
Body().Equal("GET:/something/false")
|
Body().Equal("GET:/something/false")
|
||||||
e.GET("/something/truee").Expect().Status(httptest.StatusNotFound)
|
e.GET("/something/truee").Expect().Status(httptest.StatusNotFound)
|
||||||
e.GET("/something/falsee").Expect().Status(httptest.StatusNotFound)
|
e.GET("/something/falsee").Expect().Status(httptest.StatusNotFound)
|
||||||
|
e.GET("/something/true/else/this/42").Expect().Status(httptest.StatusOK).
|
||||||
|
Body().Equal("GET:/something/true/else/this/42")
|
||||||
|
|
||||||
e.GET("/login").Expect().Status(httptest.StatusOK).
|
e.GET("/login").Expect().Status(httptest.StatusOK).
|
||||||
Body().Equal("GET:/login")
|
Body().Equal("GET:/login")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user