mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
parent
9e8a58bf3b
commit
3d99983d96
|
@ -369,5 +369,12 @@ func main() {
|
|||
// should differ e.g.
|
||||
// /path/{name:string}
|
||||
// /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")
|
||||
}
|
||||
|
|
|
@ -98,19 +98,22 @@ func joinPath(path1 string, path2 string) string {
|
|||
// 6. Remove trailing '/'.
|
||||
//
|
||||
// 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.
|
||||
if s == "" || s == "." {
|
||||
if path == "" || path == "." {
|
||||
return "/"
|
||||
}
|
||||
|
||||
// remove suffix "/", if it's root "/" then it will add it as a prefix below.
|
||||
if lidx := len(s) - 1; s[lidx] == '/' {
|
||||
s = s[:lidx]
|
||||
if lidx := len(path) - 1; path[lidx] == '/' {
|
||||
path = path[:lidx]
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
if !insideMacro {
|
||||
if s[i] == '/' {
|
||||
if len(s)-1 >= i+1 && s[i+1] == '/' { // we have "//".
|
||||
bckp := s
|
||||
s = bckp[:i] + "/"
|
||||
// forward two, we ignore the second "/" in the raw.
|
||||
i = i + 2
|
||||
if len(bckp)-1 >= i {
|
||||
s += bckp[i:]
|
||||
}
|
||||
if s[i] == '\\' {
|
||||
s[i] = '/'
|
||||
|
||||
if len(s)-1 > i+1 && s[i+1] == '\\' {
|
||||
s = deleteCharacter(s, i+1)
|
||||
} else {
|
||||
i-- // set to minus in order for the next check to be applied for prev tokens too.
|
||||
}
|
||||
// if we have just a single slash then 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++
|
||||
}
|
||||
}
|
||||
|
||||
if s[i] == '/' && len(s)-1 > i+1 && s[i+1] == '/' {
|
||||
s[i] = '/'
|
||||
s = deleteCharacter(s, i+1)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return s
|
||||
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:]...)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
@ -47,6 +47,18 @@ func TestCleanPath(t *testing.T) {
|
|||
"/single/{id:uint64}",
|
||||
"/single/{id:uint64}",
|
||||
},
|
||||
{
|
||||
"0\\\\\\0",
|
||||
"/0/0",
|
||||
},
|
||||
{
|
||||
"*\\*\\*",
|
||||
"/*/*/*",
|
||||
},
|
||||
{
|
||||
"\\",
|
||||
"/",
|
||||
},
|
||||
}
|
||||
|
||||
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.
|
||||
func NewRoute(p Party, statusErrorCode int, method, subdomain, unparsedPath string,
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := convertMacroTmplToNodePath(tmpl)
|
||||
path = convertMacroTmplToNodePath(tmpl)
|
||||
// prepend the macro handler to the route, now,
|
||||
// right before the register to the tree, so APIBuilder#UseGlobal will work as expected.
|
||||
if handler.CanMakeHandler(tmpl) {
|
||||
|
@ -103,7 +105,6 @@ func NewRoute(p Party, statusErrorCode int, method, subdomain, unparsedPath stri
|
|||
handlers = append(context.Handlers{macroEvaluatorHandler}, handlers...)
|
||||
}
|
||||
|
||||
path = cleanPath(path) // maybe unnecessary here.
|
||||
defaultName := method + subdomain + tmpl.Src
|
||||
if statusErrorCode > 0 {
|
||||
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)
|
||||
pio.WriteRich(w, title, color)
|
||||
|
||||
path := r.Tmpl().Src
|
||||
path := r.tmpl.Src
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
|
|
|
@ -9,9 +9,13 @@ import (
|
|||
const (
|
||||
// ParamStart the character in string representation where the underline router starts its dynamic named parameter.
|
||||
ParamStart = ":"
|
||||
// paramStartCharacter is the character as rune of ParamStart.
|
||||
paramStartCharacter = ':'
|
||||
// WildcardParamStart the character in string representation where the underline router starts its dynamic wildcard
|
||||
// path parameter.
|
||||
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
|
||||
|
@ -112,6 +116,9 @@ func slowPathSplit(path string) []string {
|
|||
|
||||
func (tr *trie) insert(path string, route context.RouteReadOnly, handlers context.Handlers) {
|
||||
input := slowPathSplit(path)
|
||||
if len(input) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
n := tr.root
|
||||
if path == pathSep {
|
||||
|
@ -121,9 +128,13 @@ func (tr *trie) insert(path string, route context.RouteReadOnly, handlers contex
|
|||
var paramKeys []string
|
||||
|
||||
for _, s := range input {
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
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]
|
||||
|
||||
// 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 {
|
||||
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
|
||||
// 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 {
|
||||
n = n.getChild(ParamStart)
|
||||
if ln := len(paramValues); cap(paramValues) > ln {
|
||||
paramValues = paramValues[:ln+1]
|
||||
paramValues[ln] = q[start:i]
|
||||
paramValues[ln] = segment
|
||||
} else {
|
||||
paramValues = append(paramValues, q[start:i])
|
||||
paramValues = append(paramValues, segment)
|
||||
}
|
||||
} else if n.childWildcardParameter {
|
||||
n = n.getChild(WildcardParamStart)
|
||||
|
@ -213,7 +234,7 @@ func (tr *trie) search(q string, params *context.RequestParams) *trieNode {
|
|||
break
|
||||
} else {
|
||||
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
|
||||
// but the next path segment is not the /static, so go back to *wildcard
|
||||
// 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 { // 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):])
|
||||
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.
|
||||
//
|
||||
n = tr.root.getChild(WildcardParamStart)
|
||||
if len(n.paramKeys) == 0 { // fix crashes on /*/*/*.
|
||||
return nil
|
||||
}
|
||||
|
||||
params.Set(n.paramKeys[0], q[1:])
|
||||
return n
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ func MakeFilter(tmpl macro.Template) context.Filter {
|
|||
entry, found := ctx.Params().Store.GetEntryAt(p.Index)
|
||||
if !found {
|
||||
// should never happen.
|
||||
ctx.StatusCode(p.ErrCode) // status code can change from an error handler, set it here.
|
||||
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 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)
|
||||
|
|
|
@ -50,6 +50,31 @@ var (
|
|||
return func(paramValue string) bool {
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user