diff --git a/context/acceptencoder.go b/context/acceptencoder.go index 1bd2cc3d..0a0586ac 100644 --- a/context/acceptencoder.go +++ b/context/acceptencoder.go @@ -18,6 +18,7 @@ import ( "bytes" "compress/flate" "compress/gzip" + "compress/zlib" "io" "net/http" "os" @@ -31,9 +32,13 @@ type acceptEncoder struct { } var ( - 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) }} - deflateCompressEncoder = acceptEncoder{"deflate", func(wr io.Writer, level int) (io.Writer, error) { return flate.NewWriter(wr, level) }} + 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) }} + //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 ( diff --git a/orm/types.go b/orm/types.go index 1bdd441a..3a3113f1 100644 --- a/orm/types.go +++ b/orm/types.go @@ -36,20 +36,77 @@ type Fielder interface { // Ormer define the orm interface type Ormer interface { - Read(interface{}, ...string) error - ReadOrCreate(interface{}, string, ...string) (bool, int64, error) + // read data to model + // 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) - InsertMulti(int, interface{}) (int64, error) - Update(interface{}, ...string) (int64, error) - Delete(interface{}) (int64, error) - LoadRelated(interface{}, string, ...interface{}) (int64, error) - QueryM2M(interface{}, string) QueryM2Mer - QueryTable(interface{}) QuerySeter - Using(string) error + // insert some models to database + InsertMulti(bulk int, mds interface{}) (int64, error) + // update model to database. + // cols set the columns those want to update. + // find model by Id(pk) field and update columns specified by fields, if cols is null then update all columns + // for example: + // 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 + // commit transaction Commit() error + // rollback transaction 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 } @@ -61,38 +118,164 @@ type Inserter interface { // QuerySeter query seter 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 + // add NOT condition to querySeter. + // have the same usage as Filter 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 - Limit(interface{}, ...interface{}) QuerySeter - Offset(interface{}) QuerySeter - GroupBy(...string) QuerySeter - OrderBy(...string) QuerySeter - Distinct() QuerySeter - RelatedSel(...interface{}) QuerySeter + // add LIMIT value. + // args[0] means offset, e.g. LIMIT num,offset. + // if Limit <= 0 then Limit will be set to default limit ,eg 1000 + // if QuerySeter doesn't call Limit, the sql's Limit will be set to default limit, eg 1000 + // for example: + // 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) + // check result empty or not after QuerySeter executed + // the same as QuerySeter.Count > 0 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) + // 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) - All(interface{}, ...string) (int64, error) - One(interface{}, ...string) error - Values(*[]Params, ...string) (int64, error) - ValuesList(*[]ParamsList, ...string) (int64, error) - ValuesFlat(*ParamsList, string) (int64, error) - RowsToMap(*Params, string, string) (int64, error) - RowsToStruct(interface{}, string, string) (int64, error) + // query all data and map to containers. + // cols means the columns when querying. + // for example: + // var users []*User + // qs.All(&users) // users[0],users[1],users[2] ... + All(container interface{}, cols ...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 +// all operations are on the m2m table only, will not affect the origin model table 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) + // 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) + // check model is existed in relationship of origin model Exist(interface{}) bool + // clean all models in related of origin model Clear() (int64, error) + // count all related models of origin model Count() (int64, error) } + // RawPreparer raw query statement type RawPreparer interface { Exec(...interface{}) (sql.Result, error) @@ -100,16 +283,63 @@ type RawPreparer interface { } // 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 { + //execute sql and get result Exec() (sql.Result, error) - QueryRow(...interface{}) error - QueryRows(...interface{}) (int64, error) + //query data and map to container + //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 - Values(*[]Params, ...string) (int64, error) - ValuesList(*[]ParamsList, ...string) (int64, error) - ValuesFlat(*ParamsList, ...string) (int64, error) - RowsToMap(*Params, string, string) (int64, error) - RowsToStruct(interface{}, string, string) (int64, error) + // query data to []map[string]interface + // see QuerySeter's Values + Values(container *[]Params, cols ...string) (int64, error) + // query data to [][]interface + // 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) } diff --git a/plugins/cors/cors_test.go b/plugins/cors/cors_test.go index ee5ee238..84d21cd0 100644 --- a/plugins/cors/cors_test.go +++ b/plugins/cors/cors_test.go @@ -225,8 +225,8 @@ func Benchmark_WithoutCORS(b *testing.B) { ctx.Output.SetStatus(500) }) 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) } } @@ -246,8 +246,8 @@ func Benchmark_WithCORS(b *testing.B) { ctx.Output.SetStatus(500) }) 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) } } diff --git a/staticfile_test.go b/staticfile_test.go index e635fcc6..d3333570 100644 --- a/staticfile_test.go +++ b/staticfile_test.go @@ -2,8 +2,8 @@ package beego import ( "bytes" - "compress/flate" "compress/gzip" + "compress/zlib" "io" "io/ioutil" "os" @@ -43,7 +43,7 @@ func TestOpenStaticFileGzip_1(t *testing.T) { func TestOpenStaticFileDeflate_1(t *testing.T) { file, _ := os.Open(licenseFile) var zipBuf bytes.Buffer - fileWriter, _ := flate.NewWriter(&zipBuf, flate.BestCompression) + fileWriter, _ := zlib.NewWriterLevel(&zipBuf, zlib.BestCompression) io.Copy(fileWriter, file) fileWriter.Close() content, _ := ioutil.ReadAll(&zipBuf) diff --git a/tree.go b/tree.go index 2092016f..b43db3a9 100644 --- a/tree.go +++ b/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 for _, str := range allowSuffixExt { 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) } - if runObject == nil { - segments := splitPath(pattern) + if runObject == nil && len(t.leaves) > 0 { 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 { - if ok := l.match(append(wildcardValues, segments...), ctx); ok { + if ok := l.match(wildcardValues, ctx); ok { return l.runObject } }