mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
parent
9e8a58bf3b
commit
3d99983d96
|
@ -369,5 +369,12 @@ func main() {
|
||||||
// should differ e.g.
|
// should differ e.g.
|
||||||
// /path/{name:string}
|
// /path/{name:string}
|
||||||
// /path/{id:uint}
|
// /path/{id:uint}
|
||||||
|
//
|
||||||
|
// Note:
|
||||||
|
// If * path part is declared at the end of the route path, then
|
||||||
|
// it's considered a wildcard (same as {p:path}). In order to declare
|
||||||
|
// literal * and over pass this limitation use the string's path parameter 'eq' function
|
||||||
|
// as shown below:
|
||||||
|
// app.Get("/*/*/{p:string eq(*)}", handler) <- This will match only: /*/*/* and not /*/*/anything.
|
||||||
app.Listen(":8080")
|
app.Listen(":8080")
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,19 +98,22 @@ func joinPath(path1 string, path2 string) string {
|
||||||
// 6. Remove trailing '/'.
|
// 6. Remove trailing '/'.
|
||||||
//
|
//
|
||||||
// The returned path ends in a slash only if it is the root "/".
|
// The returned path ends in a slash only if it is the root "/".
|
||||||
func cleanPath(s string) string {
|
// The function does not modify the dynamic path parts.
|
||||||
|
func cleanPath(path string) string {
|
||||||
// note that we don't care about the performance here, it's before the server ran.
|
// note that we don't care about the performance here, it's before the server ran.
|
||||||
if s == "" || s == "." {
|
if path == "" || path == "." {
|
||||||
return "/"
|
return "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove suffix "/", if it's root "/" then it will add it as a prefix below.
|
// remove suffix "/", if it's root "/" then it will add it as a prefix below.
|
||||||
if lidx := len(s) - 1; s[lidx] == '/' {
|
if lidx := len(path) - 1; path[lidx] == '/' {
|
||||||
s = s[:lidx]
|
path = path[:lidx]
|
||||||
}
|
}
|
||||||
|
|
||||||
// prefix with "/".
|
// prefix with "/".
|
||||||
s = prefix(s, "/")
|
path = prefix(path, "/")
|
||||||
|
|
||||||
|
s := []rune(path)
|
||||||
|
|
||||||
// If you're learning go through Iris I will ask you to ignore the
|
// If you're learning go through Iris I will ask you to ignore the
|
||||||
// following part, it's not the recommending way to do that,
|
// following part, it's not the recommending way to do that,
|
||||||
|
@ -138,46 +141,34 @@ func cleanPath(s string) string {
|
||||||
|
|
||||||
// when inside {} then don't try to clean it.
|
// when inside {} then don't try to clean it.
|
||||||
if !insideMacro {
|
if !insideMacro {
|
||||||
if s[i] == '/' {
|
if s[i] == '\\' {
|
||||||
if len(s)-1 >= i+1 && s[i+1] == '/' { // we have "//".
|
s[i] = '/'
|
||||||
bckp := s
|
|
||||||
s = bckp[:i] + "/"
|
if len(s)-1 > i+1 && s[i+1] == '\\' {
|
||||||
// forward two, we ignore the second "/" in the raw.
|
s = deleteCharacter(s, i+1)
|
||||||
i = i + 2
|
} else {
|
||||||
if len(bckp)-1 >= i {
|
i-- // set to minus in order for the next check to be applied for prev tokens too.
|
||||||
s += bckp[i:]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we have just a single slash then continue.
|
|
||||||
|
if s[i] == '/' && len(s)-1 > i+1 && s[i+1] == '/' {
|
||||||
|
s[i] = '/'
|
||||||
|
s = deleteCharacter(s, i+1)
|
||||||
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if s[i] == '\\' { // this will catch "\\" and "\".
|
|
||||||
bckp := s
|
|
||||||
s = bckp[:i] + "/"
|
|
||||||
|
|
||||||
if len(bckp)-1 >= i+1 {
|
|
||||||
s += bckp[i+1:]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s)-1 > i && s[i] == '\\' {
|
|
||||||
bckp := s
|
|
||||||
s = bckp[:i]
|
|
||||||
if len(bckp)-1 >= i+2 {
|
|
||||||
s = bckp[:i-1] + bckp[i+1:]
|
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
if len(s) > 1 && s[len(s)-1] == '/' { // remove any last //.
|
||||||
|
s = s[:len(s)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
func deleteCharacter(s []rune, index int) []rune {
|
||||||
|
return append(s[0:index], s[index+1:]...)
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -47,6 +47,18 @@ func TestCleanPath(t *testing.T) {
|
||||||
"/single/{id:uint64}",
|
"/single/{id:uint64}",
|
||||||
"/single/{id:uint64}",
|
"/single/{id:uint64}",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"0\\\\\\0",
|
||||||
|
"/0/0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"*\\*\\*",
|
||||||
|
"/*/*/*",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"\\",
|
||||||
|
"/",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
|
|
@ -90,12 +90,14 @@ type Route struct {
|
||||||
// handlers are being changed to validate the macros at serve time, if needed.
|
// handlers are being changed to validate the macros at serve time, if needed.
|
||||||
func NewRoute(p Party, statusErrorCode int, method, subdomain, unparsedPath string,
|
func NewRoute(p Party, statusErrorCode int, method, subdomain, unparsedPath string,
|
||||||
handlers context.Handlers, macros macro.Macros) (*Route, error) {
|
handlers context.Handlers, macros macro.Macros) (*Route, error) {
|
||||||
tmpl, err := macro.Parse(unparsedPath, macros)
|
path := cleanPath(unparsedPath) // required. Before macro template parse as the cleanPath does not modify the dynamic path route parts.
|
||||||
|
|
||||||
|
tmpl, err := macro.Parse(path, macros)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
path := convertMacroTmplToNodePath(tmpl)
|
path = convertMacroTmplToNodePath(tmpl)
|
||||||
// prepend the macro handler to the route, now,
|
// prepend the macro handler to the route, now,
|
||||||
// right before the register to the tree, so APIBuilder#UseGlobal will work as expected.
|
// right before the register to the tree, so APIBuilder#UseGlobal will work as expected.
|
||||||
if handler.CanMakeHandler(tmpl) {
|
if handler.CanMakeHandler(tmpl) {
|
||||||
|
@ -103,7 +105,6 @@ func NewRoute(p Party, statusErrorCode int, method, subdomain, unparsedPath stri
|
||||||
handlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)
|
handlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
path = cleanPath(path) // maybe unnecessary here.
|
|
||||||
defaultName := method + subdomain + tmpl.Src
|
defaultName := method + subdomain + tmpl.Src
|
||||||
if statusErrorCode > 0 {
|
if statusErrorCode > 0 {
|
||||||
defaultName = fmt.Sprintf("%d_%s", statusErrorCode, defaultName)
|
defaultName = fmt.Sprintf("%d_%s", statusErrorCode, defaultName)
|
||||||
|
@ -506,7 +507,7 @@ func (r *Route) Trace(w io.Writer, stoppedIndex int) {
|
||||||
// s := fmt.Sprintf("%s: %s", pio.Rich(title, color), path)
|
// s := fmt.Sprintf("%s: %s", pio.Rich(title, color), path)
|
||||||
pio.WriteRich(w, title, color)
|
pio.WriteRich(w, title, color)
|
||||||
|
|
||||||
path := r.Tmpl().Src
|
path := r.tmpl.Src
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = "/"
|
path = "/"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,13 @@ import (
|
||||||
const (
|
const (
|
||||||
// ParamStart the character in string representation where the underline router starts its dynamic named parameter.
|
// ParamStart the character in string representation where the underline router starts its dynamic named parameter.
|
||||||
ParamStart = ":"
|
ParamStart = ":"
|
||||||
|
// paramStartCharacter is the character as rune of ParamStart.
|
||||||
|
paramStartCharacter = ':'
|
||||||
// WildcardParamStart the character in string representation where the underline router starts its dynamic wildcard
|
// WildcardParamStart the character in string representation where the underline router starts its dynamic wildcard
|
||||||
// path parameter.
|
// path parameter.
|
||||||
WildcardParamStart = "*"
|
WildcardParamStart = "*"
|
||||||
|
// wildcardParamStartCharacter is the character as rune of WildcardParamStart.
|
||||||
|
wildcardParamStartCharacter = '*'
|
||||||
)
|
)
|
||||||
|
|
||||||
// An iris-specific identical version of the https://github.com/kataras/muxie version 1.0.0 released at 15 Oct 2018
|
// An iris-specific identical version of the https://github.com/kataras/muxie version 1.0.0 released at 15 Oct 2018
|
||||||
|
@ -112,6 +116,9 @@ func slowPathSplit(path string) []string {
|
||||||
|
|
||||||
func (tr *trie) insert(path string, route context.RouteReadOnly, handlers context.Handlers) {
|
func (tr *trie) insert(path string, route context.RouteReadOnly, handlers context.Handlers) {
|
||||||
input := slowPathSplit(path)
|
input := slowPathSplit(path)
|
||||||
|
if len(input) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
n := tr.root
|
n := tr.root
|
||||||
if path == pathSep {
|
if path == pathSep {
|
||||||
|
@ -121,9 +128,13 @@ func (tr *trie) insert(path string, route context.RouteReadOnly, handlers contex
|
||||||
var paramKeys []string
|
var paramKeys []string
|
||||||
|
|
||||||
for _, s := range input {
|
for _, s := range input {
|
||||||
|
if len(s) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
c := s[0]
|
c := s[0]
|
||||||
|
|
||||||
if isParam, isWildcard := c == ParamStart[0], c == WildcardParamStart[0]; isParam || isWildcard {
|
if isParam, isWildcard := c == paramStartCharacter, c == wildcardParamStartCharacter; isParam || isWildcard {
|
||||||
n.hasDynamicChild = true
|
n.hasDynamicChild = true
|
||||||
paramKeys = append(paramKeys, s[1:]) // without : or *.
|
paramKeys = append(paramKeys, s[1:]) // without : or *.
|
||||||
|
|
||||||
|
@ -166,7 +177,6 @@ func (tr *trie) insert(path string, route context.RouteReadOnly, handlers contex
|
||||||
}
|
}
|
||||||
|
|
||||||
n.staticKey = path[:i]
|
n.staticKey = path[:i]
|
||||||
|
|
||||||
// fmt.Printf("trie.insert: (whole path=%v) Path: %s, Route name: %s, Handlers len: %d\n", n.end, n.key, route.Name(), len(handlers))
|
// fmt.Printf("trie.insert: (whole path=%v) Path: %s, Route name: %s, Handlers len: %d\n", n.end, n.key, route.Name(), len(handlers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,15 +202,26 @@ func (tr *trie) search(q string, params *context.RequestParams) *trieNode {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if i == end || q[i] == pathSepB {
|
if i == end || q[i] == pathSepB {
|
||||||
if child := n.getChild(q[start:i]); child != nil {
|
segment := q[start:i]
|
||||||
|
if child := n.getChild(segment); child != nil {
|
||||||
n = child
|
n = child
|
||||||
|
// Possible reserved param character, should catch it as
|
||||||
|
// dynamic node instead of static-path based.
|
||||||
|
if segment == ParamStart { // len(n.paramKeys) > 0 && (segment == ParamStart || segment == WildcardParamStart)
|
||||||
|
if ln := len(paramValues); cap(paramValues) > ln {
|
||||||
|
paramValues = paramValues[:ln+1]
|
||||||
|
paramValues[ln] = segment
|
||||||
|
} else {
|
||||||
|
paramValues = append(paramValues, segment)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if n.childNamedParameter {
|
} else if n.childNamedParameter {
|
||||||
n = n.getChild(ParamStart)
|
n = n.getChild(ParamStart)
|
||||||
if ln := len(paramValues); cap(paramValues) > ln {
|
if ln := len(paramValues); cap(paramValues) > ln {
|
||||||
paramValues = paramValues[:ln+1]
|
paramValues = paramValues[:ln+1]
|
||||||
paramValues[ln] = q[start:i]
|
paramValues[ln] = segment
|
||||||
} else {
|
} else {
|
||||||
paramValues = append(paramValues, q[start:i])
|
paramValues = append(paramValues, segment)
|
||||||
}
|
}
|
||||||
} else if n.childWildcardParameter {
|
} else if n.childWildcardParameter {
|
||||||
n = n.getChild(WildcardParamStart)
|
n = n.getChild(WildcardParamStart)
|
||||||
|
@ -213,7 +234,7 @@ func (tr *trie) search(q string, params *context.RequestParams) *trieNode {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
n = n.findClosestParentWildcardNode()
|
n = n.findClosestParentWildcardNode()
|
||||||
if n != nil {
|
if n != nil && len(n.paramKeys) > 0 {
|
||||||
// means that it has :param/static and *wildcard, we go trhough the :param
|
// means that it has :param/static and *wildcard, we go trhough the :param
|
||||||
// but the next path segment is not the /static, so go back to *wildcard
|
// but the next path segment is not the /static, so go back to *wildcard
|
||||||
// instead of not found.
|
// instead of not found.
|
||||||
|
@ -248,7 +269,7 @@ func (tr *trie) search(q string, params *context.RequestParams) *trieNode {
|
||||||
|
|
||||||
if n == nil || !n.end {
|
if n == nil || !n.end {
|
||||||
if n != nil { // we need it on both places, on last segment (below) or on the first unnknown (above).
|
if n != nil { // we need it on both places, on last segment (below) or on the first unnknown (above).
|
||||||
if n = n.findClosestParentWildcardNode(); n != nil {
|
if n = n.findClosestParentWildcardNode(); n != nil && len(n.paramKeys) > 0 {
|
||||||
params.Set(n.paramKeys[0], q[len(n.staticKey):])
|
params.Set(n.paramKeys[0], q[len(n.staticKey):])
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
@ -263,6 +284,10 @@ func (tr *trie) search(q string, params *context.RequestParams) *trieNode {
|
||||||
// the /other2/*myparam and not the root wildcard, which is what we want.
|
// the /other2/*myparam and not the root wildcard, which is what we want.
|
||||||
//
|
//
|
||||||
n = tr.root.getChild(WildcardParamStart)
|
n = tr.root.getChild(WildcardParamStart)
|
||||||
|
if len(n.paramKeys) == 0 { // fix crashes on /*/*/*.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
params.Set(n.paramKeys[0], q[1:])
|
params.Set(n.paramKeys[0], q[1:])
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,7 @@ func MakeFilter(tmpl macro.Template) context.Filter {
|
||||||
entry, found := ctx.Params().Store.GetEntryAt(p.Index)
|
entry, found := ctx.Params().Store.GetEntryAt(p.Index)
|
||||||
if !found {
|
if !found {
|
||||||
// should never happen.
|
// should never happen.
|
||||||
|
ctx.StatusCode(p.ErrCode) // status code can change from an error handler, set it here.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ func Parse(fullpath string, paramTypes []ast.ParamType) ([]*ast.ParamStatement,
|
||||||
}
|
}
|
||||||
// if we have param type path but it's not the last path part
|
// if we have param type path but it's not the last path part
|
||||||
if ast.IsTrailing(stmt.Type) && i < len(pathParts)-1 {
|
if ast.IsTrailing(stmt.Type) && i < len(pathParts)-1 {
|
||||||
return nil, fmt.Errorf("%s: parameter type \"%s\" should be registered to the very end of a path", s, stmt.Type.Indent())
|
return nil, fmt.Errorf("%s: parameter type \"%s\" should be registered to the very end of a path once", s, stmt.Type.Indent())
|
||||||
}
|
}
|
||||||
|
|
||||||
statements = append(statements, stmt)
|
statements = append(statements, stmt)
|
||||||
|
|
|
@ -50,6 +50,31 @@ var (
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return max >= len(paramValue)
|
return max >= len(paramValue)
|
||||||
}
|
}
|
||||||
|
}).
|
||||||
|
// checks if param value's matches the given input
|
||||||
|
RegisterFunc("eq", func(s string) func(string) bool {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
return paramValue == s
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if param value's matches at least one of the inputs
|
||||||
|
RegisterFunc("eqor", func(texts []string) func(string) bool {
|
||||||
|
if len(texts) == 1 {
|
||||||
|
text := texts[0]
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
return paramValue == text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
for _, s := range texts {
|
||||||
|
if paramValue == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Int or number type
|
// Int or number type
|
||||||
|
|
Loading…
Reference in New Issue
Block a user