mirror of
https://github.com/astaxie/beego.git
synced 2024-11-23 00:40:54 +00:00
Merge branch 'develop' of https://github.com/astaxie/beego into develop
This commit is contained in:
commit
f6c508f138
@ -18,6 +18,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"compress/flate"
|
"compress/flate"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -31,9 +32,13 @@ type acceptEncoder struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
noneCompressEncoder = acceptEncoder{"", func(wr io.Writer, level int) (io.Writer, error) { return wr, nil }}
|
noneCompressEncoder = acceptEncoder{"", func(wr io.Writer, level int) (io.Writer, error) { return wr, nil }}
|
||||||
gzipCompressEncoder = acceptEncoder{"gzip", func(wr io.Writer, level int) (io.Writer, error) { return gzip.NewWriterLevel(wr, level) }}
|
gzipCompressEncoder = acceptEncoder{"gzip", func(wr io.Writer, level int) (io.Writer, error) { return gzip.NewWriterLevel(wr, level) }}
|
||||||
deflateCompressEncoder = acceptEncoder{"deflate", func(wr io.Writer, level int) (io.Writer, error) { return flate.NewWriter(wr, level) }}
|
//according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed
|
||||||
|
//deflate
|
||||||
|
//The "zlib" format defined in RFC 1950 [31] in combination with
|
||||||
|
//the "deflate" compression mechanism described in RFC 1951 [29].
|
||||||
|
deflateCompressEncoder = acceptEncoder{"deflate", func(wr io.Writer, level int) (io.Writer, error) { return zlib.NewWriterLevel(wr, level) }}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
292
orm/types.go
292
orm/types.go
@ -36,20 +36,77 @@ type Fielder interface {
|
|||||||
|
|
||||||
// Ormer define the orm interface
|
// Ormer define the orm interface
|
||||||
type Ormer interface {
|
type Ormer interface {
|
||||||
Read(interface{}, ...string) error
|
// read data to model
|
||||||
ReadOrCreate(interface{}, string, ...string) (bool, int64, error)
|
// for example:
|
||||||
|
// this will find User by Id field
|
||||||
|
// u = &User{Id: user.Id}
|
||||||
|
// err = Ormer.Read(u)
|
||||||
|
// this will find User by UserName field
|
||||||
|
// u = &User{UserName: "astaxie", Password: "pass"}
|
||||||
|
// err = Ormer.Read(u, "UserName")
|
||||||
|
Read(md interface{}, cols ...string) error
|
||||||
|
// Try to read a row from the database, or insert one if it doesn't exist
|
||||||
|
ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error)
|
||||||
|
// insert model data to database
|
||||||
|
// for example:
|
||||||
|
// user := new(User)
|
||||||
|
// id, err = Ormer.Insert(user)
|
||||||
|
// user must a pointer and Insert will set user's pk field
|
||||||
Insert(interface{}) (int64, error)
|
Insert(interface{}) (int64, error)
|
||||||
InsertMulti(int, interface{}) (int64, error)
|
// insert some models to database
|
||||||
Update(interface{}, ...string) (int64, error)
|
InsertMulti(bulk int, mds interface{}) (int64, error)
|
||||||
Delete(interface{}) (int64, error)
|
// update model to database.
|
||||||
LoadRelated(interface{}, string, ...interface{}) (int64, error)
|
// cols set the columns those want to update.
|
||||||
QueryM2M(interface{}, string) QueryM2Mer
|
// find model by Id(pk) field and update columns specified by fields, if cols is null then update all columns
|
||||||
QueryTable(interface{}) QuerySeter
|
// for example:
|
||||||
Using(string) error
|
// user := User{Id: 2}
|
||||||
|
// user.Langs = append(user.Langs, "zh-CN", "en-US")
|
||||||
|
// user.Extra.Name = "beego"
|
||||||
|
// user.Extra.Data = "orm"
|
||||||
|
// num, err = Ormer.Update(&user, "Langs", "Extra")
|
||||||
|
Update(md interface{}, cols ...string) (int64, error)
|
||||||
|
// delete model in database
|
||||||
|
Delete(md interface{}) (int64, error)
|
||||||
|
// load related models to md model.
|
||||||
|
// args are limit, offset int and order string.
|
||||||
|
//
|
||||||
|
// example:
|
||||||
|
// Ormer.LoadRelated(post,"Tags")
|
||||||
|
// for _,tag := range post.Tags{...}
|
||||||
|
//args[0] bool true useDefaultRelsDepth ; false depth 0
|
||||||
|
//args[0] int loadRelationDepth
|
||||||
|
//args[1] int limit default limit 1000
|
||||||
|
//args[2] int offset default offset 0
|
||||||
|
//args[3] string order for example : "-Id"
|
||||||
|
// make sure the relation is defined in model struct tags.
|
||||||
|
LoadRelated(md interface{}, name string, args ...interface{}) (int64, error)
|
||||||
|
// create a models to models queryer
|
||||||
|
// for example:
|
||||||
|
// post := Post{Id: 4}
|
||||||
|
// m2m := Ormer.QueryM2M(&post, "Tags")
|
||||||
|
QueryM2M(md interface{}, name string) QueryM2Mer
|
||||||
|
// return a QuerySeter for table operations.
|
||||||
|
// table name can be string or struct.
|
||||||
|
// e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)),
|
||||||
|
QueryTable(ptrStructOrTableName interface{}) QuerySeter
|
||||||
|
// switch to another registered database driver by given name.
|
||||||
|
Using(name string) error
|
||||||
|
// begin transaction
|
||||||
|
// for example:
|
||||||
|
// o := NewOrm()
|
||||||
|
// err := o.Begin()
|
||||||
|
// ...
|
||||||
|
// err = o.Rollback()
|
||||||
Begin() error
|
Begin() error
|
||||||
|
// commit transaction
|
||||||
Commit() error
|
Commit() error
|
||||||
|
// rollback transaction
|
||||||
Rollback() error
|
Rollback() error
|
||||||
Raw(string, ...interface{}) RawSeter
|
// return a raw query seter for raw sql string.
|
||||||
|
// for example:
|
||||||
|
// ormer.Raw("UPDATE `user` SET `user_name` = ? WHERE `user_name` = ?", "slene", "testing").Exec()
|
||||||
|
// // update user testing's name to slene
|
||||||
|
Raw(query string, args ...interface{}) RawSeter
|
||||||
Driver() Driver
|
Driver() Driver
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,38 +118,164 @@ type Inserter interface {
|
|||||||
|
|
||||||
// QuerySeter query seter
|
// QuerySeter query seter
|
||||||
type QuerySeter interface {
|
type QuerySeter interface {
|
||||||
|
// add condition expression to QuerySeter.
|
||||||
|
// for example:
|
||||||
|
// filter by UserName == 'slene'
|
||||||
|
// qs.Filter("UserName", "slene")
|
||||||
|
// sql : left outer join profile on t0.id1==t1.id2 where t1.age == 28
|
||||||
|
// Filter("profile__Age", 28)
|
||||||
|
// // time compare
|
||||||
|
// qs.Filter("created", time.Now())
|
||||||
Filter(string, ...interface{}) QuerySeter
|
Filter(string, ...interface{}) QuerySeter
|
||||||
|
// add NOT condition to querySeter.
|
||||||
|
// have the same usage as Filter
|
||||||
Exclude(string, ...interface{}) QuerySeter
|
Exclude(string, ...interface{}) QuerySeter
|
||||||
|
// set condition to QuerySeter.
|
||||||
|
// sql's where condition
|
||||||
|
// cond := orm.NewCondition()
|
||||||
|
// cond1 := cond.And("profile__isnull", false).AndNot("status__in", 1).Or("profile__age__gt", 2000)
|
||||||
|
// //sql-> WHERE T0.`profile_id` IS NOT NULL AND NOT T0.`Status` IN (?) OR T1.`age` > 2000
|
||||||
|
// num, err := qs.SetCond(cond1).Count()
|
||||||
SetCond(*Condition) QuerySeter
|
SetCond(*Condition) QuerySeter
|
||||||
Limit(interface{}, ...interface{}) QuerySeter
|
// add LIMIT value.
|
||||||
Offset(interface{}) QuerySeter
|
// args[0] means offset, e.g. LIMIT num,offset.
|
||||||
GroupBy(...string) QuerySeter
|
// if Limit <= 0 then Limit will be set to default limit ,eg 1000
|
||||||
OrderBy(...string) QuerySeter
|
// if QuerySeter doesn't call Limit, the sql's Limit will be set to default limit, eg 1000
|
||||||
Distinct() QuerySeter
|
// for example:
|
||||||
RelatedSel(...interface{}) QuerySeter
|
// qs.Limit(10, 2)
|
||||||
|
// // sql-> limit 10 offset 2
|
||||||
|
Limit(limit interface{}, args ...interface{}) QuerySeter
|
||||||
|
// add OFFSET value
|
||||||
|
// same as Limit function's args[0]
|
||||||
|
Offset(offset interface{}) QuerySeter
|
||||||
|
// add ORDER expression.
|
||||||
|
// "column" means ASC, "-column" means DESC.
|
||||||
|
// for example:
|
||||||
|
// qs.OrderBy("-status")
|
||||||
|
OrderBy(exprs ...string) QuerySeter
|
||||||
|
// set relation model to query together.
|
||||||
|
// it will query relation models and assign to parent model.
|
||||||
|
// for example:
|
||||||
|
// // will load all related fields use left join .
|
||||||
|
// qs.RelatedSel().One(&user)
|
||||||
|
// // will load related field only profile
|
||||||
|
// qs.RelatedSel("profile").One(&user)
|
||||||
|
// user.Profile.Age = 32
|
||||||
|
RelatedSel(params ...interface{}) QuerySeter
|
||||||
|
// return QuerySeter execution result number
|
||||||
|
// for example:
|
||||||
|
// num, err = qs.Filter("profile__age__gt", 28).Count()
|
||||||
Count() (int64, error)
|
Count() (int64, error)
|
||||||
|
// check result empty or not after QuerySeter executed
|
||||||
|
// the same as QuerySeter.Count > 0
|
||||||
Exist() bool
|
Exist() bool
|
||||||
Update(Params) (int64, error)
|
// execute update with parameters
|
||||||
|
// for example:
|
||||||
|
// num, err = qs.Filter("user_name", "slene").Update(Params{
|
||||||
|
// "Nums": ColValue(Col_Minus, 50),
|
||||||
|
// }) // user slene's Nums will minus 50
|
||||||
|
// num, err = qs.Filter("UserName", "slene").Update(Params{
|
||||||
|
// "user_name": "slene2"
|
||||||
|
// }) // user slene's name will change to slene2
|
||||||
|
Update(values Params) (int64, error)
|
||||||
|
// delete from table
|
||||||
|
//for example:
|
||||||
|
// num ,err = qs.Filter("user_name__in", "testing1", "testing2").Delete()
|
||||||
|
// //delete two user who's name is testing1 or testing2
|
||||||
Delete() (int64, error)
|
Delete() (int64, error)
|
||||||
|
// return a insert queryer.
|
||||||
|
// it can be used in times.
|
||||||
|
// example:
|
||||||
|
// i,err := sq.PrepareInsert()
|
||||||
|
// num, err = i.Insert(&user1) // user table will add one record user1 at once
|
||||||
|
// num, err = i.Insert(&user2) // user table will add one record user2 at once
|
||||||
|
// err = i.Close() //don't forget call Close
|
||||||
PrepareInsert() (Inserter, error)
|
PrepareInsert() (Inserter, error)
|
||||||
All(interface{}, ...string) (int64, error)
|
// query all data and map to containers.
|
||||||
One(interface{}, ...string) error
|
// cols means the columns when querying.
|
||||||
Values(*[]Params, ...string) (int64, error)
|
// for example:
|
||||||
ValuesList(*[]ParamsList, ...string) (int64, error)
|
// var users []*User
|
||||||
ValuesFlat(*ParamsList, string) (int64, error)
|
// qs.All(&users) // users[0],users[1],users[2] ...
|
||||||
RowsToMap(*Params, string, string) (int64, error)
|
All(container interface{}, cols ...string) (int64, error)
|
||||||
RowsToStruct(interface{}, string, string) (int64, error)
|
// query one row data and map to containers.
|
||||||
|
// cols means the columns when querying.
|
||||||
|
// for example:
|
||||||
|
// var user User
|
||||||
|
// qs.One(&user) //user.UserName == "slene"
|
||||||
|
One(container interface{}, cols ...string) error
|
||||||
|
// query all data and map to []map[string]interface.
|
||||||
|
// expres means condition expression.
|
||||||
|
// it converts data to []map[column]value.
|
||||||
|
// for example:
|
||||||
|
// var maps []Params
|
||||||
|
// qs.Values(&maps) //maps[0]["UserName"]=="slene"
|
||||||
|
Values(results *[]Params, exprs ...string) (int64, error)
|
||||||
|
// query all data and map to [][]interface
|
||||||
|
// it converts data to [][column_index]value
|
||||||
|
// for example:
|
||||||
|
// var list []ParamsList
|
||||||
|
// qs.ValuesList(&list) // list[0][1] == "slene"
|
||||||
|
ValuesList(results *[]ParamsList, exprs ...string) (int64, error)
|
||||||
|
// query all data and map to []interface.
|
||||||
|
// it's designed for one column record set, auto change to []value, not [][column]value.
|
||||||
|
// for example:
|
||||||
|
// var list ParamsList
|
||||||
|
// qs.ValuesFlat(&list, "UserName") // list[0] == "slene"
|
||||||
|
ValuesFlat(result *ParamsList, expr string) (int64, error)
|
||||||
|
// query all rows into map[string]interface with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to map[string]interface{}{
|
||||||
|
// "total": 100,
|
||||||
|
// "found": 200,
|
||||||
|
// }
|
||||||
|
RowsToMap(result *Params, keyCol, valueCol string) (int64, error)
|
||||||
|
// query all rows into struct with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to struct {
|
||||||
|
// Total int
|
||||||
|
// Found int
|
||||||
|
// }
|
||||||
|
RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryM2Mer model to model query struct
|
// QueryM2Mer model to model query struct
|
||||||
|
// all operations are on the m2m table only, will not affect the origin model table
|
||||||
type QueryM2Mer interface {
|
type QueryM2Mer interface {
|
||||||
|
// add models to origin models when creating queryM2M.
|
||||||
|
// example:
|
||||||
|
// m2m := orm.QueryM2M(post,"Tag")
|
||||||
|
// m2m.Add(&Tag1{},&Tag2{})
|
||||||
|
// for _,tag := range post.Tags{}{ ... }
|
||||||
|
// param could also be any of the follow
|
||||||
|
// []*Tag{{Id:3,Name: "TestTag1"}, {Id:4,Name: "TestTag2"}}
|
||||||
|
// &Tag{Id:5,Name: "TestTag3"}
|
||||||
|
// []interface{}{&Tag{Id:6,Name: "TestTag4"}}
|
||||||
|
// insert one or more rows to m2m table
|
||||||
|
// make sure the relation is defined in post model struct tag.
|
||||||
Add(...interface{}) (int64, error)
|
Add(...interface{}) (int64, error)
|
||||||
|
// remove models following the origin model relationship
|
||||||
|
// only delete rows from m2m table
|
||||||
|
// for example:
|
||||||
|
//tag3 := &Tag{Id:5,Name: "TestTag3"}
|
||||||
|
//num, err = m2m.Remove(tag3)
|
||||||
Remove(...interface{}) (int64, error)
|
Remove(...interface{}) (int64, error)
|
||||||
|
// check model is existed in relationship of origin model
|
||||||
Exist(interface{}) bool
|
Exist(interface{}) bool
|
||||||
|
// clean all models in related of origin model
|
||||||
Clear() (int64, error)
|
Clear() (int64, error)
|
||||||
|
// count all related models of origin model
|
||||||
Count() (int64, error)
|
Count() (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// RawPreparer raw query statement
|
// RawPreparer raw query statement
|
||||||
type RawPreparer interface {
|
type RawPreparer interface {
|
||||||
Exec(...interface{}) (sql.Result, error)
|
Exec(...interface{}) (sql.Result, error)
|
||||||
@ -100,16 +283,63 @@ type RawPreparer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RawSeter raw query seter
|
// RawSeter raw query seter
|
||||||
|
// create From Ormer.Raw
|
||||||
|
// for example:
|
||||||
|
// sql := fmt.Sprintf("SELECT %sid%s,%sname%s FROM %suser%s WHERE id = ?",Q,Q,Q,Q,Q,Q)
|
||||||
|
// rs := Ormer.Raw(sql, 1)
|
||||||
type RawSeter interface {
|
type RawSeter interface {
|
||||||
|
//execute sql and get result
|
||||||
Exec() (sql.Result, error)
|
Exec() (sql.Result, error)
|
||||||
QueryRow(...interface{}) error
|
//query data and map to container
|
||||||
QueryRows(...interface{}) (int64, error)
|
//for example:
|
||||||
|
// var name string
|
||||||
|
// var id int
|
||||||
|
// rs.QueryRow(&id,&name) // id==2 name=="slene"
|
||||||
|
QueryRow(containers ...interface{}) error
|
||||||
|
|
||||||
|
// query data rows and map to container
|
||||||
|
// var ids []int
|
||||||
|
// var names []int
|
||||||
|
// query = fmt.Sprintf("SELECT 'id','name' FROM %suser%s", Q, Q)
|
||||||
|
// num, err = dORM.Raw(query).QueryRows(&ids,&names) // ids=>{1,2},names=>{"nobody","slene"}
|
||||||
|
QueryRows(containers ...interface{}) (int64, error)
|
||||||
SetArgs(...interface{}) RawSeter
|
SetArgs(...interface{}) RawSeter
|
||||||
Values(*[]Params, ...string) (int64, error)
|
// query data to []map[string]interface
|
||||||
ValuesList(*[]ParamsList, ...string) (int64, error)
|
// see QuerySeter's Values
|
||||||
ValuesFlat(*ParamsList, ...string) (int64, error)
|
Values(container *[]Params, cols ...string) (int64, error)
|
||||||
RowsToMap(*Params, string, string) (int64, error)
|
// query data to [][]interface
|
||||||
RowsToStruct(interface{}, string, string) (int64, error)
|
// see QuerySeter's ValuesList
|
||||||
|
ValuesList(container *[]ParamsList, cols ...string) (int64, error)
|
||||||
|
// query data to []interface
|
||||||
|
// see QuerySeter's ValuesFlat
|
||||||
|
ValuesFlat(container *ParamsList, cols ...string) (int64, error)
|
||||||
|
// query all rows into map[string]interface with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to map[string]interface{}{
|
||||||
|
// "total": 100,
|
||||||
|
// "found": 200,
|
||||||
|
// }
|
||||||
|
RowsToMap(result *Params, keyCol, valueCol string) (int64, error)
|
||||||
|
// query all rows into struct with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to struct {
|
||||||
|
// Total int
|
||||||
|
// Found int
|
||||||
|
// }
|
||||||
|
RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error)
|
||||||
|
|
||||||
|
// return prepared raw statement for used in times.
|
||||||
|
// for example:
|
||||||
|
// pre, err := dORM.Raw("INSERT INTO tag (name) VALUES (?)").Prepare()
|
||||||
|
// r, err := pre.Exec("name1") // INSERT INTO tag (name) VALUES (`name1`)
|
||||||
Prepare() (RawPreparer, error)
|
Prepare() (RawPreparer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,8 +225,8 @@ func Benchmark_WithoutCORS(b *testing.B) {
|
|||||||
ctx.Output.SetStatus(500)
|
ctx.Output.SetStatus(500)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < 100; i++ {
|
r, _ := http.NewRequest("PUT", "/foo", nil)
|
||||||
r, _ := http.NewRequest("PUT", "/foo", nil)
|
for i := 0; i < b.N; i++ {
|
||||||
handler.ServeHTTP(recorder, r)
|
handler.ServeHTTP(recorder, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,8 +246,8 @@ func Benchmark_WithCORS(b *testing.B) {
|
|||||||
ctx.Output.SetStatus(500)
|
ctx.Output.SetStatus(500)
|
||||||
})
|
})
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < 100; i++ {
|
r, _ := http.NewRequest("PUT", "/foo", nil)
|
||||||
r, _ := http.NewRequest("PUT", "/foo", nil)
|
for i := 0; i < b.N; i++ {
|
||||||
handler.ServeHTTP(recorder, r)
|
handler.ServeHTTP(recorder, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package beego
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/flate"
|
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@ -43,7 +43,7 @@ func TestOpenStaticFileGzip_1(t *testing.T) {
|
|||||||
func TestOpenStaticFileDeflate_1(t *testing.T) {
|
func TestOpenStaticFileDeflate_1(t *testing.T) {
|
||||||
file, _ := os.Open(licenseFile)
|
file, _ := os.Open(licenseFile)
|
||||||
var zipBuf bytes.Buffer
|
var zipBuf bytes.Buffer
|
||||||
fileWriter, _ := flate.NewWriter(&zipBuf, flate.BestCompression)
|
fileWriter, _ := zlib.NewWriterLevel(&zipBuf, zlib.BestCompression)
|
||||||
io.Copy(fileWriter, file)
|
io.Copy(fileWriter, file)
|
||||||
fileWriter.Close()
|
fileWriter.Close()
|
||||||
content, _ := ioutil.ReadAll(&zipBuf)
|
content, _ := ioutil.ReadAll(&zipBuf)
|
||||||
|
20
tree.go
20
tree.go
@ -334,7 +334,7 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if runObject == nil {
|
if runObject == nil && len(t.fixrouters) > 0 {
|
||||||
// Filter the .json .xml .html extension
|
// Filter the .json .xml .html extension
|
||||||
for _, str := range allowSuffixExt {
|
for _, str := range allowSuffixExt {
|
||||||
if strings.HasSuffix(seg, str) {
|
if strings.HasSuffix(seg, str) {
|
||||||
@ -353,11 +353,23 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte
|
|||||||
runObject = t.wildcard.match(pattern, append(wildcardValues, seg), ctx)
|
runObject = t.wildcard.match(pattern, append(wildcardValues, seg), ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if runObject == nil {
|
if runObject == nil && len(t.leaves) > 0 {
|
||||||
segments := splitPath(pattern)
|
|
||||||
wildcardValues = append(wildcardValues, seg)
|
wildcardValues = append(wildcardValues, seg)
|
||||||
|
start, i := 0, 0
|
||||||
|
for ; i < len(pattern); i++ {
|
||||||
|
if pattern[i] == '/' {
|
||||||
|
if i != 0 && start < len(pattern) {
|
||||||
|
wildcardValues = append(wildcardValues, pattern[start:i])
|
||||||
|
}
|
||||||
|
start = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start > 0 {
|
||||||
|
wildcardValues = append(wildcardValues, pattern[start:i])
|
||||||
|
}
|
||||||
for _, l := range t.leaves {
|
for _, l := range t.leaves {
|
||||||
if ok := l.match(append(wildcardValues, segments...), ctx); ok {
|
if ok := l.match(wildcardValues, ctx); ok {
|
||||||
return l.runObject
|
return l.runObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user