1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-22 14:20:54 +00:00

refactor router

This commit is contained in:
astaxie 2015-12-16 23:11:03 +08:00
parent 7dcbcf0748
commit 29752e2575
8 changed files with 206 additions and 165 deletions

View File

@ -33,6 +33,7 @@ var (
acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`) acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
maxParam = 50
) )
// BeegoInput operates the http request header, data, cookie and body. // BeegoInput operates the http request header, data, cookie and body.
@ -40,16 +41,17 @@ var (
type BeegoInput struct { type BeegoInput struct {
Context *Context Context *Context
CruSession session.Store CruSession session.Store
Params map[string]string pnames []string
Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. pvalues []string
data map[interface{}]interface{} // store some values in this context when calling context in filter or controller.
RequestBody []byte RequestBody []byte
} }
// NewInput return BeegoInput generated by Context. // NewInput return BeegoInput generated by Context.
func NewInput() *BeegoInput { func NewInput() *BeegoInput {
return &BeegoInput{ return &BeegoInput{
Params: make(map[string]string), pvalues: make([]string, maxParam),
Data: make(map[interface{}]interface{}), data: make(map[interface{}]interface{}),
} }
} }
@ -57,8 +59,8 @@ func NewInput() *BeegoInput {
func (input *BeegoInput) Reset(ctx *Context) { func (input *BeegoInput) Reset(ctx *Context) {
input.Context = ctx input.Context = ctx
input.CruSession = nil input.CruSession = nil
input.Params = make(map[string]string) input.pnames = input.pnames[:0]
input.Data = make(map[interface{}]interface{}) input.data = nil
input.RequestBody = []byte{} input.RequestBody = []byte{}
} }
@ -254,14 +256,27 @@ func (input *BeegoInput) UserAgent() string {
return input.Header("User-Agent") return input.Header("User-Agent")
} }
// ParamsLen return the length of the params
func (input *BeegoInput) ParamsLen() int {
return len(input.pnames)
}
// Param returns router param by a given key. // Param returns router param by a given key.
func (input *BeegoInput) Param(key string) string { func (input *BeegoInput) Param(key string) string {
if v, ok := input.Params[key]; ok { for i, v := range input.pnames {
return v if v == key {
return input.pvalues[i]
}
} }
return "" return ""
} }
// SetParam will set the param with key and value
func (input *BeegoInput) SetParam(key, val string) {
input.pvalues[len(input.pnames)] = val
input.pnames = append(input.pnames, key)
}
// Query returns input data item string by a given string. // Query returns input data item string by a given string.
func (input *BeegoInput) Query(key string) string { func (input *BeegoInput) Query(key string) string {
if val := input.Param(key); val != "" { if val := input.Param(key); val != "" {
@ -305,9 +320,17 @@ func (input *BeegoInput) CopyBody() []byte {
return requestbody return requestbody
} }
// Data return the implicit data in the input
func (input *BeegoInput) Data() map[interface{}]interface{} {
if input.data == nil {
input.data = make(map[interface{}]interface{})
}
return input.data
}
// GetData returns the stored data in this context. // GetData returns the stored data in this context.
func (input *BeegoInput) GetData(key interface{}) interface{} { func (input *BeegoInput) GetData(key interface{}) interface{} {
if v, ok := input.Data[key]; ok { if v, ok := input.data[key]; ok {
return v return v
} }
return nil return nil
@ -316,7 +339,10 @@ func (input *BeegoInput) GetData(key interface{}) interface{} {
// SetData stores data with given key in this context. // SetData stores data with given key in this context.
// This data are only available in this context. // This data are only available in this context.
func (input *BeegoInput) SetData(key, val interface{}) { func (input *BeegoInput) SetData(key, val interface{}) {
input.Data[key] = val if input.data == nil {
input.data = make(map[interface{}]interface{})
}
input.data[key] = val
} }
// ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type // ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type

View File

@ -105,7 +105,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin
c.AppController = app c.AppController = app
c.EnableRender = true c.EnableRender = true
c.EnableXSRF = true c.EnableXSRF = true
c.Data = ctx.Input.Data c.Data = ctx.Input.Data()
c.methodMapping = make(map[string]func()) c.methodMapping = make(map[string]func())
} }

View File

@ -21,7 +21,8 @@ import (
) )
func TestGetInt(t *testing.T) { func TestGetInt(t *testing.T) {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}} i := context.NewInput()
i.SetParam("age", "40")
ctx := &context.Context{Input: i} ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx} ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt("age") val, _ := ctrlr.GetInt("age")
@ -31,7 +32,8 @@ func TestGetInt(t *testing.T) {
} }
func TestGetInt8(t *testing.T) { func TestGetInt8(t *testing.T) {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}} i := context.NewInput()
i.SetParam("age", "40")
ctx := &context.Context{Input: i} ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx} ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt8("age") val, _ := ctrlr.GetInt8("age")
@ -42,7 +44,8 @@ func TestGetInt8(t *testing.T) {
} }
func TestGetInt16(t *testing.T) { func TestGetInt16(t *testing.T) {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}} i := context.NewInput()
i.SetParam("age", "40")
ctx := &context.Context{Input: i} ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx} ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt16("age") val, _ := ctrlr.GetInt16("age")
@ -52,7 +55,8 @@ func TestGetInt16(t *testing.T) {
} }
func TestGetInt32(t *testing.T) { func TestGetInt32(t *testing.T) {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}} i := context.NewInput()
i.SetParam("age", "40")
ctx := &context.Context{Input: i} ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx} ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt32("age") val, _ := ctrlr.GetInt32("age")
@ -62,7 +66,8 @@ func TestGetInt32(t *testing.T) {
} }
func TestGetInt64(t *testing.T) { func TestGetInt64(t *testing.T) {
i := &context.BeegoInput{Params: map[string]string{"age": "40"}} i := context.NewInput()
i.SetParam("age", "40")
ctx := &context.Context{Input: i} ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx} ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetInt64("age") val, _ := ctrlr.GetInt64("age")

View File

@ -29,7 +29,7 @@ func init() {
} }
var FilterUser = func(ctx *context.Context) { var FilterUser = func(ctx *context.Context) {
ctx.Output.Body([]byte("i am " + ctx.Input.Params[":last"] + ctx.Input.Params[":first"])) ctx.Output.Body([]byte("i am " + ctx.Input.Param(":last") + ctx.Input.Param(":first")))
} }
func TestFilter(t *testing.T) { func TestFilter(t *testing.T) {

View File

@ -466,8 +466,8 @@ func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) stri
} }
func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) { func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
for k, subtree := range t.fixrouters { for _, subtree := range t.fixrouters {
u := path.Join(url, k) u := path.Join(url, subtree.prefix)
ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod) ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
if ok { if ok {
return ok, u return ok, u
@ -675,10 +675,10 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if r, ok := runObject.(*controllerInfo); ok { if r, ok := runObject.(*controllerInfo); ok {
routerInfo = r routerInfo = r
findrouter = true findrouter = true
if splat, ok := context.Input.Params[":splat"]; ok { if splat := context.Input.Param(":splat"); splat != "" {
splatlist := strings.Split(splat, "/") splatlist := strings.Split(splat, "/")
for k, v := range splatlist { for k, v := range splatlist {
context.Input.Params[strconv.Itoa(k)] = v context.Input.SetParam(strconv.Itoa(k), v)
} }
} }
} }

View File

@ -45,7 +45,7 @@ func (tc *TestController) List() {
} }
func (tc *TestController) Params() { func (tc *TestController) Params() {
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Params["0"] + tc.Ctx.Input.Params["1"] + tc.Ctx.Input.Params["2"])) tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param("0") + tc.Ctx.Input.Param("1") + tc.Ctx.Input.Param("2")))
} }
func (tc *TestController) Myext() { func (tc *TestController) Myext() {

255
tree.go
View File

@ -23,26 +23,28 @@ import (
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
var (
allowSuffixExt = []string{".json", ".xml", ".html"}
)
// Tree has three elements: FixRouter/wildcard/leaves // Tree has three elements: FixRouter/wildcard/leaves
// fixRouter sotres Fixed Router // fixRouter sotres Fixed Router
// wildcard stores params // wildcard stores params
// leaves store the endpoint information // leaves store the endpoint information
type Tree struct { type Tree struct {
//prefix set for static router
prefix string
//search fix route first //search fix route first
fixrouters map[string]*Tree fixrouters []*Tree
//if set, failure to match fixrouters search then search wildcard //if set, failure to match fixrouters search then search wildcard
wildcard *Tree wildcard *Tree
//if set, failure to match wildcard search //if set, failure to match wildcard search
leaves []*leafInfo leaves []*leafInfo
} }
// NewTree return a new Tree // NewTree return a new Tree
func NewTree() *Tree { func NewTree() *Tree {
return &Tree{ return &Tree{}
fixrouters: make(map[string]*Tree),
}
} }
// AddTree will add tree to the exist Tree // AddTree will add tree to the exist Tree
@ -57,15 +59,31 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st
} }
seg := segments[0] seg := segments[0]
iswild, params, regexpStr := splitSegment(seg) iswild, params, regexpStr := splitSegment(seg)
// if it's ? meaning can igone this, so add one more rule for it
if len(params) > 0 && params[0] == ":" {
params = params[1:]
if len(segments[1:]) > 0 {
t.addtree(segments[1:], tree, append(wildcards, params...), reg)
} else {
filterTreeWithPrefix(tree, wildcards, reg)
}
}
//Rule: /login/*/access match /login/2009/11/access
//if already has *, and when loop the access, should as a regexpStr
if !iswild && utils.InSlice(":splat", wildcards) {
iswild = true
regexpStr = seg
}
//Rule: /user/:id/*
if seg == "*" && len(wildcards) > 0 && reg == "" {
regexpStr = "(.+)"
}
if len(segments) == 1 { if len(segments) == 1 {
if iswild { if iswild {
if regexpStr != "" { if regexpStr != "" {
if reg == "" { if reg == "" {
rr := "" rr := ""
for _, w := range wildcards { for _, w := range wildcards {
if w == "." || w == ":" {
continue
}
if w == ":splat" { if w == ":splat" {
rr = rr + "(.+)/" rr = rr + "(.+)/"
} else { } else {
@ -94,10 +112,12 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st
} else { } else {
reg = strings.Trim(reg+"/"+regexpStr, "/") reg = strings.Trim(reg+"/"+regexpStr, "/")
filterTreeWithPrefix(tree, append(wildcards, params...), reg) filterTreeWithPrefix(tree, append(wildcards, params...), reg)
t.fixrouters[seg] = tree tree.prefix = seg
t.fixrouters = append(t.fixrouters, tree)
} }
return return
} }
if iswild { if iswild {
if t.wildcard == nil { if t.wildcard == nil {
t.wildcard = NewTree() t.wildcard = NewTree()
@ -106,9 +126,6 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st
if reg == "" { if reg == "" {
rr := "" rr := ""
for _, w := range wildcards { for _, w := range wildcards {
if w == "." || w == ":" {
continue
}
if w == ":splat" { if w == ":splat" {
rr = rr + "(.+)/" rr = rr + "(.+)/"
} else { } else {
@ -122,20 +139,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st
} else if reg != "" { } else if reg != "" {
if seg == "*.*" { if seg == "*.*" {
regexpStr = "([^.]+).(.+)" regexpStr = "([^.]+).(.+)"
params = params[1:]
} else { } else {
for _, w := range params { for _ = range params {
if w == "." || w == ":" {
continue
}
regexpStr = "([^/]+)/" + regexpStr regexpStr = "([^/]+)/" + regexpStr
} }
} }
} else {
if seg == "*.*" {
params = params[1:]
}
} }
reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/") reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/")
t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg)
} else { } else {
subTree := NewTree() subTree := NewTree()
t.fixrouters[seg] = subTree subTree.prefix = seg
t.fixrouters = append(t.fixrouters, subTree)
subTree.addtree(segments[1:], tree, append(wildcards, params...), reg) subTree.addtree(segments[1:], tree, append(wildcards, params...), reg)
} }
} }
@ -154,9 +174,6 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$") l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$")
} else { } else {
for _, v := range l.wildcards { for _, v := range l.wildcards {
if v == ":" || v == "." {
continue
}
if v == ":splat" { if v == ":splat" {
reg = reg + "/(.+)" reg = reg + "/(.+)"
} else { } else {
@ -166,21 +183,10 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
l.regexps = regexp.MustCompile("^" + reg + "$") l.regexps = regexp.MustCompile("^" + reg + "$")
l.wildcards = append(wildcards, l.wildcards...) l.wildcards = append(wildcards, l.wildcards...)
} }
filterCards := []string{}
for _, v := range l.wildcards {
if v == ":" || v == "." {
continue
}
filterCards = append(filterCards, v)
}
l.wildcards = filterCards
} else { } else {
l.wildcards = append(wildcards, l.wildcards...) l.wildcards = append(wildcards, l.wildcards...)
if l.regexps != nil { if l.regexps != nil {
for _, w := range wildcards { for _, w := range wildcards {
if w == "." || w == ":" {
continue
}
if w == ":splat" { if w == ":splat" {
reg = "(.+)/" + reg reg = "(.+)/" + reg
} else { } else {
@ -203,32 +209,26 @@ func (t *Tree) AddRouter(pattern string, runObject interface{}) {
func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) { func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) {
if len(segments) == 0 { if len(segments) == 0 {
if reg != "" { if reg != "" {
filterCards := []string{} t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")})
for _, v := range wildcards {
if v == ":" || v == "." {
continue
}
filterCards = append(filterCards, v)
}
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: filterCards, regexps: regexp.MustCompile("^" + reg + "$")})
} else { } else {
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards})
} }
for i, v := range wildcards {
if v == ":" {
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards[:i+1]})
}
}
} else { } else {
seg := segments[0] seg := segments[0]
iswild, params, regexpStr := splitSegment(seg) iswild, params, regexpStr := splitSegment(seg)
//for the router /login/*/access match /login/2009/11/access // if it's ? meaning can igone this, so add one more rule for it
if len(params) > 0 && params[0] == ":" {
t.addseg(segments[1:], route, wildcards, reg)
params = params[1:]
}
//Rule: /login/*/access match /login/2009/11/access
//if already has *, and when loop the access, should as a regexpStr
if !iswild && utils.InSlice(":splat", wildcards) { if !iswild && utils.InSlice(":splat", wildcards) {
iswild = true iswild = true
regexpStr = seg regexpStr = seg
} }
//Rule: /user/:id/*
if seg == "*" && len(wildcards) > 0 && reg == "" { if seg == "*" && len(wildcards) > 0 && reg == "" {
iswild = true
regexpStr = "(.+)" regexpStr = "(.+)"
} }
if iswild { if iswild {
@ -239,9 +239,6 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string,
if reg == "" { if reg == "" {
rr := "" rr := ""
for _, w := range wildcards { for _, w := range wildcards {
if w == "." || w == ":" {
continue
}
if w == ":splat" { if w == ":splat" {
rr = rr + "(.+)/" rr = rr + "(.+)/"
} else { } else {
@ -255,22 +252,31 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string,
} else if reg != "" { } else if reg != "" {
if seg == "*.*" { if seg == "*.*" {
regexpStr = "/([^.]+).(.+)" regexpStr = "/([^.]+).(.+)"
params = params[1:]
} else { } else {
for _, w := range params { for _ = range params {
if w == "." || w == ":" {
continue
}
regexpStr = "/([^/]+)" + regexpStr regexpStr = "/([^/]+)" + regexpStr
} }
} }
} else {
if seg == "*.*" {
params = params[1:]
}
} }
t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
} else { } else {
subTree, ok := t.fixrouters[seg] var ok bool
var subTree *Tree
for _, subTree = range t.fixrouters {
if t.prefix == seg {
ok = true
break
}
}
if !ok { if !ok {
subTree = NewTree() subTree = NewTree()
t.fixrouters[seg] = subTree subTree.prefix = seg
t.fixrouters = append(t.fixrouters, subTree)
} }
subTree.addseg(segments[1:], route, wildcards, reg) subTree.addseg(segments[1:], route, wildcards, reg)
} }
@ -282,13 +288,18 @@ func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{
if len(pattern) == 0 || pattern[0] != '/' { if len(pattern) == 0 || pattern[0] != '/' {
return nil return nil
} }
return t.match(pattern, nil, ctx)
return t.match(splitPath(pattern), nil, ctx)
} }
func (t *Tree) match(segments []string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) {
if len(pattern) > 0 {
i := 0
for ; i < len(pattern) && pattern[i] == '/'; i++ {
}
pattern = pattern[i:]
}
// Handle leaf nodes: // Handle leaf nodes:
if len(segments) == 0 { if len(pattern) == 0 {
for _, l := range t.leaves { for _, l := range t.leaves {
if ok := l.match(wildcardValues, ctx); ok { if ok := l.match(wildcardValues, ctx); ok {
return l.runObject return l.runObject
@ -303,28 +314,47 @@ func (t *Tree) match(segments []string, wildcardValues []string, ctx *context.Co
} }
return nil return nil
} }
var seg string
seg, segs := segments[0], segments[1:] i, l := 0, len(pattern)
for ; i < l && pattern[i] != '/'; i++ {
subTree, ok := t.fixrouters[seg] }
if ok { if i == 0 {
runObject = subTree.match(segs, wildcardValues, ctx) seg = pattern
} else if len(segs) == 0 { //.json .xml pattern = ""
if subindex := strings.LastIndex(seg, "."); subindex != -1 { } else {
subTree, ok = t.fixrouters[seg[:subindex]] seg = pattern[:i]
if ok { pattern = pattern[i:]
runObject = subTree.match(segs, wildcardValues, ctx) }
for _, subTree := range t.fixrouters {
if subTree.prefix == seg {
runObject = subTree.match(pattern, wildcardValues, ctx)
if runObject != nil { if runObject != nil {
ctx.Input.Params[":ext"] = seg[subindex+1:] break
return runObject }
}
}
if runObject == nil {
// Filter the .json .xml .html extension
for _, str := range allowSuffixExt {
if strings.HasSuffix(seg, str) {
for _, subTree := range t.fixrouters {
if subTree.prefix == seg[:len(seg)-len(str)] {
runObject = subTree.match(pattern, wildcardValues, ctx)
if runObject != nil {
ctx.Input.SetParam(":ext", str[1:])
}
}
} }
} }
} }
} }
if runObject == nil && t.wildcard != nil { if runObject == nil && t.wildcard != nil {
runObject = t.wildcard.match(segs, append(wildcardValues, seg), ctx) runObject = t.wildcard.match(pattern, append(wildcardValues, seg), ctx)
} }
if runObject == nil { if runObject == nil {
segments := splitPath(pattern)
wildcardValues = append(wildcardValues, seg)
for _, l := range t.leaves { for _, l := range t.leaves {
if ok := l.match(append(wildcardValues, segments...), ctx); ok { if ok := l.match(append(wildcardValues, segments...), ctx); ok {
return l.runObject return l.runObject
@ -345,70 +375,47 @@ type leafInfo struct {
} }
func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok bool) { func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok bool) {
//fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps)
if leaf.regexps == nil { if leaf.regexps == nil {
// has error if len(wildcardValues) == 0 { // static path
if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 {
if utils.InSlice(":", leaf.wildcards) {
j := 0
for _, v := range leaf.wildcards {
if v == ":" {
continue
}
ctx.Input.Params[v] = ""
j++
}
return true
}
return false
} else if len(wildcardValues) == 0 { // static path
return true return true
} }
// match * // match *
if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
ctx.Input.Params[":splat"] = path.Join(wildcardValues...) ctx.Input.SetParam(":splat", path.Join(wildcardValues...))
return true return true
} }
// match *.* // match *.* or :id
if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." { if len(leaf.wildcards) >= 2 && leaf.wildcards[len(leaf.wildcards)-2] == ":path" && leaf.wildcards[len(leaf.wildcards)-1] == ":ext" {
if len(leaf.wildcards) == 2 {
lastone := wildcardValues[len(wildcardValues)-1] lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2) strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 { if len(strs) == 2 {
ctx.Input.Params[":ext"] = strs[1] ctx.Input.SetParam(":ext", strs[1])
} else {
ctx.Input.Params[":ext"] = ""
} }
ctx.Input.Params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0] ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[:len(wildcardValues)-1]...), strs[0]))
return true
} else if len(wildcardValues) < 2 {
return false
}
var index int
for index = 0; index < len(leaf.wildcards)-2; index++ {
ctx.Input.SetParam(leaf.wildcards[index], wildcardValues[index])
}
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
ctx.Input.SetParam(":ext", strs[1])
}
ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0]))
return true return true
} }
// match :id // match :id
j := 0 if len(leaf.wildcards) != len(wildcardValues) {
for _, v := range leaf.wildcards {
if v == ":" {
continue
}
if v == "." {
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
ctx.Input.Params[":ext"] = strs[1]
} else {
ctx.Input.Params[":ext"] = ""
}
if len(wildcardValues[j:]) == 1 {
ctx.Input.Params[":path"] = strs[0]
} else {
ctx.Input.Params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0]
}
return true
}
if len(wildcardValues) <= j {
return false return false
} }
ctx.Input.Params[v] = wildcardValues[j] for j, v := range leaf.wildcards {
j++ ctx.Input.SetParam(v, wildcardValues[j])
}
if len(ctx.Input.Params) != len(wildcardValues) {
return false
} }
return true return true
} }
@ -418,7 +425,7 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b
} }
matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...)) matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
for i, match := range matches[1:] { for i, match := range matches[1:] {
ctx.Input.Params[leaf.wildcards[i]] = match ctx.Input.SetParam(leaf.wildcards[i], match)
} }
return true return true
} }

View File

@ -45,6 +45,7 @@ func init() {
routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}}) routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}}) routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}})
routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}}) routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}})
routers = append(routers, testinfo{"/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}})
routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}}) routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}})
routers = append(routers, testinfo{"/thumbnail/:size/uploads/*", routers = append(routers, testinfo{"/thumbnail/:size/uploads/*",
"/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg", "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg",
@ -76,14 +77,14 @@ func TestTreeRouters(t *testing.T) {
ctx := context.NewContext() ctx := context.NewContext()
obj := tr.Match(r.requesturl, ctx) obj := tr.Match(r.requesturl, ctx)
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal(r.url + " can't get obj ") t.Fatal(r.url+" can't get obj, Expect ", r.requesturl)
} }
if r.params != nil { if r.params != nil {
for k, v := range r.params { for k, v := range r.params {
if vv, ok := ctx.Input.Params[k]; !ok { if vv := ctx.Input.Param(k); vv != v {
t.Fatal("The Rule: " + r.url + "\nThe RequestURL:" + r.requesturl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv)
} else if vv == "" && v != "" {
t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k) t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k)
} else if vv != v {
t.Fatal(r.url + " " + r.requesturl + " should be:" + v + " get param:" + vv)
} }
} }
} }
@ -101,10 +102,10 @@ func TestAddTree(t *testing.T) {
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/zl/shop/:id/account can't get obj ") t.Fatal("/v1/zl/shop/:id/account can't get obj ")
} }
if len(ctx.Input.Params) == 0 { if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error") t.Fatal("get param error")
} }
if ctx.Input.Params[":id"] != "123" { if ctx.Input.Param(":id") != "123" {
t.Fatal("get :id param error") t.Fatal("get :id param error")
} }
ctx.Input.Reset(ctx) ctx.Input.Reset(ctx)
@ -112,10 +113,10 @@ func TestAddTree(t *testing.T) {
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
} }
if len(ctx.Input.Params) == 0 { if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error") t.Fatal("get param error")
} }
if ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":id"] != "1" || ctx.Input.Params[":page"] != "12" { if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" {
t.Fatal("get :sd :id :page param error") t.Fatal("get :sd :id :page param error")
} }
@ -126,10 +127,10 @@ func TestAddTree(t *testing.T) {
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/:shopid/shop/:id/account can't get obj ") t.Fatal("/v1/:shopid/shop/:id/account can't get obj ")
} }
if len(ctx.Input.Params) == 0 { if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error") t.Fatal("get param error")
} }
if ctx.Input.Params[":id"] != "123" || ctx.Input.Params[":shopid"] != "zl" { if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":shopid") != "zl" {
t.Fatal("get :id :shopid param error") t.Fatal("get :id :shopid param error")
} }
ctx.Input.Reset(ctx) ctx.Input.Reset(ctx)
@ -137,10 +138,10 @@ func TestAddTree(t *testing.T) {
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
} }
if len(ctx.Input.Params) == 0 { if ctx.Input.ParamsLen() == 0 {
t.Fatal("get :shopid param error") t.Fatal("get :shopid param error")
} }
if ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":id"] != "1" || ctx.Input.Params[":page"] != "12" || ctx.Input.Params[":shopid"] != "zl" { if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" || ctx.Input.Param(":shopid") != "zl" {
t.Fatal("get :sd :id :page :shopid param error") t.Fatal("get :sd :id :page :shopid param error")
} }
} }
@ -156,10 +157,10 @@ func TestAddTree2(t *testing.T) {
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ") t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ")
} }
if len(ctx.Input.Params) == 0 { if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error") t.Fatal("get param error")
} }
if ctx.Input.Params[":id"] != "123" || ctx.Input.Params[":prefix"] != "zl" || ctx.Input.Params[":version"] != "v1" { if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":prefix") != "zl" || ctx.Input.Param(":version") != "v1" {
t.Fatal("get :id :prefix :version param error") t.Fatal("get :id :prefix :version param error")
} }
} }
@ -175,10 +176,10 @@ func TestAddTree3(t *testing.T) {
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/table/:num/shop/:sd/account can't get obj ") t.Fatal("/table/:num/shop/:sd/account can't get obj ")
} }
if len(ctx.Input.Params) == 0 { if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error") t.Fatal("get param error")
} }
if ctx.Input.Params[":num"] != "123" || ctx.Input.Params[":sd"] != "123" { if ctx.Input.Param(":num") != "123" || ctx.Input.Param(":sd") != "123" {
t.Fatal("get :num :sd param error") t.Fatal("get :num :sd param error")
} }
ctx.Input.Reset(ctx) ctx.Input.Reset(ctx)
@ -199,10 +200,12 @@ func TestAddTree4(t *testing.T) {
if obj == nil || obj.(string) != "astaxie" { if obj == nil || obj.(string) != "astaxie" {
t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ") t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ")
} }
if len(ctx.Input.Params) == 0 { if ctx.Input.ParamsLen() == 0 {
t.Fatal("get param error") t.Fatal("get param error")
} }
if ctx.Input.Params[":info"] != "12" || ctx.Input.Params[":num"] != "123" || ctx.Input.Params[":id"] != "456" || ctx.Input.Params[":sd"] != "123" || ctx.Input.Params[":account"] != "account" { if ctx.Input.Param(":info") != "12" || ctx.Input.Param(":num") != "123" ||
ctx.Input.Param(":id") != "456" || ctx.Input.Param(":sd") != "123" ||
ctx.Input.Param(":account") != "account" {
t.Fatal("get :info :num :id :sd :account param error") t.Fatal("get :info :num :id :sd :account param error")
} }
ctx.Input.Reset(ctx) ctx.Input.Reset(ctx)