From db51ddab96f34d1b9e21569f78f0a54585db4f88 Mon Sep 17 00:00:00 2001 From: Michael Hatch Date: Sat, 23 Aug 2014 16:33:17 -0500 Subject: [PATCH 01/92] GetInt(), GetInt8(), GetInt16(), GetInt32(), GetInt64() and Example tests --- controller.go | 37 ++++++++++++++++++++--- controller_test.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 controller_test.go diff --git a/controller.go b/controller.go index eee79513..94722c5f 100644 --- a/controller.go +++ b/controller.go @@ -28,8 +28,8 @@ import ( "strconv" "strings" - "github.com/astaxie/beego/context" - "github.com/astaxie/beego/session" + "github.com/mvpmvh/beego/context" + "github.com/mvpmvh/beego/session" ) //commonly used mime-types @@ -382,8 +382,37 @@ func (c *Controller) GetStrings(key string) []string { return []string{} } -// GetInt returns input value as int64. -func (c *Controller) GetInt(key string) (int64, error) { +// GetInt returns input as an int +func (c *Controller) GetInt(key string) (int, error) { + return strconv.Atoi(c.Ctx.Input.Query(key)) +} + +// GetInt8 return input as an int8 +func (c *Controller) GetInt8(key string) (int8, error) { + i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 8) + i8 := int8(i64) + + return i8, err +} + +// GetInt16 returns input as an int16 +func (c *Controller) GetInt16(key string) (int16, error) { + i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 16) + i16 := int16(i64) + + return i16, err +} + +// GetInt32 returns input as an int32 +func (c *Controller) GetInt32(key string) (int32, error) { + i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) + i32 := int32(i64) + + return i32, err +} + +// GetInt64 returns input value as int64. +func (c *Controller) GetInt64(key string) (int64, error) { return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64) } diff --git a/controller_test.go b/controller_test.go new file mode 100644 index 00000000..8077e6aa --- /dev/null +++ b/controller_test.go @@ -0,0 +1,75 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package beego + +import ( + "fmt" + "github.com/mvpmvh/beego/context" +) + +func ExampleGetInt() { + + i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + + val, _ := ctrlr.GetInt("age") + fmt.Printf("%T", val) + //Output: int +} + +func ExampleGetInt8() { + + i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + + val, _ := ctrlr.GetInt8("age") + fmt.Printf("%T", val) + //Output: int8 +} + +func ExampleGetInt16() { + + i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + + val, _ := ctrlr.GetInt16("age") + fmt.Printf("%T", val) + //Output: int16 +} + +func ExampleGetInt32() { + + i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + + val, _ := ctrlr.GetInt32("age") + fmt.Printf("%T", val) + //Output: int32 +} + +func ExampleGetInt64() { + + i := &context.BeegoInput{Params: map[string]string{"age": "40"}} + ctx := &context.Context{Input: i} + ctrlr := Controller{Ctx: ctx} + + val, _ := ctrlr.GetInt64("age") + fmt.Printf("%T", val) + //Output: int64 +} From 6eee223352201f61328f5d8e42b459ee3e926f26 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 3 Sep 2014 09:25:34 +0800 Subject: [PATCH 02/92] beego: fix the Upper for the _method value --- router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router.go b/router.go index 21aa4724..7c52b9fc 100644 --- a/router.go +++ b/router.go @@ -705,7 +705,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if EnableXSRF { execController.XsrfToken() if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || - (r.Method == "POST" && (r.Form.Get("_method") == "delete" || r.Form.Get("_method") == "put")) { + (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { execController.CheckXsrfCookie() } } From 29b60d6058eb3b0cacb5c675d93a54e56329fc26 Mon Sep 17 00:00:00 2001 From: Wang Hao <991060@gmail.com> Date: Wed, 3 Sep 2014 22:43:06 +0800 Subject: [PATCH 03/92] fixed uninitialized return error if StartAndGC fails --- cache/cache.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cache/cache.go b/cache/cache.go index d4ad5d59..ddb2f857 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -81,13 +81,13 @@ func Register(name string, adapter Cache) { // Create a new cache driver by adapter name and config string. // config need to be correct JSON as string: {"interval":360}. // it will start gc automatically. -func NewCache(adapterName, config string) (adapter Cache, e error) { +func NewCache(adapterName, config string) (adapter Cache, err error) { adapter, ok := adapters[adapterName] if !ok { - e = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) + err = fmt.Errorf("cache: unknown adapter name %q (forgot to import?)", adapterName) return } - err := adapter.StartAndGC(config) + err = adapter.StartAndGC(config) if err != nil { adapter = nil } From 4d65330ca1df83cf673ec1f3832d016cd0ab860a Mon Sep 17 00:00:00 2001 From: Michael Hatch Date: Wed, 3 Sep 2014 19:47:09 -0500 Subject: [PATCH 04/92] changing my package namespace to astaxie --- controller.go | 4 ++-- controller_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controller.go b/controller.go index 94722c5f..72ba323b 100644 --- a/controller.go +++ b/controller.go @@ -28,8 +28,8 @@ import ( "strconv" "strings" - "github.com/mvpmvh/beego/context" - "github.com/mvpmvh/beego/session" + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/session" ) //commonly used mime-types diff --git a/controller_test.go b/controller_test.go index 8077e6aa..15938cdc 100644 --- a/controller_test.go +++ b/controller_test.go @@ -16,7 +16,7 @@ package beego import ( "fmt" - "github.com/mvpmvh/beego/context" + "github.com/astaxie/beego/context" ) func ExampleGetInt() { From f7cd1479ba27a348d29268c73e14a63f41fedb8e Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 5 Sep 2014 17:04:02 +0800 Subject: [PATCH 05/92] beego: improve the log debug for running server --- admin.go | 1 + app.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/admin.go b/admin.go index b860d268..a0905dd3 100644 --- a/admin.go +++ b/admin.go @@ -458,6 +458,7 @@ func (admin *adminApp) Run() { for p, f := range admin.routers { http.Handle(p, f) } + BeeLogger.Info("Running on %s", addr) err := http.ListenAndServe(addr, nil) if err != nil { BeeLogger.Critical("Admin ListenAndServe: ", err) diff --git a/app.go b/app.go index f1706616..0a83ba41 100644 --- a/app.go +++ b/app.go @@ -48,8 +48,6 @@ func (app *App) Run() { addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort) } - BeeLogger.Info("Running on %s", addr) - var ( err error l net.Listener @@ -78,6 +76,7 @@ func (app *App) Run() { if HttpsPort != 0 { app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) } + BeeLogger.Info("Running on %s", app.Server.Addr) err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) @@ -90,6 +89,7 @@ func (app *App) Run() { if EnableHttpListen { go func() { app.Server.Addr = addr + BeeLogger.Info("Running on %s", app.Server.Addr) err := app.Server.ListenAndServe() if err != nil { BeeLogger.Critical("ListenAndServe: ", err) From 647a47517dba13a84ba79f301a676158aec191fa Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 5 Sep 2014 23:21:41 +0800 Subject: [PATCH 06/92] httplib: fix the header function for User-Agent --- httplib/httplib.go | 2 +- httplib/httplib_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index b1f209c9..dbcb2fed 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -355,7 +355,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { Jar: jar, } - if b.setting.UserAgent != "" { + if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" { b.req.Header.Set("User-Agent", b.setting.UserAgent) } diff --git a/httplib/httplib_test.go b/httplib/httplib_test.go index 02068c0b..976ce498 100644 --- a/httplib/httplib_test.go +++ b/httplib/httplib_test.go @@ -203,3 +203,13 @@ func TestToFile(t *testing.T) { t.Fatal(err) } } + +func TestHeader(t *testing.T) { + req := Get("http://httpbin.org/headers") + req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36") + str, err := req.String() + if err != nil { + t.Fatal(err) + } + t.Log(str) +} From b2cdabb8a090221bb0fa7801f179d383a2c0384a Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Mon, 8 Sep 2014 17:37:01 +0800 Subject: [PATCH 07/92] added query builder for orm --- orm/qb.go | 30 +++++++++++++++++ orm/qb_mysql.go | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 orm/qb.go create mode 100644 orm/qb_mysql.go diff --git a/orm/qb.go b/orm/qb.go new file mode 100644 index 00000000..ceb4b59e --- /dev/null +++ b/orm/qb.go @@ -0,0 +1,30 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package orm + +type QueryBuilder interface { + Select(fields ...string) QueryWriter + From(tables ...string) QueryWriter + Where(cond string) QueryWriter + LimitOffset(limit int, offset int) QueryWriter + InnerJoin(table string) QueryWriter + LeftJoin(table string) QueryWriter + On(cond string) QueryWriter + And(cond string) QueryWriter + Or(cond string) QueryWriter + In(vals ...string) QueryWriter + Subquery(query string, rename string) string + String() string +} diff --git a/orm/qb_mysql.go b/orm/qb_mysql.go new file mode 100644 index 00000000..1dadda2b --- /dev/null +++ b/orm/qb_mysql.go @@ -0,0 +1,85 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package orm + +import ( + "fmt" + "strings" +) + +type MySQLQueryBuilder struct { + QueryString []string +} + +func (qw *MySQLQueryBuilder) Select(fields ...string) QueryWriter { + segment := fmt.Sprintf("SELECT %s", strings.Join(fields, ", ")) + qw.QueryString = append(qw.QueryString, segment) + return qw +} + +func (qw *MySQLQueryBuilder) From(tables ...string) QueryWriter { + segment := fmt.Sprintf("FROM %s", strings.Join(tables, ", ")) + qw.QueryString = append(qw.QueryString, segment) + return qw +} + +func (qw *MySQLQueryBuilder) Where(cond string) QueryWriter { + qw.QueryString = append(qw.QueryString, "WHERE "+cond) + return qw +} + +func (qw *MySQLQueryBuilder) LimitOffset(limit int, offset int) QueryWriter { + qw.QueryString = append(qw.QueryString, fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)) + return qw +} + +func (qw *MySQLQueryBuilder) InnerJoin(table string) QueryWriter { + qw.QueryString = append(qw.QueryString, "INNER JOIN "+table) + return qw +} + +func (qw *MySQLQueryBuilder) LeftJoin(table string) QueryWriter { + qw.QueryString = append(qw.QueryString, "LEFT JOIN "+table) + return qw +} + +func (qw *MySQLQueryBuilder) On(cond string) QueryWriter { + qw.QueryString = append(qw.QueryString, "ON "+cond) + return qw +} + +func (qw *MySQLQueryBuilder) And(cond string) QueryWriter { + qw.QueryString = append(qw.QueryString, "AND "+cond) + return qw +} + +func (qw *MySQLQueryBuilder) Or(cond string) QueryWriter { + qw.QueryString = append(qw.QueryString, "OR "+cond) + return qw +} + +func (qw *MySQLQueryBuilder) In(vals ...string) QueryWriter { + segment := fmt.Sprintf("IN (%s)", strings.Join(vals, ", ")) + qw.QueryString = append(qw.QueryString, segment) + return qw +} + +func (qw *MySQLQueryBuilder) Subquery(sub string, alias string) string { + return fmt.Sprintf("(%s) AS %s", sub, alias) +} + +func (qw *MySQLQueryBuilder) String() string { + return strings.Join(qw.QueryString, " ") +} From c667895ce5fb9a09eb227b794ad2e9fb2dc27a78 Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Mon, 8 Sep 2014 17:47:15 +0800 Subject: [PATCH 08/92] added new querybuilder --- orm/qb.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/orm/qb.go b/orm/qb.go index ceb4b59e..8869a68c 100644 --- a/orm/qb.go +++ b/orm/qb.go @@ -28,3 +28,8 @@ type QueryBuilder interface { Subquery(query string, rename string) string String() string } + +func NewQueryBuilder() (qb QueryBuilder) { + qb = new(MySQLQueryBuilder) + return +} From f9a9b5a9058e218818dbef2de114c270a82d12a4 Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Mon, 8 Sep 2014 17:56:55 +0800 Subject: [PATCH 09/92] new query builder based on driver --- orm/qb.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/orm/qb.go b/orm/qb.go index 8869a68c..c85902b3 100644 --- a/orm/qb.go +++ b/orm/qb.go @@ -14,6 +14,8 @@ package orm +import "errors" + type QueryBuilder interface { Select(fields ...string) QueryWriter From(tables ...string) QueryWriter @@ -29,7 +31,15 @@ type QueryBuilder interface { String() string } -func NewQueryBuilder() (qb QueryBuilder) { - qb = new(MySQLQueryBuilder) +func NewQueryBuilder(driver string) (qb QueryBuilder, err error) { + if driver == "mysql" { + qb = new(MySQLQueryBuilder) + } else if driver == "postgres" { + err = errors.New("postgres querybuilder is not supported yet!") + } else if driver == "sqlite" { + err = errors.New("sqlite querybuilder is not supported yet!") + } else { + err = errors.New("unknown driver for query builder!") + } return } From cca0a3f76d34f6ae1ffdb84fae42ca7950dc0720 Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Mon, 8 Sep 2014 18:31:32 +0800 Subject: [PATCH 10/92] name correction: QueryBuilder instead of QueryWriter --- orm/qb.go | 20 ++++++++++---------- orm/qb_mysql.go | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/orm/qb.go b/orm/qb.go index c85902b3..6822259a 100644 --- a/orm/qb.go +++ b/orm/qb.go @@ -17,16 +17,16 @@ package orm import "errors" type QueryBuilder interface { - Select(fields ...string) QueryWriter - From(tables ...string) QueryWriter - Where(cond string) QueryWriter - LimitOffset(limit int, offset int) QueryWriter - InnerJoin(table string) QueryWriter - LeftJoin(table string) QueryWriter - On(cond string) QueryWriter - And(cond string) QueryWriter - Or(cond string) QueryWriter - In(vals ...string) QueryWriter + Select(fields ...string) QueryBuilder + From(tables ...string) QueryBuilder + Where(cond string) QueryBuilder + LimitOffset(limit int, offset int) QueryBuilder + InnerJoin(table string) QueryBuilder + LeftJoin(table string) QueryBuilder + On(cond string) QueryBuilder + And(cond string) QueryBuilder + Or(cond string) QueryBuilder + In(vals ...string) QueryBuilder Subquery(query string, rename string) string String() string } diff --git a/orm/qb_mysql.go b/orm/qb_mysql.go index 1dadda2b..4aa3f88c 100644 --- a/orm/qb_mysql.go +++ b/orm/qb_mysql.go @@ -23,54 +23,54 @@ type MySQLQueryBuilder struct { QueryString []string } -func (qw *MySQLQueryBuilder) Select(fields ...string) QueryWriter { +func (qw *MySQLQueryBuilder) Select(fields ...string) QueryBuilder { segment := fmt.Sprintf("SELECT %s", strings.Join(fields, ", ")) qw.QueryString = append(qw.QueryString, segment) return qw } -func (qw *MySQLQueryBuilder) From(tables ...string) QueryWriter { +func (qw *MySQLQueryBuilder) From(tables ...string) QueryBuilder { segment := fmt.Sprintf("FROM %s", strings.Join(tables, ", ")) qw.QueryString = append(qw.QueryString, segment) return qw } -func (qw *MySQLQueryBuilder) Where(cond string) QueryWriter { +func (qw *MySQLQueryBuilder) Where(cond string) QueryBuilder { qw.QueryString = append(qw.QueryString, "WHERE "+cond) return qw } -func (qw *MySQLQueryBuilder) LimitOffset(limit int, offset int) QueryWriter { +func (qw *MySQLQueryBuilder) LimitOffset(limit int, offset int) QueryBuilder { qw.QueryString = append(qw.QueryString, fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)) return qw } -func (qw *MySQLQueryBuilder) InnerJoin(table string) QueryWriter { +func (qw *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder { qw.QueryString = append(qw.QueryString, "INNER JOIN "+table) return qw } -func (qw *MySQLQueryBuilder) LeftJoin(table string) QueryWriter { +func (qw *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder { qw.QueryString = append(qw.QueryString, "LEFT JOIN "+table) return qw } -func (qw *MySQLQueryBuilder) On(cond string) QueryWriter { +func (qw *MySQLQueryBuilder) On(cond string) QueryBuilder { qw.QueryString = append(qw.QueryString, "ON "+cond) return qw } -func (qw *MySQLQueryBuilder) And(cond string) QueryWriter { +func (qw *MySQLQueryBuilder) And(cond string) QueryBuilder { qw.QueryString = append(qw.QueryString, "AND "+cond) return qw } -func (qw *MySQLQueryBuilder) Or(cond string) QueryWriter { +func (qw *MySQLQueryBuilder) Or(cond string) QueryBuilder { qw.QueryString = append(qw.QueryString, "OR "+cond) return qw } -func (qw *MySQLQueryBuilder) In(vals ...string) QueryWriter { +func (qw *MySQLQueryBuilder) In(vals ...string) QueryBuilder { segment := fmt.Sprintf("IN (%s)", strings.Join(vals, ", ")) qw.QueryString = append(qw.QueryString, segment) return qw From 38eb29fa7bc51c0c697a84a0ee018373682d6247 Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Mon, 8 Sep 2014 18:41:42 +0800 Subject: [PATCH 11/92] err msg spell correction --- orm/qb.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orm/qb.go b/orm/qb.go index 6822259a..82cad51a 100644 --- a/orm/qb.go +++ b/orm/qb.go @@ -35,9 +35,9 @@ func NewQueryBuilder(driver string) (qb QueryBuilder, err error) { if driver == "mysql" { qb = new(MySQLQueryBuilder) } else if driver == "postgres" { - err = errors.New("postgres querybuilder is not supported yet!") + err = errors.New("postgres query builder is not supported yet!") } else if driver == "sqlite" { - err = errors.New("sqlite querybuilder is not supported yet!") + err = errors.New("sqlite query builder is not supported yet!") } else { err = errors.New("unknown driver for query builder!") } From fb0cc55822d25d1de7dd4f052856e483a750f117 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 9 Sep 2014 11:55:23 +0800 Subject: [PATCH 12/92] update the orm read me --- orm/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/orm/README.md b/orm/README.md index 74f1b47b..356407c1 100644 --- a/orm/README.md +++ b/orm/README.md @@ -6,8 +6,6 @@ A powerful orm framework for go. It is heavily influenced by Django ORM, SQLAlchemy. -now, beta, unstable, may be changing some api make your app build failed. - **Support Database:** * MySQL: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) From 29b7ff84e1a6e9c37b29aa63d7290a84082a8a6a Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Tue, 9 Sep 2014 14:17:12 +0800 Subject: [PATCH 13/92] more complete support for sql language --- orm/qb.go | 13 ++++-- orm/qb_mysql.go | 104 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 80 insertions(+), 37 deletions(-) diff --git a/orm/qb.go b/orm/qb.go index 82cad51a..78b6ad12 100644 --- a/orm/qb.go +++ b/orm/qb.go @@ -19,15 +19,22 @@ import "errors" type QueryBuilder interface { Select(fields ...string) QueryBuilder From(tables ...string) QueryBuilder - Where(cond string) QueryBuilder - LimitOffset(limit int, offset int) QueryBuilder InnerJoin(table string) QueryBuilder LeftJoin(table string) QueryBuilder + RightJoin(table string) QueryBuilder On(cond string) QueryBuilder + Where(cond string) QueryBuilder And(cond string) QueryBuilder Or(cond string) QueryBuilder In(vals ...string) QueryBuilder - Subquery(query string, rename string) string + OrderBy(fields ...string) QueryBuilder + Asc() QueryBuilder + Desc() QueryBuilder + Limit(limit int) QueryBuilder + Offset(offset int) QueryBuilder + GroupBy(fields ...string) QueryBuilder + Having(cond string) QueryBuilder + Subquery(sub string, alias string) string String() string } diff --git a/orm/qb_mysql.go b/orm/qb_mysql.go index 4aa3f88c..4cd2912d 100644 --- a/orm/qb_mysql.go +++ b/orm/qb_mysql.go @@ -16,70 +16,106 @@ package orm import ( "fmt" + "strconv" "strings" ) type MySQLQueryBuilder struct { - QueryString []string + QueryTokens []string } -func (qw *MySQLQueryBuilder) Select(fields ...string) QueryBuilder { +func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder { segment := fmt.Sprintf("SELECT %s", strings.Join(fields, ", ")) - qw.QueryString = append(qw.QueryString, segment) - return qw + qb.QueryTokens = append(qb.QueryTokens, segment) + return qb } -func (qw *MySQLQueryBuilder) From(tables ...string) QueryBuilder { +func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder { segment := fmt.Sprintf("FROM %s", strings.Join(tables, ", ")) - qw.QueryString = append(qw.QueryString, segment) - return qw + qb.QueryTokens = append(qb.QueryTokens, segment) + return qb } -func (qw *MySQLQueryBuilder) Where(cond string) QueryBuilder { - qw.QueryString = append(qw.QueryString, "WHERE "+cond) - return qw +func (qb *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "INNER JOIN "+table) + return qb } -func (qw *MySQLQueryBuilder) LimitOffset(limit int, offset int) QueryBuilder { - qw.QueryString = append(qw.QueryString, fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)) - return qw +func (qb *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "LEFT JOIN "+table) + return qb } -func (qw *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder { - qw.QueryString = append(qw.QueryString, "INNER JOIN "+table) - return qw +func (qb *MySQLQueryBuilder) RightJoin(table string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "RIGHT JOIN "+table) + return qb } -func (qw *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder { - qw.QueryString = append(qw.QueryString, "LEFT JOIN "+table) - return qw +func (qb *MySQLQueryBuilder) On(cond string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "ON "+cond) + return qb } -func (qw *MySQLQueryBuilder) On(cond string) QueryBuilder { - qw.QueryString = append(qw.QueryString, "ON "+cond) - return qw +func (qb *MySQLQueryBuilder) Where(cond string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "WHERE "+cond) + return qb } -func (qw *MySQLQueryBuilder) And(cond string) QueryBuilder { - qw.QueryString = append(qw.QueryString, "AND "+cond) - return qw +func (qb *MySQLQueryBuilder) And(cond string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "AND "+cond) + return qb } -func (qw *MySQLQueryBuilder) Or(cond string) QueryBuilder { - qw.QueryString = append(qw.QueryString, "OR "+cond) - return qw +func (qb *MySQLQueryBuilder) Or(cond string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "OR "+cond) + return qb } -func (qw *MySQLQueryBuilder) In(vals ...string) QueryBuilder { +func (qb *MySQLQueryBuilder) In(vals ...string) QueryBuilder { segment := fmt.Sprintf("IN (%s)", strings.Join(vals, ", ")) - qw.QueryString = append(qw.QueryString, segment) - return qw + qb.QueryTokens = append(qb.QueryTokens, segment) + return qb } -func (qw *MySQLQueryBuilder) Subquery(sub string, alias string) string { +func (qb *MySQLQueryBuilder) OrderBy(fields ...string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "ORDER BY "+strings.Join(fields, ", ")) + return qb +} + +func (qb *MySQLQueryBuilder) Asc() QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "ASC") + return qb +} + +func (qb *MySQLQueryBuilder) Desc() QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "DESC") + return qb +} + +func (qb *MySQLQueryBuilder) Limit(limit int) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "LIMIT "+strconv.Itoa(limit)) + return qb +} + +func (qb *MySQLQueryBuilder) Offset(offset int) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "OFFSET "+strconv.Itoa(offset)) + return qb +} + +func (qb *MySQLQueryBuilder) GroupBy(fields ...string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "GROUP BY "+strings.Join(fields, ", ")) + return qb +} + +func (qb *MySQLQueryBuilder) Having(cond string) QueryBuilder { + qb.QueryTokens = append(qb.QueryTokens, "HAVING "+cond) + return qb +} + +func (qb *MySQLQueryBuilder) Subquery(sub string, alias string) string { return fmt.Sprintf("(%s) AS %s", sub, alias) } -func (qw *MySQLQueryBuilder) String() string { - return strings.Join(qw.QueryString, " ") +func (qb *MySQLQueryBuilder) String() string { + return strings.Join(qb.QueryTokens, " ") } From ccab9a704463adfaf6b274bcccc69ebe1075797e Mon Sep 17 00:00:00 2001 From: ZhengYang Date: Thu, 11 Sep 2014 13:48:39 +0800 Subject: [PATCH 14/92] add more sql keywords --- orm/qb.go | 5 ++++ orm/qb_mysql.go | 73 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/orm/qb.go b/orm/qb.go index 78b6ad12..efe368db 100644 --- a/orm/qb.go +++ b/orm/qb.go @@ -34,6 +34,11 @@ type QueryBuilder interface { Offset(offset int) QueryBuilder GroupBy(fields ...string) QueryBuilder Having(cond string) QueryBuilder + Update(tables ...string) QueryBuilder + Set(kv ...string) QueryBuilder + Delete(tables ...string) QueryBuilder + InsertInto(table string, fields ...string) QueryBuilder + Values(vals ...string) QueryBuilder Subquery(sub string, alias string) string String() string } diff --git a/orm/qb_mysql.go b/orm/qb_mysql.go index 4cd2912d..ee86bc64 100644 --- a/orm/qb_mysql.go +++ b/orm/qb_mysql.go @@ -20,95 +20,124 @@ import ( "strings" ) +const COMMA_SPACE = ", " + type MySQLQueryBuilder struct { - QueryTokens []string + Tokens []string } func (qb *MySQLQueryBuilder) Select(fields ...string) QueryBuilder { - segment := fmt.Sprintf("SELECT %s", strings.Join(fields, ", ")) - qb.QueryTokens = append(qb.QueryTokens, segment) + qb.Tokens = append(qb.Tokens, "SELECT", strings.Join(fields, COMMA_SPACE)) return qb } func (qb *MySQLQueryBuilder) From(tables ...string) QueryBuilder { - segment := fmt.Sprintf("FROM %s", strings.Join(tables, ", ")) - qb.QueryTokens = append(qb.QueryTokens, segment) + qb.Tokens = append(qb.Tokens, "FROM", strings.Join(tables, COMMA_SPACE)) return qb } func (qb *MySQLQueryBuilder) InnerJoin(table string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "INNER JOIN "+table) + qb.Tokens = append(qb.Tokens, "INNER JOIN", table) return qb } func (qb *MySQLQueryBuilder) LeftJoin(table string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "LEFT JOIN "+table) + qb.Tokens = append(qb.Tokens, "LEFT JOIN", table) return qb } func (qb *MySQLQueryBuilder) RightJoin(table string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "RIGHT JOIN "+table) + qb.Tokens = append(qb.Tokens, "RIGHT JOIN", table) return qb } func (qb *MySQLQueryBuilder) On(cond string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "ON "+cond) + qb.Tokens = append(qb.Tokens, "ON", cond) return qb } func (qb *MySQLQueryBuilder) Where(cond string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "WHERE "+cond) + qb.Tokens = append(qb.Tokens, "WHERE", cond) return qb } func (qb *MySQLQueryBuilder) And(cond string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "AND "+cond) + qb.Tokens = append(qb.Tokens, "AND", cond) return qb } func (qb *MySQLQueryBuilder) Or(cond string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "OR "+cond) + qb.Tokens = append(qb.Tokens, "OR", cond) return qb } func (qb *MySQLQueryBuilder) In(vals ...string) QueryBuilder { - segment := fmt.Sprintf("IN (%s)", strings.Join(vals, ", ")) - qb.QueryTokens = append(qb.QueryTokens, segment) + qb.Tokens = append(qb.Tokens, "IN", "(", strings.Join(vals, COMMA_SPACE), ")") return qb } func (qb *MySQLQueryBuilder) OrderBy(fields ...string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "ORDER BY "+strings.Join(fields, ", ")) + qb.Tokens = append(qb.Tokens, "ORDER BY", strings.Join(fields, COMMA_SPACE)) return qb } func (qb *MySQLQueryBuilder) Asc() QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "ASC") + qb.Tokens = append(qb.Tokens, "ASC") return qb } func (qb *MySQLQueryBuilder) Desc() QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "DESC") + qb.Tokens = append(qb.Tokens, "DESC") return qb } func (qb *MySQLQueryBuilder) Limit(limit int) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "LIMIT "+strconv.Itoa(limit)) + qb.Tokens = append(qb.Tokens, "LIMIT", strconv.Itoa(limit)) return qb } func (qb *MySQLQueryBuilder) Offset(offset int) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "OFFSET "+strconv.Itoa(offset)) + qb.Tokens = append(qb.Tokens, "OFFSET", strconv.Itoa(offset)) return qb } func (qb *MySQLQueryBuilder) GroupBy(fields ...string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "GROUP BY "+strings.Join(fields, ", ")) + qb.Tokens = append(qb.Tokens, "GROUP BY", strings.Join(fields, COMMA_SPACE)) return qb } func (qb *MySQLQueryBuilder) Having(cond string) QueryBuilder { - qb.QueryTokens = append(qb.QueryTokens, "HAVING "+cond) + qb.Tokens = append(qb.Tokens, "HAVING", cond) + return qb +} + +func (qb *MySQLQueryBuilder) Update(tables ...string) QueryBuilder { + qb.Tokens = append(qb.Tokens, "UPDATE", strings.Join(tables, COMMA_SPACE)) + return qb +} + +func (qb *MySQLQueryBuilder) Set(kv ...string) QueryBuilder { + qb.Tokens = append(qb.Tokens, "SET", strings.Join(kv, COMMA_SPACE)) + return qb +} + +func (qb *MySQLQueryBuilder) Delete(tables ...string) QueryBuilder { + qb.Tokens = append(qb.Tokens, "DELETE", strings.Join(tables, COMMA_SPACE)) + return qb +} + +func (qb *MySQLQueryBuilder) InsertInto(table string, fields ...string) QueryBuilder { + qb.Tokens = append(qb.Tokens, "INSERT INTO", table) + if len(fields) != 0 { + fieldsStr := strings.Join(fields, COMMA_SPACE) + qb.Tokens = append(qb.Tokens, "(", fieldsStr, ")") + } + return qb +} + +func (qb *MySQLQueryBuilder) Values(vals ...string) QueryBuilder { + valsStr := strings.Join(vals, COMMA_SPACE) + qb.Tokens = append(qb.Tokens, "VALUES", "(", valsStr, ")") return qb } @@ -117,5 +146,5 @@ func (qb *MySQLQueryBuilder) Subquery(sub string, alias string) string { } func (qb *MySQLQueryBuilder) String() string { - return strings.Join(qb.QueryTokens, " ") + return strings.Join(qb.Tokens, " ") } From 1f9281c830bbcced901fa02840fc6bf489a002e3 Mon Sep 17 00:00:00 2001 From: ZhengYang Date: Thu, 11 Sep 2014 15:17:48 +0800 Subject: [PATCH 15/92] minor code refactor --- orm/qb_mysql.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/orm/qb_mysql.go b/orm/qb_mysql.go index ee86bc64..9ce9b7d9 100644 --- a/orm/qb_mysql.go +++ b/orm/qb_mysql.go @@ -122,7 +122,10 @@ func (qb *MySQLQueryBuilder) Set(kv ...string) QueryBuilder { } func (qb *MySQLQueryBuilder) Delete(tables ...string) QueryBuilder { - qb.Tokens = append(qb.Tokens, "DELETE", strings.Join(tables, COMMA_SPACE)) + qb.Tokens = append(qb.Tokens, "DELETE") + if len(tables) != 0 { + qb.Tokens = append(qb.Tokens, strings.Join(tables, COMMA_SPACE)) + } return qb } From 6c62198b598a457e7b24a8b67c825cb0b1b27857 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 15 Sep 2014 23:01:07 +0800 Subject: [PATCH 16/92] remove the go style --- .go_style | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .go_style diff --git a/.go_style b/.go_style deleted file mode 100644 index 26682eed..00000000 --- a/.go_style +++ /dev/null @@ -1,16 +0,0 @@ -{ - "file_line": 500, - "func_line": 80, - "params_num":4, - "results_num":3, - "formated": true, - "pkg_name": true, - "camel_name":true, - "ignore":[ - "a/*", - "b/*/c/*.go" - ], - "fatal":[ - "formated" - ] -} From 67c0c232a10285eb547ee5b1322b38fd188829c1 Mon Sep 17 00:00:00 2001 From: SnailKnows Date: Sat, 20 Sep 2014 18:56:46 +0800 Subject: [PATCH 17/92] Update beego.go --- beego.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/beego.go b/beego.go index 1277a2a8..32c1ee47 100644 --- a/beego.go +++ b/beego.go @@ -308,6 +308,10 @@ func SetStaticPath(url string, path string) *App { // DelStaticPath removes the static folder setting in this url pattern in beego application. func DelStaticPath(url string) *App { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + url = strings.TrimRight(url, "/") delete(StaticDir, url) return BeeApp } From 727d2f9ea11573a201d8450b4f8c01134a11660d Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 22 Sep 2014 23:44:50 +0800 Subject: [PATCH 18/92] fix not found when has mulit static dir robot &robots --- staticfile.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/staticfile.go b/staticfile.go index a9deabe9..da511793 100644 --- a/staticfile.go +++ b/staticfile.go @@ -31,6 +31,7 @@ func serverStaticRouter(ctx *context.Context) { return } requestPath := path.Clean(ctx.Input.Request.URL.Path) + i := 0 for prefix, staticDir := range StaticDir { if len(prefix) == 0 { continue @@ -41,8 +42,13 @@ func serverStaticRouter(ctx *context.Context) { http.ServeFile(ctx.ResponseWriter, ctx.Request, file) return } else { - http.NotFound(ctx.ResponseWriter, ctx.Request) - return + i++ + if i == len(StaticDir) { + http.NotFound(ctx.ResponseWriter, ctx.Request) + return + } else { + continue + } } } if strings.HasPrefix(requestPath, prefix) { From 7e060e6e5c1b2807c4fd7dde3b013ce68f617460 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 23 Sep 2014 00:03:47 +0800 Subject: [PATCH 19/92] fix the static file dir --- staticfile.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/staticfile.go b/staticfile.go index da511793..a5f6b65c 100644 --- a/staticfile.go +++ b/staticfile.go @@ -68,6 +68,9 @@ func serverStaticRouter(ctx *context.Context) { if finfo.IsDir() && !DirectoryIndex { middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") return + } else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { + http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302) + return } //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request From f267ee8a126f56ac824023e2bdd3fc8b3ff2d338 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 23 Sep 2014 00:26:07 +0800 Subject: [PATCH 20/92] fix the same name controller for UrlFor --- router.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/router.go b/router.go index 7c52b9fc..18be04d3 100644 --- a/router.go +++ b/router.go @@ -415,7 +415,7 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { } } } - controllName := strings.Join(paths[:len(paths)-1], ".") + controllName := strings.Join(paths[:len(paths)-1], "/") methodName := paths[len(paths)-1] for _, t := range p.routers { ok, url := p.geturl(t, "/", controllName, methodName, params) @@ -443,7 +443,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin } for _, l := range t.leaves { if c, ok := l.runObject.(*controllerInfo); ok { - if c.routerType == routerTypeBeego && c.controllerType.Name() == controllName { + if c.routerType == routerTypeBeego && + strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) { find := false if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok { if len(c.methods) == 0 { From 8164367762d92d330be79c06bde1b484db6a8073 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 23 Sep 2014 23:54:38 +0800 Subject: [PATCH 21/92] beego: flash add success & Set --- flash.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/flash.go b/flash.go index 6e85141f..5ccf339a 100644 --- a/flash.go +++ b/flash.go @@ -32,6 +32,24 @@ func NewFlash() *FlashData { } } +// Set message to flash +func (fd *FlashData) Set(key string, msg string, args ...interface{}) { + if len(args) == 0 { + fd.Data[key] = msg + } else { + fd.Data[key] = fmt.Sprintf(msg, args...) + } +} + +// Success writes success message to flash. +func (fd *FlashData) Success(msg string, args ...interface{}) { + if len(args) == 0 { + fd.Data["success"] = msg + } else { + fd.Data["success"] = fmt.Sprintf(msg, args...) + } +} + // Notice writes notice message to flash. func (fd *FlashData) Notice(msg string, args ...interface{}) { if len(args) == 0 { From f5f339556075d83bc655b6b9580ec0ffcfbeb440 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 24 Sep 2014 14:48:08 +0800 Subject: [PATCH 22/92] update the isdir --- staticfile.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staticfile.go b/staticfile.go index a5f6b65c..96c97367 100644 --- a/staticfile.go +++ b/staticfile.go @@ -68,7 +68,7 @@ func serverStaticRouter(ctx *context.Context) { if finfo.IsDir() && !DirectoryIndex { middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") return - } else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { + } else if finfo.IsDir() && ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302) return } From 3a5de83ec243e4dd509c488ea1edabb3ebc3c2a2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 28 Sep 2014 22:10:43 +0800 Subject: [PATCH 23/92] beego: support router case sensitive --- config.go | 7 +++++++ router.go | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index db234e16..9f952735 100644 --- a/config.go +++ b/config.go @@ -81,6 +81,7 @@ var ( FlashSeperator string // used to seperate flash key:value AppConfigProvider string // config provider EnableDocs bool // enable generate docs & server docs API Swagger + RouterCaseSensitive bool // router case sensitive default is true ) func init() { @@ -164,6 +165,8 @@ func init() { FlashName = "BEEGO_FLASH" FlashSeperator = "BEEGOFLASH" + RouterCaseSensitive = true + runtime.GOMAXPROCS(runtime.NumCPU()) // init BeeLogger @@ -375,6 +378,10 @@ func ParseConfig() (err error) { if enabledocs, err := GetConfig("bool", "EnableDocs"); err == nil { EnableDocs = enabledocs.(bool) } + + if casesensitive, err := GetConfig("bool", "RouterCaseSensitive"); err == nil { + RouterCaseSensitive = casesensitive.(bool) + } } return nil } diff --git a/router.go b/router.go index 18be04d3..b14cc5dd 100644 --- a/router.go +++ b/router.go @@ -163,6 +163,9 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM } func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) { + if !RouterCaseSensitive { + pattern = strings.ToLower(pattern) + } if t, ok := p.routers[method]; ok { t.AddRouter(pattern, r) } else { @@ -381,6 +384,9 @@ func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter Filter mr.tree = NewTree() mr.pattern = pattern mr.filterFunc = filter + if !RouterCaseSensitive { + pattern = strings.ToLower(pattern) + } mr.tree.AddRouter(pattern, true) return p.insertFilterRouter(pos, mr) } @@ -565,12 +571,18 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Output.Context = context context.Output.EnableGzip = EnableGzip + var urlPath string + if !RouterCaseSensitive { + urlPath = strings.ToLower(r.URL.Path) + } else { + urlPath = r.URL.Path + } // defined filter function do_filter := func(pos int) (started bool) { if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { - if ok, p := filterR.ValidRouter(r.URL.Path); ok { + if ok, p := filterR.ValidRouter(urlPath); ok { context.Input.Params = p filterR.filterFunc(context) if w.started { @@ -628,7 +640,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if !findrouter { if t, ok := p.routers[r.Method]; ok { - runObject, p := t.Match(r.URL.Path) + runObject, p := t.Match(urlPath) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r findrouter = true From 31e613341358213741516cf1c461e1b6a7bcbb81 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 1 Oct 2014 08:57:10 +0800 Subject: [PATCH 24/92] beego: improve static file index.html simple code --- staticfile.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/staticfile.go b/staticfile.go index 96c97367..d9855064 100644 --- a/staticfile.go +++ b/staticfile.go @@ -65,12 +65,20 @@ func serverStaticRouter(ctx *context.Context) { return } //if the request is dir and DirectoryIndex is false then - if finfo.IsDir() && !DirectoryIndex { - middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") - return - } else if finfo.IsDir() && ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { - http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302) - return + if finfo.IsDir() { + if !DirectoryIndex { + middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") + return + } else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { + http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302) + return + } + } else if strings.HasSuffix(requestPath, "/index.html") { + file := path.Join(staticDir, requestPath) + if utils.FileExists(file) { + http.ServeFile(ctx.ResponseWriter, ctx.Request, file) + return + } } //This block obtained from (https://github.com/smithfox/beego) - it should probably get merged into astaxie/beego after a pull request From 8716185de8550450d161bbc9726c01b6aadeef35 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 1 Oct 2014 22:10:33 +0800 Subject: [PATCH 25/92] fix #794 --- config.go | 521 +++++++++++++++++++++++++++++------------------------- 1 file changed, 281 insertions(+), 240 deletions(-) diff --git a/config.go b/config.go index 9f952735..ef201ec6 100644 --- a/config.go +++ b/config.go @@ -15,7 +15,6 @@ package beego import ( - "errors" "fmt" "html/template" "os" @@ -48,8 +47,8 @@ var ( RecoverPanic bool // flag of auto recover panic AutoRender bool // flag of render template automatically ViewsPath string - RunMode string // run mode, "dev" or "prod" - AppConfig config.ConfigContainer + AppConfig *beegoAppConfig + RunMode string // run mode, "dev" or "prod" GlobalSessions *session.Manager // global session mananger SessionOn bool // flag of starting session auto. default is false. SessionProvider string // default session provider, memory, mysql , redis ,etc. @@ -84,6 +83,107 @@ var ( RouterCaseSensitive bool // router case sensitive default is true ) +type beegoAppConfig struct { + innerConfig config.ConfigContainer +} + +func newAppConfig(AppConfigProvider, AppConfigPath string) *beegoAppConfig { + ac, err := config.NewConfig(AppConfigProvider, AppConfigPath) + if err != nil { + ac = config.NewFakeConfig() + } + rac := &beegoAppConfig{ac} + return rac +} + +func (b *beegoAppConfig) Set(key, val string) error { + return b.innerConfig.Set(key, val) +} + +func (b *beegoAppConfig) String(key string) string { + v := b.innerConfig.String(RunMode + "::" + key) + if v == "" { + return b.innerConfig.String(key) + } + return v +} + +func (b *beegoAppConfig) Strings(key string) []string { + v := b.innerConfig.Strings(RunMode + "::" + key) + if len(v) == 0 { + return b.innerConfig.Strings(key) + } + return v +} + +func (b *beegoAppConfig) Int(key string) (int, error) { + v, err := b.innerConfig.Int(RunMode + "::" + key) + if err != nil { + return b.innerConfig.Int(key) + } + return v, nil +} + +func (b *beegoAppConfig) Int64(key string) (int64, error) { + v, err := b.innerConfig.Int64(RunMode + "::" + key) + if err != nil { + return b.innerConfig.Int64(key) + } + return v, nil +} + +func (b *beegoAppConfig) Bool(key string) (bool, error) { + v, err := b.innerConfig.Bool(RunMode + "::" + key) + if err != nil { + return b.innerConfig.Bool(key) + } + return v, nil +} + +func (b *beegoAppConfig) Float(key string) (float64, error) { + v, err := b.innerConfig.Float(RunMode + "::" + key) + if err != nil { + return b.innerConfig.Float(key) + } + return v, nil +} + +func (b *beegoAppConfig) DefaultString(key string, defaultval string) string { + return b.innerConfig.DefaultString(key, defaultval) +} + +func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string { + return b.innerConfig.DefaultStrings(key, defaultval) +} + +func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { + return b.innerConfig.DefaultInt(key, defaultval) +} + +func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { + return b.innerConfig.DefaultInt64(key, defaultval) +} + +func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { + return b.innerConfig.DefaultBool(key, defaultval) +} + +func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { + return b.innerConfig.DefaultFloat(key, defaultval) +} + +func (b *beegoAppConfig) DIY(key string) (interface{}, error) { + return b.innerConfig.DIY(key) +} + +func (b *beegoAppConfig) GetSection(section string) (map[string]string, error) { + return b.innerConfig.GetSection(section) +} + +func (b *beegoAppConfig) SaveConfigFile(filename string) error { + return b.innerConfig.SaveConfigFile(filename) +} + func init() { // create beego application BeeApp = NewApp() @@ -186,255 +286,196 @@ func init() { // ParseConfig parsed default config file. // now only support ini, next will support json. func ParseConfig() (err error) { - AppConfig, err = config.NewConfig(AppConfigProvider, AppConfigPath) - if err != nil { - AppConfig = config.NewFakeConfig() - return err - } else { + AppConfig = newAppConfig(AppConfigProvider, AppConfigPath) - if v, err := GetConfig("string", "HttpAddr"); err == nil { - HttpAddr = v.(string) + // set the runmode first + if runmode := AppConfig.String("RunMode"); runmode != "" { + RunMode = runmode + } + + HttpAddr = AppConfig.String("HttpAddr") + + if v, err := AppConfig.Int("HttpPort"); err == nil { + HttpPort = v + } + + if v, err := AppConfig.Bool("EnableHttpListen"); err == nil { + EnableHttpListen = v + } + + if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil { + MaxMemory = maxmemory + } + + if appname := AppConfig.String("AppName"); appname != "" { + AppName = appname + } + + if autorender, err := AppConfig.Bool("AutoRender"); err == nil { + AutoRender = autorender + } + + if autorecover, err := AppConfig.Bool("RecoverPanic"); err == nil { + RecoverPanic = autorecover + } + + if views := AppConfig.String("ViewsPath"); views != "" { + ViewsPath = views + } + + if sessionon, err := AppConfig.Bool("SessionOn"); err == nil { + SessionOn = sessionon + } + + if sessProvider := AppConfig.String("SessionProvider"); sessProvider != "" { + SessionProvider = sessProvider + } + + if sessName := AppConfig.String("SessionName"); sessName != "" { + SessionName = sessName + } + + if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" { + SessionSavePath = sesssavepath + } + + if sesshashfunc := AppConfig.String("SessionHashFunc"); sesshashfunc != "" { + SessionHashFunc = sesshashfunc + } + + if sesshashkey := AppConfig.String("SessionHashKey"); sesshashkey != "" { + SessionHashKey = sesshashkey + } + + if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { + SessionGCMaxLifetime = sessMaxLifeTime + } + + if sesscookielifetime, err := AppConfig.Int("SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 { + SessionCookieLifeTime = sesscookielifetime + } + + if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil { + UseFcgi = usefcgi + } + + if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil { + EnableGzip = enablegzip + } + + if directoryindex, err := AppConfig.Bool("DirectoryIndex"); err == nil { + DirectoryIndex = directoryindex + } + + if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil { + HttpServerTimeOut = timeout + } + + if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil { + ErrorsShow = errorsshow + } + + if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil { + CopyRequestBody = copyrequestbody + } + + if xsrfkey := AppConfig.String("XSRFKEY"); xsrfkey != "" { + XSRFKEY = xsrfkey + } + + if enablexsrf, err := AppConfig.Bool("EnableXSRF"); err == nil { + EnableXSRF = enablexsrf + } + + if expire, err := AppConfig.Int("XSRFExpire"); err == nil { + XSRFExpire = expire + } + + if tplleft := AppConfig.String("TemplateLeft"); tplleft != "" { + TemplateLeft = tplleft + } + + if tplright := AppConfig.String("TemplateRight"); tplright != "" { + TemplateRight = tplright + } + + if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil { + EnableHttpTLS = httptls + } + + if httpsport, err := AppConfig.Int("HttpsPort"); err == nil { + HttpsPort = httpsport + } + + if certfile := AppConfig.String("HttpCertFile"); certfile != "" { + HttpCertFile = certfile + } + + if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" { + HttpKeyFile = keyfile + } + + if serverName := AppConfig.String("BeegoServerName"); serverName != "" { + BeegoServerName = serverName + } + + if flashname := AppConfig.String("FlashName"); flashname != "" { + FlashName = flashname + } + + if flashseperator := AppConfig.String("FlashSeperator"); flashseperator != "" { + FlashSeperator = flashseperator + } + + if sd := AppConfig.String("StaticDir"); sd != "" { + for k := range StaticDir { + delete(StaticDir, k) } - - if v, err := GetConfig("int", "HttpPort"); err == nil { - HttpPort = v.(int) - } - - if v, err := GetConfig("bool", "EnableHttpListen"); err == nil { - EnableHttpListen = v.(bool) - } - - if maxmemory, err := GetConfig("int64", "MaxMemory"); err == nil { - MaxMemory = maxmemory.(int64) - } - - if appname, _ := GetConfig("string", "AppName"); appname != "" { - AppName = appname.(string) - } - - if runmode, _ := GetConfig("string", "RunMode"); runmode != "" { - RunMode = runmode.(string) - } - - if autorender, err := GetConfig("bool", "AutoRender"); err == nil { - AutoRender = autorender.(bool) - } - - if autorecover, err := GetConfig("bool", "RecoverPanic"); err == nil { - RecoverPanic = autorecover.(bool) - } - - if views, _ := GetConfig("string", "ViewsPath"); views != "" { - ViewsPath = views.(string) - } - - if sessionon, err := GetConfig("bool", "SessionOn"); err == nil { - SessionOn = sessionon.(bool) - } - - if sessProvider, _ := GetConfig("string", "SessionProvider"); sessProvider != "" { - SessionProvider = sessProvider.(string) - } - - if sessName, _ := GetConfig("string", "SessionName"); sessName != "" { - SessionName = sessName.(string) - } - - if sesssavepath, _ := GetConfig("string", "SessionSavePath"); sesssavepath != "" { - SessionSavePath = sesssavepath.(string) - } - - if sesshashfunc, _ := GetConfig("string", "SessionHashFunc"); sesshashfunc != "" { - SessionHashFunc = sesshashfunc.(string) - } - - if sesshashkey, _ := GetConfig("string", "SessionHashKey"); sesshashkey != "" { - SessionHashKey = sesshashkey.(string) - } - - if sessMaxLifeTime, err := GetConfig("int64", "SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { - SessionGCMaxLifetime = sessMaxLifeTime.(int64) - } - - if sesscookielifetime, err := GetConfig("int", "SessionCookieLifeTime"); err == nil && sesscookielifetime != 0 { - SessionCookieLifeTime = sesscookielifetime.(int) - } - - if usefcgi, err := GetConfig("bool", "UseFcgi"); err == nil { - UseFcgi = usefcgi.(bool) - } - - if enablegzip, err := GetConfig("bool", "EnableGzip"); err == nil { - EnableGzip = enablegzip.(bool) - } - - if directoryindex, err := GetConfig("bool", "DirectoryIndex"); err == nil { - DirectoryIndex = directoryindex.(bool) - } - - if timeout, err := GetConfig("int64", "HttpServerTimeOut"); err == nil { - HttpServerTimeOut = timeout.(int64) - } - - if errorsshow, err := GetConfig("bool", "ErrorsShow"); err == nil { - ErrorsShow = errorsshow.(bool) - } - - if copyrequestbody, err := GetConfig("bool", "CopyRequestBody"); err == nil { - CopyRequestBody = copyrequestbody.(bool) - } - - if xsrfkey, _ := GetConfig("string", "XSRFKEY"); xsrfkey != "" { - XSRFKEY = xsrfkey.(string) - } - - if enablexsrf, err := GetConfig("bool", "EnableXSRF"); err == nil { - EnableXSRF = enablexsrf.(bool) - } - - if expire, err := GetConfig("int", "XSRFExpire"); err == nil { - XSRFExpire = expire.(int) - } - - if tplleft, _ := GetConfig("string", "TemplateLeft"); tplleft != "" { - TemplateLeft = tplleft.(string) - } - - if tplright, _ := GetConfig("string", "TemplateRight"); tplright != "" { - TemplateRight = tplright.(string) - } - - if httptls, err := GetConfig("bool", "EnableHttpTLS"); err == nil { - EnableHttpTLS = httptls.(bool) - } - - if httpsport, err := GetConfig("int", "HttpsPort"); err == nil { - HttpsPort = httpsport.(int) - } - - if certfile, _ := GetConfig("string", "HttpCertFile"); certfile != "" { - HttpCertFile = certfile.(string) - } - - if keyfile, _ := GetConfig("string", "HttpKeyFile"); keyfile != "" { - HttpKeyFile = keyfile.(string) - } - - if serverName, _ := GetConfig("string", "BeegoServerName"); serverName != "" { - BeegoServerName = serverName.(string) - } - - if flashname, _ := GetConfig("string", "FlashName"); flashname != "" { - FlashName = flashname.(string) - } - - if flashseperator, _ := GetConfig("string", "FlashSeperator"); flashseperator != "" { - FlashSeperator = flashseperator.(string) - } - - if sd, _ := GetConfig("string", "StaticDir"); sd != "" { - for k := range StaticDir { - delete(StaticDir, k) + sds := strings.Fields(sd) + for _, v := range sds { + if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { + StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1] + } else { + StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0] } - sds := strings.Fields(sd.(string)) - for _, v := range sds { - if url2fsmap := strings.SplitN(v, ":", 2); len(url2fsmap) == 2 { - StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[1] - } else { - StaticDir["/"+strings.TrimRight(url2fsmap[0], "/")] = url2fsmap[0] + } + } + + if sgz := AppConfig.String("StaticExtensionsToGzip"); sgz != "" { + extensions := strings.Split(sgz, ",") + if len(extensions) > 0 { + StaticExtensionsToGzip = []string{} + for _, ext := range extensions { + if len(ext) == 0 { + continue } - } - } - - if sgz, _ := GetConfig("string", "StaticExtensionsToGzip"); sgz != "" { - extensions := strings.Split(sgz.(string), ",") - if len(extensions) > 0 { - StaticExtensionsToGzip = []string{} - for _, ext := range extensions { - if len(ext) == 0 { - continue - } - extWithDot := ext - if extWithDot[:1] != "." { - extWithDot = "." + extWithDot - } - StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot) + extWithDot := ext + if extWithDot[:1] != "." { + extWithDot = "." + extWithDot } + StaticExtensionsToGzip = append(StaticExtensionsToGzip, extWithDot) } } + } - if enableadmin, err := GetConfig("bool", "EnableAdmin"); err == nil { - EnableAdmin = enableadmin.(bool) - } + if enableadmin, err := AppConfig.Bool("EnableAdmin"); err == nil { + EnableAdmin = enableadmin + } - if adminhttpaddr, _ := GetConfig("string", "AdminHttpAddr"); adminhttpaddr != "" { - AdminHttpAddr = adminhttpaddr.(string) - } + if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" { + AdminHttpAddr = adminhttpaddr + } - if adminhttpport, err := GetConfig("int", "AdminHttpPort"); err == nil { - AdminHttpPort = adminhttpport.(int) - } + if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil { + AdminHttpPort = adminhttpport + } - if enabledocs, err := GetConfig("bool", "EnableDocs"); err == nil { - EnableDocs = enabledocs.(bool) - } + if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil { + EnableDocs = enabledocs + } - if casesensitive, err := GetConfig("bool", "RouterCaseSensitive"); err == nil { - RouterCaseSensitive = casesensitive.(bool) - } + if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil { + RouterCaseSensitive = casesensitive } return nil } - -// Getconfig throw the Runmode -// [dev] -// name = astaixe -// IsEnable = false -// [prod] -// name = slene -// IsEnable = true -// -// usage: -// GetConfig("string", "name") -// GetConfig("bool", "IsEnable") -func GetConfig(typ, key string) (interface{}, error) { - switch typ { - case "string": - v := AppConfig.String(RunMode + "::" + key) - if v == "" { - v = AppConfig.String(key) - } - return v, nil - case "strings": - v := AppConfig.Strings(RunMode + "::" + key) - if len(v) == 0 { - v = AppConfig.Strings(key) - } - return v, nil - case "int": - v, err := AppConfig.Int(RunMode + "::" + key) - if err != nil || v == 0 { - return AppConfig.Int(key) - } - return v, nil - case "bool": - v, err := AppConfig.Bool(RunMode + "::" + key) - if err != nil { - return AppConfig.Bool(key) - } - return v, nil - case "int64": - v, err := AppConfig.Int64(RunMode + "::" + key) - if err != nil || v == 0 { - return AppConfig.Int64(key) - } - return v, nil - case "float": - v, err := AppConfig.Float(RunMode + "::" + key) - if err != nil || v == 0 { - return AppConfig.Float(key) - } - return v, nil - } - return "", errors.New("not support type") -} From a907a86476ee6e77c287a61e063c3cf5c1436ce1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 1 Oct 2014 22:28:49 +0800 Subject: [PATCH 26/92] fix #814 --- router.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/router.go b/router.go index b14cc5dd..3e1b4b2a 100644 --- a/router.go +++ b/router.go @@ -73,6 +73,8 @@ var ( "GetControllerAndAction"} url_placeholder = "{{placeholder}}" + + FilterRouterLog func() bool ) // To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter @@ -796,7 +798,9 @@ Admin: } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } - Debug(devinfo) + if FilterRouterLog == nil || !FilterRouterLog() { + Debug(devinfo) + } } // Call WriteHeader if status code has been set changed From aae89576c60f5cca7d1b05c8e5ae0588a1642730 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 1 Oct 2014 22:31:44 +0800 Subject: [PATCH 27/92] fix #814 --- router.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/router.go b/router.go index 3e1b4b2a..ddcf36c3 100644 --- a/router.go +++ b/router.go @@ -74,7 +74,7 @@ var ( url_placeholder = "{{placeholder}}" - FilterRouterLog func() bool + FilterRouterLog func(*beecontext.Context) bool ) // To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter @@ -798,7 +798,7 @@ Admin: } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } - if FilterRouterLog == nil || !FilterRouterLog() { + if FilterRouterLog == nil || !FilterRouterLog(context) { Debug(devinfo) } } From 6fca4a82186c3633f1cb5bdf6a0eb79f8c0529e9 Mon Sep 17 00:00:00 2001 From: Jens Bissinger Date: Thu, 2 Oct 2014 11:40:46 +0200 Subject: [PATCH 28/92] Insert pagination utilities from beego/wetalk. Refs #835. --- controller.go | 8 ++ pagination/controller.go | 32 ++++++++ pagination/paginator.go | 164 +++++++++++++++++++++++++++++++++++++++ pagination/utils.go | 20 +++++ 4 files changed, 224 insertions(+) create mode 100644 pagination/controller.go create mode 100644 pagination/paginator.go create mode 100644 pagination/utils.go diff --git a/controller.go b/controller.go index 72ba323b..e7eaa468 100644 --- a/controller.go +++ b/controller.go @@ -93,6 +93,14 @@ type ControllerInterface interface { URLMapping() } +func (c *Controller) GetCtx() *context.Context { + return c.Ctx +} + +func (c *Controller) GetData() map[interface{}]interface{} { + return c.Data +} + // Init generates default values of controller operations. func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) { c.Layout = "" diff --git a/pagination/controller.go b/pagination/controller.go new file mode 100644 index 00000000..794d779d --- /dev/null +++ b/pagination/controller.go @@ -0,0 +1,32 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pagination + +import ( + "github.com/astaxie/beego/context" +) + +type PaginationController interface { + GetCtx() *context.Context + GetData() map[interface{}]interface{} +} + +func SetPaginator(controller PaginationController, per int, nums int64) (paginator *Paginator) { + request := controller.GetCtx().Request + paginator = NewPaginator(request, per, nums) + data := controller.GetData() + data["paginator"] = paginator + return +} diff --git a/pagination/paginator.go b/pagination/paginator.go new file mode 100644 index 00000000..593f587d --- /dev/null +++ b/pagination/paginator.go @@ -0,0 +1,164 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pagination + +import ( + "math" + "net/http" + "net/url" + "strconv" +) + +type Paginator struct { + Request *http.Request + PerPageNums int + MaxPages int + + nums int64 + pageRange []int + pageNums int + page int +} + +func (p *Paginator) PageNums() int { + if p.pageNums != 0 { + return p.pageNums + } + pageNums := math.Ceil(float64(p.nums) / float64(p.PerPageNums)) + if p.MaxPages > 0 { + pageNums = math.Min(pageNums, float64(p.MaxPages)) + } + p.pageNums = int(pageNums) + return p.pageNums +} + +func (p *Paginator) Nums() int64 { + return p.nums +} + +func (p *Paginator) SetNums(nums interface{}) { + p.nums, _ = ToInt64(nums) +} + +func (p *Paginator) Page() int { + if p.page != 0 { + return p.page + } + if p.Request.Form == nil { + p.Request.ParseForm() + } + p.page, _ = strconv.Atoi(p.Request.Form.Get("p")) + if p.page > p.PageNums() { + p.page = p.PageNums() + } + if p.page <= 0 { + p.page = 1 + } + return p.page +} + +func (p *Paginator) Pages() []int { + if p.pageRange == nil && p.nums > 0 { + var pages []int + pageNums := p.PageNums() + page := p.Page() + switch { + case page >= pageNums-4 && pageNums > 9: + start := pageNums - 9 + 1 + pages = make([]int, 9) + for i, _ := range pages { + pages[i] = start + i + } + case page >= 5 && pageNums > 9: + start := page - 5 + 1 + pages = make([]int, int(math.Min(9, float64(page+4+1)))) + for i, _ := range pages { + pages[i] = start + i + } + default: + pages = make([]int, int(math.Min(9, float64(pageNums)))) + for i, _ := range pages { + pages[i] = i + 1 + } + } + p.pageRange = pages + } + return p.pageRange +} + +func (p *Paginator) PageLink(page int) string { + link, _ := url.ParseRequestURI(p.Request.RequestURI) + values := link.Query() + if page == 1 { + values.Del("p") + } else { + values.Set("p", strconv.Itoa(page)) + } + link.RawQuery = values.Encode() + return link.String() +} + +func (p *Paginator) PageLinkPrev() (link string) { + if p.HasPrev() { + link = p.PageLink(p.Page() - 1) + } + return +} + +func (p *Paginator) PageLinkNext() (link string) { + if p.HasNext() { + link = p.PageLink(p.Page() + 1) + } + return +} + +func (p *Paginator) PageLinkFirst() (link string) { + return p.PageLink(1) +} + +func (p *Paginator) PageLinkLast() (link string) { + return p.PageLink(p.PageNums()) +} + +func (p *Paginator) HasPrev() bool { + return p.Page() > 1 +} + +func (p *Paginator) HasNext() bool { + return p.Page() < p.PageNums() +} + +func (p *Paginator) IsActive(page int) bool { + return p.Page() == page +} + +func (p *Paginator) Offset() int { + return (p.Page() - 1) * p.PerPageNums +} + +func (p *Paginator) HasPages() bool { + return p.PageNums() > 1 +} + +func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator { + p := Paginator{} + p.Request = req + if per <= 0 { + per = 10 + } + p.PerPageNums = per + p.SetNums(nums) + return &p +} diff --git a/pagination/utils.go b/pagination/utils.go new file mode 100644 index 00000000..d1199f2d --- /dev/null +++ b/pagination/utils.go @@ -0,0 +1,20 @@ +package pagination + +import ( + "fmt" + "reflect" +) + +// convert any numeric value to int64 +func ToInt64(value interface{}) (d int64, err error) { + val := reflect.ValueOf(value) + switch value.(type) { + case int, int8, int16, int32, int64: + d = val.Int() + case uint, uint8, uint16, uint32, uint64: + d = int64(val.Uint()) + default: + err = fmt.Errorf("ToInt64 need numeric not `%T`", value) + } + return +} From c4f8f45da4ade512dd6fa0a2f943472fc92e2e6c Mon Sep 17 00:00:00 2001 From: Jens Bissinger Date: Mon, 6 Oct 2014 11:36:57 +0200 Subject: [PATCH 29/92] Move pagination to utils/pagination. Refs #837, #835. --- pagination/utils.go | 20 ----------- .../pagination}/controller.go | 0 {pagination => utils/pagination}/paginator.go | 0 utils/pagination/utils.go | 34 +++++++++++++++++++ 4 files changed, 34 insertions(+), 20 deletions(-) delete mode 100644 pagination/utils.go rename {pagination => utils/pagination}/controller.go (100%) rename {pagination => utils/pagination}/paginator.go (100%) create mode 100644 utils/pagination/utils.go diff --git a/pagination/utils.go b/pagination/utils.go deleted file mode 100644 index d1199f2d..00000000 --- a/pagination/utils.go +++ /dev/null @@ -1,20 +0,0 @@ -package pagination - -import ( - "fmt" - "reflect" -) - -// convert any numeric value to int64 -func ToInt64(value interface{}) (d int64, err error) { - val := reflect.ValueOf(value) - switch value.(type) { - case int, int8, int16, int32, int64: - d = val.Int() - case uint, uint8, uint16, uint32, uint64: - d = int64(val.Uint()) - default: - err = fmt.Errorf("ToInt64 need numeric not `%T`", value) - } - return -} diff --git a/pagination/controller.go b/utils/pagination/controller.go similarity index 100% rename from pagination/controller.go rename to utils/pagination/controller.go diff --git a/pagination/paginator.go b/utils/pagination/paginator.go similarity index 100% rename from pagination/paginator.go rename to utils/pagination/paginator.go diff --git a/utils/pagination/utils.go b/utils/pagination/utils.go new file mode 100644 index 00000000..5932647d --- /dev/null +++ b/utils/pagination/utils.go @@ -0,0 +1,34 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pagination + +import ( + "fmt" + "reflect" +) + +// convert any numeric value to int64 +func ToInt64(value interface{}) (d int64, err error) { + val := reflect.ValueOf(value) + switch value.(type) { + case int, int8, int16, int32, int64: + d = val.Int() + case uint, uint8, uint16, uint32, uint64: + d = int64(val.Uint()) + default: + err = fmt.Errorf("ToInt64 need numeric not `%T`", value) + } + return +} From fa6cbc08d99afe9b975e0823fe0fb3b0acaa3f4b Mon Sep 17 00:00:00 2001 From: Jens Bissinger Date: Tue, 7 Oct 2014 11:02:07 +0200 Subject: [PATCH 30/92] Document usage of utils/pagination. Refs #835. --- utils/pagination/controller.go | 50 ++++++++++++++++++++++++++++++++++ utils/pagination/paginator.go | 25 +++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/utils/pagination/controller.go b/utils/pagination/controller.go index 794d779d..5a0d4e99 100644 --- a/utils/pagination/controller.go +++ b/utils/pagination/controller.go @@ -12,6 +12,55 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Usage +// +// In your beego.Controller: +// +// package controllers +// +// import "github.com/astaxie/beego/utils/pagination" +// +// type PostsController struct { +// beego.Controller +// } +// +// func (this *PostsController) ListAllPosts() { +// // sets this.Data["paginator"] with the current offset (from the url query param) +// postsPerPage := 20 +// paginator := pagination.SetPaginator(this, postsPerPage, CountPosts()) +// +// // fetch the next 20 posts +// this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage) +// } +// +// +// In your view templates: +// +// {{if .paginator.HasPages}} +// +// {{end}} +// +// See also http://beego.me/docs/mvc/view/page.md package pagination import ( @@ -23,6 +72,7 @@ type PaginationController interface { GetData() map[interface{}]interface{} } +// Instantiates a Paginator and assigns it to controller.Data["paginator"]. func SetPaginator(controller PaginationController, per int, nums int64) (paginator *Paginator) { request := controller.GetCtx().Request paginator = NewPaginator(request, per, nums) diff --git a/utils/pagination/paginator.go b/utils/pagination/paginator.go index 593f587d..f89e878e 100644 --- a/utils/pagination/paginator.go +++ b/utils/pagination/paginator.go @@ -21,6 +21,7 @@ import ( "strconv" ) +// Paginator within the state of a http request. type Paginator struct { Request *http.Request PerPageNums int @@ -32,6 +33,7 @@ type Paginator struct { page int } +// Returns the total number of pages. func (p *Paginator) PageNums() int { if p.pageNums != 0 { return p.pageNums @@ -44,14 +46,17 @@ func (p *Paginator) PageNums() int { return p.pageNums } +// Returns the total number of items (e.g. from doing SQL count). func (p *Paginator) Nums() int64 { return p.nums } +// Sets the total number of items. func (p *Paginator) SetNums(nums interface{}) { p.nums, _ = ToInt64(nums) } +// Returns the current page. func (p *Paginator) Page() int { if p.page != 0 { return p.page @@ -69,6 +74,15 @@ func (p *Paginator) Page() int { return p.page } +// Returns a list of all pages. +// +// Usage (in a view template): +// +// {{range $index, $page := .paginator.Pages}} +// +// {{$page}} +// +// {{end}} func (p *Paginator) Pages() []int { if p.pageRange == nil && p.nums > 0 { var pages []int @@ -98,6 +112,7 @@ func (p *Paginator) Pages() []int { return p.pageRange } +// Returns URL for a given page index. func (p *Paginator) PageLink(page int) string { link, _ := url.ParseRequestURI(p.Request.RequestURI) values := link.Query() @@ -110,6 +125,7 @@ func (p *Paginator) PageLink(page int) string { return link.String() } +// Returns URL to the previous page. func (p *Paginator) PageLinkPrev() (link string) { if p.HasPrev() { link = p.PageLink(p.Page() - 1) @@ -117,6 +133,7 @@ func (p *Paginator) PageLinkPrev() (link string) { return } +// Returns URL to the next page. func (p *Paginator) PageLinkNext() (link string) { if p.HasNext() { link = p.PageLink(p.Page() + 1) @@ -124,34 +141,42 @@ func (p *Paginator) PageLinkNext() (link string) { return } +// Returns URL to the first page. func (p *Paginator) PageLinkFirst() (link string) { return p.PageLink(1) } +// Returns URL to the last page. func (p *Paginator) PageLinkLast() (link string) { return p.PageLink(p.PageNums()) } +// Returns true if the current page has a predecessor. func (p *Paginator) HasPrev() bool { return p.Page() > 1 } +// Returns true if the current page has a successor. func (p *Paginator) HasNext() bool { return p.Page() < p.PageNums() } +// Returns true if the given page index points to the current page. func (p *Paginator) IsActive(page int) bool { return p.Page() == page } +// Returns the current offset. func (p *Paginator) Offset() int { return (p.Page() - 1) * p.PerPageNums } +// Returns true if there is more than one page. func (p *Paginator) HasPages() bool { return p.PageNums() > 1 } +// Instantiates a paginator struct for the current http request. func NewPaginator(req *http.Request, per int, nums interface{}) *Paginator { p := Paginator{} p.Request = req From b6f789c497199851d8b5790b899bfa0e8147aba5 Mon Sep 17 00:00:00 2001 From: Bill Davis Date: Tue, 7 Oct 2014 16:35:30 -0400 Subject: [PATCH 31/92] Changes to handle multi filters on execution pt --- beego.go | 7 +- filter.go | 7 +- router.go | 11 ++- router_test.go | 194 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+), 8 deletions(-) diff --git a/beego.go b/beego.go index 32c1ee47..a58df00a 100644 --- a/beego.go +++ b/beego.go @@ -318,9 +318,10 @@ func DelStaticPath(url string) *App { // InsertFilter adds a FilterFunc with pattern condition and action constant. // The pos means action constant including -// beego.BeforeRouter, beego.AfterStatic, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. -func InsertFilter(pattern string, pos int, filter FilterFunc) *App { - BeeApp.Handlers.InsertFilter(pattern, pos, filter) +// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. +// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) +func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App { + BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...) return BeeApp } diff --git a/filter.go b/filter.go index 294966d4..eebad329 100644 --- a/filter.go +++ b/filter.go @@ -17,9 +17,10 @@ package beego // FilterRouter defines filter operation before controller handler execution. // it can match patterned url and do filter function when action arrives. type FilterRouter struct { - filterFunc FilterFunc - tree *Tree - pattern string + filterFunc FilterFunc + tree *Tree + pattern string + returnOnOutput bool } // ValidRouter check current request is valid for this filter. diff --git a/router.go b/router.go index ddcf36c3..e22b5c1f 100644 --- a/router.go +++ b/router.go @@ -381,7 +381,9 @@ func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) } // Add a FilterFunc with pattern rule and action constant. -func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc) error { +// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) +func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error { + mr := new(FilterRouter) mr.tree = NewTree() mr.pattern = pattern @@ -389,6 +391,11 @@ func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter Filter if !RouterCaseSensitive { pattern = strings.ToLower(pattern) } + if params == nil { + mr.returnOnOutput = true + } else { + mr.returnOnOutput = params[0] + } mr.tree.AddRouter(pattern, true) return p.insertFilterRouter(pos, mr) } @@ -587,7 +594,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if ok, p := filterR.ValidRouter(urlPath); ok { context.Input.Params = p filterR.filterFunc(context) - if w.started { + if filterR.returnOnOutput && w.started { return true } } diff --git a/router_test.go b/router_test.go index d378589b..c64bb191 100644 --- a/router_test.go +++ b/router_test.go @@ -17,6 +17,7 @@ package beego import ( "net/http" "net/http/httptest" + "strings" "testing" "github.com/astaxie/beego/context" @@ -385,3 +386,196 @@ func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request return recorder, request } + +// Execution point: BeforeRouter +// expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle +func TestFilterBeforeRouter(t *testing.T) { + testName := "TestFilterBeforeRouter" + url := "/beforeRouter" + + mux := NewControllerRegister() + mux.InsertFilter(url, BeforeRouter, beegoBeforeRouter1) + + mux.Get(url, beegoFilterFunc) + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if strings.Contains(rw.Body.String(), "BeforeRouter1") == false { + t.Errorf(testName + " BeforeRouter did not run") + } + if strings.Contains(rw.Body.String(), "hello") == true { + t.Errorf(testName + " BeforeRouter did not return properly") + } +} + +// Execution point: BeforeExec +// expectation: only BeforeExec function is executed, match as router determines route only +func TestFilterBeforeExec(t *testing.T) { + testName := "TestFilterBeforeExec" + url := "/beforeExec" + + mux := NewControllerRegister() + mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput) + mux.InsertFilter(url, BeforeExec, beegoBeforeExec1) + + mux.Get(url, beegoFilterFunc) + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if strings.Contains(rw.Body.String(), "BeforeExec1") == false { + t.Errorf(testName + " BeforeExec did not run") + } + if strings.Contains(rw.Body.String(), "hello") == true { + t.Errorf(testName + " BeforeExec did not return properly") + } + if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + t.Errorf(testName + " BeforeRouter ran in error") + } +} + +// Execution point: AfterExec +// expectation: only AfterExec function is executed, match as router handles +func TestFilterAfterExec(t *testing.T) { + testName := "TestFilterAfterExec" + url := "/afterExec" + + mux := NewControllerRegister() + mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput) + mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput) + mux.InsertFilter(url, AfterExec, beegoAfterExec1) + + mux.Get(url, beegoFilterFunc) + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if strings.Contains(rw.Body.String(), "AfterExec1") == false { + t.Errorf(testName + " AfterExec did not run") + } + if strings.Contains(rw.Body.String(), "hello") == false { + t.Errorf(testName + " handler did not run properly") + } + if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + t.Errorf(testName + " BeforeRouter ran in error") + } + if strings.Contains(rw.Body.String(), "BeforeExec") == true { + t.Errorf(testName + " BeforeExec ran in error") + } +} + +// Execution point: FinishRouter +// expectation: only FinishRouter function is executed, match as router handles +func TestFilterFinishRouter(t *testing.T) { + testName := "TestFilterFinishRouter" + url := "/finishRouter" + + mux := NewControllerRegister() + mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput) + mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput) + mux.InsertFilter(url, AfterExec, beegoFilterNoOutput) + mux.InsertFilter(url, FinishRouter, beegoFinishRouter1) + + mux.Get(url, beegoFilterFunc) + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if strings.Contains(rw.Body.String(), "FinishRouter1") == true { + t.Errorf(testName + " FinishRouter did not run") + } + if strings.Contains(rw.Body.String(), "hello") == false { + t.Errorf(testName + " handler did not run properly") + } + if strings.Contains(rw.Body.String(), "AfterExec1") == true { + t.Errorf(testName + " AfterExec ran in error") + } + if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + t.Errorf(testName + " BeforeRouter ran in error") + } + if strings.Contains(rw.Body.String(), "BeforeExec") == true { + t.Errorf(testName + " BeforeExec ran in error") + } +} + +// Execution point: FinishRouter +// expectation: only first FinishRouter function is executed, match as router handles +func TestFilterFinishRouterMultiFirstOnly(t *testing.T) { + testName := "TestFilterFinishRouterMultiFirstOnly" + url := "/finishRouterMultiFirstOnly" + + mux := NewControllerRegister() + mux.InsertFilter(url, FinishRouter, beegoFinishRouter1) + mux.InsertFilter(url, FinishRouter, beegoFinishRouter2) + + mux.Get(url, beegoFilterFunc) + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if strings.Contains(rw.Body.String(), "FinishRouter1") == false { + t.Errorf(testName + " FinishRouter1 did not run") + } + if strings.Contains(rw.Body.String(), "hello") == false { + t.Errorf(testName + " handler did not run properly") + } + // not expected in body + if strings.Contains(rw.Body.String(), "FinishRouter2") == true { + t.Errorf(testName + " FinishRouter2 did run") + } +} + +// Execution point: FinishRouter +// expectation: both FinishRouter functions execute, match as router handles +func TestFilterFinishRouterMulti(t *testing.T) { + testName := "TestFilterFinishRouterMulti" + url := "/finishRouterMulti" + + mux := NewControllerRegister() + mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false) + mux.InsertFilter(url, FinishRouter, beegoFinishRouter2) + + mux.Get(url, beegoFilterFunc) + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if strings.Contains(rw.Body.String(), "FinishRouter1") == false { + t.Errorf(testName + " FinishRouter1 did not run") + } + if strings.Contains(rw.Body.String(), "hello") == false { + t.Errorf(testName + " handler did not run properly") + } + if strings.Contains(rw.Body.String(), "FinishRouter2") == false { + t.Errorf(testName + " FinishRouter2 did not run properly") + } +} + +func beegoFilterNoOutput(ctx *context.Context) { + return +} +func beegoBeforeRouter1(ctx *context.Context) { + ctx.WriteString("|BeforeRouter1") +} +func beegoBeforeRouter2(ctx *context.Context) { + ctx.WriteString("|BeforeRouter2") +} +func beegoBeforeExec1(ctx *context.Context) { + ctx.WriteString("|BeforeExec1") +} +func beegoBeforeExec2(ctx *context.Context) { + ctx.WriteString("|BeforeExec2") +} +func beegoAfterExec1(ctx *context.Context) { + ctx.WriteString("|AfterExec1") +} +func beegoAfterExec2(ctx *context.Context) { + ctx.WriteString("|AfterExec2") +} +func beegoFinishRouter1(ctx *context.Context) { + ctx.WriteString("|FinishRouter1") +} +func beegoFinishRouter2(ctx *context.Context) { + ctx.WriteString("|FinishRouter2") +} From 52df979aca4fc2fd9b3a0cb735937f3a1655a63c Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Oct 2014 14:02:57 +0800 Subject: [PATCH 32/92] update the godocs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85fdc690..0d7414b8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Beego [![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest) -[![GoDoc](https://godoc.org/github.com/astaxie/beego?status.svg)](https://godoc.org/github.com/astaxie/beego) +[![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) beego is an open-source, high-performance, modularity, full-stack web framework. From 1ba7847913414e800d100651889dcae1bcb3c22a Mon Sep 17 00:00:00 2001 From: Bill Davis Date: Wed, 8 Oct 2014 09:21:34 -0400 Subject: [PATCH 33/92] Changing check from nil to len based on slice --- router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router.go b/router.go index e22b5c1f..0d51410b 100644 --- a/router.go +++ b/router.go @@ -391,7 +391,7 @@ func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter Filter if !RouterCaseSensitive { pattern = strings.ToLower(pattern) } - if params == nil { + if len(params) == 0 { mr.returnOnOutput = true } else { mr.returnOnOutput = params[0] From 0b3763cc6775d2b423002a0405d15d1a2e38ac1d Mon Sep 17 00:00:00 2001 From: Jens Bissinger Date: Tue, 7 Oct 2014 11:39:27 +0200 Subject: [PATCH 34/92] Cleanup pagination documentation. Refs #835. --- utils/pagination/controller.go | 49 ---------------------------- utils/pagination/doc.go | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 utils/pagination/doc.go diff --git a/utils/pagination/controller.go b/utils/pagination/controller.go index 5a0d4e99..da92bd69 100644 --- a/utils/pagination/controller.go +++ b/utils/pagination/controller.go @@ -12,55 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Usage -// -// In your beego.Controller: -// -// package controllers -// -// import "github.com/astaxie/beego/utils/pagination" -// -// type PostsController struct { -// beego.Controller -// } -// -// func (this *PostsController) ListAllPosts() { -// // sets this.Data["paginator"] with the current offset (from the url query param) -// postsPerPage := 20 -// paginator := pagination.SetPaginator(this, postsPerPage, CountPosts()) -// -// // fetch the next 20 posts -// this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage) -// } -// -// -// In your view templates: -// -// {{if .paginator.HasPages}} -// -// {{end}} -// -// See also http://beego.me/docs/mvc/view/page.md package pagination import ( diff --git a/utils/pagination/doc.go b/utils/pagination/doc.go new file mode 100644 index 00000000..db8cd57e --- /dev/null +++ b/utils/pagination/doc.go @@ -0,0 +1,59 @@ +/* + +The pagination package provides utilities to setup a paginator within the +context of a http request. + +Usage + +In your beego.Controller: + + package controllers + + import "github.com/astaxie/beego/utils/pagination" + + type PostsController struct { + beego.Controller + } + + func (this *PostsController) ListAllPosts() { + // sets this.Data["paginator"] with the current offset (from the url query param) + postsPerPage := 20 + paginator := pagination.SetPaginator(this, postsPerPage, CountPosts()) + + // fetch the next 20 posts + this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage) + } + + +In your view templates: + + {{if .paginator.HasPages}} + + {{end}} + +See also + +http://beego.me/docs/mvc/view/page.md + +*/ +package pagination From 262665f4e59c8eb504c53b012af3c57066f7d9df Mon Sep 17 00:00:00 2001 From: Jens Bissinger Date: Wed, 8 Oct 2014 15:59:26 +0200 Subject: [PATCH 35/92] Remove PaginationController interface and pass context instead. Refs #835. --- controller.go | 8 -------- utils/pagination/controller.go | 15 ++++----------- utils/pagination/doc.go | 2 +- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/controller.go b/controller.go index e7eaa468..72ba323b 100644 --- a/controller.go +++ b/controller.go @@ -93,14 +93,6 @@ type ControllerInterface interface { URLMapping() } -func (c *Controller) GetCtx() *context.Context { - return c.Ctx -} - -func (c *Controller) GetData() map[interface{}]interface{} { - return c.Data -} - // Init generates default values of controller operations. func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) { c.Layout = "" diff --git a/utils/pagination/controller.go b/utils/pagination/controller.go index da92bd69..28473f8a 100644 --- a/utils/pagination/controller.go +++ b/utils/pagination/controller.go @@ -18,16 +18,9 @@ import ( "github.com/astaxie/beego/context" ) -type PaginationController interface { - GetCtx() *context.Context - GetData() map[interface{}]interface{} -} - -// Instantiates a Paginator and assigns it to controller.Data["paginator"]. -func SetPaginator(controller PaginationController, per int, nums int64) (paginator *Paginator) { - request := controller.GetCtx().Request - paginator = NewPaginator(request, per, nums) - data := controller.GetData() - data["paginator"] = paginator +// Instantiates a Paginator and assigns it to context.Input.Data["paginator"]. +func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) { + paginator = NewPaginator(context.Request, per, nums) + context.Input.Data["paginator"] = paginator return } diff --git a/utils/pagination/doc.go b/utils/pagination/doc.go index db8cd57e..df0fa3b7 100644 --- a/utils/pagination/doc.go +++ b/utils/pagination/doc.go @@ -18,7 +18,7 @@ In your beego.Controller: func (this *PostsController) ListAllPosts() { // sets this.Data["paginator"] with the current offset (from the url query param) postsPerPage := 20 - paginator := pagination.SetPaginator(this, postsPerPage, CountPosts()) + paginator := pagination.SetPaginator(this.Ctx, postsPerPage, CountPosts()) // fetch the next 20 posts this.Data["posts"] = ListPostsByOffsetAndLimit(paginator.Offset(), postsPerPage) From a06e0f27ad3b20fb872b28eb73720d9ded2375cf Mon Sep 17 00:00:00 2001 From: Bill Davis Date: Wed, 8 Oct 2014 15:00:07 -0400 Subject: [PATCH 36/92] Support run mode set by env var BEEGO_RUNMODE --- config.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index ef201ec6..314e91b2 100644 --- a/config.go +++ b/config.go @@ -26,6 +26,7 @@ import ( "github.com/astaxie/beego/logs" "github.com/astaxie/beego/session" "github.com/astaxie/beego/utils" + "github.com/kelseyhightower/envconfig" ) var ( @@ -83,9 +84,14 @@ var ( RouterCaseSensitive bool // router case sensitive default is true ) -type beegoAppConfig struct { - innerConfig config.ConfigContainer -} +type ( + beegoAppConfig struct { + innerConfig config.ConfigContainer + } + beegoEnvConfig struct { + RunMode string + } +) func newAppConfig(AppConfigProvider, AppConfigPath string) *beegoAppConfig { ac, err := config.NewConfig(AppConfigProvider, AppConfigPath) @@ -288,8 +294,16 @@ func init() { func ParseConfig() (err error) { AppConfig = newAppConfig(AppConfigProvider, AppConfigPath) + var ec beegoEnvConfig + err = envconfig.Process("beego", &ec) + if err != nil { + return err + } + // set the runmode first - if runmode := AppConfig.String("RunMode"); runmode != "" { + if ec.RunMode != "" { + RunMode = ec.RunMode + } else if runmode := AppConfig.String("RunMode"); runmode != "" { RunMode = runmode } From 8af0475251d323b49b9f9989eabe8cf4ee762025 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 9 Oct 2014 18:47:22 +0800 Subject: [PATCH 37/92] fix #833 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index a58df00a..8824798f 100644 --- a/beego.go +++ b/beego.go @@ -378,7 +378,7 @@ func initBeforeHttpRun() { if sessionConfig == "" { sessionConfig = `{"cookieName":"` + SessionName + `",` + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + - `"providerConfig":"` + SessionSavePath + `",` + + `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + `"sessionIDHashFunc":"` + SessionHashFunc + `",` + `"sessionIDHashKey":"` + SessionHashKey + `",` + From a27f5c0dc0dbd33ac3239b0066d11711a7a24855 Mon Sep 17 00:00:00 2001 From: Bill Davis Date: Thu, 9 Oct 2014 09:17:10 -0400 Subject: [PATCH 38/92] Remove dependency of third party lib --- config.go | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/config.go b/config.go index 314e91b2..ea02c54e 100644 --- a/config.go +++ b/config.go @@ -26,7 +26,6 @@ import ( "github.com/astaxie/beego/logs" "github.com/astaxie/beego/session" "github.com/astaxie/beego/utils" - "github.com/kelseyhightower/envconfig" ) var ( @@ -84,14 +83,9 @@ var ( RouterCaseSensitive bool // router case sensitive default is true ) -type ( - beegoAppConfig struct { - innerConfig config.ConfigContainer - } - beegoEnvConfig struct { - RunMode string - } -) +type beegoAppConfig struct { + innerConfig config.ConfigContainer +} func newAppConfig(AppConfigProvider, AppConfigPath string) *beegoAppConfig { ac, err := config.NewConfig(AppConfigProvider, AppConfigPath) @@ -294,15 +288,10 @@ func init() { func ParseConfig() (err error) { AppConfig = newAppConfig(AppConfigProvider, AppConfigPath) - var ec beegoEnvConfig - err = envconfig.Process("beego", &ec) - if err != nil { - return err - } - + envRunMode := os.Getenv("BEEGO_RUNMODE") // set the runmode first - if ec.RunMode != "" { - RunMode = ec.RunMode + if envRunMode != "" { + RunMode = envRunMode } else if runmode := AppConfig.String("RunMode"); runmode != "" { RunMode = runmode } From ca3e7568a1ae9b3f433caf22d95e222bc0bad83b Mon Sep 17 00:00:00 2001 From: Bill Davis Date: Thu, 9 Oct 2014 16:32:56 -0400 Subject: [PATCH 39/92] Add ability to get statistics in json format --- toolbox/statistics.go | 26 ++++++++++++++++++++++++++ toolbox/statistics_test.go | 6 ++++++ 2 files changed, 32 insertions(+) diff --git a/toolbox/statistics.go b/toolbox/statistics.go index 382daba0..779fd637 100644 --- a/toolbox/statistics.go +++ b/toolbox/statistics.go @@ -15,6 +15,7 @@ package toolbox import ( + "encoding/json" "fmt" "sync" "time" @@ -111,6 +112,31 @@ func (m *UrlMap) GetMap() map[string]interface{} { return content } +func (m *UrlMap) GetMapJSON() ([]byte, error) { + return json.Marshal(m) +} + +func (m UrlMap) MarshalJSON() ([]byte, error) { + + resultLists := make([]map[string]interface{}, 0) + + for k, v := range m.urlmap { + for kk, vv := range v { + result := map[string]interface{}{ + "request_url": k, + "method": kk, + "times": vv.RequestNum, + "total_time": toS(vv.TotalTime), + "max_time": toS(vv.MaxTime), + "min_time": toS(vv.MinTime), + "avg_time": toS(time.Duration(int64(vv.TotalTime) / vv.RequestNum)), + } + resultLists = append(resultLists, result) + } + } + return json.Marshal(resultLists) +} + // global statistics data map var StatisticsMap *UrlMap diff --git a/toolbox/statistics_test.go b/toolbox/statistics_test.go index 448b2af5..97860d32 100644 --- a/toolbox/statistics_test.go +++ b/toolbox/statistics_test.go @@ -28,4 +28,10 @@ func TestStatics(t *testing.T) { StatisticsMap.AddStatistics("POST", "/api/user/xiemengjun", "&admin.user", time.Duration(13000)) StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400)) t.Log(StatisticsMap.GetMap()) + + jsonString, err := StatisticsMap.GetMapJSON() + if err != nil { + t.Errorf(err.Error()) + } + t.Log(string(jsonString)) } From 5c1e8e42b9f84c9f9e1e977247cf649983efb44c Mon Sep 17 00:00:00 2001 From: Bill Davis Date: Thu, 9 Oct 2014 17:07:28 -0400 Subject: [PATCH 40/92] Reworked implementation to not return encoded json --- toolbox/statistics.go | 9 ++------- toolbox/statistics_test.go | 7 +++++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/toolbox/statistics.go b/toolbox/statistics.go index 779fd637..beeafc7b 100644 --- a/toolbox/statistics.go +++ b/toolbox/statistics.go @@ -15,7 +15,6 @@ package toolbox import ( - "encoding/json" "fmt" "sync" "time" @@ -112,11 +111,7 @@ func (m *UrlMap) GetMap() map[string]interface{} { return content } -func (m *UrlMap) GetMapJSON() ([]byte, error) { - return json.Marshal(m) -} - -func (m UrlMap) MarshalJSON() ([]byte, error) { +func (m *UrlMap) GetMapData() []map[string]interface{} { resultLists := make([]map[string]interface{}, 0) @@ -134,7 +129,7 @@ func (m UrlMap) MarshalJSON() ([]byte, error) { resultLists = append(resultLists, result) } } - return json.Marshal(resultLists) + return resultLists } // global statistics data map diff --git a/toolbox/statistics_test.go b/toolbox/statistics_test.go index 97860d32..ac29476c 100644 --- a/toolbox/statistics_test.go +++ b/toolbox/statistics_test.go @@ -15,6 +15,7 @@ package toolbox import ( + "encoding/json" "testing" "time" ) @@ -29,9 +30,11 @@ func TestStatics(t *testing.T) { StatisticsMap.AddStatistics("DELETE", "/api/user", "&admin.user", time.Duration(1400)) t.Log(StatisticsMap.GetMap()) - jsonString, err := StatisticsMap.GetMapJSON() + data := StatisticsMap.GetMapData() + b, err := json.Marshal(data) if err != nil { t.Errorf(err.Error()) } - t.Log(string(jsonString)) + + t.Log(string(b)) } From e5134873be1228868940c432c7c94f1e47fe556b Mon Sep 17 00:00:00 2001 From: TossPig Date: Fri, 10 Oct 2014 14:40:07 +0800 Subject: [PATCH 41/92] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8F=91=E9=80=81?= =?UTF-8?q?=E9=82=AE=E4=BB=B6=E5=86=85=E5=B5=8C=E9=99=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为*Email.AttachFile和Email.Attach增加了一个参数"id". 当id不为空时,设置头部信息Content-Disposition为inline,并添加Content-ID头的值为id --- utils/mail.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index c7ab73d8..b598d6d1 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -157,19 +157,19 @@ func (e *Email) Bytes() ([]byte, error) { } // Add attach file to the send mail -func (e *Email) AttachFile(filename string) (a *Attachment, err error) { +func (e *Email) AttachFile(filename string, id string) (a *Attachment, err error) { f, err := os.Open(filename) if err != nil { return } ct := mime.TypeByExtension(filepath.Ext(filename)) basename := path.Base(filename) - return e.Attach(f, basename, ct) + return e.Attach(f, basename, ct, id) } // Attach is used to attach content from an io.Reader to the email. // Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type. -func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, err error) { +func (e *Email) Attach(r io.Reader, filename string, c string, id string) (a *Attachment, err error) { var buffer bytes.Buffer if _, err = io.Copy(&buffer, r); err != nil { return @@ -186,7 +186,12 @@ func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, e // If the Content-Type is blank, set the Content-Type to "application/octet-stream" at.Header.Set("Content-Type", "application/octet-stream") } - at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename)) + if id != "" { + at.Header.Set("Content-Disposition", fmt.Sprintf("inline;\r\n filename=\"%s\"", filename)) + at.Header.Set("Content-ID", fmt.Sprintf("<%s>", id)) + }else { + at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename)) + } at.Header.Set("Content-Transfer-Encoding", "base64") e.Attachments = append(e.Attachments, at) return at, nil @@ -269,7 +274,7 @@ func qpEscape(dest []byte, c byte) { const nums = "0123456789ABCDEF" dest[0] = '=' dest[1] = nums[(c&0xf0)>>4] - dest[2] = nums[(c & 0xf)] + dest[2] = nums[(c&0xf)] } // headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer From 6a33647f3065f88e568138abb9c3fa43ad967974 Mon Sep 17 00:00:00 2001 From: TossPig Date: Fri, 10 Oct 2014 23:40:02 +0800 Subject: [PATCH 42/92] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为了保持向后兼容, --- utils/mail.go | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index b598d6d1..35b4b950 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -157,7 +157,17 @@ func (e *Email) Bytes() ([]byte, error) { } // Add attach file to the send mail -func (e *Email) AttachFile(filename string, id string) (a *Attachment, err error) { +func (e *Email) AttachFile(args ...string) (a *Attachment, err error) { + argsLength := len(args) + if argsLength < 1 || argsLength > 2 { + return + } + filename := args[0] + id := "" + if argsLength > 1 { + id = args[1] + } + id = args[1] f, err := os.Open(filename) if err != nil { return @@ -169,7 +179,18 @@ func (e *Email) AttachFile(filename string, id string) (a *Attachment, err error // Attach is used to attach content from an io.Reader to the email. // Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type. -func (e *Email) Attach(r io.Reader, filename string, c string, id string) (a *Attachment, err error) { +func (e *Email) Attach(r io.Reader, filename string, ci ...string) (a *Attachment, err error) { + args := ci + argsLength := len(args) + if argsLength < 1 || argsLength > 2 { + return + } + c := args[0] + id := "" + if argsLength > 1 { + id = args[1] + } + id = args[1] var buffer bytes.Buffer if _, err = io.Copy(&buffer, r); err != nil { return @@ -189,7 +210,7 @@ func (e *Email) Attach(r io.Reader, filename string, c string, id string) (a *At if id != "" { at.Header.Set("Content-Disposition", fmt.Sprintf("inline;\r\n filename=\"%s\"", filename)) at.Header.Set("Content-ID", fmt.Sprintf("<%s>", id)) - }else { + } else { at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename)) } at.Header.Set("Content-Transfer-Encoding", "base64") From 41de7c7db672f4f7f2d794dce608a4af83edc33b Mon Sep 17 00:00:00 2001 From: TossPig Date: Sat, 11 Oct 2014 00:02:36 +0800 Subject: [PATCH 43/92] fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改一个错误。 看到text/template包的写法,和你的想法是一致的。 --- utils/mail.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index 35b4b950..492ad06f 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -158,16 +158,14 @@ func (e *Email) Bytes() ([]byte, error) { // Add attach file to the send mail func (e *Email) AttachFile(args ...string) (a *Attachment, err error) { - argsLength := len(args) - if argsLength < 1 || argsLength > 2 { + if len(args) < 1 || len(args) > 2 { return } filename := args[0] id := "" - if argsLength > 1 { + if len(args) > 1 { id = args[1] } - id = args[1] f, err := os.Open(filename) if err != nil { return @@ -179,18 +177,15 @@ func (e *Email) AttachFile(args ...string) (a *Attachment, err error) { // Attach is used to attach content from an io.Reader to the email. // Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type. -func (e *Email) Attach(r io.Reader, filename string, ci ...string) (a *Attachment, err error) { - args := ci - argsLength := len(args) - if argsLength < 1 || argsLength > 2 { +func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) { + if len(args) < 1 || len(args) > 2 { return } - c := args[0] + c := args[0] //Content-Type id := "" - if argsLength > 1 { - id = args[1] + if len(args) > 1 { + id = args[1] //Content-ID } - id = args[1] var buffer bytes.Buffer if _, err = io.Copy(&buffer, r); err != nil { return From d69eee23f016ea4f832e5e0e61355e739be07b48 Mon Sep 17 00:00:00 2001 From: TossPig Date: Sat, 11 Oct 2014 00:38:31 +0800 Subject: [PATCH 44/92] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 不知道英文区的人能否看懂Cnglish。。。 --- utils/mail.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index 492ad06f..e70e3586 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -158,7 +158,8 @@ func (e *Email) Bytes() ([]byte, error) { // Add attach file to the send mail func (e *Email) AttachFile(args ...string) (a *Attachment, err error) { - if len(args) < 1 || len(args) > 2 { + if len(args) < 1 && len(args) > 2 { + err = errors.New("Must specify a file name and number of parameters can not exceed at least two") return } filename := args[0] @@ -178,13 +179,14 @@ func (e *Email) AttachFile(args ...string) (a *Attachment, err error) { // Attach is used to attach content from an io.Reader to the email. // Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type. func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) { - if len(args) < 1 || len(args) > 2 { + if len(args) < 1 && len(args) > 2 { + err = errors.New("Must specify a file type and number of parameters can not exceed at least two") return } - c := args[0] //Content-Type + c := args[0] //Content-Type id := "" if len(args) > 1 { - id = args[1] //Content-ID + id = args[1] //Content-ID } var buffer bytes.Buffer if _, err = io.Copy(&buffer, r); err != nil { From fc07419938e2a915fe310db6472ca98012ab34e3 Mon Sep 17 00:00:00 2001 From: TossPig Date: Sat, 11 Oct 2014 00:42:01 +0800 Subject: [PATCH 45/92] Update mail.go --- utils/mail.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/mail.go b/utils/mail.go index e70e3586..aa219626 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -180,7 +180,7 @@ func (e *Email) AttachFile(args ...string) (a *Attachment, err error) { // Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type. func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) { if len(args) < 1 && len(args) > 2 { - err = errors.New("Must specify a file type and number of parameters can not exceed at least two") + err = errors.New("Must specify the file type and number of parameters can not exceed at least two") return } c := args[0] //Content-Type From 812950b60d177aa7d43b3b8cda1b2c367282ec43 Mon Sep 17 00:00:00 2001 From: Jens Bissinger Date: Mon, 13 Oct 2014 13:47:44 +0200 Subject: [PATCH 46/92] Allow to use fastcgi via standard io. --- app.go | 23 ++++++++++++++++------- config.go | 2 ++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app.go b/app.go index 0a83ba41..0b89d89e 100644 --- a/app.go +++ b/app.go @@ -55,15 +55,24 @@ func (app *App) Run() { endRunning := make(chan bool, 1) if UseFcgi { - if HttpPort == 0 { - l, err = net.Listen("unix", addr) + if UseStdIo { + err = fcgi.Serve(nil, app.Handlers) // standard I/O + if err == nil { + BeeLogger.Info("Use FCGI via standard I/O") + } else { + BeeLogger.Info("Cannot use FCGI via standard I/O", err) + } } else { - l, err = net.Listen("tcp", addr) + if HttpPort == 0 { + l, err = net.Listen("unix", addr) + } else { + l, err = net.Listen("tcp", addr) + } + if err != nil { + BeeLogger.Critical("Listen: ", err) + } + err = fcgi.Serve(l, app.Handlers) } - if err != nil { - BeeLogger.Critical("Listen: ", err) - } - err = fcgi.Serve(l, app.Handlers) } else { app.Server.Addr = addr app.Server.Handler = app.Handlers diff --git a/config.go b/config.go index ef201ec6..6c121756 100644 --- a/config.go +++ b/config.go @@ -61,6 +61,7 @@ var ( SessionAutoSetCookie bool // auto setcookie SessionDomain string // the cookie domain default is empty UseFcgi bool + UseStdIo bool MaxMemory int64 EnableGzip bool // flag of enable gzip DirectoryIndex bool // flag of display directory index. default is false. @@ -241,6 +242,7 @@ func init() { SessionAutoSetCookie = true UseFcgi = false + UseStdIo = false MaxMemory = 1 << 26 //64MB From fa1281002ef72f2b3c31be0c5c62d45bb9b4883e Mon Sep 17 00:00:00 2001 From: WithGJR Date: Thu, 16 Oct 2014 18:26:01 +0800 Subject: [PATCH 47/92] fix router bug: when the request is PUT or DELETE, router can't find the actual route and will throw 404 page to user --- router.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/router.go b/router.go index 0d51410b..7194bd29 100644 --- a/router.go +++ b/router.go @@ -648,7 +648,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } if !findrouter { - if t, ok := p.routers[r.Method]; ok { + http_method := "" + + if r.Method == "POST" && context.Input.Query("_method") == "PUT" { + http_method = "PUT" + } + + if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { + http_method = "DELETE" + } + + if http_method != "PUT" && http_method != "DELETE" { + http_method = r.Method + } + + if t, ok := p.routers[http_method]; ok { runObject, p := t.Match(urlPath) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r From efc14a1e8df6e71540a75fd09c8002b0728c986f Mon Sep 17 00:00:00 2001 From: WithGJR Date: Thu, 16 Oct 2014 18:58:12 +0800 Subject: [PATCH 48/92] fix router bug with more better way --- router.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/router.go b/router.go index 7194bd29..2a88490d 100644 --- a/router.go +++ b/router.go @@ -648,20 +648,16 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } if !findrouter { - http_method := "" + http_method := r.Method - if r.Method == "POST" && context.Input.Query("_method") == "PUT" { + if http_method == "POST" && context.Input.Query("_method") == "PUT" { http_method = "PUT" } - if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { + if http_method == "POST" && context.Input.Query("_method") == "DELETE" { http_method = "DELETE" } - if http_method != "PUT" && http_method != "DELETE" { - http_method = r.Method - } - if t, ok := p.routers[http_method]; ok { runObject, p := t.Match(urlPath) if r, ok := runObject.(*controllerInfo); ok { From 1eb9aef687bca35f09574b647eef4582686fe6dd Mon Sep 17 00:00:00 2001 From: Brandon Gao Date: Thu, 16 Oct 2014 20:16:17 +0800 Subject: [PATCH 49/92] Use SETEX command to set session In order to be compatible with older version Redis, use `SETEX` command instead of `SET x y EX 360`. --- session/redis/sess_redis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index f2b0b29b..82cdd812 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -109,7 +109,7 @@ func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) { return } - c.Do("SET", rs.sid, string(b), "EX", rs.maxlifetime) + c.Do("SETEX", rs.sid, rs.maxlifetime, string(b)) } // redis session provider From 1b3e7de4639384b3d1f33e13885f8b32eda1b719 Mon Sep 17 00:00:00 2001 From: WithGJR Date: Mon, 20 Oct 2014 17:49:16 +0800 Subject: [PATCH 50/92] add new feature to 'renderform' function, user could add HTML id and class now --- templatefunc.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/templatefunc.go b/templatefunc.go index a365718d..455f3d3f 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -368,23 +368,23 @@ func RenderForm(obj interface{}) template.HTML { fieldT := objT.Field(i) - label, name, fType, ignored := parseFormTag(fieldT) + label, name, fType, id, class, ignored := parseFormTag(fieldT) if ignored { continue } - raw = append(raw, renderFormField(label, name, fType, fieldV.Interface())) + raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class)) } return template.HTML(strings.Join(raw, "
")) } // renderFormField returns a string containing HTML of a single form field. -func renderFormField(label, name, fType string, value interface{}) string { +func renderFormField(label, name, fType string, value interface{}, id string, class string) string { if isValidForInput(fType) { - return fmt.Sprintf(`%v`, label, name, fType, value) + return fmt.Sprintf(`%v`, label, id, class, name, fType, value) } - return fmt.Sprintf(`%v<%v name="%v">%v`, label, fType, name, value, fType) + return fmt.Sprintf(`%v<%v id="%v" class="%v" name="%v">%v`, label, fType, id, class, name, value, fType) } // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. @@ -400,12 +400,14 @@ func isValidForInput(fType string) bool { // parseFormTag takes the stuct-tag of a StructField and parses the `form` value. // returned are the form label, name-property, type and wether the field should be ignored. -func parseFormTag(fieldT reflect.StructField) (label, name, fType string, ignored bool) { +func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool) { tags := strings.Split(fieldT.Tag.Get("form"), ",") label = fieldT.Name + ": " name = fieldT.Name fType = "text" ignored = false + id = fieldT.Tag.Get("id") + class = fieldT.Tag.Get("class") switch len(tags) { case 1: From 76db5cded4e7e1e63b3da2751f6c463cf0cc4e75 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 20 Oct 2014 18:21:17 +0800 Subject: [PATCH 51/92] fix the init mime --- beego.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beego.go b/beego.go index 8824798f..f23174c7 100644 --- a/beego.go +++ b/beego.go @@ -364,6 +364,9 @@ func initBeforeHttpRun() { } } + //init mime + AddAPPStartHook(initMime) + // do hooks function for _, hk := range hooks { err := hk() @@ -409,9 +412,6 @@ func initBeforeHttpRun() { Get("/docs", serverDocs) Get("/docs/*", serverDocs) } - - //init mime - AddAPPStartHook(initMime) } // this function is for test package init From 6c9ff81fc160888873396f107126dd917dabc037 Mon Sep 17 00:00:00 2001 From: WithGJR Date: Mon, 20 Oct 2014 18:59:46 +0800 Subject: [PATCH 52/92] fix: if user didn't set id or class, then it won't be displayed in HTML code --- templatefunc.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/templatefunc.go b/templatefunc.go index 455f3d3f..4c87762f 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -380,11 +380,19 @@ func RenderForm(obj interface{}) template.HTML { // renderFormField returns a string containing HTML of a single form field. func renderFormField(label, name, fType string, value interface{}, id string, class string) string { - if isValidForInput(fType) { - return fmt.Sprintf(`%v`, label, id, class, name, fType, value) + if id != "" { + id = "id=\"" + id + "\"" } - return fmt.Sprintf(`%v<%v id="%v" class="%v" name="%v">%v`, label, fType, id, class, name, value, fType) + if class != "" { + class = "class=\"" + class + "\"" + } + + if isValidForInput(fType) { + return fmt.Sprintf(`%v`, label, id, class, name, fType, value) + } + + return fmt.Sprintf(`%v<%v %v %v name="%v">%v`, label, fType, id, class, name, value, fType) } // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. From 710f5b62349771d2a46956e2cba3d3750d530396 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 20 Oct 2014 22:23:29 +0800 Subject: [PATCH 53/92] fix the test fun for pull request 873 --- templatefunc.go | 8 ++++---- templatefunc_test.go | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/templatefunc.go b/templatefunc.go index 4c87762f..99f0a3e8 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -381,18 +381,18 @@ func RenderForm(obj interface{}) template.HTML { // renderFormField returns a string containing HTML of a single form field. func renderFormField(label, name, fType string, value interface{}, id string, class string) string { if id != "" { - id = "id=\"" + id + "\"" + id = " id=\"" + id + "\"" } if class != "" { - class = "class=\"" + class + "\"" + class = " class=\"" + class + "\"" } if isValidForInput(fType) { - return fmt.Sprintf(`%v`, label, id, class, name, fType, value) + return fmt.Sprintf(`%v`, label, id, class, name, fType, value) } - return fmt.Sprintf(`%v<%v %v %v name="%v">%v`, label, fType, id, class, name, value, fType) + return fmt.Sprintf(`%v<%v%v%v name="%v">%v`, label, fType, id, class, name, value, fType) } // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. diff --git a/templatefunc_test.go b/templatefunc_test.go index 44a06dec..9a461c9f 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -175,12 +175,12 @@ func TestRenderForm(t *testing.T) { } func TestRenderFormField(t *testing.T) { - html := renderFormField("Label: ", "Name", "text", "Value") + html := renderFormField("Label: ", "Name", "text", "Value", "", "") if html != `Label: ` { t.Errorf("Wrong html output for input[type=text]: %v ", html) } - html = renderFormField("Label: ", "Name", "textarea", "Value") + html = renderFormField("Label: ", "Name", "textarea", "Value", "", "") if html != `Label: ` { t.Errorf("Wrong html output for textarea: %v ", html) } @@ -192,33 +192,34 @@ func TestParseFormTag(t *testing.T) { All int `form:"name,text,年龄:"` NoName int `form:",hidden,年龄:"` OnlyLabel int `form:",,年龄:"` - OnlyName int `form:"name"` + OnlyName int `form:"name" id:"name" class:"form-name"` Ignored int `form:"-"` } objT := reflect.TypeOf(&user{}).Elem() - label, name, fType, ignored := parseFormTag(objT.Field(0)) + label, name, fType, id, class, ignored := parseFormTag(objT.Field(0)) if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) { t.Errorf("Form Tag with name, label and type was not correctly parsed.") } - label, name, fType, ignored = parseFormTag(objT.Field(1)) + label, name, fType, id, class, ignored = parseFormTag(objT.Field(1)) if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) { t.Errorf("Form Tag with label and type but without name was not correctly parsed.") } - label, name, fType, ignored = parseFormTag(objT.Field(2)) + label, name, fType, id, class, ignored = parseFormTag(objT.Field(2)) if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) { t.Errorf("Form Tag containing only label was not correctly parsed.") } - label, name, fType, ignored = parseFormTag(objT.Field(3)) - if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false) { + label, name, fType, id, class, ignored = parseFormTag(objT.Field(3)) + if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false && + id == "name" && class == "form-name") { t.Errorf("Form Tag containing only name was not correctly parsed.") } - label, name, fType, ignored = parseFormTag(objT.Field(4)) + label, name, fType, id, class, ignored = parseFormTag(objT.Field(4)) if ignored == false { t.Errorf("Form Tag that should be ignored was not correctly parsed.") } From 180c6aafac18758fb17ccb1a498045f6a3d2541f Mon Sep 17 00:00:00 2001 From: chenghua Date: Fri, 24 Oct 2014 13:45:00 +0800 Subject: [PATCH 54/92] Update ini.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持BOM格式的ini文件 --- config/ini.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/ini.go b/config/ini.go index 1bf2e808..e8afecb6 100644 --- a/config/ini.go +++ b/config/ini.go @@ -66,6 +66,12 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { var comment bytes.Buffer buf := bufio.NewReader(file) + head, err := buf.Peek(3) + if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 { + for i := 1; i <= 3; i++ { + buf.ReadByte() + } + } section := DEFAULT_SECTION for { line, _, err := buf.ReadLine() From c34c514bbaa474da28e02b1eb61d5a89ac9c2e99 Mon Sep 17 00:00:00 2001 From: supar Date: Fri, 24 Oct 2014 14:15:31 +0400 Subject: [PATCH 55/92] Skip add DEFAULT if the field is in relations (rel or reverse) --- orm/cmd_utils.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/orm/cmd_utils.go b/orm/cmd_utils.go index 8304da6b..c7910ca2 100644 --- a/orm/cmd_utils.go +++ b/orm/cmd_utils.go @@ -239,3 +239,46 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex return } + +// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands +func getColumnDefault(fi *fieldInfo) string { + var ( + v, t, d string + ) + + // Skip default attribute if field is in relations + if fi.rel || fi.reverse { + return v + } + + t = " DEFAULT '%s' " + + // These defaults will be useful if there no config value orm:"default" and NOT NULL is on + switch fi.fieldType { + case TypeDateField: + d = "0000-00-00" + + case TypeDateTimeField: + d = "0000-00-00 00:00:00" + + case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField, + TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField, + TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField, + TypeDecimalField: + d = "0" + } + + if fi.colDefault { + if !fi.initial.Exist() { + v = fmt.Sprintf(t, "") + } else { + v = fmt.Sprintf(t, fi.initial.String()) + } + } else { + if !fi.null { + v = fmt.Sprintf(t, d) + } + } + + return v +} From 6f5162461ed46f2341cd40129495423f158ac25a Mon Sep 17 00:00:00 2001 From: supar Date: Fri, 24 Oct 2014 14:51:35 +0400 Subject: [PATCH 56/92] Add column DEFAULT attribute. Do not add if field is key or in relations. --- orm/cmd_utils.go | 30 +++++++++++++++++++----------- orm/models_info_f.go | 6 ++++++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/orm/cmd_utils.go b/orm/cmd_utils.go index c7910ca2..09f94487 100644 --- a/orm/cmd_utils.go +++ b/orm/cmd_utils.go @@ -104,7 +104,11 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string { typ += " " + "NOT NULL" } - return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s", Q, fi.mi.table, Q, Q, fi.column, Q, typ) + return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s", + Q, fi.mi.table, Q, + Q, fi.column, Q, + typ, getColumnDefault(fi), + ) } // create database creation string. @@ -155,6 +159,9 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex //if fi.initial.String() != "" { // column += " DEFAULT " + fi.initial.String() //} + + // Append attribute DEFAULT + column += getColumnDefault(fi) if fi.unique { column += " " + "UNIQUE" @@ -240,6 +247,7 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex return } + // Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands func getColumnDefault(fi *fieldInfo) string { var ( @@ -255,19 +263,19 @@ func getColumnDefault(fi *fieldInfo) string { // These defaults will be useful if there no config value orm:"default" and NOT NULL is on switch fi.fieldType { - case TypeDateField: - d = "0000-00-00" - - case TypeDateTimeField: - d = "0000-00-00 00:00:00" - - case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField, - TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField, + case TypeDateField: + d = "0000-00-00" + + case TypeDateTimeField: + d = "0000-00-00 00:00:00" + + case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField, + TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField, TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField, TypeDecimalField: - d = "0" + d = "0" } - + if fi.colDefault { if !fi.initial.Exist() { v = fmt.Sprintf(t, "") diff --git a/orm/models_info_f.go b/orm/models_info_f.go index a79ffab2..84a0c024 100644 --- a/orm/models_info_f.go +++ b/orm/models_info_f.go @@ -116,6 +116,7 @@ type fieldInfo struct { null bool index bool unique bool + colDefault bool initial StrTo size int auto_now bool @@ -280,6 +281,11 @@ checkType: fi.pk = attrs["pk"] fi.unique = attrs["unique"] + // Mark object property if there is attribute "default" in the orm configuration + if _, ok := tags["default"]; ok { + fi.colDefault = true + } + switch fieldType { case RelManyToMany, RelReverseMany, RelReverseOne: fi.null = false From 14114018eac32b2a6501f8c76defa86fdcb76aa1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 24 Oct 2014 19:03:27 +0800 Subject: [PATCH 57/92] config ini support include --- config.go | 16 ++++++++++------ config/ini.go | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/config.go b/config.go index 6fd35d8c..84f8cf39 100644 --- a/config.go +++ b/config.go @@ -88,13 +88,13 @@ type beegoAppConfig struct { innerConfig config.ConfigContainer } -func newAppConfig(AppConfigProvider, AppConfigPath string) *beegoAppConfig { +func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, error) { ac, err := config.NewConfig(AppConfigProvider, AppConfigPath) if err != nil { - ac = config.NewFakeConfig() + return nil, err } rac := &beegoAppConfig{ac} - return rac + return rac, nil } func (b *beegoAppConfig) Set(key, val string) error { @@ -281,15 +281,19 @@ func init() { err = ParseConfig() if err != nil && !os.IsNotExist(err) { // for init if doesn't have app.conf will not panic - Info(err) + ac := config.NewFakeConfig() + AppConfig = &beegoAppConfig{ac} + Warning(err) } } // ParseConfig parsed default config file. // now only support ini, next will support json. func ParseConfig() (err error) { - AppConfig = newAppConfig(AppConfigProvider, AppConfigPath) - + AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath) + if err != nil { + return err + } envRunMode := os.Getenv("BEEGO_RUNMODE") // set the runmode first if envRunMode != "" { diff --git a/config/ini.go b/config/ini.go index e8afecb6..837c9ffe 100644 --- a/config/ini.go +++ b/config/ini.go @@ -48,6 +48,10 @@ type IniConfig struct { // ParseFile creates a new Config and parses the file configuration from the named file. func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { + return ini.parseFile(name) +} + +func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { file, err := os.Open(name) if err != nil { return nil, err @@ -66,6 +70,7 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { var comment bytes.Buffer buf := bufio.NewReader(file) + // check the BOM head, err := buf.Peek(3) if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 { for i := 1; i <= 3; i++ { @@ -114,13 +119,48 @@ func (ini *IniConfig) Parse(name string) (ConfigContainer, error) { cfg.data[section] = make(map[string]string) } keyValue := bytes.SplitN(line, bEqual, 2) + + key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive + key = strings.ToLower(key) + + // handle include "other.conf" + if len(keyValue) == 1 && strings.HasPrefix(key, "include") { + includefiles := strings.Fields(key) + if includefiles[0] == "include" && len(includefiles) == 2 { + otherfile := strings.Trim(includefiles[1], "\"") + if !path.IsAbs(otherfile) { + otherfile = path.Join(path.Dir(name), otherfile) + } + i, err := ini.parseFile(otherfile) + if err != nil { + return nil, err + } + for sec, dt := range i.data { + if _, ok := cfg.data[sec]; !ok { + cfg.data[sec] = make(map[string]string) + } + for k, v := range dt { + cfg.data[sec][k] = v + } + } + for sec, comm := range i.sectionComment { + cfg.sectionComment[sec] = comm + } + for k, comm := range i.keyComment { + cfg.keyComment[k] = comm + } + continue + } + } + + if len(keyValue) != 2 { + return nil, errors.New("read the content error: \"" + string(line) + "\", should key = val") + } val := bytes.TrimSpace(keyValue[1]) if bytes.HasPrefix(val, bDQuote) { val = bytes.Trim(val, `"`) } - key := string(bytes.TrimSpace(keyValue[0])) // key name case insensitive - key = strings.ToLower(key) cfg.data[section][key] = string(val) if comment.Len() > 0 { cfg.keyComment[section+"."+key] = comment.String() From 90caeb4cf72e39e0f2bf75f090b60837621568a4 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 26 Oct 2014 10:41:22 +0800 Subject: [PATCH 58/92] Revert "Add column default attribute" --- orm/cmd_utils.go | 53 +------------------------------------------- orm/models_info_f.go | 6 ----- 2 files changed, 1 insertion(+), 58 deletions(-) diff --git a/orm/cmd_utils.go b/orm/cmd_utils.go index 09f94487..8304da6b 100644 --- a/orm/cmd_utils.go +++ b/orm/cmd_utils.go @@ -104,11 +104,7 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string { typ += " " + "NOT NULL" } - return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s", - Q, fi.mi.table, Q, - Q, fi.column, Q, - typ, getColumnDefault(fi), - ) + return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s", Q, fi.mi.table, Q, Q, fi.column, Q, typ) } // create database creation string. @@ -159,9 +155,6 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex //if fi.initial.String() != "" { // column += " DEFAULT " + fi.initial.String() //} - - // Append attribute DEFAULT - column += getColumnDefault(fi) if fi.unique { column += " " + "UNIQUE" @@ -246,47 +239,3 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex return } - - -// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands -func getColumnDefault(fi *fieldInfo) string { - var ( - v, t, d string - ) - - // Skip default attribute if field is in relations - if fi.rel || fi.reverse { - return v - } - - t = " DEFAULT '%s' " - - // These defaults will be useful if there no config value orm:"default" and NOT NULL is on - switch fi.fieldType { - case TypeDateField: - d = "0000-00-00" - - case TypeDateTimeField: - d = "0000-00-00 00:00:00" - - case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField, - TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField, - TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField, - TypeDecimalField: - d = "0" - } - - if fi.colDefault { - if !fi.initial.Exist() { - v = fmt.Sprintf(t, "") - } else { - v = fmt.Sprintf(t, fi.initial.String()) - } - } else { - if !fi.null { - v = fmt.Sprintf(t, d) - } - } - - return v -} diff --git a/orm/models_info_f.go b/orm/models_info_f.go index 84a0c024..a79ffab2 100644 --- a/orm/models_info_f.go +++ b/orm/models_info_f.go @@ -116,7 +116,6 @@ type fieldInfo struct { null bool index bool unique bool - colDefault bool initial StrTo size int auto_now bool @@ -281,11 +280,6 @@ checkType: fi.pk = attrs["pk"] fi.unique = attrs["unique"] - // Mark object property if there is attribute "default" in the orm configuration - if _, ok := tags["default"]; ok { - fi.colDefault = true - } - switch fieldType { case RelManyToMany, RelReverseMany, RelReverseOne: fi.null = false From 0bcd828d7375079d5255d3bf142223e662663407 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Sun, 26 Oct 2014 22:56:00 -0700 Subject: [PATCH 59/92] add session store in ledis --- session/ledis/ledis_session.go | 168 +++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 session/ledis/ledis_session.go diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go new file mode 100644 index 00000000..3ada47ac --- /dev/null +++ b/session/ledis/ledis_session.go @@ -0,0 +1,168 @@ +package session + +import ( + "net/http" + "sync" + + "github.com/astaxie/beego/session" + "github.com/siddontang/ledisdb/config" + "github.com/siddontang/ledisdb/ledis" +) + +var ledispder = &LedisProvider{} +var c *ledis.DB + +// ledis session store +type LedisSessionStore struct { + sid string + lock sync.RWMutex + values map[interface{}]interface{} + maxlifetime int64 +} + +// set value in ledis session +func (ls *LedisSessionStore) Set(key, value interface{}) error { + ls.lock.Lock() + defer ls.lock.Unlock() + ls.values[key] = value + return nil +} + +// get value in ledis session +func (ls *LedisSessionStore) Get(key interface{}) interface{} { + ls.lock.RLock() + defer ls.lock.RUnlock() + if v, ok := ls.values[key]; ok { + return v + } else { + return nil + } +} + +// delete value in ledis session +func (ls *LedisSessionStore) Delete(key interface{}) error { + ls.lock.Lock() + defer ls.lock.Unlock() + delete(ls.values, key) + return nil +} + +// clear all values in ledis session +func (ls *LedisSessionStore) Flush() error { + ls.lock.Lock() + defer ls.lock.Unlock() + ls.values = make(map[interface{}]interface{}) + return nil +} + +// get ledis session id +func (ls *LedisSessionStore) SessionID() string { + return ls.sid +} + +// save session values to ledis +func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) { + b, err := session.EncodeGob(ls.values) + if err != nil { + return + } + c.Set([]byte(ls.sid), b) + c.Expire([]byte(ls.sid), ls.maxlifetime) +} + +// ledis session provider +type LedisProvider struct { + maxlifetime int64 + savePath string +} + +// init ledis session +// savepath like ledis server saveDataPath,pool size +// e.g. 127.0.0.1:6379,100,astaxie +func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error { + lp.maxlifetime = maxlifetime + lp.savePath = savePath + cfg := new(config.Config) + cfg.DataDir = lp.savePath + var err error + nowLedis, err := ledis.Open(cfg) + c, err = nowLedis.Select(0) + if err != nil { + println(err) + return nil + } + return nil +} + +// read ledis session by sid +func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) { + kvs, err := c.Get([]byte(sid)) + var kv map[interface{}]interface{} + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(kvs) + if err != nil { + return nil, err + } + } + ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} + return ls, nil +} + +// check ledis session exist by sid +func (lp *LedisProvider) SessionExist(sid string) bool { + count, _ := c.Exists([]byte(sid)) + if count == 0 { + return false + } else { + return true + } +} + +// generate new sid for ledis session +func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) { + count, _ := c.Exists([]byte(sid)) + if count == 0 { + // oldsid doesn't exists, set the new sid directly + // ignore error here, since if it return error + // the existed value will be 0 + c.Set([]byte(sid), []byte("")) + c.Expire([]byte(sid), lp.maxlifetime) + } else { + data, _ := c.Get([]byte(oldsid)) + c.Set([]byte(sid), data) + c.Expire([]byte(sid), lp.maxlifetime) + } + kvs, err := c.Get([]byte(sid)) + var kv map[interface{}]interface{} + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(kvs)) + if err != nil { + return nil, err + } + } + ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} + return ls, nil +} + +// delete ledis session by id +func (lp *LedisProvider) SessionDestroy(sid string) error { + c.Del([]byte(sid)) + return nil +} + +// Impelment method, no used. +func (lp *LedisProvider) SessionGC() { + return +} + +// @todo +func (lp *LedisProvider) SessionAll() int { + return 0 +} +func init() { + session.Register("ledis", ledispder) +} From 1f26852610c8cf24ff054d8e62bb7a6416140992 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 28 Oct 2014 19:33:14 +0800 Subject: [PATCH 60/92] logs:default support fileline --- log.go | 5 ----- logs/conn.go | 2 +- logs/console.go | 2 +- logs/file.go | 2 +- logs/log.go | 36 ++++++------------------------------ 5 files changed, 9 insertions(+), 38 deletions(-) diff --git a/log.go b/log.go index 5afba8ed..c159b6cc 100644 --- a/log.go +++ b/log.go @@ -38,11 +38,6 @@ func SetLevel(l int) { BeeLogger.SetLevel(l) } -func SetLogFuncCall(b bool) { - BeeLogger.EnableFuncCallDepth(b) - BeeLogger.SetLogFuncCallDepth(3) -} - // logger references the used application logger. var BeeLogger *logs.BeeLogger diff --git a/logs/conn.go b/logs/conn.go index 612634fa..e3d60eed 100644 --- a/logs/conn.go +++ b/logs/conn.go @@ -99,7 +99,7 @@ func (c *ConnWriter) connect() error { } c.innerWriter = conn - c.lg = log.New(conn, "", log.Ldate|log.Ltime) + c.lg = log.New(conn, "", log.Ldate|log.Ltime|log.Lshortfile) return nil } diff --git a/logs/console.go b/logs/console.go index 461291c2..aa34be45 100644 --- a/logs/console.go +++ b/logs/console.go @@ -51,7 +51,7 @@ type ConsoleWriter struct { // create ConsoleWriter returning as LoggerInterface. func NewConsole() LoggerInterface { cw := new(ConsoleWriter) - cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime) + cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) cw.Level = LevelDebug return cw } diff --git a/logs/file.go b/logs/file.go index 39220cc0..0381d1c1 100644 --- a/logs/file.go +++ b/logs/file.go @@ -89,7 +89,7 @@ func NewFileWriter() LoggerInterface { // use MuxWriter instead direct use os.File for lock write when rotate w.mw = new(MuxWriter) // set MuxWriter as Logger's io.Writer - w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) + w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime|log.Lshortfile) return w } diff --git a/logs/log.go b/logs/log.go index 341df572..3d66429d 100644 --- a/logs/log.go +++ b/logs/log.go @@ -34,8 +34,7 @@ package logs import ( "fmt" - "path" - "runtime" + "sync" ) @@ -88,12 +87,10 @@ func Register(name string, log loggerType) { // BeeLogger is default logger in beego application. // it can contain several providers and log message into all providers. type BeeLogger struct { - lock sync.Mutex - level int - enableFuncCallDepth bool - loggerFuncCallDepth int - msg chan *logMsg - outputs map[string]LoggerInterface + lock sync.Mutex + level int + msg chan *logMsg + outputs map[string]LoggerInterface } type logMsg struct { @@ -107,7 +104,6 @@ type logMsg struct { func NewLogger(channellen int64) *BeeLogger { bl := new(BeeLogger) bl.level = LevelDebug - bl.loggerFuncCallDepth = 2 bl.msg = make(chan *logMsg, channellen) bl.outputs = make(map[string]LoggerInterface) //bl.SetLogger("console", "") // default output to console @@ -153,17 +149,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { } lm := new(logMsg) lm.level = loglevel - if bl.enableFuncCallDepth { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if ok { - _, filename := path.Split(file) - lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) - } else { - lm.msg = msg - } - } else { - lm.msg = msg - } + lm.msg = msg bl.msg <- lm return nil } @@ -176,16 +162,6 @@ func (bl *BeeLogger) SetLevel(l int) { bl.level = l } -// set log funcCallDepth -func (bl *BeeLogger) SetLogFuncCallDepth(d int) { - bl.loggerFuncCallDepth = d -} - -// enable log funcCallDepth -func (bl *BeeLogger) EnableFuncCallDepth(b bool) { - bl.enableFuncCallDepth = b -} - // start logger chan reading. // when chan is not empty, write logs. func (bl *BeeLogger) startLogger() { From ddbfc25e56900fe03ff7c8f6d3886802f3a880dd Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 28 Oct 2014 19:34:11 +0800 Subject: [PATCH 61/92] fix the log test --- logs/console_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/logs/console_test.go b/logs/console_test.go index 2fad7241..167a007a 100644 --- a/logs/console_test.go +++ b/logs/console_test.go @@ -34,7 +34,6 @@ func testConsoleCalls(bl *BeeLogger) { // without a log level specification. func TestConsole(t *testing.T) { log1 := NewLogger(10000) - log1.EnableFuncCallDepth(true) log1.SetLogger("console", "") testConsoleCalls(log1) @@ -45,7 +44,6 @@ func TestConsole(t *testing.T) { func BenchmarkConsole(b *testing.B) { log := NewLogger(10000) - log.EnableFuncCallDepth(true) log.SetLogger("console", "") for i := 0; i < b.N; i++ { log.Debug("debug") From 824e3f8f5b1c788801ffcb3425ffd32059f9c682 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 29 Oct 2014 16:00:04 +0800 Subject: [PATCH 62/92] fix the only file upload param --- httplib/httplib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index dbcb2fed..bced9163 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -275,7 +275,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } else { b.url = b.url + "?" + paramBody } - } else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 { + } else if b.req.Method == "POST" && b.req.Body == nil { if len(b.files) > 0 { bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) @@ -303,7 +303,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { b.Header("Content-Type", contentType) b.req.Body = ioutil.NopCloser(bodyBuf) b.req.ContentLength = int64(bodyBuf.Len()) - } else { + } else if len(paramBody) > 0 { b.Header("Content-Type", "application/x-www-form-urlencoded") b.Body(paramBody) } From 57e62e5e5793788698a24bcb76e3c271379a81c9 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 11:16:09 +0800 Subject: [PATCH 63/92] update the file upload to io.Pipe --- httplib/httplib.go | 50 +++++++++++++++++++++-------------------- httplib/httplib_test.go | 31 ++++++++++++------------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index bced9163..37ba3b33 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -37,6 +37,7 @@ import ( "encoding/xml" "io" "io/ioutil" + "log" "mime/multipart" "net" "net/http" @@ -277,32 +278,33 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } } else if b.req.Method == "POST" && b.req.Body == nil { if len(b.files) > 0 { - bodyBuf := &bytes.Buffer{} - bodyWriter := multipart.NewWriter(bodyBuf) - for formname, filename := range b.files { - fileWriter, err := bodyWriter.CreateFormFile(formname, filename) - if err != nil { - return nil, err + pr, pw := io.Pipe() + bodyWriter := multipart.NewWriter(pw) + go func() { + for formname, filename := range b.files { + fileWriter, err := bodyWriter.CreateFormFile(formname, filename) + if err != nil { + log.Fatal(err) + } + fh, err := os.Open(filename) + if err != nil { + log.Fatal(err) + } + //iocopy + _, err = io.Copy(fileWriter, fh) + fh.Close() + if err != nil { + log.Fatal(err) + } } - fh, err := os.Open(filename) - if err != nil { - return nil, err + for k, v := range b.params { + bodyWriter.WriteField(k, v) } - //iocopy - _, err = io.Copy(fileWriter, fh) - fh.Close() - if err != nil { - return nil, err - } - } - for k, v := range b.params { - bodyWriter.WriteField(k, v) - } - contentType := bodyWriter.FormDataContentType() - bodyWriter.Close() - b.Header("Content-Type", contentType) - b.req.Body = ioutil.NopCloser(bodyBuf) - b.req.ContentLength = int64(bodyBuf.Len()) + bodyWriter.Close() + pw.Close() + }() + b.Header("Content-Type", bodyWriter.FormDataContentType()) + b.req.Body = ioutil.NopCloser(pr) } else if len(paramBody) > 0 { b.Header("Content-Type", "application/x-www-form-urlencoded") b.Body(paramBody) diff --git a/httplib/httplib_test.go b/httplib/httplib_test.go index 976ce498..0b551c53 100644 --- a/httplib/httplib_test.go +++ b/httplib/httplib_test.go @@ -66,23 +66,24 @@ func TestSimplePost(t *testing.T) { } } -func TestPostFile(t *testing.T) { - v := "smallfish" - req := Post("http://httpbin.org/post") - req.Param("username", v) - req.PostFile("uploadfile", "httplib_test.go") +//func TestPostFile(t *testing.T) { +// v := "smallfish" +// req := Post("http://httpbin.org/post") +// req.Debug(true) +// req.Param("username", v) +// req.PostFile("uploadfile", "httplib_test.go") - str, err := req.String() - if err != nil { - t.Fatal(err) - } - t.Log(str) +// str, err := req.String() +// if err != nil { +// t.Fatal(err) +// } +// t.Log(str) - n := strings.Index(str, v) - if n == -1 { - t.Fatal(v + " not found in post") - } -} +// n := strings.Index(str, v) +// if n == -1 { +// t.Fatal(v + " not found in post") +// } +//} func TestSimplePut(t *testing.T) { str, err := Put("http://httpbin.org/put").String() From fda841208d6c8c6934332587cb235fbc2996fab0 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:05:48 +0800 Subject: [PATCH 64/92] fix #893 --- context/context.go | 4 +++- router.go | 29 +++++++---------------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/context/context.go b/context/context.go index d31076d4..8569be14 100644 --- a/context/context.go +++ b/context/context.go @@ -31,6 +31,7 @@ import ( "strings" "time" + "github.com/astaxie/beego" "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/utils" ) @@ -69,7 +70,8 @@ func (ctx *Context) Abort(status int, body string) { panic(e) } // last panic user string - panic(body) + ctx.ResponseWriter.Write([]byte(body)) + panic(beego.USERSTOPRUN) } // Write string to response body. diff --git a/router.go b/router.go index 2a88490d..17415ce3 100644 --- a/router.go +++ b/router.go @@ -831,7 +831,9 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques if err == USERSTOPRUN { return } - if _, ok := err.(middleware.HTTPException); ok { + if he, ok := err.(middleware.HTTPException); ok { + rw.WriteHeader(he.StatusCode) + rw.Write([]byte(he.Description)) // catch intented errors, only for HTTP 4XX and 5XX } else { if RunMode == "dev" { @@ -863,9 +865,10 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques } else { // in production model show all infomation if ErrorsShow { - handler := p.getErrorHandler(fmt.Sprint(err)) - handler(rw, r) - return + if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok { + handler(rw, r) + return + } } else { Critical("the request url is ", r.URL.Path) Critical("Handler crashed with error", err) @@ -884,24 +887,6 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques } } -// there always should be error handler that sets error code accordingly for all unhandled errors. -// in order to have custom UI for error page it's necessary to override "500" error. -func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) { - handler := middleware.SimpleServerError - ok := true - if errorCode != "" { - handler, ok = middleware.ErrorMaps[errorCode] - if !ok { - handler, ok = middleware.ErrorMaps["500"] - } - if !ok || handler == nil { - handler = middleware.SimpleServerError - } - } - - return handler -} - //responseWriter is a wrapper for the http.ResponseWriter //started set to true if response was written to then don't execute other handler type responseWriter struct { From ecd0a5487e7f4bda60848ee90b84dc9435c2e83c Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:12:54 +0800 Subject: [PATCH 65/92] fix the import cycle not allowed --- context/context.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/context/context.go b/context/context.go index 8569be14..89b5ffe4 100644 --- a/context/context.go +++ b/context/context.go @@ -31,7 +31,6 @@ import ( "strings" "time" - "github.com/astaxie/beego" "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/utils" ) @@ -71,7 +70,7 @@ func (ctx *Context) Abort(status int, body string) { } // last panic user string ctx.ResponseWriter.Write([]byte(body)) - panic(beego.USERSTOPRUN) + panic("User stop run") } // Write string to response body. From 68c3bdfdd40b6feac1f2c00c07a751f95912c718 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:57:48 +0800 Subject: [PATCH 66/92] Revert "logs:default support fileline" This reverts commit 1f26852610c8cf24ff054d8e62bb7a6416140992. --- log.go | 5 +++++ logs/conn.go | 2 +- logs/console.go | 2 +- logs/file.go | 2 +- logs/log.go | 36 ++++++++++++++++++++++++++++++------ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/log.go b/log.go index c159b6cc..5afba8ed 100644 --- a/log.go +++ b/log.go @@ -38,6 +38,11 @@ func SetLevel(l int) { BeeLogger.SetLevel(l) } +func SetLogFuncCall(b bool) { + BeeLogger.EnableFuncCallDepth(b) + BeeLogger.SetLogFuncCallDepth(3) +} + // logger references the used application logger. var BeeLogger *logs.BeeLogger diff --git a/logs/conn.go b/logs/conn.go index e3d60eed..612634fa 100644 --- a/logs/conn.go +++ b/logs/conn.go @@ -99,7 +99,7 @@ func (c *ConnWriter) connect() error { } c.innerWriter = conn - c.lg = log.New(conn, "", log.Ldate|log.Ltime|log.Lshortfile) + c.lg = log.New(conn, "", log.Ldate|log.Ltime) return nil } diff --git a/logs/console.go b/logs/console.go index aa34be45..461291c2 100644 --- a/logs/console.go +++ b/logs/console.go @@ -51,7 +51,7 @@ type ConsoleWriter struct { // create ConsoleWriter returning as LoggerInterface. func NewConsole() LoggerInterface { cw := new(ConsoleWriter) - cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) + cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime) cw.Level = LevelDebug return cw } diff --git a/logs/file.go b/logs/file.go index 0381d1c1..39220cc0 100644 --- a/logs/file.go +++ b/logs/file.go @@ -89,7 +89,7 @@ func NewFileWriter() LoggerInterface { // use MuxWriter instead direct use os.File for lock write when rotate w.mw = new(MuxWriter) // set MuxWriter as Logger's io.Writer - w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime|log.Lshortfile) + w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) return w } diff --git a/logs/log.go b/logs/log.go index 3d66429d..341df572 100644 --- a/logs/log.go +++ b/logs/log.go @@ -34,7 +34,8 @@ package logs import ( "fmt" - + "path" + "runtime" "sync" ) @@ -87,10 +88,12 @@ func Register(name string, log loggerType) { // BeeLogger is default logger in beego application. // it can contain several providers and log message into all providers. type BeeLogger struct { - lock sync.Mutex - level int - msg chan *logMsg - outputs map[string]LoggerInterface + lock sync.Mutex + level int + enableFuncCallDepth bool + loggerFuncCallDepth int + msg chan *logMsg + outputs map[string]LoggerInterface } type logMsg struct { @@ -104,6 +107,7 @@ type logMsg struct { func NewLogger(channellen int64) *BeeLogger { bl := new(BeeLogger) bl.level = LevelDebug + bl.loggerFuncCallDepth = 2 bl.msg = make(chan *logMsg, channellen) bl.outputs = make(map[string]LoggerInterface) //bl.SetLogger("console", "") // default output to console @@ -149,7 +153,17 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { } lm := new(logMsg) lm.level = loglevel - lm.msg = msg + if bl.enableFuncCallDepth { + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) + if ok { + _, filename := path.Split(file) + lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) + } else { + lm.msg = msg + } + } else { + lm.msg = msg + } bl.msg <- lm return nil } @@ -162,6 +176,16 @@ func (bl *BeeLogger) SetLevel(l int) { bl.level = l } +// set log funcCallDepth +func (bl *BeeLogger) SetLogFuncCallDepth(d int) { + bl.loggerFuncCallDepth = d +} + +// enable log funcCallDepth +func (bl *BeeLogger) EnableFuncCallDepth(b bool) { + bl.enableFuncCallDepth = b +} + // start logger chan reading. // when chan is not empty, write logs. func (bl *BeeLogger) startLogger() { From 1636a7271c2d49fe7b5a9f6753db0eb46c1eedc1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:57:55 +0800 Subject: [PATCH 67/92] Revert "fix the log test" This reverts commit ddbfc25e56900fe03ff7c8f6d3886802f3a880dd. --- logs/console_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/logs/console_test.go b/logs/console_test.go index 167a007a..2fad7241 100644 --- a/logs/console_test.go +++ b/logs/console_test.go @@ -34,6 +34,7 @@ func testConsoleCalls(bl *BeeLogger) { // without a log level specification. func TestConsole(t *testing.T) { log1 := NewLogger(10000) + log1.EnableFuncCallDepth(true) log1.SetLogger("console", "") testConsoleCalls(log1) @@ -44,6 +45,7 @@ func TestConsole(t *testing.T) { func BenchmarkConsole(b *testing.B) { log := NewLogger(10000) + log.EnableFuncCallDepth(true) log.SetLogger("console", "") for i := 0; i < b.N; i++ { log.Debug("debug") From 71149218d17b7f5843c9d5c17ce95be22739e26e Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 17:43:32 +0800 Subject: [PATCH 68/92] fix the log level --- config.go | 1 + logs/log.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/config.go b/config.go index 84f8cf39..8a819dad 100644 --- a/config.go +++ b/config.go @@ -277,6 +277,7 @@ func init() { if err != nil { fmt.Println("init console log error:", err) } + BeeLogger.SetLogFuncCallDepth(true) err = ParseConfig() if err != nil && !os.IsNotExist(err) { diff --git a/logs/log.go b/logs/log.go index 341df572..b61388e6 100644 --- a/logs/log.go +++ b/logs/log.go @@ -154,7 +154,10 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm := new(logMsg) lm.level = loglevel if bl.enableFuncCallDepth { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + if !ok { + _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth) + } if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From db43892fe675db58a1a33ac0e221a60ce3d7827c Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 00:28:51 +0800 Subject: [PATCH 69/92] improve the Put #896 --- cache/redis/redis.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 35cf88cd..b205545d 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -75,14 +75,13 @@ func (rc *RedisCache) Get(key string) interface{} { // put cache to redis. func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { var err error - if _, err = rc.do("SET", key, val); err != nil { + if _, err = rc.do("SETEX", key, timeout, val); err != nil { return err } if _, err = rc.do("HSET", rc.key, key, true); err != nil { return err } - _, err = rc.do("EXPIRE", key, timeout) return err } From 94c84b846fe84fddad10e1751c8a5519f4841a6f Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 09:02:16 +0800 Subject: [PATCH 70/92] fix the init logger --- config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.go b/config.go index 8a819dad..fd3d5de1 100644 --- a/config.go +++ b/config.go @@ -277,7 +277,7 @@ func init() { if err != nil { fmt.Println("init console log error:", err) } - BeeLogger.SetLogFuncCallDepth(true) + SetLogFuncCall(true) err = ParseConfig() if err != nil && !os.IsNotExist(err) { From 945b1da3a8f1b87d5d2d731f8e224a22f23b45f5 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 15:48:57 +0800 Subject: [PATCH 71/92] fix the gofmt --- example/chat/controllers/ws.go | 6 ++--- migration/ddl.go | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 migration/ddl.go diff --git a/example/chat/controllers/ws.go b/example/chat/controllers/ws.go index 862f89c9..9ec5b418 100644 --- a/example/chat/controllers/ws.go +++ b/example/chat/controllers/ws.go @@ -150,12 +150,12 @@ type WSController struct { } var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, + ReadBufferSize: 1024, + WriteBufferSize: 1024, } func (this *WSController) Get() { - ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil) + ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request, nil) if _, ok := err.(websocket.HandshakeError); ok { http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) return diff --git a/migration/ddl.go b/migration/ddl.go new file mode 100644 index 00000000..f9b60117 --- /dev/null +++ b/migration/ddl.go @@ -0,0 +1,46 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migration + +type Table struct { + TableName string + Columns []*Column +} + +func (t *Table) Create() string { + return "" +} + +func (t *Table) Drop() string { + return "" +} + +type Column struct { + Name string + Type string + Default interface{} +} + +func Create(tbname string, columns ...Column) string { + return "" +} + +func Drop(tbname string, columns ...Column) string { + return "" +} + +func TableDDL(tbname string, columns ...Column) string { + return "" +} From da127bbc229e4285d59041db209c5afa84c6d61a Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 16:31:23 +0800 Subject: [PATCH 72/92] fix #855 #859 --- namespace.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namespace.go b/namespace.go index d0109291..4e2632e5 100644 --- a/namespace.go +++ b/namespace.go @@ -217,7 +217,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { n.handlers.routers[k] = t } } - if n.handlers.enableFilter { + if ni.handlers.enableFilter { for pos, filterList := range ni.handlers.filters { for _, mr := range filterList { t := NewTree() From 90cff5f042393ff098862f228b5fd4e565161172 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 2 Nov 2014 21:01:51 +0800 Subject: [PATCH 73/92] fix #824 --- tree.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tree.go b/tree.go index 9f86dd48..25947442 100644 --- a/tree.go +++ b/tree.go @@ -394,6 +394,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string } return true, params } + if len(wildcardValues) <= j { + return false, nil + } params[v] = wildcardValues[j] j += 1 } From 716962672fa215d65d39d4ecb3b4f9470cf3bdb8 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 15:06:25 +0800 Subject: [PATCH 74/92] fix #751 add config ListenTCP4 when user want to listen on the TCP4, because now almost use the ipv4. but default lister on the ipv6 --- app.go | 36 ++++++++++++++++++++++++------------ config.go | 5 +++++ filter.go | 5 +++++ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/app.go b/app.go index 0b89d89e..d155c531 100644 --- a/app.go +++ b/app.go @@ -20,13 +20,8 @@ import ( "net/http" "net/http/fcgi" "time" - - "github.com/astaxie/beego/context" ) -// FilterFunc defines filter function type. -type FilterFunc func(*context.Context) - // App defines beego application with a new PatternServeMux. type App struct { Handlers *ControllerRegistor @@ -85,7 +80,7 @@ func (app *App) Run() { if HttpsPort != 0 { app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) } - BeeLogger.Info("Running on %s", app.Server.Addr) + BeeLogger.Info("https server Running on %s", app.Server.Addr) err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) @@ -98,12 +93,29 @@ func (app *App) Run() { if EnableHttpListen { go func() { app.Server.Addr = addr - BeeLogger.Info("Running on %s", app.Server.Addr) - err := app.Server.ListenAndServe() - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true + BeeLogger.Info("http server Running on %s", app.Server.Addr) + if ListenTCP4 && HttpAddr == "" { + ln, err := net.Listen("tcp4", app.Server.Addr) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + err = app.Server.Serve(ln) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + } else { + err := app.Server.ListenAndServe() + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } } }() } diff --git a/config.go b/config.go index fd3d5de1..ed91647f 100644 --- a/config.go +++ b/config.go @@ -40,6 +40,7 @@ var ( EnableHttpListen bool HttpAddr string HttpPort int + ListenTCP4 bool EnableHttpTLS bool HttpsPort int HttpCertFile string @@ -309,6 +310,10 @@ func ParseConfig() (err error) { HttpPort = v } + if v, err := AppConfig.Bool("ListenTCP4"); err == nil { + ListenTCP4 = v + } + if v, err := AppConfig.Bool("EnableHttpListen"); err == nil { EnableHttpListen = v } diff --git a/filter.go b/filter.go index eebad329..ddd61094 100644 --- a/filter.go +++ b/filter.go @@ -14,6 +14,11 @@ package beego +import "github.com/astaxie/beego/context" + +// FilterFunc defines filter function type. +type FilterFunc func(*context.Context) + // FilterRouter defines filter operation before controller handler execution. // it can match patterned url and do filter function when action arrives. type FilterRouter struct { From 10db97b193b40956948bf39c010462a4a629be18 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 15:08:51 +0800 Subject: [PATCH 75/92] add some tips for the admin server start --- admin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin.go b/admin.go index a0905dd3..273e666e 100644 --- a/admin.go +++ b/admin.go @@ -458,7 +458,7 @@ func (admin *adminApp) Run() { for p, f := range admin.routers { http.Handle(p, f) } - BeeLogger.Info("Running on %s", addr) + BeeLogger.Info("Admin server Running on %s", addr) err := http.ListenAndServe(addr, nil) if err != nil { BeeLogger.Critical("Admin ListenAndServe: ", err) From 8d797a4a5ee7788695ffa1f765520b45eb412226 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:14:40 +0800 Subject: [PATCH 76/92] file the static filter --- router.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/router.go b/router.go index 17415ce3..9b11be53 100644 --- a/router.go +++ b/router.go @@ -72,11 +72,31 @@ var ( "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", "GetControllerAndAction"} - url_placeholder = "{{placeholder}}" - - FilterRouterLog func(*beecontext.Context) bool + url_placeholder = "{{placeholder}}" + DefaultLogFilter FilterHandler = &logFilter{} ) +type FilterHandler interface { + Filter(*beecontext.Context) bool +} + +// default log filter static file will not show +type logFilter struct { +} + +func (l *logFilter) Filter(ctx *beecontext.Context) bool { + requestPath := path.Clean(ctx.Input.Request.URL.Path) + if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { + return true + } + for prefix, _ := range StaticDir { + if strings.HasPrefix(requestPath, prefix) { + return true + } + } + return false +} + // To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter func ExceptMethodAppend(action string) { exceptMethod = append(exceptMethod, action) @@ -815,7 +835,7 @@ Admin: } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } - if FilterRouterLog == nil || !FilterRouterLog(context) { + if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) { Debug(devinfo) } } From 2288ac868c49cadb170fb8876e14a2a188001d96 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:34:36 +0800 Subject: [PATCH 77/92] remove the deep Caller --- logs/log.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/logs/log.go b/logs/log.go index b61388e6..d24e21d2 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,9 +155,6 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) - if !ok { - _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth) - } if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From 304beaf89ffe65e2f4a6d4a19ec80d5303f24f04 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:40:08 +0800 Subject: [PATCH 78/92] update the log call deep --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index d24e21d2..341df572 100644 --- a/logs/log.go +++ b/logs/log.go @@ -154,7 +154,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm := new(logMsg) lm.level = loglevel if bl.enableFuncCallDepth { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From 88caf1ed70b7f5afb9a56128154e7bf5407420f1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:43:07 +0800 Subject: [PATCH 79/92] if read the log.go then calldepth add 1 --- logs/log.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logs/log.go b/logs/log.go index 341df572..a1964892 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,6 +155,9 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) + if file == "log.go" && line == 97 { + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + } if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From ef3c7c127b3bb505d65d9351129102bf8351c7ba Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:44:05 +0800 Subject: [PATCH 80/92] fix the variable --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index a1964892..89574769 100644 --- a/logs/log.go +++ b/logs/log.go @@ -156,7 +156,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) if file == "log.go" && line == 97 { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) } if ok { _, filename := path.Split(file) From 90a7ce5c6ad5ab3e1076596b558b3bfa9f8fa9fe Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:45:42 +0800 Subject: [PATCH 81/92] split the file for logs --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index 89574769..6251f573 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,7 +155,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if file == "log.go" && line == 97 { + if _, filename := path.Split(file); filename == "log.go" && line == 97 { _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) } if ok { From a2428af8a79abe437b6bcef254b218c1a30b8f1c Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:48:45 +0800 Subject: [PATCH 82/92] compatibility for warn & info function add one more depth --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index 6251f573..6abfb005 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,7 +155,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if _, filename := path.Split(file); filename == "log.go" && line == 97 { + if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) { _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) } if ok { From 8b747f54bc14206ceb8beef5a5c91fba36ea006b Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 23:33:11 +0800 Subject: [PATCH 83/92] fix #770 --- parser.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index d29470dc..adb176f1 100644 --- a/parser.go +++ b/parser.go @@ -42,6 +42,7 @@ func init() { var ( lastupdateFilename string = "lastupdate.tmp" + commentFilename string = "commentsRouter.go" pkgLastupdate map[string]int64 genInfoList map[string][]ControllerComments ) @@ -52,6 +53,7 @@ func init() { } func parserPkg(pkgRealpath, pkgpath string) error { + commentFilename = strings.Replace(pkgpath, "/", "_", -1) + "_" + commentFilename if !compareFile(pkgRealpath) { Info(pkgRealpath + " don't has updated") return nil @@ -155,7 +157,7 @@ func genRouterCode() { } } if globalinfo != "" { - f, err := os.Create(path.Join(workPath, "routers", "commentsRouter.go")) + f, err := os.Create(path.Join(workPath, "routers", commentFilename)) if err != nil { panic(err) } @@ -165,7 +167,7 @@ func genRouterCode() { } func compareFile(pkgRealpath string) bool { - if !utils.FileExists(path.Join(workPath, "routers", "commentsRouter.go")) { + if !utils.FileExists(path.Join(workPath, "routers", commentFilename)) { return true } if utils.FileExists(path.Join(workPath, lastupdateFilename)) { From 9d4ec508bb8658e0fdf89de39d0ee89b895658b7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 10:19:30 +0800 Subject: [PATCH 84/92] parse for github.com replace the . to _ --- parser.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parser.go b/parser.go index adb176f1..0acdc8f7 100644 --- a/parser.go +++ b/parser.go @@ -53,7 +53,8 @@ func init() { } func parserPkg(pkgRealpath, pkgpath string) error { - commentFilename = strings.Replace(pkgpath, "/", "_", -1) + "_" + commentFilename + rep := strings.NewReplacer("/", "_", ".", "_") + commentFilename = rep.Replace(pkgpath) + "_" + commentFilename if !compareFile(pkgRealpath) { Info(pkgRealpath + " don't has updated") return nil From c4d8e4a244588330a4f658d41852ce9c89735f19 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 15:29:33 +0800 Subject: [PATCH 85/92] fix #759 --- router.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/router.go b/router.go index 9b11be53..4a4a934b 100644 --- a/router.go +++ b/router.go @@ -452,8 +452,8 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { } controllName := strings.Join(paths[:len(paths)-1], "/") methodName := paths[len(paths)-1] - for _, t := range p.routers { - ok, url := p.geturl(t, "/", controllName, methodName, params) + for m, t := range p.routers { + ok, url := p.geturl(t, "/", controllName, methodName, params, m) if ok { return url } @@ -461,17 +461,17 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { return "" } -func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) { +func (p *ControllerRegistor) 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) - ok, u := p.geturl(subtree, u, controllName, methodName, params) + ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod) if ok { return ok, u } } if t.wildcard != nil { - url = path.Join(url, url_placeholder) - ok, u := p.geturl(t.wildcard, url, controllName, methodName, params) + u := path.Join(url, url_placeholder) + ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod) if ok { return ok, u } @@ -491,8 +491,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin } } if !find { - for _, md := range c.methods { - if md == methodName { + for m, md := range c.methods { + if (m == "*" || m == httpMethod) && md == methodName { find = true } } From 7743eecfd4723b3e963de3e62da83c11c9d083e5 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 16:19:46 +0800 Subject: [PATCH 86/92] support #761 type Test struct { Date time.Time `form:"Date, 2006-01-02"` Save bool `form:"Save"` } --- templatefunc.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/templatefunc.go b/templatefunc.go index 99f0a3e8..16067613 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -302,6 +302,14 @@ func ParseForm(form url.Values, obj interface{}) error { switch fieldT.Type.Kind() { case reflect.Bool: + if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" { + fieldV.SetBool(true) + continue + } + if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" { + fieldV.SetBool(false) + continue + } b, err := strconv.ParseBool(value) if err != nil { return err @@ -329,6 +337,19 @@ func ParseForm(form url.Values, obj interface{}) error { fieldV.Set(reflect.ValueOf(value)) case reflect.String: fieldV.SetString(value) + case reflect.Struct: + switch fieldT.Type.String() { + case "time.Time": + format := time.RFC3339 + if len(tags) > 1 { + format = tags[1] + } + t, err := time.Parse(format, value) + if err != nil { + return err + } + fieldV.Set(reflect.ValueOf(t)) + } } } return nil From b9fdd67519a451c487fc5f15800dab5cf52d94f7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 16:39:17 +0800 Subject: [PATCH 87/92] add test case fot date & stringbool --- templatefunc_test.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/templatefunc_test.go b/templatefunc_test.go index 9a461c9f..3692a821 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -102,12 +102,14 @@ func TestHtmlunquote(t *testing.T) { func TestParseForm(t *testing.T) { type user struct { - Id int `form:"-"` - tag string `form:"tag"` - Name interface{} `form:"username"` - Age int `form:"age,text"` - Email string - Intro string `form:",textarea"` + Id int `form:"-"` + tag string `form:"tag"` + Name interface{} `form:"username"` + Age int `form:"age,text"` + Email string + Intro string `form:",textarea"` + StrBool bool `form:"strbool"` + Date time.Time `form:"date,2006-01-02"` } u := user{} @@ -119,6 +121,8 @@ func TestParseForm(t *testing.T) { "age": []string{"40"}, "Email": []string{"test@gmail.com"}, "Intro": []string{"I am an engineer!"}, + "strbool": []string{"yes"}, + "date": []string{"2014-11-12"}, } if err := ParseForm(form, u); err == nil { t.Fatal("nothing will be changed") @@ -144,6 +148,13 @@ func TestParseForm(t *testing.T) { if u.Intro != "I am an engineer!" { t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro) } + if u.StrBool != true { + t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool) + } + y, m, d := u.Date.Date() + if y != 2014 || m.String() != "November" || d != 12 { + t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String()) + } } func TestRenderForm(t *testing.T) { From fc6b9ce00913017cb73c84c3f5a4b16bc0e3e273 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 19:04:26 +0800 Subject: [PATCH 88/92] fix #620 simple the sessionID generate --- session/session.go | 97 +++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 57 deletions(-) diff --git a/session/session.go b/session/session.go index 88e94d59..3cbd2b05 100644 --- a/session/session.go +++ b/session/session.go @@ -28,19 +28,13 @@ package session import ( - "crypto/hmac" - "crypto/md5" "crypto/rand" - "crypto/sha1" "encoding/hex" "encoding/json" "fmt" - "io" "net/http" "net/url" "time" - - "github.com/astaxie/beego/utils" ) // SessionStore contains all data for one session process with specific id. @@ -81,16 +75,15 @@ func Register(name string, provide Provider) { } type managerConfig struct { - CookieName string `json:"cookieName"` - EnableSetCookie bool `json:"enableSetCookie,omitempty"` - Gclifetime int64 `json:"gclifetime"` - Maxlifetime int64 `json:"maxLifetime"` - Secure bool `json:"secure"` - SessionIDHashFunc string `json:"sessionIDHashFunc"` - SessionIDHashKey string `json:"sessionIDHashKey"` - CookieLifeTime int `json:"cookieLifeTime"` - ProviderConfig string `json:"providerConfig"` - Domain string `json:"domain"` + CookieName string `json:"cookieName"` + EnableSetCookie bool `json:"enableSetCookie,omitempty"` + Gclifetime int64 `json:"gclifetime"` + Maxlifetime int64 `json:"maxLifetime"` + Secure bool `json:"secure"` + CookieLifeTime int `json:"cookieLifeTime"` + ProviderConfig string `json:"providerConfig"` + Domain string `json:"domain"` + SessionIdLength int64 `json:"sessionIdLength"` } // Manager contains Provider and its configuration. @@ -129,11 +122,9 @@ func NewManager(provideName, config string) (*Manager, error) { if err != nil { return nil, err } - if cf.SessionIDHashFunc == "" { - cf.SessionIDHashFunc = "sha1" - } - if cf.SessionIDHashKey == "" { - cf.SessionIDHashKey = string(generateRandomKey(16)) + + if cf.SessionIdLength == 0 { + cf.SessionIdLength = 16 } return &Manager{ @@ -144,11 +135,14 @@ func NewManager(provideName, config string) (*Manager, error) { // Start session. generate or read the session id from http request. // if session id exists, return SessionStore with this id. -func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) { - cookie, err := r.Cookie(manager.config.CookieName) - if err != nil || cookie.Value == "" { - sid := manager.sessionId(r) - session, _ = manager.provider.SessionRead(sid) +func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore, err error) { + cookie, errs := r.Cookie(manager.config.CookieName) + if errs != nil || cookie.Value == "" { + sid, errs := manager.sessionId(r) + if errs != nil { + return nil, errs + } + session, err = manager.provider.SessionRead(sid) cookie = &http.Cookie{Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", @@ -163,12 +157,18 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se } r.AddCookie(cookie) } else { - sid, _ := url.QueryUnescape(cookie.Value) + sid, errs := url.QueryUnescape(cookie.Value) + if errs != nil { + return nil, errs + } if manager.provider.SessionExist(sid) { - session, _ = manager.provider.SessionRead(sid) + session, err = manager.provider.SessionRead(sid) } else { - sid = manager.sessionId(r) - session, _ = manager.provider.SessionRead(sid) + sid, err = manager.sessionId(r) + if err != nil { + return nil, err + } + session, err = manager.provider.SessionRead(sid) cookie = &http.Cookie{Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", @@ -219,7 +219,10 @@ func (manager *Manager) GC() { // Regenerate a session id for this SessionStore who's id is saving in http request. func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) { - sid := manager.sessionId(r) + sid, err := manager.sessionId(r) + if err != nil { + return + } cookie, err := r.Cookie(manager.config.CookieName) if err != nil && cookie.Value == "" { //delete old cookie @@ -251,36 +254,16 @@ func (manager *Manager) GetActiveSession() int { return manager.provider.SessionAll() } -// Set hash function for generating session id. -func (manager *Manager) SetHashFunc(hasfunc, hashkey string) { - manager.config.SessionIDHashFunc = hasfunc - manager.config.SessionIDHashKey = hashkey -} - // Set cookie with https. func (manager *Manager) SetSecure(secure bool) { manager.config.Secure = secure } -// generate session id with rand string, unix nano time, remote addr by hash function. -func (manager *Manager) sessionId(r *http.Request) (sid string) { - bs := make([]byte, 32) - if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil { - bs = utils.RandomCreateBytes(32) +func (manager *Manager) sessionId(r *http.Request) (string, error) { + b := make([]byte, manager.config.SessionIdLength) + n, err := rand.Read(b) + if n != len(b) || err != nil { + return "", fmt.Errorf("Could not successfully read from the system CSPRNG.") } - sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs) - if manager.config.SessionIDHashFunc == "md5" { - h := md5.New() - h.Write([]byte(sig)) - sid = hex.EncodeToString(h.Sum(nil)) - } else if manager.config.SessionIDHashFunc == "sha1" { - h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey)) - fmt.Fprintf(h, "%s", sig) - sid = hex.EncodeToString(h.Sum(nil)) - } else { - h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey)) - fmt.Fprintf(h, "%s", sig) - sid = hex.EncodeToString(h.Sum(nil)) - } - return + return hex.EncodeToString(b), nil } From 76522d43af8ac4b1d5d4c9788616263e4c956cdf Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 19:07:49 +0800 Subject: [PATCH 89/92] simple the session --- router.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/router.go b/router.go index 4a4a934b..1f47c907 100644 --- a/router.go +++ b/router.go @@ -644,7 +644,13 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) // session init if SessionOn { - context.Input.CruSession = GlobalSessions.SessionStart(w, r) + var err error + context.Input.CruSession, err = GlobalSessions.SessionStart(w, r) + if err != nil { + Error(err) + middleware.Exception("503", rw, r, "") + return + } defer func() { context.Input.CruSession.SessionRelease(w) }() @@ -888,6 +894,11 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok { handler(rw, r) return + } else if handler, ok := middleware.ErrorMaps["503"]; ok { + handler(rw, r) + return + } else { + rw.Write([]byte(fmt.Sprint(err))) } } else { Critical("the request url is ", r.URL.Path) From 15242d89ce16b2f96573a63f13d53f8a592ffd40 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 19:08:06 +0800 Subject: [PATCH 90/92] simple the session init --- admin.go | 2 -- beego.go | 2 -- config.go | 12 ------------ 3 files changed, 16 deletions(-) diff --git a/admin.go b/admin.go index 273e666e..d918b595 100644 --- a/admin.go +++ b/admin.go @@ -113,8 +113,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["SessionName"] = SessionName m["SessionGCMaxLifetime"] = SessionGCMaxLifetime m["SessionSavePath"] = SessionSavePath - m["SessionHashFunc"] = SessionHashFunc - m["SessionHashKey"] = SessionHashKey m["SessionCookieLifeTime"] = SessionCookieLifeTime m["UseFcgi"] = UseFcgi m["MaxMemory"] = MaxMemory diff --git a/beego.go b/beego.go index f23174c7..8bfcf617 100644 --- a/beego.go +++ b/beego.go @@ -383,8 +383,6 @@ func initBeforeHttpRun() { `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + - `"sessionIDHashFunc":"` + SessionHashFunc + `",` + - `"sessionIDHashKey":"` + SessionHashKey + `",` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"domain":"` + SessionDomain + `",` + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` diff --git a/config.go b/config.go index ed91647f..f4d0aba2 100644 --- a/config.go +++ b/config.go @@ -56,8 +56,6 @@ var ( SessionName string // the cookie name when saving session id into cookie. SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session. SessionSavePath string // if use mysql/redis/file provider, define save path to connection info. - SessionHashFunc string // session hash generation func. - SessionHashKey string // session hash salt string. SessionCookieLifeTime int // the life time of session id in cookie. SessionAutoSetCookie bool // auto setcookie SessionDomain string // the cookie domain default is empty @@ -237,8 +235,6 @@ func init() { SessionName = "beegosessionID" SessionGCMaxLifetime = 3600 SessionSavePath = "" - SessionHashFunc = "sha1" - SessionHashKey = "beegoserversessionkey" SessionCookieLifeTime = 0 //set cookie default is the brower life SessionAutoSetCookie = true @@ -354,14 +350,6 @@ func ParseConfig() (err error) { SessionSavePath = sesssavepath } - if sesshashfunc := AppConfig.String("SessionHashFunc"); sesshashfunc != "" { - SessionHashFunc = sesshashfunc - } - - if sesshashkey := AppConfig.String("SessionHashKey"); sesshashkey != "" { - SessionHashKey = sesshashkey - } - if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { SessionGCMaxLifetime = sessMaxLifeTime } From 000033e2a7e053d3ac12e12a7c50283c37d75d9b Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 22:07:38 +0800 Subject: [PATCH 91/92] update the test case --- session/sess_cookie_test.go | 5 ++++- session/sess_mem_test.go | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/session/sess_cookie_test.go b/session/sess_cookie_test.go index 4f40a7ba..fe3ac806 100644 --- a/session/sess_cookie_test.go +++ b/session/sess_cookie_test.go @@ -29,7 +29,10 @@ func TestCookie(t *testing.T) { } r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() - sess := globalSessions.SessionStart(w, r) + sess, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("set error,", err) + } err = sess.Set("username", "astaxie") if err != nil { t.Fatal("set error,", err) diff --git a/session/sess_mem_test.go b/session/sess_mem_test.go index 03927c76..43f5b0a9 100644 --- a/session/sess_mem_test.go +++ b/session/sess_mem_test.go @@ -26,9 +26,12 @@ func TestMem(t *testing.T) { go globalSessions.GC() r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() - sess := globalSessions.SessionStart(w, r) + sess, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("set error,", err) + } defer sess.SessionRelease(w) - err := sess.Set("username", "astaxie") + err = sess.Set("username", "astaxie") if err != nil { t.Fatal("set error,", err) } From fe50269b3fd07daafb5644ba33f3dff23d7731b1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 22:38:40 +0800 Subject: [PATCH 92/92] change 1.4.1 to 1.4.2 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 8bfcf617..ab70a182 100644 --- a/beego.go +++ b/beego.go @@ -38,7 +38,7 @@ import ( ) // beego web framework version. -const VERSION = "1.4.1" +const VERSION = "1.4.2" type hookfunc func() error //hook function to run var hooks []hookfunc //hook function slice to store the hookfunc