mirror of
https://github.com/gofiber/fiber.git
synced 2025-02-21 23:33:18 +00:00
🚀 improve routing behavior
This commit is contained in:
parent
67caca823b
commit
053d463fa6
50
path.go
50
path.go
@ -16,41 +16,44 @@ import (
|
|||||||
|
|
||||||
// routeParser holds the path segments and param names
|
// routeParser holds the path segments and param names
|
||||||
type routeParser struct {
|
type routeParser struct {
|
||||||
segs []routeSegment
|
segs []routeSegment // the parsed segments of the route
|
||||||
params []string
|
params []string // that parameter names the parsed route
|
||||||
wildCardCount int
|
wildCardCount int // number of wildcard parameters, used internally to give the wildcard parameter its number
|
||||||
plusCount int
|
plusCount int // number of plus parameters, used internally to give the plus parameter its number
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramsSeg holds the segment metadata
|
// paramsSeg holds the segment metadata
|
||||||
type routeSegment struct {
|
type routeSegment struct {
|
||||||
Const string
|
// const information
|
||||||
|
Const string // constant part of the route
|
||||||
IsParam bool
|
// parameter information
|
||||||
ParamName string
|
IsParam bool // Truth value that indicates whether it is a parameter or a constant part
|
||||||
|
ParamName string // name of the parameter for access to it, for wildcards and plus parameters access iterators starting with 1 are added
|
||||||
ComparePart string // search part to find the end of the parameter
|
ComparePart string // search part to find the end of the parameter
|
||||||
PartCount int // how often is the search part contained in the non-param segments? -> necessary for greedy search
|
PartCount int // how often is the search part contained in the non-param segments? -> necessary for greedy search
|
||||||
IsGreedy bool
|
IsGreedy bool // indicates whether the parameter is greedy or not, is used with wildcard and plus
|
||||||
IsOptional bool
|
IsOptional bool // indicates whether the parameter is optional or not
|
||||||
IsLast bool
|
// common information
|
||||||
// TODO: add support for optional groups ?
|
IsLast bool // shows if the segment is the last one for the route
|
||||||
|
// future TODO: add support for optional groups "/abc(/def)?"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// different special routing signs
|
||||||
const (
|
const (
|
||||||
wildcardParam byte = '*'
|
wildcardParam byte = '*' // indicates a optional greedy parameter
|
||||||
plusParam byte = '+'
|
plusParam byte = '+' // indicates a required greedy parameter
|
||||||
optionalParam byte = '?'
|
optionalParam byte = '?' // concludes a parameter by name and makes it optional
|
||||||
slashDelimiter byte = '/'
|
paramStarterChar byte = ':' // start character for a parameter with name
|
||||||
paramStarterChar byte = ':'
|
slashDelimiter byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// list of possible parameter and segment delimiter
|
||||||
var (
|
var (
|
||||||
// list of possible parameter and segment delimiter
|
|
||||||
// slash has a special role, unlike the other parameters it must not be interpreted as a parameter
|
// slash has a special role, unlike the other parameters it must not be interpreted as a parameter
|
||||||
routeDelimiter = []byte{slashDelimiter, '-', '.'}
|
routeDelimiter = []byte{slashDelimiter, '-', '.'}
|
||||||
// list of chars for the parameter recognising
|
// list of chars for the parameter recognising
|
||||||
parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar}
|
parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar}
|
||||||
// list of chars at the end of the parameter
|
// list of chars of delimiters and the starting parameter name char
|
||||||
parameterDelimiterChars = append([]byte{paramStarterChar}, routeDelimiter...)
|
parameterDelimiterChars = append([]byte{paramStarterChar}, routeDelimiter...)
|
||||||
// list of chars to find the end of a parameter
|
// list of chars to find the end of a parameter
|
||||||
parameterEndChars = append([]byte{optionalParam}, parameterDelimiterChars...)
|
parameterEndChars = append([]byte{optionalParam}, parameterDelimiterChars...)
|
||||||
@ -96,6 +99,7 @@ func addParameterMetaInfo(segs []routeSegment) []routeSegment {
|
|||||||
for i := len(segs) - 1; i >= 0; i-- {
|
for i := len(segs) - 1; i >= 0; i-- {
|
||||||
// set the compare part for the parameter
|
// set the compare part for the parameter
|
||||||
if segs[i].IsParam {
|
if segs[i].IsParam {
|
||||||
|
// important for finding the end of the parameter
|
||||||
segs[i].ComparePart = comparePart
|
segs[i].ComparePart = comparePart
|
||||||
} else {
|
} else {
|
||||||
comparePart = segs[i].Const
|
comparePart = segs[i].Const
|
||||||
@ -109,8 +113,9 @@ func addParameterMetaInfo(segs []routeSegment) []routeSegment {
|
|||||||
for i := 0; i < len(segs); i++ {
|
for i := 0; i < len(segs); i++ {
|
||||||
// check how often the compare part is in the following const parts
|
// check how often the compare part is in the following const parts
|
||||||
if segs[i].IsParam && segs[i].ComparePart != "" {
|
if segs[i].IsParam && segs[i].ComparePart != "" {
|
||||||
for j := i + 1; j < len(segs)-1; j++ {
|
for j := i + 1; j <= len(segs)-1; j++ {
|
||||||
if !segs[j].IsParam {
|
if !segs[j].IsParam {
|
||||||
|
// count is important for the greedy match
|
||||||
segs[i].PartCount += strings.Count(segs[j].Const, segs[i].ComparePart)
|
segs[i].PartCount += strings.Count(segs[j].Const, segs[i].ComparePart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,6 +160,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, ro
|
|||||||
isWildCard := pattern[0] == wildcardParam
|
isWildCard := pattern[0] == wildcardParam
|
||||||
isPlusParam := pattern[0] == plusParam
|
isPlusParam := pattern[0] == plusParam
|
||||||
parameterEndPosition := findNextCharsetPosition(pattern[1:], parameterEndChars)
|
parameterEndPosition := findNextCharsetPosition(pattern[1:], parameterEndChars)
|
||||||
|
|
||||||
// handle wildcard end
|
// handle wildcard end
|
||||||
if isWildCard || isPlusParam {
|
if isWildCard || isPlusParam {
|
||||||
parameterEndPosition = 0
|
parameterEndPosition = 0
|
||||||
@ -166,7 +172,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, ro
|
|||||||
// cut params part
|
// cut params part
|
||||||
processedPart := pattern[0 : parameterEndPosition+1]
|
processedPart := pattern[0 : parameterEndPosition+1]
|
||||||
|
|
||||||
paramName := processedPart
|
paramName := utils.GetTrimmedParam(processedPart)
|
||||||
// add access iterator to wildcard and plus
|
// add access iterator to wildcard and plus
|
||||||
if isWildCard {
|
if isWildCard {
|
||||||
routeParser.wildCardCount++
|
routeParser.wildCardCount++
|
||||||
@ -174,8 +180,6 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, ro
|
|||||||
} else if isPlusParam {
|
} else if isPlusParam {
|
||||||
routeParser.plusCount++
|
routeParser.plusCount++
|
||||||
paramName += strconv.Itoa(routeParser.plusCount)
|
paramName += strconv.Itoa(routeParser.plusCount)
|
||||||
} else {
|
|
||||||
paramName = utils.GetTrimmedParam(paramName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return processedPart, routeSegment{
|
return processedPart, routeSegment{
|
||||||
|
34
path_test.go
34
path_test.go
@ -26,9 +26,7 @@ func Test_Path_parseRoute(t *testing.T) {
|
|||||||
{Const: "/size:"},
|
{Const: "/size:"},
|
||||||
{IsParam: true, ParamName: "size", IsLast: true},
|
{IsParam: true, ParamName: "size", IsLast: true},
|
||||||
},
|
},
|
||||||
params: []string{"filter", "color", "size"},
|
params: []string{"filter", "color", "size"},
|
||||||
wildCardCount: 0,
|
|
||||||
plusCount: 0,
|
|
||||||
}, rp)
|
}, rp)
|
||||||
|
|
||||||
rp = parseRoute("/api/v1/:param/abc/*")
|
rp = parseRoute("/api/v1/:param/abc/*")
|
||||||
@ -41,7 +39,6 @@ func Test_Path_parseRoute(t *testing.T) {
|
|||||||
},
|
},
|
||||||
params: []string{"param", "*1"},
|
params: []string{"param", "*1"},
|
||||||
wildCardCount: 1,
|
wildCardCount: 1,
|
||||||
plusCount: 0,
|
|
||||||
}, rp)
|
}, rp)
|
||||||
|
|
||||||
rp = parseRoute("/api/*/:param/:param2")
|
rp = parseRoute("/api/*/:param/:param2")
|
||||||
@ -56,7 +53,6 @@ func Test_Path_parseRoute(t *testing.T) {
|
|||||||
},
|
},
|
||||||
params: []string{"*1", "param", "param2"},
|
params: []string{"*1", "param", "param2"},
|
||||||
wildCardCount: 1,
|
wildCardCount: 1,
|
||||||
plusCount: 0,
|
|
||||||
}, rp)
|
}, rp)
|
||||||
|
|
||||||
rp = parseRoute("/test:optional?:optional2?")
|
rp = parseRoute("/test:optional?:optional2?")
|
||||||
@ -66,21 +62,18 @@ func Test_Path_parseRoute(t *testing.T) {
|
|||||||
{IsParam: true, ParamName: "optional", IsOptional: true},
|
{IsParam: true, ParamName: "optional", IsOptional: true},
|
||||||
{IsParam: true, ParamName: "optional2", IsOptional: true, IsLast: true},
|
{IsParam: true, ParamName: "optional2", IsOptional: true, IsLast: true},
|
||||||
},
|
},
|
||||||
params: []string{"optional", "optional2"},
|
params: []string{"optional", "optional2"},
|
||||||
wildCardCount: 0,
|
|
||||||
plusCount: 0,
|
|
||||||
}, rp)
|
}, rp)
|
||||||
|
|
||||||
rp = parseRoute("/config/+.json")
|
rp = parseRoute("/config/+.json")
|
||||||
utils.AssertEqual(t, routeParser{
|
utils.AssertEqual(t, routeParser{
|
||||||
segs: []routeSegment{
|
segs: []routeSegment{
|
||||||
{Const: "/config/"},
|
{Const: "/config/"},
|
||||||
{IsParam: true, ParamName: "+1", IsGreedy: true, IsOptional: false, ComparePart: ".json", PartCount: 0},
|
{IsParam: true, ParamName: "+1", IsGreedy: true, IsOptional: false, ComparePart: ".json", PartCount: 1},
|
||||||
{Const: ".json", IsLast: true},
|
{Const: ".json", IsLast: true},
|
||||||
},
|
},
|
||||||
params: []string{"+1"},
|
params: []string{"+1"},
|
||||||
wildCardCount: 0,
|
plusCount: 1,
|
||||||
plusCount: 1,
|
|
||||||
}, rp)
|
}, rp)
|
||||||
|
|
||||||
rp = parseRoute("/api/:day.:month?.:year?")
|
rp = parseRoute("/api/:day.:month?.:year?")
|
||||||
@ -93,9 +86,20 @@ func Test_Path_parseRoute(t *testing.T) {
|
|||||||
{Const: "."},
|
{Const: "."},
|
||||||
{IsParam: true, ParamName: "year", IsOptional: true, IsLast: true},
|
{IsParam: true, ParamName: "year", IsOptional: true, IsLast: true},
|
||||||
},
|
},
|
||||||
params: []string{"day", "month", "year"},
|
params: []string{"day", "month", "year"},
|
||||||
wildCardCount: 0,
|
}, rp)
|
||||||
plusCount: 0,
|
|
||||||
|
rp = parseRoute("/*v1*/proxy")
|
||||||
|
utils.AssertEqual(t, routeParser{
|
||||||
|
segs: []routeSegment{
|
||||||
|
{Const: "/"},
|
||||||
|
{IsParam: true, ParamName: "*1", IsGreedy: true, IsOptional: true, ComparePart: "v1", PartCount: 1},
|
||||||
|
{Const: "v1"},
|
||||||
|
{IsParam: true, ParamName: "*2", IsGreedy: true, IsOptional: true, ComparePart: "/proxy", PartCount: 1},
|
||||||
|
{Const: "/proxy", IsLast: true},
|
||||||
|
},
|
||||||
|
params: []string{"*1", "*2"},
|
||||||
|
wildCardCount: 2,
|
||||||
}, rp)
|
}, rp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user