mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
e4df35e351
Former-commit-id: 02f6656aceb890217bfc19688d7f45224df274ec
257 lines
7.3 KiB
Go
257 lines
7.3 KiB
Go
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package router
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/kataras/iris/context"
|
|
"github.com/kataras/iris/core/errors"
|
|
"github.com/kataras/iris/core/router/macro"
|
|
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
|
)
|
|
|
|
// defaultMacros returns a new macro map which
|
|
// contains the default router's named param types functions.
|
|
func defaultMacros() *macro.Map {
|
|
macros := macro.NewMap()
|
|
// registers the String and Int default macro funcs
|
|
// user can add or override of his own funcs later on
|
|
// i.e:
|
|
// app.Macro.String.RegisterFunc("equal", func(eqWith string) func(string) bool {
|
|
// return func(paramValue string) bool {
|
|
// return eqWith == paramValue
|
|
// }})
|
|
registerBuiltinsMacroFuncs(macros)
|
|
|
|
return macros
|
|
}
|
|
|
|
func registerBuiltinsMacroFuncs(out *macro.Map) {
|
|
// register the String which is the default type if not
|
|
// parameter type is specified or
|
|
// if a given parameter into path given but the func doesn't exist on the
|
|
// parameter type's function list.
|
|
//
|
|
// these can be overridden by the user, later on.
|
|
registerStringMacroFuncs(out.String)
|
|
registerIntMacroFuncs(out.Int)
|
|
registerAlphabeticalMacroFuncs(out.Alphabetical)
|
|
registerFileMacroFuncs(out.File)
|
|
registerPathMacroFuncs(out.Path)
|
|
}
|
|
|
|
// String
|
|
// anything one part
|
|
func registerStringMacroFuncs(out *macro.Macro) {
|
|
// this can be used everywhere, it's to help users to define custom regexp expressions
|
|
// on all macros
|
|
out.RegisterFunc("regexp", func(expr string) macro.EvaluatorFunc {
|
|
regexpEvaluator := macro.MustNewEvaluatorFromRegexp(expr)
|
|
return regexpEvaluator
|
|
})
|
|
|
|
// checks if param value starts with the 'prefix' arg
|
|
out.RegisterFunc("prefix", func(prefix string) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
return strings.HasPrefix(paramValue, prefix)
|
|
}
|
|
})
|
|
|
|
// checks if param value ends with the 'suffix' arg
|
|
out.RegisterFunc("suffix", func(suffix string) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
return strings.HasSuffix(paramValue, suffix)
|
|
}
|
|
})
|
|
|
|
// checks if param value contains the 's' arg
|
|
out.RegisterFunc("contains", func(s string) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
return strings.Contains(paramValue, s)
|
|
}
|
|
})
|
|
|
|
// checks if param value's length is at least 'min'
|
|
out.RegisterFunc("min", func(min int) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
return len(paramValue) >= min
|
|
}
|
|
})
|
|
// checks if param value's length is not bigger than 'max'
|
|
out.RegisterFunc("max", func(max int) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
return max >= len(paramValue)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Int
|
|
// only numbers (0-9)
|
|
func registerIntMacroFuncs(out *macro.Macro) {
|
|
// checks if the param value's int representation is
|
|
// bigger or equal than 'min'
|
|
out.RegisterFunc("min", func(min int) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
n, err := strconv.Atoi(paramValue)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return n >= min
|
|
}
|
|
})
|
|
|
|
// checks if the param value's int representation is
|
|
// smaller or equal than 'max'
|
|
out.RegisterFunc("max", func(max int) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
n, err := strconv.Atoi(paramValue)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return n <= max
|
|
}
|
|
})
|
|
|
|
// checks if the param value's int representation is
|
|
// between min and max, including 'min' and 'max'
|
|
out.RegisterFunc("range", func(min, max int) macro.EvaluatorFunc {
|
|
return func(paramValue string) bool {
|
|
n, err := strconv.Atoi(paramValue)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
if n < min || n > max {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
}
|
|
|
|
// Alphabetical
|
|
// letters only (upper or lowercase)
|
|
func registerAlphabeticalMacroFuncs(out *macro.Macro) {
|
|
|
|
}
|
|
|
|
// File
|
|
// letters (upper or lowercase)
|
|
// numbers (0-9)
|
|
// underscore (_)
|
|
// dash (-)
|
|
// point (.)
|
|
// no spaces! or other character
|
|
func registerFileMacroFuncs(out *macro.Macro) {
|
|
|
|
}
|
|
|
|
// Path
|
|
// File+slashes(anywhere)
|
|
// should be the latest param, it's the wildcard
|
|
func registerPathMacroFuncs(out *macro.Macro) {
|
|
|
|
}
|
|
|
|
// compileRoutePathAndHandlers receives a route info and returns its parsed/"compiled" path
|
|
// and the new handlers (prepend all the macro's handler, if any).
|
|
//
|
|
// It's not exported for direct use.
|
|
func compileRoutePathAndHandlers(handlers context.Handlers, tmpl *macro.Template) (string, context.Handlers, error) {
|
|
// parse the path to node's path, now.
|
|
path, err := convertTmplToNodePath(tmpl)
|
|
if err != nil {
|
|
return tmpl.Src, handlers, err
|
|
}
|
|
// prepend the macro handler to the route, now,
|
|
// right before the register to the tree, so routerbuilder.UseGlobal will work as expected.
|
|
if len(tmpl.Params) > 0 {
|
|
macroEvaluatorHandler := convertTmplToHandler(tmpl)
|
|
// may return nil if no really need a macro handler evaluator
|
|
if macroEvaluatorHandler != nil {
|
|
handlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)
|
|
}
|
|
}
|
|
|
|
return path, handlers, nil
|
|
}
|
|
|
|
func convertTmplToNodePath(tmpl *macro.Template) (string, error) {
|
|
routePath := tmpl.Src
|
|
if len(tmpl.Params) > 0 {
|
|
if routePath[len(routePath)-1] == '/' {
|
|
routePath = routePath[0 : len(routePath)-2] // remove the last "/" if macro syntax instead of underline's
|
|
}
|
|
}
|
|
|
|
// if it has started with {} and it's valid
|
|
// then the tmpl.Params will be filled,
|
|
// so no any further check needed
|
|
for i, p := range tmpl.Params {
|
|
if p.Type == ast.ParamTypePath {
|
|
if i != len(tmpl.Params)-1 {
|
|
return "", errors.New("parameter type \"ParamTypePath\" should be putted to the very last of a path")
|
|
}
|
|
routePath = strings.Replace(routePath, p.Src, WildcardParam(p.Name), 1)
|
|
} else {
|
|
routePath = strings.Replace(routePath, p.Src, Param(p.Name), 1)
|
|
}
|
|
}
|
|
|
|
return routePath, nil
|
|
}
|
|
|
|
// note: returns nil if not needed, the caller(router) should be check for that before adding that on route's Middleware
|
|
func convertTmplToHandler(tmpl *macro.Template) context.Handler {
|
|
|
|
needMacroHandler := false
|
|
|
|
// check if we have params like: {name:string} or {name} or {anything:path} without else keyword or any functions used inside these params.
|
|
// 1. if we don't have, then we don't need to add a handler before the main route's handler (as I said, no performance if macro is not really used)
|
|
// 2. if we don't have any named params then we don't need a handler too.
|
|
for _, p := range tmpl.Params {
|
|
if len(p.Funcs) == 0 && (p.Type == ast.ParamTypeUnExpected || p.Type == ast.ParamTypeString || p.Type == ast.ParamTypePath) && p.ErrCode == http.StatusNotFound {
|
|
} else {
|
|
// println("we need handler for: " + tmpl.Src)
|
|
needMacroHandler = true
|
|
}
|
|
}
|
|
|
|
if !needMacroHandler {
|
|
// println("we don't need handler for: " + tmpl.Src)
|
|
return nil
|
|
}
|
|
|
|
return func(tmpl macro.Template) context.Handler {
|
|
return func(ctx context.Context) {
|
|
for _, p := range tmpl.Params {
|
|
paramValue := ctx.Params().Get(p.Name)
|
|
// first, check for type evaluator
|
|
if !p.TypeEvaluator(paramValue) {
|
|
ctx.StatusCode(p.ErrCode)
|
|
ctx.StopExecution()
|
|
return
|
|
}
|
|
|
|
// then check for all of its functions
|
|
for _, evalFunc := range p.Funcs {
|
|
if !evalFunc(paramValue) {
|
|
ctx.StatusCode(p.ErrCode)
|
|
ctx.StopExecution()
|
|
return
|
|
}
|
|
}
|
|
|
|
}
|
|
// if all passed, just continue
|
|
ctx.Next()
|
|
}
|
|
}(*tmpl)
|
|
|
|
}
|