diff --git a/context/input.go b/context/input.go index 9649e14f..482b431a 100644 --- a/context/input.go +++ b/context/input.go @@ -17,6 +17,7 @@ package context import ( "bytes" "errors" + "io" "io/ioutil" "net/url" "reflect" @@ -33,6 +34,7 @@ var ( acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`) + maxParam = 50 ) // BeegoInput operates the http request header, data, cookie and body. @@ -40,16 +42,18 @@ var ( type BeegoInput struct { Context *Context CruSession session.Store - Params map[string]string - Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. + pnames []string + pvalues []string + data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. RequestBody []byte } // NewInput return BeegoInput generated by Context. func NewInput() *BeegoInput { return &BeegoInput{ - Params: make(map[string]string), - Data: make(map[interface{}]interface{}), + pnames: make([]string, 0, maxParam), + pvalues: make([]string, 0, maxParam), + data: make(map[interface{}]interface{}), } } @@ -57,8 +61,9 @@ func NewInput() *BeegoInput { func (input *BeegoInput) Reset(ctx *Context) { input.Context = ctx input.CruSession = nil - input.Params = make(map[string]string) - input.Data = make(map[interface{}]interface{}) + input.pnames = input.pnames[:0] + input.pvalues = input.pvalues[:0] + input.data = nil input.RequestBody = []byte{} } @@ -254,14 +259,27 @@ func (input *BeegoInput) UserAgent() string { 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. func (input *BeegoInput) Param(key string) string { - if v, ok := input.Params[key]; ok { - return v + for i, v := range input.pnames { + if v == key && i <= len(input.pvalues) { + return input.pvalues[i] + } } return "" } +// SetParam will set the param with key and value +func (input *BeegoInput) SetParam(key, val string) { + input.pvalues = append(input.pvalues, val) + input.pnames = append(input.pnames, key) +} + // Query returns input data item string by a given string. func (input *BeegoInput) Query(key string) string { if val := input.Param(key); val != "" { @@ -296,8 +314,9 @@ func (input *BeegoInput) Session(key interface{}) interface{} { } // CopyBody returns the raw request body data as bytes. -func (input *BeegoInput) CopyBody() []byte { - requestbody, _ := ioutil.ReadAll(input.Context.Request.Body) +func (input *BeegoInput) CopyBody(MaxMemory int64) []byte { + safe := &io.LimitedReader{R:input.Context.Request.Body, N:MaxMemory} + requestbody, _ := ioutil.ReadAll(safe) input.Context.Request.Body.Close() bf := bytes.NewBuffer(requestbody) input.Context.Request.Body = ioutil.NopCloser(bf) @@ -305,9 +324,17 @@ func (input *BeegoInput) CopyBody() []byte { 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. func (input *BeegoInput) GetData(key interface{}) interface{} { - if v, ok := input.Data[key]; ok { + if v, ok := input.data[key]; ok { return v } return nil @@ -316,7 +343,10 @@ func (input *BeegoInput) GetData(key interface{}) interface{} { // SetData stores data with given key in this context. // This data are only available in this context. 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 diff --git a/controller.go b/controller.go index 826ad54d..ebe99c5c 100644 --- a/controller.go +++ b/controller.go @@ -105,7 +105,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin c.AppController = app c.EnableRender = true c.EnableXSRF = true - c.Data = ctx.Input.Data + c.Data = ctx.Input.Data() c.methodMapping = make(map[string]func()) } diff --git a/controller_test.go b/controller_test.go index ebf38b2c..51d3a5b7 100644 --- a/controller_test.go +++ b/controller_test.go @@ -21,7 +21,8 @@ import ( ) 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} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt("age") @@ -31,7 +32,8 @@ func TestGetInt(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} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt8("age") @@ -42,7 +44,8 @@ func TestGetInt8(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} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt16("age") @@ -52,7 +55,8 @@ func TestGetInt16(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} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt32("age") @@ -62,7 +66,8 @@ func TestGetInt32(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} ctrlr := Controller{Ctx: ctx} val, _ := ctrlr.GetInt64("age") diff --git a/filter.go b/filter.go index 7dbc7028..cefd74a1 100644 --- a/filter.go +++ b/filter.go @@ -32,13 +32,13 @@ type FilterRouter struct { // ValidRouter checks if the current request is matched by this filter. // If the request is matched, the values of the URL parameters defined // by the filter pattern are also returned. -func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) { - isok, params := f.tree.Match(url) +func (f *FilterRouter) ValidRouter(url string, ctx *context.Context) bool { + isok := f.tree.Match(url, ctx) if isok == nil { - return false, nil + return false } if isok, ok := isok.(bool); ok { - return isok, params + return isok } - return false, nil + return false } diff --git a/filter_test.go b/filter_test.go index b1cd9c94..d9928d8d 100644 --- a/filter_test.go +++ b/filter_test.go @@ -29,7 +29,7 @@ func init() { } 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) { diff --git a/router.go b/router.go index 01dae8aa..8d724ac6 100644 --- a/router.go +++ b/router.go @@ -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) { - for k, subtree := range t.fixrouters { - u := path.Join(url, k) + for _, subtree := range t.fixrouters { + u := path.Join(url, subtree.prefix) ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod) if ok { return ok, u @@ -583,13 +583,7 @@ func (p *ControllerRegister) execFilter(context *beecontext.Context, pos int, ur if filterR.returnOnOutput && context.ResponseWriter.Started { return true } - if ok, params := filterR.ValidRouter(urlPath); ok { - for k, v := range params { - if context.Input.Params == nil { - context.Input.Params = make(map[string]string) - } - context.Input.Params[k] = v - } + if ok := filterR.ValidRouter(urlPath, context); ok { filterR.filterFunc(context) } if filterR.returnOnOutput && context.ResponseWriter.Started { @@ -659,7 +653,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) if r.Method != "GET" && r.Method != "HEAD" { if BConfig.CopyRequestBody && !context.Input.IsUpload() { - context.Input.CopyBody() + context.Input.CopyBody(BConfig.MaxMemory) } context.Input.ParseFormOrMulitForm(BConfig.MaxMemory) } @@ -677,19 +671,16 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) httpMethod = "DELETE" } if t, ok := p.routers[httpMethod]; ok { - runObject, p := t.Match(urlPath) + runObject := t.Match(urlPath, context) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r findrouter = true - if splat, ok := p[":splat"]; ok { + if splat := context.Input.Param(":splat"); splat != "" { splatlist := strings.Split(splat, "/") for k, v := range splatlist { - p[strconv.Itoa(k)] = v + context.Input.SetParam(strconv.Itoa(k), v) } } - if p != nil { - context.Input.Params = p - } } } diff --git a/router_test.go b/router_test.go index 9598f99c..b0ae7a18 100644 --- a/router_test.go +++ b/router_test.go @@ -45,7 +45,7 @@ func (tc *TestController) List() { } 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() { diff --git a/tree.go b/tree.go index bbda2840..2092016f 100644 --- a/tree.go +++ b/tree.go @@ -19,29 +19,32 @@ import ( "regexp" "strings" + "github.com/astaxie/beego/context" "github.com/astaxie/beego/utils" ) +var ( + allowSuffixExt = []string{".json", ".xml", ".html"} +) + // Tree has three elements: FixRouter/wildcard/leaves // fixRouter sotres Fixed Router // wildcard stores params // leaves store the endpoint information type Tree struct { + //prefix set for static router + prefix string //search fix route first - fixrouters map[string]*Tree - + fixrouters []*Tree //if set, failure to match fixrouters search then search wildcard wildcard *Tree - //if set, failure to match wildcard search leaves []*leafInfo } // NewTree return a new Tree func NewTree() *Tree { - return &Tree{ - fixrouters: make(map[string]*Tree), - } + return &Tree{} } // AddTree will add tree to the exist Tree @@ -56,15 +59,31 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } seg := segments[0] 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 iswild { if regexpStr != "" { if reg == "" { rr := "" for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { rr = rr + "(.+)/" } else { @@ -93,10 +112,12 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } else { reg = strings.Trim(reg+"/"+regexpStr, "/") filterTreeWithPrefix(tree, append(wildcards, params...), reg) - t.fixrouters[seg] = tree + tree.prefix = seg + t.fixrouters = append(t.fixrouters, tree) } return } + if iswild { if t.wildcard == nil { t.wildcard = NewTree() @@ -105,9 +126,6 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st if reg == "" { rr := "" for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { rr = rr + "(.+)/" } else { @@ -121,20 +139,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } else if reg != "" { if seg == "*.*" { regexpStr = "([^.]+).(.+)" + params = params[1:] } else { - for _, w := range params { - if w == "." || w == ":" { - continue - } + for _ = range params { regexpStr = "([^/]+)/" + regexpStr } } + } else { + if seg == "*.*" { + params = params[1:] + } } reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/") t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) } else { subTree := NewTree() - t.fixrouters[seg] = subTree + subTree.prefix = seg + t.fixrouters = append(t.fixrouters, subTree) subTree.addtree(segments[1:], tree, append(wildcards, params...), reg) } } @@ -153,9 +174,6 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) { l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$") } else { for _, v := range l.wildcards { - if v == ":" || v == "." { - continue - } if v == ":splat" { reg = reg + "/(.+)" } else { @@ -165,21 +183,10 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) { l.regexps = regexp.MustCompile("^" + reg + "$") 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 { l.wildcards = append(wildcards, l.wildcards...) if l.regexps != nil { for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { reg = "(.+)/" + reg } else { @@ -202,32 +209,26 @@ func (t *Tree) AddRouter(pattern string, runObject interface{}) { func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) { if len(segments) == 0 { if reg != "" { - filterCards := []string{} - 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 + "$")}) + t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")}) } else { 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 { seg := segments[0] 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) { iswild = true regexpStr = seg } + //Rule: /user/:id/* if seg == "*" && len(wildcards) > 0 && reg == "" { - iswild = true regexpStr = "(.+)" } if iswild { @@ -238,9 +239,6 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, if reg == "" { rr := "" for _, w := range wildcards { - if w == "." || w == ":" { - continue - } if w == ":splat" { rr = rr + "(.+)/" } else { @@ -254,22 +252,31 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, } else if reg != "" { if seg == "*.*" { regexpStr = "/([^.]+).(.+)" + params = params[1:] } else { - for _, w := range params { - - if w == "." || w == ":" { - continue - } + for _ = range params { regexpStr = "/([^/]+)" + regexpStr } } + } else { + if seg == "*.*" { + params = params[1:] + } } t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) } 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 { subTree = NewTree() - t.fixrouters[seg] = subTree + subTree.prefix = seg + t.fixrouters = append(t.fixrouters, subTree) } subTree.addseg(segments[1:], route, wildcards, reg) } @@ -277,64 +284,85 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, } // Match router to runObject & params -func (t *Tree) Match(pattern string) (runObject interface{}, params map[string]string) { +func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{}) { if len(pattern) == 0 || pattern[0] != '/' { - return nil, nil + return nil } - - return t.match(splitPath(pattern), nil) + w := make([]string, 0, 20) + return t.match(pattern, w, ctx) } -func (t *Tree) match(segments []string, wildcardValues []string) (runObject interface{}, params map[string]string) { +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: - if len(segments) == 0 { + if len(pattern) == 0 { for _, l := range t.leaves { - if ok, pa := l.match(wildcardValues); ok { - return l.runObject, pa + if ok := l.match(wildcardValues, ctx); ok { + return l.runObject } } if t.wildcard != nil { for _, l := range t.wildcard.leaves { - if ok, pa := l.match(wildcardValues); ok { - return l.runObject, pa + if ok := l.match(wildcardValues, ctx); ok { + return l.runObject } } - } - return nil, nil + return nil } - - seg, segs := segments[0], segments[1:] - - subTree, ok := t.fixrouters[seg] - if ok { - runObject, params = subTree.match(segs, wildcardValues) - } else if len(segs) == 0 { //.json .xml - if subindex := strings.LastIndex(seg, "."); subindex != -1 { - subTree, ok = t.fixrouters[seg[:subindex]] - if ok { - runObject, params = subTree.match(segs, wildcardValues) - if runObject != nil { - if params == nil { - params = make(map[string]string) + var seg string + i, l := 0, len(pattern) + for ; i < l && pattern[i] != '/'; i++ { + } + if i == 0 { + seg = pattern + pattern = "" + } else { + seg = pattern[:i] + pattern = pattern[i:] + } + for _, subTree := range t.fixrouters { + if subTree.prefix == seg { + runObject = subTree.match(pattern, wildcardValues, ctx) + if runObject != nil { + break + } + } + } + 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:]) + } } - params[":ext"] = seg[subindex+1:] - return runObject, params } } } } if runObject == nil && t.wildcard != nil { - runObject, params = t.wildcard.match(segs, append(wildcardValues, seg)) + runObject = t.wildcard.match(pattern, append(wildcardValues, seg), ctx) } + if runObject == nil { + segments := splitPath(pattern) + wildcardValues = append(wildcardValues, seg) for _, l := range t.leaves { - if ok, pa := l.match(append(wildcardValues, segments...)); ok { - return l.runObject, pa + if ok := l.match(append(wildcardValues, segments...), ctx); ok { + return l.runObject } } } - return runObject, params + return runObject } type leafInfo struct { @@ -347,88 +375,60 @@ type leafInfo struct { runObject interface{} } -func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string]string) { +func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok bool) { + //fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps) if leaf.regexps == nil { - // has error - if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 { - if utils.InSlice(":", leaf.wildcards) { - params = make(map[string]string) - j := 0 - for _, v := range leaf.wildcards { - if v == ":" { - continue - } - params[v] = "" - j++ - } - return true, params - } - return false, nil - } else if len(wildcardValues) == 0 { // static path - return true, nil + if len(wildcardValues) == 0 { // static path + return true } // match * if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { - params = make(map[string]string) - params[":splat"] = path.Join(wildcardValues...) - return true, params + ctx.Input.SetParam(":splat", path.Join(wildcardValues...)) + return true } - // match *.* - if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." { - params = make(map[string]string) - lastone := wildcardValues[len(wildcardValues)-1] - strs := strings.SplitN(lastone, ".", 2) - if len(strs) == 2 { - params[":ext"] = strs[1] - } else { - params[":ext"] = "" - } - params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0] - return true, params - } - // match :id - params = make(map[string]string) - j := 0 - for _, v := range leaf.wildcards { - if v == ":" { - continue - } - if v == "." { + // match *.* or :id + 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] strs := strings.SplitN(lastone, ".", 2) if len(strs) == 2 { - params[":ext"] = strs[1] - } else { - params[":ext"] = "" + ctx.Input.SetParam(":ext", strs[1]) } - if len(wildcardValues[j:]) == 1 { - params[":path"] = strs[0] - } else { - params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0] - } - return true, params + ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[:len(wildcardValues)-1]...), strs[0])) + return true + } else if len(wildcardValues) < 2 { + return false } - if len(wildcardValues) <= j { - return false, nil + var index int + for index = 0; index < len(leaf.wildcards)-2; index++ { + ctx.Input.SetParam(leaf.wildcards[index], wildcardValues[index]) } - params[v] = wildcardValues[j] - j++ + 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 } - if len(params) != len(wildcardValues) { - return false, nil + // match :id + if len(leaf.wildcards) != len(wildcardValues) { + return false } - return true, params + for j, v := range leaf.wildcards { + ctx.Input.SetParam(v, wildcardValues[j]) + } + return true } if !leaf.regexps.MatchString(path.Join(wildcardValues...)) { - return false, nil + return false } - params = make(map[string]string) matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...)) for i, match := range matches[1:] { - params[leaf.wildcards[i]] = match + ctx.Input.SetParam(leaf.wildcards[i], match) } - return true, params + return true } // "/" -> [] diff --git a/tree_test.go b/tree_test.go index 7fe70f51..f0923c88 100644 --- a/tree_test.go +++ b/tree_test.go @@ -14,7 +14,11 @@ package beego -import "testing" +import ( + "testing" + + "github.com/astaxie/beego/context" +) type testinfo struct { url string @@ -27,11 +31,11 @@ var routers []testinfo func init() { routers = make([]testinfo, 0) routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil}) - routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth":"123"}}) + routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}}) routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}}) - routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1",":auth":"2"}}) + routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}}) routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}}) - routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1",":auth":"123"}}) + routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}}) routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}}) routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}}) routers = append(routers, testinfo{"/", "/", nil}) @@ -41,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{"/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/: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{"/thumbnail/:size/uploads/*", "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg", @@ -69,16 +74,17 @@ func TestTreeRouters(t *testing.T) { for _, r := range routers { tr := NewTree() tr.AddRouter(r.url, "astaxie") - obj, param := tr.Match(r.requesturl) + ctx := context.NewContext() + obj := tr.Match(r.requesturl, ctx) 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 { for k, v := range r.params { - if vv, ok := param[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) - } else if vv != v { - t.Fatal(r.url + " " + r.requesturl + " should be:" + v + " get param:" + vv) } } } @@ -91,47 +97,51 @@ func TestAddTree(t *testing.T) { tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie") t1 := NewTree() t1.AddTree("/v1/zl", tr) - obj, param := t1.Match("/v1/zl/shop/123/account") + ctx := context.NewContext() + obj := t1.Match("/v1/zl/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/zl/shop/:id/account can't get obj ") } - if param == nil { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if param[":id"] != "123" { + if ctx.Input.Param(":id") != "123" { t.Fatal("get :id param error") } - obj, param = t1.Match("/v1/zl/shop/123/ttt_1_12.html") + ctx.Input.Reset(ctx) + obj = t1.Match("/v1/zl/shop/123/ttt_1_12.html", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") } - if param == nil { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if param[":sd"] != "123" || param[":id"] != "1" || param[":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") } t2 := NewTree() t2.AddTree("/v1/:shopid", tr) - obj, param = t2.Match("/v1/zl/shop/123/account") + ctx.Input.Reset(ctx) + obj = t2.Match("/v1/zl/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/:shopid/shop/:id/account can't get obj ") } - if param == nil { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if param[":id"] != "123" || param[":shopid"] != "zl" { + if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":shopid") != "zl" { t.Fatal("get :id :shopid param error") } - obj, param = t2.Match("/v1/zl/shop/123/ttt_1_12.html") + ctx.Input.Reset(ctx) + obj = t2.Match("/v1/zl/shop/123/ttt_1_12.html", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ") } - if param == nil { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get :shopid param error") } - if param[":sd"] != "123" || param[":id"] != "1" || param[":page"] != "12" || param[":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") } } @@ -142,14 +152,15 @@ func TestAddTree2(t *testing.T) { tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie") t3 := NewTree() t3.AddTree("/:version(v1|v2)/:prefix", tr) - obj, param := t3.Match("/v1/zl/shop/123/account") + ctx := context.NewContext() + obj := t3.Match("/v1/zl/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ") } - if param == nil { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if param[":id"] != "123" || param[":prefix"] != "zl" || param[":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") } } @@ -160,17 +171,19 @@ func TestAddTree3(t *testing.T) { tr.AddRouter("/shop/:sd/account", "astaxie") t3 := NewTree() t3.AddTree("/table/:num", tr) - obj, param := t3.Match("/table/123/shop/123/account") + ctx := context.NewContext() + obj := t3.Match("/table/123/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/table/:num/shop/:sd/account can't get obj ") } - if param == nil { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if param[":num"] != "123" || param[":sd"] != "123" { + if ctx.Input.Param(":num") != "123" || ctx.Input.Param(":sd") != "123" { t.Fatal("get :num :sd param error") } - obj, param = t3.Match("/table/123/create") + ctx.Input.Reset(ctx) + obj = t3.Match("/table/123/create", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/table/:num/create can't get obj ") } @@ -182,17 +195,21 @@ func TestAddTree4(t *testing.T) { tr.AddRouter("/shop/:sd/:account", "astaxie") t4 := NewTree() t4.AddTree("/:info:int/:num/:id", tr) - obj, param := t4.Match("/12/123/456/shop/123/account") + ctx := context.NewContext() + obj := t4.Match("/12/123/456/shop/123/account", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ") } - if param == nil { + if ctx.Input.ParamsLen() == 0 { t.Fatal("get param error") } - if param[":info"] != "12" || param[":num"] != "123" || param[":id"] != "456" || param[":sd"] != "123" || param[":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") } - obj, param = t4.Match("/12/123/456/create") + ctx.Input.Reset(ctx) + obj = t4.Match("/12/123/456/create", ctx) if obj == nil || obj.(string) != "astaxie" { t.Fatal("/:info:int/:num/:id/create can't get obj ") }