1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-23 05:00:55 +00:00

Merge branch 'develop' of https://github.com/astaxie/beego into develop

This commit is contained in:
fud 2015-12-21 15:49:23 +08:00
commit f6c508f138
5 changed files with 291 additions and 44 deletions

View File

@ -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"
@ -33,7 +34,11 @@ 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 (

View File

@ -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)
} }

View File

@ -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)
} }
} }

View File

@ -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
View File

@ -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
} }
} }