mirror of
https://github.com/astaxie/beego.git
synced 2024-12-22 23:40:50 +00:00
Merge remote-tracking branch 'remotes/upstream/develop' into develop
This commit is contained in:
commit
8771634fe4
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
10
filter.go
10
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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
23
router.go
23
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
302
tree.go
302
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
|
||||
}
|
||||
|
||||
// "/" -> []
|
||||
|
81
tree_test.go
81
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 ")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user