From 1a1b0c14b98f44922c8310f5a3a09759fce047c3 Mon Sep 17 00:00:00 2001 From: supar Date: Thu, 6 Nov 2014 16:01:22 +0300 Subject: [PATCH 01/39] Add attribute default to the column on create or alter commands. Skip columns which are keys and date or time data type --- orm/cmd_utils.go | 50 +++++++++++++++++++++++++++++++++++++++++++- orm/models_info_f.go | 6 ++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/orm/cmd_utils.go b/orm/cmd_utils.go index 8304da6b..ea105624 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" @@ -239,3 +246,44 @@ 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, TypeDateTimeField: + return v; + + 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 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 f96245786ae0e7b454420fb339a66cadb6676917 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 8 Nov 2014 15:10:47 +0800 Subject: [PATCH 02/39] fix #912 --- router.go | 1 - 1 file changed, 1 deletion(-) diff --git a/router.go b/router.go index 1f47c907..79111226 100644 --- a/router.go +++ b/router.go @@ -858,7 +858,6 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques return } 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 { From c9bb9d6a09bbfb70cd01821b3e4670920e165b07 Mon Sep 17 00:00:00 2001 From: xuewuhen Date: Tue, 18 Nov 2014 22:54:48 +0800 Subject: [PATCH 03/39] SubDomains function bugfixed --- context/input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/input.go b/context/input.go index 46b31352..1282d277 100644 --- a/context/input.go +++ b/context/input.go @@ -194,7 +194,7 @@ func (input *BeegoInput) Refer() string { // if aa.bb.domain.com, returns aa.bb . func (input *BeegoInput) SubDomains() string { parts := strings.Split(input.Host(), ".") - return strings.Join(parts[len(parts)-2:], ".") + return strings.Join(parts[:len(parts)-2], ".") } // Port returns request client port. From d0b43ef4f5c6bb4a9dd9b2ae456f39a6042b54b4 Mon Sep 17 00:00:00 2001 From: git Date: Thu, 20 Nov 2014 16:35:04 +0800 Subject: [PATCH 04/39] beego.AppConfig.Strings bug --- config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.go b/config.go index 7d2a94de..5210ea93 100644 --- a/config.go +++ b/config.go @@ -110,7 +110,7 @@ func (b *beegoAppConfig) String(key string) string { func (b *beegoAppConfig) Strings(key string) []string { v := b.innerConfig.Strings(RunMode + "::" + key) - if len(v) == 0 { + if v[0] == "" { return b.innerConfig.Strings(key) } return v From 93ca11f83d25cfb36ce20b37e7cd32d36e9b819c Mon Sep 17 00:00:00 2001 From: Lei Cao Date: Fri, 21 Nov 2014 01:35:30 +0800 Subject: [PATCH 05/39] Return the response directly if it's a options PreflightHeader request --- plugins/cors/cors.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/cors/cors.go b/plugins/cors/cors.go index dce750eb..052d3bc6 100644 --- a/plugins/cors/cors.go +++ b/plugins/cors/cors.go @@ -217,6 +217,7 @@ func Allow(opts *Options) beego.FilterFunc { ctx.Output.Header(key, value) } ctx.Output.SetStatus(http.StatusOK) + ctx.WriteString("") return } headers = opts.Header(origin) From 6a9d04c269dd0741d3d9b37f3926c2105c635f68 Mon Sep 17 00:00:00 2001 From: DeanThompson Date: Fri, 21 Nov 2014 18:12:39 +0800 Subject: [PATCH 06/39] count log file lines --- logs/file.go | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/logs/file.go b/logs/file.go index 39220cc0..e237170b 100644 --- a/logs/file.go +++ b/logs/file.go @@ -15,10 +15,11 @@ package logs import ( + "bytes" "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "log" "os" "path/filepath" @@ -170,17 +171,44 @@ func (w *FileLogWriter) initFd() error { w.maxsize_cursize = int(finfo.Size()) w.daily_opendate = time.Now().Day() if finfo.Size() > 0 { - content, err := ioutil.ReadFile(w.Filename) + count, err := w.lines() if err != nil { return err } - w.maxlines_curlines = len(strings.Split(string(content), "\n")) + w.maxlines_curlines = count } else { w.maxlines_curlines = 0 } return nil } +func (w *FileLogWriter) lines() (int, error) { + fd, err := os.Open(w.Filename) + if err != nil { + return 0, err + } + defer fd.Close() + + buf := make([]byte, 32768) // 32k + count := 0 + lineSep := []byte{'\n'} + + for { + c, err := fd.Read(buf) + if err != nil && err != io.EOF { + return count, err + } + + count += bytes.Count(buf[:c], lineSep) + + if err == io.EOF { + break + } + } + + return count, nil +} + // DoRotate means it need to write file in new file. // new file name like xx.log.2013-01-01.2 func (w *FileLogWriter) DoRotate() error { From dc89f844f35b61f57ee27b6952bbd37430d1a290 Mon Sep 17 00:00:00 2001 From: Ryan Bastic Date: Sun, 23 Nov 2014 14:12:29 +0100 Subject: [PATCH 07/39] Reword message about reloading packages.. --- parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.go b/parser.go index bd673044..be91b4cb 100644 --- a/parser.go +++ b/parser.go @@ -57,7 +57,7 @@ func parserPkg(pkgRealpath, pkgpath string) error { rep := strings.NewReplacer("/", "_", ".", "_") commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go" if !compareFile(pkgRealpath) { - Info(pkgRealpath + " don't has updated") + Info(pkgRealpath + " has not changed, not reloading") return nil } genInfoList = make(map[string][]ControllerComments) From db04c3cbb4dcbaa7d78d30d69076381b9031d177 Mon Sep 17 00:00:00 2001 From: Hily Jiang Date: Mon, 24 Nov 2014 23:12:09 +0800 Subject: [PATCH 08/39] make Content-Type header more human-readable --- context/output.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/context/output.go b/context/output.go index 6298ee77..2141513d 100644 --- a/context/output.go +++ b/context/output.go @@ -188,7 +188,7 @@ func sanitizeValue(v string) string { // Json writes json to response body. // if coding is true, it converts utf-8 to \u0000 type. func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error { - output.Header("Content-Type", "application/json;charset=UTF-8") + output.Header("Content-Type", "application/json; charset=utf-8") var content []byte var err error if hasIndent { @@ -209,7 +209,7 @@ func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) e // Jsonp writes jsonp to response body. func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error { - output.Header("Content-Type", "application/javascript;charset=UTF-8") + output.Header("Content-Type", "application/javascript; charset=utf-8") var content []byte var err error if hasIndent { @@ -235,7 +235,7 @@ func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error { // Xml writes xml string to response body. func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error { - output.Header("Content-Type", "application/xml;charset=UTF-8") + output.Header("Content-Type", "application/xml; charset=utf-8") var content []byte var err error if hasIndent { From 29d98731c616ffc5fca5286d4c38f6c7a88e3fe9 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 25 Nov 2014 14:41:51 -0800 Subject: [PATCH 09/39] add sess_ledis select db config --- session/ledis/ledis_session.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go index 3ada47ac..400d7238 100644 --- a/session/ledis/ledis_session.go +++ b/session/ledis/ledis_session.go @@ -2,6 +2,8 @@ package session import ( "net/http" + "strconv" + "strings" "sync" "github.com/astaxie/beego/session" @@ -74,19 +76,29 @@ func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) { type LedisProvider struct { maxlifetime int64 savePath string + db int } // 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 { + var err error lp.maxlifetime = maxlifetime - lp.savePath = savePath + configs := strings.Split(savepath, ",") + if len(configs) == 1 { + lp.savePath = configs[0] + } else if len(configs) == 2 { + lp.savePath = configs[0] + lp.db, err = strconv.Atoi(configs[1]) + if err != nil { + return err + } + } cfg := new(config.Config) cfg.DataDir = lp.savePath - var err error nowLedis, err := ledis.Open(cfg) - c, err = nowLedis.Select(0) + c, err = nowLedis.Select(lp.db) if err != nil { println(err) return nil From 77ed1512435b4d8f55b75beacbbf8f5c6512f170 Mon Sep 17 00:00:00 2001 From: kristen1980 Date: Sun, 7 Dec 2014 10:00:35 -0700 Subject: [PATCH 10/39] Allow absolute path for filesystem cache Gives more flexibility by making it an absolute path. A relative path can easily be created by the user. --- cache/file.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cache/file.go b/cache/file.go index 6ecf6568..bbbbbad2 100644 --- a/cache/file.go +++ b/cache/file.go @@ -92,8 +92,6 @@ func (fc *FileCache) StartAndGC(config string) error { // Init will make new dir for file cache if not exist. func (fc *FileCache) Init() { - app := filepath.Dir(os.Args[0]) - fc.CachePath = filepath.Join(app, fc.CachePath) if ok, _ := exists(fc.CachePath); !ok { // todo : error handle _ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle } From 9c665afc0437d91317f01f3568e710cbae5aa0d8 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 8 Dec 2014 14:57:45 +0800 Subject: [PATCH 11/39] improve the error tips --- router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router.go b/router.go index 79111226..a26a8309 100644 --- a/router.go +++ b/router.go @@ -153,7 +153,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM if val := reflectVal.MethodByName(colon[1]); val.IsValid() { methods[strings.ToUpper(m)] = colon[1] } else { - panic(colon[1] + " method doesn't exist in the controller " + t.Name()) + panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name()) } } else { panic(v + " is an invalid method mapping. Method doesn't exist " + m) From c52f634d9c83a307c0bac057507d009c92b074b9 Mon Sep 17 00:00:00 2001 From: Athurg Gooth Date: Thu, 11 Dec 2014 16:42:50 +0800 Subject: [PATCH 12/39] Fix paginator attributes cannot be modified bug We can only use SetPaginator to create a pagination. After that, we always need to modify something, like the totalNum, perPageNum. These change should be seen in the view. So we should give the view a pointer than a object. --- utils/pagination/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/pagination/controller.go b/utils/pagination/controller.go index 28473f8a..f63b30e9 100644 --- a/utils/pagination/controller.go +++ b/utils/pagination/controller.go @@ -21,6 +21,6 @@ import ( // 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 + context.Input.Data["paginator"] = &paginator return } From 22671c524edf776949f5ba6b3afb9e58001f2a3c Mon Sep 17 00:00:00 2001 From: shuo li Date: Wed, 17 Dec 2014 12:06:53 +0800 Subject: [PATCH 13/39] Fix subdomain, add test, space and comment fix --- context/input.go | 22 ++++++++++++++++------ context/input_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/context/input.go b/context/input.go index 3ed0bbe5..d9aa2634 100644 --- a/context/input.go +++ b/context/input.go @@ -27,7 +27,7 @@ import ( "github.com/astaxie/beego/session" ) -// BeegoInput operates the http request header ,data ,cookie and body. +// BeegoInput operates the http request header, data, cookie and body. // it also contains router params and current session. type BeegoInput struct { CruSession session.SessionStore @@ -153,12 +153,12 @@ func (input *BeegoInput) IsSecure() bool { return input.Scheme() == "https" } -// IsSecure returns boolean of this request is in webSocket. +// IsWebsocket returns boolean of this request is in webSocket. func (input *BeegoInput) IsWebsocket() bool { return input.Header("Upgrade") == "websocket" } -// IsSecure returns boolean of whether file uploads in this request or not.. +// IsUpload returns boolean of whether file uploads in this request or not.. func (input *BeegoInput) IsUpload() bool { return strings.Contains(input.Header("Content-Type"), "multipart/form-data") } @@ -189,16 +189,24 @@ func (input *BeegoInput) Proxy() []string { return []string{} } +// Referer returns http referer header. +func (input *BeegoInput) Referer() string { + return input.Header("Referer") +} + // Refer returns http referer header. func (input *BeegoInput) Refer() string { - return input.Header("Referer") + return input.Referer() } // SubDomains returns sub domain string. // if aa.bb.domain.com, returns aa.bb . func (input *BeegoInput) SubDomains() string { parts := strings.Split(input.Host(), ".") - return strings.Join(parts[:len(parts)-2], ".") + if len(parts) >= 3 { + return strings.Join(parts[:len(parts)-2], ".") + } + return "" } // Port returns request client port. @@ -237,6 +245,7 @@ func (input *BeegoInput) Query(key string) string { } // Header returns request header item string by a given string. +// if non-existed, return empty string. func (input *BeegoInput) Header(key string) string { return input.Request.Header.Get(key) } @@ -252,11 +261,12 @@ func (input *BeegoInput) Cookie(key string) string { } // Session returns current session item value by a given key. +// if non-existed, return empty string. func (input *BeegoInput) Session(key interface{}) interface{} { return input.CruSession.Get(key) } -// Body returns the raw request body data as bytes. +// CopyBody returns the raw request body data as bytes. func (input *BeegoInput) CopyBody() []byte { requestbody, _ := ioutil.ReadAll(input.Request.Body) input.Request.Body.Close() diff --git a/context/input_test.go b/context/input_test.go index ddd1a056..4566f6d6 100644 --- a/context/input_test.go +++ b/context/input_test.go @@ -70,3 +70,45 @@ func TestParse(t *testing.T) { } fmt.Println(user) } + +func TestSubDomain(t *testing.T) { + r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil) + beegoInput := NewInput(r) + + subdomain := beegoInput.SubDomains() + if subdomain != "www" { + t.Fatal("Subdomain parse error, got" + subdomain) + } + + r, _ = http.NewRequest("GET", "http://localhost/", nil) + beegoInput.Request = r + if beegoInput.SubDomains() != "" { + t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains()) + } + + r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil) + beegoInput.Request = r + if beegoInput.SubDomains() != "aa.bb" { + t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains()) + } + + /* TODO Fix this + r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil) + beegoInput.Request = r + if beegoInput.SubDomains() != "" { + t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains()) + } + */ + + r, _ = http.NewRequest("GET", "http://example.com/", nil) + beegoInput.Request = r + if beegoInput.SubDomains() != "" { + t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains()) + } + + r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil) + beegoInput.Request = r + if beegoInput.SubDomains() != "aa.bb.cc.dd" { + t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains()) + } +} From daf85f06f8c7e35f05639812dd8f45a5b7a731bb Mon Sep 17 00:00:00 2001 From: Jianbo Feng Date: Wed, 17 Dec 2014 15:23:11 +0800 Subject: [PATCH 14/39] =?UTF-8?q?Support=20default=20value=20for=20control?= =?UTF-8?q?ler=E2=80=99s=20params=20get?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.go | 127 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 25 deletions(-) diff --git a/controller.go b/controller.go index 72ba323b..71e836fc 100644 --- a/controller.go +++ b/controller.go @@ -364,66 +364,143 @@ func (c *Controller) ParseForm(obj interface{}) error { } // GetString returns the input value by key string. -func (c *Controller) GetString(key string) string { - return c.Ctx.Input.Query(key) +func (c *Controller) GetString(key string, def ...string) string { + var defv string + if len(def) > 0 { + defv = def[0] + } + + if v := c.Ctx.Input.Query(key); v != "" { + return v + } else { + return defv + } } // GetStrings returns the input string slice by key string. // it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection. -func (c *Controller) GetStrings(key string) []string { +func (c *Controller) GetStrings(key string, def ...[]string) []string { + var defv []string + if len(def) > 0 { + defv = def[0] + } + f := c.Input() if f == nil { - return []string{} + return defv } + vs := f[key] if len(vs) > 0 { return vs + } else { + return defv } - return []string{} } // GetInt returns input as an int -func (c *Controller) GetInt(key string) (int, error) { - return strconv.Atoi(c.Ctx.Input.Query(key)) +func (c *Controller) GetInt(key string, def ...int) (int, error) { + var defv int + if len(def) > 0 { + defv = def[0] + } + + if strv := c.Ctx.Input.Query(key); strv != "" { + return strconv.Atoi(strv) + } else { + return defv, nil + } } // 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) +func (c *Controller) GetInt8(key string, def ...int8) (int8, error) { + var defv int8 + if len(def) > 0 { + defv = def[0] + } - return i8, err + if strv := c.Ctx.Input.Query(key); strv != "" { + i64, err := strconv.ParseInt(strv, 10, 8) + i8 := int8(i64) + return i8, err + } else { + return defv, nil + } } // 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) +func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { + var defv int16 + if len(def) > 0 { + defv = def[0] + } - return i16, err + if strv := c.Ctx.Input.Query(key); strv != "" { + i64, err := strconv.ParseInt(strv, 10, 16) + i16 := int16(i64) + + return i16, err + } else { + return defv, nil + } } // 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) +func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { + var defv int32 + if len(def) > 0 { + defv = def[0] + } - return i32, err + if strv := c.Ctx.Input.Query(key); strv != "" { + i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) + i32 := int32(i64) + return i32, err + } else { + return defv, nil + } } // GetInt64 returns input value as int64. -func (c *Controller) GetInt64(key string) (int64, error) { - return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64) +func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { + var defv int64 + if len(def) > 0 { + defv = def[0] + } + + if strv := c.Ctx.Input.Query(key); strv != "" { + return strconv.ParseInt(strv, 10, 64) + } else { + return defv, nil + } } // GetBool returns input value as bool. -func (c *Controller) GetBool(key string) (bool, error) { - return strconv.ParseBool(c.Ctx.Input.Query(key)) +func (c *Controller) GetBool(key string, def ...bool) (bool, error) { + var defv bool + if len(def) > 0 { + defv = def[0] + } + + if strv := c.Ctx.Input.Query(key); strv != "" { + return strconv.ParseBool(strv) + } else { + return defv, nil + } } // GetFloat returns input value as float64. -func (c *Controller) GetFloat(key string) (float64, error) { - return strconv.ParseFloat(c.Ctx.Input.Query(key), 64) +func (c *Controller) GetFloat(key string, def ...float64) (float64, error) { + var defv float64 + if len(def) > 0 { + defv = def[0] + } + + if strv := c.Ctx.Input.Query(key); strv != "" { + return strconv.ParseFloat(c.Ctx.Input.Query(key), 64) + } else { + return defv, nil + } } // GetFile returns the file data in file upload field named as key. From e34f8479bbc189ece311e0ca20d06dc0fa92e5b3 Mon Sep 17 00:00:00 2001 From: Jianbo Feng Date: Wed, 17 Dec 2014 15:52:48 +0800 Subject: [PATCH 15/39] =?UTF-8?q?Add=20all=20type=20support=20for=20UrlFor?= =?UTF-8?q?=E2=80=99s=20params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller.go | 2 +- router.go | 6 +++--- templatefunc.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/controller.go b/controller.go index 72ba323b..cc992abc 100644 --- a/controller.go +++ b/controller.go @@ -289,7 +289,7 @@ func (c *Controller) StopRun() { // UrlFor does another controller handler in this request function. // it goes to this controller method if endpoint is not clear. -func (c *Controller) UrlFor(endpoint string, values ...string) string { +func (c *Controller) UrlFor(endpoint string, values ...interface{}) string { if len(endpoint) <= 0 { return "" } diff --git a/router.go b/router.go index 79111226..f12e2e9b 100644 --- a/router.go +++ b/router.go @@ -429,7 +429,7 @@ func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error // UrlFor does another controller handler in this request function. // it can access any controller method. -func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { +func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) string { paths := strings.Split(endpoint, ".") if len(paths) <= 1 { Warn("urlfor endpoint must like path.controller.method") @@ -444,9 +444,9 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { key := "" for k, v := range values { if k%2 == 0 { - key = v + key = fmt.Sprint(v) } else { - params[key] = v + params[key] = fmt.Sprint(v) } } } diff --git a/templatefunc.go b/templatefunc.go index 16067613..cacaae07 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -246,7 +246,7 @@ func Htmlunquote(src string) string { // /user/John%20Doe // // more detail http://beego.me/docs/mvc/controller/urlbuilding.md -func UrlFor(endpoint string, values ...string) string { +func UrlFor(endpoint string, values ...interface{}) string { return BeeApp.Handlers.UrlFor(endpoint, values...) } From 572508ddd850f474404c0d18ce9dac023cbc5113 Mon Sep 17 00:00:00 2001 From: shuo li Date: Wed, 17 Dec 2014 17:02:46 +0800 Subject: [PATCH 16/39] Clean json config. Fix DefaultStrings --- config/json.go | 77 +++++++++++++++++++++------------------------ config/json_test.go | 15 +++++++-- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/config/json.go b/config/json.go index ae86ea53..e2b53793 100644 --- a/config/json.go +++ b/config/json.go @@ -17,13 +17,10 @@ package config import ( "encoding/json" "errors" - "fmt" "io/ioutil" "os" - "path" "strings" "sync" - "time" ) // JsonConfig is a json config parser and implements Config interface. @@ -41,13 +38,19 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) { if err != nil { return nil, err } + + return js.ParseData(content) +} + +// ParseData returns a ConfigContainer with json string +func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) { x := &JsonConfigContainer{ data: make(map[string]interface{}), } - err = json.Unmarshal(content, &x.data) + err := json.Unmarshal(data, &x.data) if err != nil { var wrappingArray []interface{} - err2 := json.Unmarshal(content, &wrappingArray) + err2 := json.Unmarshal(data, &wrappingArray) if err2 != nil { return nil, err } @@ -56,16 +59,6 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) { return x, nil } -func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) { - // Save memory data to temporary file - tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond())) - os.MkdirAll(path.Dir(tmpName), os.ModePerm) - if err := ioutil.WriteFile(tmpName, data, 0655); err != nil { - return nil, err - } - return js.Parse(tmpName) -} - // A Config represents the json configuration. // Only when get value, support key as section:name type. type JsonConfigContainer struct { @@ -88,11 +81,10 @@ func (c *JsonConfigContainer) Bool(key string) (bool, error) { // DefaultBool return the bool value if has no error // otherwise return the defaultval func (c *JsonConfigContainer) DefaultBool(key string, defaultval bool) bool { - if v, err := c.Bool(key); err != nil { - return defaultval - } else { + if v, err := c.Bool(key); err == nil { return v } + return defaultval } // Int returns the integer value for a given key. @@ -110,11 +102,10 @@ func (c *JsonConfigContainer) Int(key string) (int, error) { // DefaultInt returns the integer value for a given key. // if err != nil return defaltval func (c *JsonConfigContainer) DefaultInt(key string, defaultval int) int { - if v, err := c.Int(key); err != nil { - return defaultval - } else { + if v, err := c.Int(key); err == nil { return v } + return defaultval } // Int64 returns the int64 value for a given key. @@ -132,11 +123,10 @@ func (c *JsonConfigContainer) Int64(key string) (int64, error) { // DefaultInt64 returns the int64 value for a given key. // if err != nil return defaltval func (c *JsonConfigContainer) DefaultInt64(key string, defaultval int64) int64 { - if v, err := c.Int64(key); err != nil { - return defaultval - } else { + if v, err := c.Int64(key); err == nil { return v } + return defaultval } // Float returns the float value for a given key. @@ -154,11 +144,10 @@ func (c *JsonConfigContainer) Float(key string) (float64, error) { // DefaultFloat returns the float64 value for a given key. // if err != nil return defaltval func (c *JsonConfigContainer) DefaultFloat(key string, defaultval float64) float64 { - if v, err := c.Float(key); err != nil { - return defaultval - } else { + if v, err := c.Float(key); err == nil { return v } + return defaultval } // String returns the string value for a given key. @@ -175,35 +164,37 @@ func (c *JsonConfigContainer) String(key string) string { // DefaultString returns the string value for a given key. // if err != nil return defaltval func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string { - if v := c.String(key); v == "" { - return defaultval - } else { + // TODO FIXME should not use "" to replace non existance + if v := c.String(key); v != "" { return v } + return defaultval } // Strings returns the []string value for a given key. func (c *JsonConfigContainer) Strings(key string) []string { + stringVal := c.String(key) + if stringVal == "" { + return []string{} + } return strings.Split(c.String(key), ";") } // DefaultStrings returns the []string value for a given key. // if err != nil return defaltval func (c *JsonConfigContainer) DefaultStrings(key string, defaultval []string) []string { - if v := c.Strings(key); len(v) == 0 { - return defaultval - } else { + if v := c.Strings(key); len(v) > 0 { return v } + return defaultval } // GetSection returns map for the given section func (c *JsonConfigContainer) GetSection(section string) (map[string]string, error) { if v, ok := c.data[section]; ok { return v.(map[string]string), nil - } else { - return nil, errors.New("not exist setction") } + return nil, errors.New("nonexist section " + section) } // SaveConfigFile save the config into file @@ -222,7 +213,7 @@ func (c *JsonConfigContainer) SaveConfigFile(filename string) (err error) { return err } -// WriteValue writes a new value for key. +// Set writes a new value for key. func (c *JsonConfigContainer) Set(key, val string) error { c.Lock() defer c.Unlock() @@ -241,18 +232,20 @@ func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) { // section.key or key func (c *JsonConfigContainer) getData(key string) interface{} { - c.RLock() - defer c.RUnlock() if len(key) == 0 { return nil } - sectionKey := strings.Split(key, "::") - if len(sectionKey) >= 2 { - curValue, ok := c.data[sectionKey[0]] + + c.RLock() + defer c.RUnlock() + + sectionKeys := strings.Split(key, "::") + if len(sectionKeys) >= 2 { + curValue, ok := c.data[sectionKeys[0]] if !ok { return nil } - for _, key := range sectionKey[1:] { + for _, key := range sectionKeys[1:] { if v, ok := curValue.(map[string]interface{}); ok { if curValue, ok = v[key]; !ok { return nil diff --git a/config/json_test.go b/config/json_test.go index 409e2c12..5aedae36 100644 --- a/config/json_test.go +++ b/config/json_test.go @@ -21,6 +21,7 @@ import ( var jsoncontext = `{ "appname": "beeapi", +"testnames": "foo;bar", "httpport": 8080, "mysqlport": 3600, "PI": 3.1415976, @@ -28,8 +29,8 @@ var jsoncontext = `{ "autorender": false, "copyrequestbody": true, "database": { - "host": "host", - "port": "port", + "host": "host", + "port": "port", "database": "database", "username": "username", "password": "password", @@ -122,6 +123,12 @@ func TestJson(t *testing.T) { if jsonconf.String("runmode") != "dev" { t.Fatal("runmode not equal to dev") } + if v := jsonconf.Strings("unknown"); len(v) > 0 { + t.Fatal("unknown strings, the length should be 0") + } + if v := jsonconf.Strings("testnames"); len(v) != 2 { + t.Fatal("testnames length should be 2") + } if v, err := jsonconf.Bool("autorender"); err != nil || v != false { t.Error(v) t.Fatal(err) @@ -179,4 +186,8 @@ func TestJson(t *testing.T) { if _, err := jsonconf.Bool("unknown"); err == nil { t.Error("unknown keys should return an error when expecting a Bool") } + + if !jsonconf.DefaultBool("unknow", true) { + t.Error("unknown keys with default value wrong") + } } From d3ab15791545fba0da00038cdb876a2c68c539d4 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 19 Dec 2014 14:40:16 +0800 Subject: [PATCH 17/39] fix the cache test --- cache/cache_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cache/cache_test.go b/cache/cache_test.go index bf9e79c2..7c43e539 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -15,6 +15,7 @@ package cache import ( + "os" "testing" "time" ) @@ -67,7 +68,7 @@ func TestCache(t *testing.T) { } func TestFileCache(t *testing.T) { - bm, err := NewCache("file", `{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`) + bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`) if err != nil { t.Error("init err") } @@ -112,4 +113,5 @@ func TestFileCache(t *testing.T) { if v := bm.Get("astaxie"); v.(string) != "author" { t.Error("get err") } + os.RemoveAll("cache") } From d2c5daa5ee1dc8030a2ac0e971fead28e2359b4f Mon Sep 17 00:00:00 2001 From: Jianbo Feng Date: Fri, 19 Dec 2014 15:28:18 +0800 Subject: [PATCH 18/39] Update comments for controller's GetXXX functions --- controller.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/controller.go b/controller.go index 71e836fc..6f621a12 100644 --- a/controller.go +++ b/controller.go @@ -363,7 +363,7 @@ func (c *Controller) ParseForm(obj interface{}) error { return ParseForm(c.Input(), obj) } -// GetString returns the input value by key string. +// GetString returns the input value by key string or the default value while it's present and input is blank func (c *Controller) GetString(key string, def ...string) string { var defv string if len(def) > 0 { @@ -377,7 +377,7 @@ func (c *Controller) GetString(key string, def ...string) string { } } -// GetStrings returns the input string slice by key string. +// GetStrings returns the input string slice by key string or the default value while it's present and input is blank // it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection. func (c *Controller) GetStrings(key string, def ...[]string) []string { var defv []string @@ -398,7 +398,7 @@ func (c *Controller) GetStrings(key string, def ...[]string) []string { } } -// GetInt returns input as an int +// GetInt returns input as an int or the default value while it's present and input is blank func (c *Controller) GetInt(key string, def ...int) (int, error) { var defv int if len(def) > 0 { @@ -412,7 +412,7 @@ func (c *Controller) GetInt(key string, def ...int) (int, error) { } } -// GetInt8 return input as an int8 +// GetInt8 return input as an int8 or the default value while it's present and input is blank func (c *Controller) GetInt8(key string, def ...int8) (int8, error) { var defv int8 if len(def) > 0 { @@ -428,7 +428,7 @@ func (c *Controller) GetInt8(key string, def ...int8) (int8, error) { } } -// GetInt16 returns input as an int16 +// GetInt16 returns input as an int16 or the default value while it's present and input is blank func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { var defv int16 if len(def) > 0 { @@ -445,7 +445,7 @@ func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { } } -// GetInt32 returns input as an int32 +// GetInt32 returns input as an int32 or the default value while it's present and input is blank func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { var defv int32 if len(def) > 0 { @@ -461,7 +461,7 @@ func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { } } -// GetInt64 returns input value as int64. +// GetInt64 returns input value as int64 or the default value while it's present and input is blank. func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { var defv int64 if len(def) > 0 { @@ -475,7 +475,7 @@ func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { } } -// GetBool returns input value as bool. +// GetBool returns input value as bool or the default value while it's present and input is blank. func (c *Controller) GetBool(key string, def ...bool) (bool, error) { var defv bool if len(def) > 0 { @@ -489,7 +489,7 @@ func (c *Controller) GetBool(key string, def ...bool) (bool, error) { } } -// GetFloat returns input value as float64. +// GetFloat returns input value as float64 or the default value while it's present and input is blank. func (c *Controller) GetFloat(key string, def ...float64) (float64, error) { var defv float64 if len(def) > 0 { From 0c933643e2361c1b246522d30c18240c26a4ccf1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 19 Dec 2014 15:33:51 +0800 Subject: [PATCH 19/39] improve the empty router --- tree.go | 3 +++ tree_test.go | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 25947442..93e87470 100644 --- a/tree.go +++ b/tree.go @@ -422,6 +422,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string // "/admin/" -> ["admin"] // "/admin/users" -> ["admin", "users"] func splitPath(key string) []string { + if key == "" { + return []string{} + } elements := strings.Split(key, "/") if elements[0] == "" { elements = elements[1:] diff --git a/tree_test.go b/tree_test.go index 358898e7..fa289716 100644 --- a/tree_test.go +++ b/tree_test.go @@ -149,7 +149,11 @@ func TestAddTree2(t *testing.T) { } func TestSplitPath(t *testing.T) { - a := splitPath("/") + a := splitPath("") + if len(a) != 0 { + t.Fatal("/ should retrun []") + } + a = splitPath("/") if len(a) != 0 { t.Fatal("/ should retrun []") } From d961ae4cd8cbde67f4b23ad5e58739169c8cab10 Mon Sep 17 00:00:00 2001 From: Athurg Gooth Date: Thu, 25 Dec 2014 11:23:04 +0800 Subject: [PATCH 20/39] Fix RequestURI nil caused template parse failed Sometime RequestURI is not set, e.g. running after a front proxy server. We should always follow the document's directive, to use Request.URL instead of RequestURI. Refer: http://golang.org/pkg/net/http/#Request --- utils/pagination/paginator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/pagination/paginator.go b/utils/pagination/paginator.go index f89e878e..e537f1ad 100644 --- a/utils/pagination/paginator.go +++ b/utils/pagination/paginator.go @@ -114,7 +114,7 @@ func (p *Paginator) Pages() []int { // Returns URL for a given page index. func (p *Paginator) PageLink(page int) string { - link, _ := url.ParseRequestURI(p.Request.RequestURI) + link, _ := url.ParseRequestURI(p.Request.URL.String()) values := link.Query() if page == 1 { values.Del("p") From 18659e16ba32a9b925ce8c6e270dce13d0881a36 Mon Sep 17 00:00:00 2001 From: "Black.Lee" Date: Mon, 5 Jan 2015 16:38:57 +0800 Subject: [PATCH 21/39] add compare_not/not_nil methods for template --- template.go | 3 +++ templatefunc.go | 8 ++++++++ templatefunc_test.go | 11 ++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/template.go b/template.go index 2ca84f22..64b1939e 100644 --- a/template.go +++ b/template.go @@ -42,6 +42,9 @@ func init() { beegoTplFuncMap["dateformat"] = DateFormat beegoTplFuncMap["date"] = Date beegoTplFuncMap["compare"] = Compare + beegoTplFuncMap["compare_not"] = CompareNot + beegoTplFuncMap["not_nil"] = NotNil + beegoTplFuncMap["not_null"] = NotNil beegoTplFuncMap["substr"] = Substr beegoTplFuncMap["html2str"] = Html2str beegoTplFuncMap["str2html"] = Str2html diff --git a/templatefunc.go b/templatefunc.go index 16067613..7d3ac6c9 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -139,6 +139,14 @@ func Compare(a, b interface{}) (equal bool) { return } +func CompareNot(a, b interface{}) (equal bool) { + return ! Compare(a, b) +} + +func NotNil(a interface{}) (is_nil bool) { + return CompareNot(a, nil) +} + func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) { switch returnType { case "String": diff --git a/templatefunc_test.go b/templatefunc_test.go index 3692a821..60af5bf5 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -72,7 +72,7 @@ func TestDate(t *testing.T) { } } -func TestCompare(t *testing.T) { +func TestCompareRelated(t *testing.T) { if !Compare("abc", "abc") { t.Error("should be equal") } @@ -82,6 +82,15 @@ func TestCompare(t *testing.T) { if !Compare("1", 1) { t.Error("should be equal") } + if CompareNot("abc", "abc") { + t.Error("should be equal") + } + if !CompareNot("abc", "aBc") { + t.Error("should be not equal") + } + if !NotNil("a string") { + t.Error("should not be nil") + } } func TestHtmlquote(t *testing.T) { From 3731088b4a5f644464ab339d129e94f7a6fbaaf4 Mon Sep 17 00:00:00 2001 From: Peter Fern Date: Sat, 10 Jan 2015 15:25:22 +1100 Subject: [PATCH 22/39] Add GroupBy to QuerySeter Adds support for GroupBy to QuerySeter SELECT operations. --- orm/db.go | 9 ++++++--- orm/db_tables.go | 24 ++++++++++++++++++++++++ orm/orm_queryset.go | 7 +++++++ orm/orm_test.go | 21 +++++++++++++++++++++ orm/types.go | 1 + 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/orm/db.go b/orm/db.go index 10f65fee..677a20ef 100644 --- a/orm/db.go +++ b/orm/db.go @@ -800,6 +800,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi tables.parseRelated(qs.related, qs.relDepth) where, args := tables.getCondSql(cond, false, tz) + groupBy := tables.getGroupSql(qs.groups) orderBy := tables.getOrderSql(qs.orders) limit := tables.getLimitSql(mi, offset, rlimit) join := tables.getJoinSql() @@ -812,7 +813,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi } } - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s", sels, Q, mi.table, Q, join, where, orderBy, limit) + query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s%s", sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) d.ins.ReplaceMarks(&query) @@ -936,12 +937,13 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition tables.parseRelated(qs.related, qs.relDepth) where, args := tables.getCondSql(cond, false, tz) + groupBy := tables.getGroupSql(qs.groups) tables.getOrderSql(qs.orders) join := tables.getJoinSql() Q := d.ins.TableQuote() - query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s", Q, mi.table, Q, join, where) + query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s%s", Q, mi.table, Q, join, where, groupBy) d.ins.ReplaceMarks(&query) @@ -1442,13 +1444,14 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond } where, args := tables.getCondSql(cond, false, tz) + groupBy := tables.getGroupSql(qs.groups) orderBy := tables.getOrderSql(qs.orders) limit := tables.getLimitSql(mi, qs.offset, qs.limit) join := tables.getJoinSql() sels := strings.Join(cols, ", ") - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s", sels, Q, mi.table, Q, join, where, orderBy, limit) + query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s%s", sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) d.ins.ReplaceMarks(&query) diff --git a/orm/db_tables.go b/orm/db_tables.go index a9aa10ab..3677932a 100644 --- a/orm/db_tables.go +++ b/orm/db_tables.go @@ -390,6 +390,30 @@ func (t *dbTables) getCondSql(cond *Condition, sub bool, tz *time.Location) (whe return } +// generate group sql. +func (t *dbTables) getGroupSql(groups []string) (groupSql string) { + if len(groups) == 0 { + return + } + + Q := t.base.TableQuote() + + groupSqls := make([]string, 0, len(groups)) + for _, group := range groups { + exprs := strings.Split(group, ExprSep) + + index, _, fi, suc := t.parseExprs(t.mi, exprs) + if suc == false { + panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) + } + + groupSqls = append(groupSqls, fmt.Sprintf("%s.%s%s%s", index, Q, fi.column, Q)) + } + + groupSql = fmt.Sprintf("GROUP BY %s ", strings.Join(groupSqls, ", ")) + return +} + // generate order sql. func (t *dbTables) getOrderSql(orders []string) (orderSql string) { if len(orders) == 0 { diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index 4f5d5485..26e82379 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -60,6 +60,7 @@ type querySet struct { relDepth int limit int64 offset int64 + groups []string orders []string orm *orm } @@ -105,6 +106,12 @@ func (o querySet) Offset(offset interface{}) QuerySeter { return &o } +// add GROUP expression. +func (o querySet) GroupBy(exprs ...string) QuerySeter { + o.groups = exprs + return &o +} + // add ORDER expression. // "column" means ASC, "-column" means DESC. func (o querySet) OrderBy(exprs ...string) QuerySeter { diff --git a/orm/orm_test.go b/orm/orm_test.go index e1c8e0f0..c223a463 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -845,6 +845,27 @@ func TestOffset(t *testing.T) { throwFail(t, AssertIs(num, 2)) } +func TestGroupBy(t *testing.T) { + var users []*User + var maps []Params + qs := dORM.QueryTable("user") + num, err := qs.GroupBy("is_staff").Filter("user_name", "nobody").Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + + num, err = qs.GroupBy("is_staff").Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 2)) + + num, err = qs.GroupBy("is_staff").Values(&maps) + throwFail(t, err) + throwFail(t, AssertIs(num, 2)) + + num, err = qs.GroupBy("profile__age").Filter("user_name", "astaxie").All(&users) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) +} + func TestOrderBy(t *testing.T) { qs := dORM.QueryTable("user") num, err := qs.OrderBy("-status").Filter("user_name", "nobody").Count() diff --git a/orm/types.go b/orm/types.go index c342e1c2..d308642b 100644 --- a/orm/types.go +++ b/orm/types.go @@ -67,6 +67,7 @@ type QuerySeter interface { SetCond(*Condition) QuerySeter Limit(interface{}, ...interface{}) QuerySeter Offset(interface{}) QuerySeter + GroupBy(...string) QuerySeter OrderBy(...string) QuerySeter RelatedSel(...interface{}) QuerySeter Count() (int64, error) From 30871e261790b5a8a9801534e6fe0ef6a7994a70 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 10 Jan 2015 17:35:35 +0800 Subject: [PATCH 23/39] Fixed the status issue at error handler. --- middleware/error.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/error.go b/middleware/error.go index 3b7191e9..052e977c 100644 --- a/middleware/error.go +++ b/middleware/error.go @@ -328,7 +328,7 @@ func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg strin if err != nil { isint = 500 } - if isint == 400 { + if isint == 404 { msg = "404 page not found" } w.Header().Set("Content-Type", "text/plain; charset=utf-8") From b1baf4503d6f28680e91c7451868545a16926ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A9=BA=E8=A7=81?= Date: Wed, 4 Feb 2015 18:07:31 +0800 Subject: [PATCH 24/39] beego task list update for task spec list and task run url error --- admin.go | 6 ++++-- adminui.go | 2 +- toolbox/task.go | 10 +++++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/admin.go b/admin.go index d918b595..90142c55 100644 --- a/admin.go +++ b/admin.go @@ -397,7 +397,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { if err != nil { data["Message"] = []string{"error", fmt.Sprintf("%s", err)} } - data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is %s", taskname, t.GetStatus())} + data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is
%s", taskname, t.GetStatus())} } else { data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)} } @@ -410,12 +410,14 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { var fields = []string{ fmt.Sprintf("Task Name"), fmt.Sprintf("Task Spec"), - fmt.Sprintf("Task Function"), + fmt.Sprintf("Task Status"), + fmt.Sprintf("Last Time"), fmt.Sprintf(""), } for tname, tk := range toolbox.AdminTaskList { result = []string{ fmt.Sprintf("%s", tname), + fmt.Sprintf("%s", tk.GetSpec()), fmt.Sprintf("%s", tk.GetStatus()), fmt.Sprintf("%s", tk.GetPrev().String()), } diff --git a/adminui.go b/adminui.go index 77b5cd33..7bb32b34 100644 --- a/adminui.go +++ b/adminui.go @@ -186,7 +186,7 @@ bg-warning {{end}} - Run + Run {{end}} diff --git a/toolbox/task.go b/toolbox/task.go index b50c54fb..23e2fb5e 100644 --- a/toolbox/task.go +++ b/toolbox/task.go @@ -84,6 +84,7 @@ type TaskFunc func() error // task interface type Tasker interface { + GetSpec() string GetStatus() string Run() error SetNext(time.Time) @@ -102,6 +103,7 @@ type taskerr struct { type Task struct { Taskname string Spec *Schedule + SpecStr string DoFunc TaskFunc Prev time.Time Next time.Time @@ -116,16 +118,22 @@ func NewTask(tname string, spec string, f TaskFunc) *Task { Taskname: tname, DoFunc: f, ErrLimit: 100, + SpecStr: spec, } task.SetCron(spec) return task } +//get spec string +func (s *Task) GetSpec() string { + return s.SpecStr +} + // get current task status func (tk *Task) GetStatus() string { var str string for _, v := range tk.Errlist { - str += v.t.String() + ":" + v.errinfo + "\n" + str += v.t.String() + ":" + v.errinfo + "
" } return str } From 433e8f2ce3197e9fd49000d7d60c4a11029020b7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 14 Feb 2015 20:40:43 +0800 Subject: [PATCH 25/39] Revert "Add GroupBy to QuerySeter" --- orm/db.go | 9 +++------ orm/db_tables.go | 24 ------------------------ orm/orm_queryset.go | 7 ------- orm/orm_test.go | 21 --------------------- orm/types.go | 1 - 5 files changed, 3 insertions(+), 59 deletions(-) diff --git a/orm/db.go b/orm/db.go index 677a20ef..10f65fee 100644 --- a/orm/db.go +++ b/orm/db.go @@ -800,7 +800,6 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi tables.parseRelated(qs.related, qs.relDepth) where, args := tables.getCondSql(cond, false, tz) - groupBy := tables.getGroupSql(qs.groups) orderBy := tables.getOrderSql(qs.orders) limit := tables.getLimitSql(mi, offset, rlimit) join := tables.getJoinSql() @@ -813,7 +812,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi } } - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s%s", sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) + query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s", sels, Q, mi.table, Q, join, where, orderBy, limit) d.ins.ReplaceMarks(&query) @@ -937,13 +936,12 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition tables.parseRelated(qs.related, qs.relDepth) where, args := tables.getCondSql(cond, false, tz) - groupBy := tables.getGroupSql(qs.groups) tables.getOrderSql(qs.orders) join := tables.getJoinSql() Q := d.ins.TableQuote() - query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s%s", Q, mi.table, Q, join, where, groupBy) + query := fmt.Sprintf("SELECT COUNT(*) FROM %s%s%s T0 %s%s", Q, mi.table, Q, join, where) d.ins.ReplaceMarks(&query) @@ -1444,14 +1442,13 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond } where, args := tables.getCondSql(cond, false, tz) - groupBy := tables.getGroupSql(qs.groups) orderBy := tables.getOrderSql(qs.orders) limit := tables.getLimitSql(mi, qs.offset, qs.limit) join := tables.getJoinSql() sels := strings.Join(cols, ", ") - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s%s", sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) + query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s", sels, Q, mi.table, Q, join, where, orderBy, limit) d.ins.ReplaceMarks(&query) diff --git a/orm/db_tables.go b/orm/db_tables.go index 3677932a..a9aa10ab 100644 --- a/orm/db_tables.go +++ b/orm/db_tables.go @@ -390,30 +390,6 @@ func (t *dbTables) getCondSql(cond *Condition, sub bool, tz *time.Location) (whe return } -// generate group sql. -func (t *dbTables) getGroupSql(groups []string) (groupSql string) { - if len(groups) == 0 { - return - } - - Q := t.base.TableQuote() - - groupSqls := make([]string, 0, len(groups)) - for _, group := range groups { - exprs := strings.Split(group, ExprSep) - - index, _, fi, suc := t.parseExprs(t.mi, exprs) - if suc == false { - panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) - } - - groupSqls = append(groupSqls, fmt.Sprintf("%s.%s%s%s", index, Q, fi.column, Q)) - } - - groupSql = fmt.Sprintf("GROUP BY %s ", strings.Join(groupSqls, ", ")) - return -} - // generate order sql. func (t *dbTables) getOrderSql(orders []string) (orderSql string) { if len(orders) == 0 { diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index 26e82379..4f5d5485 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -60,7 +60,6 @@ type querySet struct { relDepth int limit int64 offset int64 - groups []string orders []string orm *orm } @@ -106,12 +105,6 @@ func (o querySet) Offset(offset interface{}) QuerySeter { return &o } -// add GROUP expression. -func (o querySet) GroupBy(exprs ...string) QuerySeter { - o.groups = exprs - return &o -} - // add ORDER expression. // "column" means ASC, "-column" means DESC. func (o querySet) OrderBy(exprs ...string) QuerySeter { diff --git a/orm/orm_test.go b/orm/orm_test.go index c223a463..e1c8e0f0 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -845,27 +845,6 @@ func TestOffset(t *testing.T) { throwFail(t, AssertIs(num, 2)) } -func TestGroupBy(t *testing.T) { - var users []*User - var maps []Params - qs := dORM.QueryTable("user") - num, err := qs.GroupBy("is_staff").Filter("user_name", "nobody").Count() - throwFail(t, err) - throwFail(t, AssertIs(num, 1)) - - num, err = qs.GroupBy("is_staff").Count() - throwFail(t, err) - throwFail(t, AssertIs(num, 2)) - - num, err = qs.GroupBy("is_staff").Values(&maps) - throwFail(t, err) - throwFail(t, AssertIs(num, 2)) - - num, err = qs.GroupBy("profile__age").Filter("user_name", "astaxie").All(&users) - throwFail(t, err) - throwFail(t, AssertIs(num, 1)) -} - func TestOrderBy(t *testing.T) { qs := dORM.QueryTable("user") num, err := qs.OrderBy("-status").Filter("user_name", "nobody").Count() diff --git a/orm/types.go b/orm/types.go index d308642b..c342e1c2 100644 --- a/orm/types.go +++ b/orm/types.go @@ -67,7 +67,6 @@ type QuerySeter interface { SetCond(*Condition) QuerySeter Limit(interface{}, ...interface{}) QuerySeter Offset(interface{}) QuerySeter - GroupBy(...string) QuerySeter OrderBy(...string) QuerySeter RelatedSel(...interface{}) QuerySeter Count() (int64, error) From f988f035e5da9afa389f7e51ef7c25944907d524 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 16 Feb 2015 21:56:32 +0800 Subject: [PATCH 26/39] redis provider for session and cache support select db --- cache/redis/redis.go | 14 ++++++++++++-- session/redis/sess_redis.go | 20 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/cache/redis/redis.go b/cache/redis/redis.go index b205545d..0e07eaed 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -32,6 +32,7 @@ package redis import ( "encoding/json" "errors" + "strconv" "time" "github.com/garyburd/redigo/redis" @@ -48,6 +49,7 @@ var ( type RedisCache struct { p *redis.Pool // redis connection pool conninfo string + dbNum int key string } @@ -137,7 +139,7 @@ func (rc *RedisCache) ClearAll() error { } // start redis cache adapter. -// config is like {"key":"collection key","conn":"connection info"} +// config is like {"key":"collection key","conn":"connection info","dbNum":"0"} // the cache item in redis are stored forever, // so no gc operation. func (rc *RedisCache) StartAndGC(config string) error { @@ -151,9 +153,12 @@ func (rc *RedisCache) StartAndGC(config string) error { if _, ok := cf["conn"]; !ok { return errors.New("config has no conn key") } - + if _, ok := cf["dbNum"]; !ok { + cf["dbNum"] = "0" + } rc.key = cf["key"] rc.conninfo = cf["conn"] + rc.dbNum, _ = strconv.Atoi(cf["dbNum"]) rc.connectInit() c := rc.p.Get() @@ -166,6 +171,11 @@ func (rc *RedisCache) StartAndGC(config string) error { func (rc *RedisCache) connectInit() { dialFunc := func() (c redis.Conn, err error) { c, err = redis.Dial("tcp", rc.conninfo) + _, selecterr := c.Do("SELECT", rc.dbNum) + if selecterr != nil { + c.Close() + return nil, selecterr + } return } // initialize a new pool diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index 82cdd812..887fb520 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -118,12 +118,13 @@ type RedisProvider struct { savePath string poolsize int password string + dbNum int poollist *redis.Pool } // init redis session -// savepath like redis server addr,pool size,password -// e.g. 127.0.0.1:6379,100,astaxie +// savepath like redis server addr,pool size,password,dbnum +// e.g. 127.0.0.1:6379,100,astaxie,0 func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { rp.maxlifetime = maxlifetime configs := strings.Split(savePath, ",") @@ -143,6 +144,16 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { if len(configs) > 2 { rp.password = configs[2] } + if len(configs) > 3 { + dbnum, err := strconv.Atoi(configs[1]) + if err != nil || dbnum < 0 { + rp.dbNum = 0 + } else { + rp.dbNum = dbnum + } + } else { + rp.dbNum = 0 + } rp.poollist = redis.NewPool(func() (redis.Conn, error) { c, err := redis.Dial("tcp", rp.savePath) if err != nil { @@ -154,6 +165,11 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { return nil, err } } + _, err = c.Do("SELECT", rp.dbNum) + if err != nil { + c.Close() + return nil, err + } return c, err }, rp.poolsize) From 0c31c2d689f3a35c9a2c9651914f05fe7f590be8 Mon Sep 17 00:00:00 2001 From: Sergey Shcherbina Date: Sun, 22 Feb 2015 22:13:06 +0500 Subject: [PATCH 27/39] Added support to parse slices of ints and strings in ParseForm func --- templatefunc.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/templatefunc.go b/templatefunc.go index 1224e82f..28ed15a3 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -358,11 +358,32 @@ func ParseForm(form url.Values, obj interface{}) error { } fieldV.Set(reflect.ValueOf(t)) } + case reflect.Slice: + if fieldT.Type == sliceOfInts { + formVals := form[tag] + fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals))) + for i := 0; i < len(formVals); i++ { + val, err := strconv.Atoi(formVals[i]) + if err != nil { + return err + } + fieldV.Index(i).SetInt(int64(val)) + } + } else if fieldT.Type == sliceOfStrings { + formVals := form[tag] + fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals))) + for i := 0; i < len(formVals); i++ { + fieldV.Index(i).SetString(formVals[i]) + } + } } } return nil } +var sliceOfInts = reflect.TypeOf([]int(nil)) +var sliceOfStrings = reflect.TypeOf([]string(nil)) + var unKind = map[reflect.Kind]bool{ reflect.Uintptr: true, reflect.Complex64: true, From 24cf06d2885fcdd5c732694150d25c6021500072 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Feb 2015 11:15:55 +0800 Subject: [PATCH 28/39] code style simplify for context package --- context/context.go | 5 ++++- context/input.go | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/context/context.go b/context/context.go index 89b5ffe4..b60650a8 100644 --- a/context/context.go +++ b/context/context.go @@ -155,8 +155,11 @@ func (ctx *Context) CheckXsrfCookie() bool { } if token == "" { ctx.Abort(403, "'_xsrf' argument missing from POST") - } else if ctx._xsrf_token != token { + return false + } + if ctx._xsrf_token != token { ctx.Abort(403, "XSRF cookie does not match POST argument") + return false } return true } diff --git a/context/input.go b/context/input.go index d9aa2634..f535e6a2 100644 --- a/context/input.go +++ b/context/input.go @@ -72,11 +72,11 @@ func (input *BeegoInput) Site() string { func (input *BeegoInput) Scheme() string { if input.Request.URL.Scheme != "" { return input.Request.URL.Scheme - } else if input.Request.TLS == nil { - return "http" - } else { - return "https" } + if input.Request.TLS == nil { + return "http" + } + return "https" } // Domain returns host name. From 29d4823866f78cfc16bf719ab8a407816cebf479 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Feb 2015 11:30:59 +0800 Subject: [PATCH 29/39] code simplify for package httplib --- httplib/httplib.go | 66 ++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 37ba3b33..7ff2f1d2 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -253,30 +253,20 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { return b } -func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { - if b.resp.StatusCode != 0 { - return b.resp, nil - } - var paramBody string - if len(b.params) > 0 { - var buf bytes.Buffer - for k, v := range b.params { - buf.WriteString(url.QueryEscape(k)) - buf.WriteByte('=') - buf.WriteString(url.QueryEscape(v)) - buf.WriteByte('&') - } - paramBody = buf.String() - paramBody = paramBody[0 : len(paramBody)-1] - } - +func (b *BeegoHttpRequest) buildUrl(paramBody string) { + // build GET url with query string if b.req.Method == "GET" && len(paramBody) > 0 { if strings.Index(b.url, "?") != -1 { b.url += "&" + paramBody } else { b.url = b.url + "?" + paramBody } - } else if b.req.Method == "POST" && b.req.Body == nil { + return + } + + // build POST url and body + if b.req.Method == "POST" && b.req.Body == nil { + // with files if len(b.files) > 0 { pr, pw := io.Pipe() bodyWriter := multipart.NewWriter(pw) @@ -305,12 +295,35 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { }() b.Header("Content-Type", bodyWriter.FormDataContentType()) b.req.Body = ioutil.NopCloser(pr) - } else if len(paramBody) > 0 { + return + } + + // with params + if len(paramBody) > 0 { b.Header("Content-Type", "application/x-www-form-urlencoded") b.Body(paramBody) } } +} +func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { + if b.resp.StatusCode != 0 { + return b.resp, nil + } + var paramBody string + if len(b.params) > 0 { + var buf bytes.Buffer + for k, v := range b.params { + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v)) + buf.WriteByte('&') + } + paramBody = buf.String() + paramBody = paramBody[0 : len(paramBody)-1] + } + + b.buildUrl(paramBody) url, err := url.Parse(b.url) if err != nil { return nil, err @@ -342,14 +355,12 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } } - var jar http.CookieJar + var jar http.CookieJar = nil if b.setting.EnableCookie { if defaultCookieJar == nil { createDefaultCookie() } jar = defaultCookieJar - } else { - jar = nil } client := &http.Client{ @@ -402,12 +413,11 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { return nil, nil } defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) + b.body, err = ioutil.ReadAll(resp.Body) if err != nil { return nil, err } - b.body = data - return data, nil + return b.body, nil } // ToFile saves the body data in response to one file. @@ -438,8 +448,7 @@ func (b *BeegoHttpRequest) ToJson(v interface{}) error { if err != nil { return err } - err = json.Unmarshal(data, v) - return err + return json.Unmarshal(data, v) } // ToXml returns the map that marshals from the body bytes as xml in response . @@ -449,8 +458,7 @@ func (b *BeegoHttpRequest) ToXml(v interface{}) error { if err != nil { return err } - err = xml.Unmarshal(data, v) - return err + return xml.Unmarshal(data, v) } // Response executes request client gets response mannually. From 77c110913424f49afadbafef7180ec106300e1fb Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Feb 2015 11:42:46 +0800 Subject: [PATCH 30/39] code simplify for package logs --- logs/conn.go | 11 +++-------- logs/console.go | 18 ++++++++---------- logs/file.go | 9 ++------- logs/log.go | 4 ++-- logs/smtp.go | 7 +++---- 5 files changed, 18 insertions(+), 31 deletions(-) diff --git a/logs/conn.go b/logs/conn.go index 612634fa..2240eece 100644 --- a/logs/conn.go +++ b/logs/conn.go @@ -43,11 +43,7 @@ func NewConn() LoggerInterface { // init connection writer with json config. // json config only need key "level". func (c *ConnWriter) Init(jsonconfig string) error { - err := json.Unmarshal([]byte(jsonconfig), c) - if err != nil { - return err - } - return nil + return json.Unmarshal([]byte(jsonconfig), c) } // write message in connection. @@ -77,10 +73,9 @@ func (c *ConnWriter) Flush() { // destroy connection writer and close tcp listener. func (c *ConnWriter) Destroy() { - if c.innerWriter == nil { - return + if c.innerWriter != nil { + c.innerWriter.Close() } - c.innerWriter.Close() } func (c *ConnWriter) connect() error { diff --git a/logs/console.go b/logs/console.go index 461291c2..ce7ecd54 100644 --- a/logs/console.go +++ b/logs/console.go @@ -50,9 +50,10 @@ 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.Level = LevelDebug + cw := &ConsoleWriter{ + lg: log.New(os.Stdout, "", log.Ldate|log.Ltime), + Level: LevelDebug, + } return cw } @@ -62,11 +63,7 @@ func (c *ConsoleWriter) Init(jsonconfig string) error { if len(jsonconfig) == 0 { return nil } - err := json.Unmarshal([]byte(jsonconfig), c) - if err != nil { - return err - } - return nil + return json.Unmarshal([]byte(jsonconfig), c) } // write message in console. @@ -76,9 +73,10 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error { } if goos := runtime.GOOS; goos == "windows" { c.lg.Println(msg) - } else { - c.lg.Println(colors[level](msg)) + return nil } + c.lg.Println(colors[level](msg)) + return nil } diff --git a/logs/file.go b/logs/file.go index e237170b..2d3449ce 100644 --- a/logs/file.go +++ b/logs/file.go @@ -123,11 +123,7 @@ func (w *FileLogWriter) startLogger() error { return err } w.mw.SetFd(fd) - err = w.initFd() - if err != nil { - return err - } - return nil + return w.initFd() } func (w *FileLogWriter) docheck(size int) { @@ -170,14 +166,13 @@ func (w *FileLogWriter) initFd() error { } w.maxsize_cursize = int(finfo.Size()) w.daily_opendate = time.Now().Day() + w.maxlines_curlines = 0 if finfo.Size() > 0 { count, err := w.lines() if err != nil { return err } w.maxlines_curlines = count - } else { - w.maxlines_curlines = 0 } return nil } diff --git a/logs/log.go b/logs/log.go index 6abfb005..32e0187c 100644 --- a/logs/log.go +++ b/logs/log.go @@ -292,9 +292,9 @@ func (bl *BeeLogger) Close() { fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err) } } - } else { - break + continue } + break } for _, l := range bl.outputs { l.Flush() diff --git a/logs/smtp.go b/logs/smtp.go index 19a0f510..95123ebf 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -25,7 +25,8 @@ import ( ) const ( - subjectPhrase = "Diagnostic message from server" +// no usage +// subjectPhrase = "Diagnostic message from server" ) // smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server. @@ -146,9 +147,7 @@ func (s *SmtpWriter) WriteMsg(msg string, level int) error { mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress + ">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg) - err := s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg) - - return err + return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg) } // implementing method. empty. From 2ed272aeb27cef6f73b8a9e78df409334c61b419 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Feb 2015 11:50:13 +0800 Subject: [PATCH 31/39] code simplify for package middleware --- middleware/error.go | 24 ++++++++++++------------ middleware/i18n.go | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/middleware/error.go b/middleware/error.go index 052e977c..16553543 100644 --- a/middleware/error.go +++ b/middleware/error.go @@ -323,17 +323,17 @@ func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg strin w.WriteHeader(isint) h(w, r) return - } else { - isint, err := strconv.Atoi(errcode) - if err != nil { - isint = 500 - } - if isint == 404 { - msg = "404 page not found" - } - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(isint) - fmt.Fprintln(w, msg) - return } + isint, err := strconv.Atoi(errcode) + if err != nil { + isint = 500 + } + if isint == 404 { + msg = "404 page not found" + } + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.WriteHeader(isint) + fmt.Fprintln(w, msg) + return + } diff --git a/middleware/i18n.go b/middleware/i18n.go index e4dab693..f54b4bb5 100644 --- a/middleware/i18n.go +++ b/middleware/i18n.go @@ -34,7 +34,6 @@ type Translation struct { } func NewLocale(filepath string, defaultlocal string) *Translation { - i18n := make(map[string]map[string]string) file, err := os.Open(filepath) if err != nil { panic("open " + filepath + " err :" + err.Error()) @@ -43,8 +42,9 @@ func NewLocale(filepath string, defaultlocal string) *Translation { if err != nil { panic("read " + filepath + " err :" + err.Error()) } - err = json.Unmarshal(data, &i18n) - if err != nil { + + i18n := make(map[string]map[string]string) + if err = json.Unmarshal(data, &i18n); err != nil { panic("json.Unmarshal " + filepath + " err :" + err.Error()) } return &Translation{ From 181a7c35fe9bfd9a02b6fa1c4d708e29ee09b785 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Mon, 23 Feb 2015 11:50:45 +0800 Subject: [PATCH 32/39] code simplify for package middleware --- middleware/error.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/middleware/error.go b/middleware/error.go index 16553543..00cf9de5 100644 --- a/middleware/error.go +++ b/middleware/error.go @@ -334,6 +334,4 @@ func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg strin w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(isint) fmt.Fprintln(w, msg) - return - } From 3aceaf88389ec677fa170f19806f0eb9143ebace Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 26 Feb 2015 23:34:43 +0800 Subject: [PATCH 33/39] error support controller --- beego.go | 14 +-- config.go | 1 + context/context.go | 16 +--- middleware/error.go => error.go | 153 +++++++++++++++++++++++--------- middleware/exceptions.go | 49 ---------- namespace.go | 3 +- router.go | 98 ++++++++++---------- staticfile.go | 3 +- 8 files changed, 167 insertions(+), 170 deletions(-) rename middleware/error.go => error.go (71%) delete mode 100644 middleware/exceptions.go diff --git a/beego.go b/beego.go index ab70a182..ba28855d 100644 --- a/beego.go +++ b/beego.go @@ -33,7 +33,6 @@ import ( "strconv" "strings" - "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/session" ) @@ -280,15 +279,6 @@ func Handler(rootpath string, h http.Handler, options ...interface{}) *App { return BeeApp } -// ErrorHandler registers http.HandlerFunc to each http err code string. -// usage: -// beego.ErrorHandler("404",NotFound) -// beego.ErrorHandler("500",InternalServerError) -func Errorhandler(err string, h http.HandlerFunc) *App { - middleware.Errorhandler(err, h) - return BeeApp -} - // SetViewsPath sets view directory path in beego application. func SetViewsPath(path string) *App { ViewsPath = path @@ -402,9 +392,7 @@ func initBeforeHttpRun() { } } - middleware.VERSION = VERSION - middleware.AppName = AppName - middleware.RegisterErrorHandler() + registerDefaultErrorHandler() if EnableDocs { Get("/docs", serverDocs) diff --git a/config.go b/config.go index 5210ea93..f326ad22 100644 --- a/config.go +++ b/config.go @@ -81,6 +81,7 @@ var ( AppConfigProvider string // config provider EnableDocs bool // enable generate docs & server docs API Swagger RouterCaseSensitive bool // router case sensitive default is true + AccessLogs bool // print access logs, default is false ) type beegoAppConfig struct { diff --git a/context/context.go b/context/context.go index b60650a8..0cb8df6d 100644 --- a/context/context.go +++ b/context/context.go @@ -31,7 +31,7 @@ import ( "strings" "time" - "github.com/astaxie/beego/middleware" + "github.com/astaxie/beego" "github.com/astaxie/beego/utils" ) @@ -53,24 +53,16 @@ func (ctx *Context) Redirect(status int, localurl string) { } // Abort stops this request. -// if middleware.ErrorMaps exists, panic body. -// if middleware.HTTPExceptionMaps exists, panic HTTPException struct with status and body string. +// if beego.ErrorMaps exists, panic body. func (ctx *Context) Abort(status int, body string) { ctx.ResponseWriter.WriteHeader(status) // first panic from ErrorMaps, is is user defined error functions. - if _, ok := middleware.ErrorMaps[body]; ok { + if _, ok := beego.ErrorMaps[body]; ok { panic(body) } - // second panic from HTTPExceptionMaps, it is system defined functions. - if e, ok := middleware.HTTPExceptionMaps[status]; ok { - if len(body) >= 1 { - e.Description = body - } - panic(e) - } // last panic user string ctx.ResponseWriter.Write([]byte(body)) - panic("User stop run") + panic(beego.USERSTOPRUN) } // Write string to response body. diff --git a/middleware/error.go b/error.go similarity index 71% rename from middleware/error.go rename to error.go index 00cf9de5..211c54e4 100644 --- a/middleware/error.go +++ b/error.go @@ -12,20 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package middleware +package beego import ( "fmt" "html/template" "net/http" + "reflect" "runtime" "strconv" + "strings" + + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/utils" ) -var ( - AppName string - VERSION string +const ( + errorTypeHandler = iota + errorTypeController ) + var tpl = ` @@ -76,18 +82,18 @@ var tpl = ` ` // render default application error page with error and stack string. -func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack string) { +func showErr(err interface{}, ctx *context.Context, Stack string) { t, _ := template.New("beegoerrortemp").Parse(tpl) data := make(map[string]string) data["AppError"] = AppName + ":" + fmt.Sprint(err) - data["RequestMethod"] = r.Method - data["RequestURL"] = r.RequestURI - data["RemoteAddr"] = r.RemoteAddr + data["RequestMethod"] = ctx.Input.Method() + data["RequestURL"] = ctx.Input.Uri() + data["RemoteAddr"] = ctx.Input.IP() data["Stack"] = Stack data["BeegoVersion"] = VERSION data["GoVersion"] = runtime.Version() - rw.WriteHeader(500) - t.Execute(rw, data) + ctx.Output.SetStatus(500) + t.Execute(ctx.ResponseWriter, data) } var errtpl = ` @@ -190,11 +196,18 @@ var errtpl = ` ` +type errorInfo struct { + controllerType reflect.Type + handler http.HandlerFunc + method string + errorType int +} + // map of http handlers for each error string. -var ErrorMaps map[string]http.HandlerFunc +var ErrorMaps map[string]*errorInfo func init() { - ErrorMaps = make(map[string]http.HandlerFunc) + ErrorMaps = make(map[string]*errorInfo) } // show 404 notfound error. @@ -283,55 +296,115 @@ func SimpleServerError(rw http.ResponseWriter, r *http.Request) { http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } -// add http handler for given error string. -func Errorhandler(err string, h http.HandlerFunc) { - ErrorMaps[err] = h -} - // register default error http handlers, 404,401,403,500 and 503. -func RegisterErrorHandler() { +func registerDefaultErrorHandler() { if _, ok := ErrorMaps["404"]; !ok { - ErrorMaps["404"] = NotFound + Errorhandler("404", NotFound) } if _, ok := ErrorMaps["401"]; !ok { - ErrorMaps["401"] = Unauthorized + Errorhandler("401", Unauthorized) } if _, ok := ErrorMaps["403"]; !ok { - ErrorMaps["403"] = Forbidden + Errorhandler("403", Forbidden) } if _, ok := ErrorMaps["503"]; !ok { - ErrorMaps["503"] = ServiceUnavailable + Errorhandler("503", ServiceUnavailable) } if _, ok := ErrorMaps["500"]; !ok { - ErrorMaps["500"] = InternalServerError + Errorhandler("500", InternalServerError) } } +// ErrorHandler registers http.HandlerFunc to each http err code string. +// usage: +// beego.ErrorHandler("404",NotFound) +// beego.ErrorHandler("500",InternalServerError) +func Errorhandler(code string, h http.HandlerFunc) *App { + errinfo := &errorInfo{} + errinfo.errorType = errorTypeHandler + errinfo.handler = h + errinfo.method = code + ErrorMaps[code] = errinfo + return BeeApp +} + +// ErrorController registers ControllerInterface to each http err code string. +// usage: +// beego.ErrorHandler(&controllers.ErrorController{}) +func ErrorController(c ControllerInterface) *App { + reflectVal := reflect.ValueOf(c) + rt := reflectVal.Type() + ct := reflect.Indirect(reflectVal).Type() + for i := 0; i < rt.NumMethod(); i++ { + if !utils.InSlice(rt.Method(i).Name, exceptMethod) && strings.HasPrefix(rt.Method(i).Name, "Error") { + errinfo := &errorInfo{} + errinfo.errorType = errorTypeController + errinfo.controllerType = ct + errinfo.method = rt.Method(i).Name + errname := strings.TrimPrefix(rt.Method(i).Name, "Error") + ErrorMaps[errname] = errinfo + } + } + return BeeApp +} + // show error string as simple text message. // if error string is empty, show 500 error as default. -func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg string) { +func exception(errcode string, ctx *context.Context) { + code, err := strconv.Atoi(errcode) + if err != nil { + code = 503 + } + ctx.ResponseWriter.WriteHeader(code) if h, ok := ErrorMaps[errcode]; ok { - isint, err := strconv.Atoi(errcode) - if err != nil { - isint = 500 - } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(isint) - h(w, r) + executeError(h, ctx) + return + } else if h, ok := ErrorMaps["503"]; ok { + executeError(h, ctx) + return + } else { + ctx.WriteString(errcode) + } +} + +func executeError(err *errorInfo, ctx *context.Context) { + if err.errorType == errorTypeHandler { + err.handler(ctx.ResponseWriter, ctx.Request) return } - isint, err := strconv.Atoi(errcode) - if err != nil { - isint = 500 + if err.errorType == errorTypeController { + //Invoke the request handler + vc := reflect.New(err.controllerType) + execController, ok := vc.Interface().(ControllerInterface) + if !ok { + panic("controller is not ControllerInterface") + } + //call the controller init function + execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface()) + + //call prepare function + execController.Prepare() + + execController.URLMapping() + + in := make([]reflect.Value, 0) + method := vc.MethodByName(err.method) + method.Call(in) + + //render template + if ctx.Output.Status == 0 { + if AutoRender { + if err := execController.Render(); err != nil { + panic(err) + } + } + } + + // finish all runrouter. release resource + execController.Finish() } - if isint == 404 { - msg = "404 page not found" - } - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(isint) - fmt.Fprintln(w, msg) } diff --git a/middleware/exceptions.go b/middleware/exceptions.go deleted file mode 100644 index a08a7358..00000000 --- a/middleware/exceptions.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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 middleware - -import "fmt" - -// http exceptions -type HTTPException struct { - StatusCode int // http status code 4xx, 5xx - Description string -} - -// return http exception error string, e.g. "400 Bad Request". -func (e *HTTPException) Error() string { - return fmt.Sprintf("%d %s", e.StatusCode, e.Description) -} - -// map of http exceptions for each http status code int. -// defined 400,401,403,404,405,500,502,503 and 504 default. -var HTTPExceptionMaps map[int]HTTPException - -func init() { - HTTPExceptionMaps = make(map[int]HTTPException) - - // Normal 4XX HTTP Status - HTTPExceptionMaps[400] = HTTPException{400, "Bad Request"} - HTTPExceptionMaps[401] = HTTPException{401, "Unauthorized"} - HTTPExceptionMaps[403] = HTTPException{403, "Forbidden"} - HTTPExceptionMaps[404] = HTTPException{404, "Not Found"} - HTTPExceptionMaps[405] = HTTPException{405, "Method Not Allowed"} - - // Normal 5XX HTTP Status - HTTPExceptionMaps[500] = HTTPException{500, "Internal Server Error"} - HTTPExceptionMaps[502] = HTTPException{502, "Bad Gateway"} - HTTPExceptionMaps[503] = HTTPException{503, "Service Unavailable"} - HTTPExceptionMaps[504] = HTTPException{504, "Gateway Timeout"} -} diff --git a/namespace.go b/namespace.go index 4e2632e5..ebb7c14f 100644 --- a/namespace.go +++ b/namespace.go @@ -19,7 +19,6 @@ import ( "strings" beecontext "github.com/astaxie/beego/context" - "github.com/astaxie/beego/middleware" ) type namespaceCond func(*beecontext.Context) bool @@ -57,7 +56,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace { func (n *Namespace) Cond(cond namespaceCond) *Namespace { fn := func(ctx *beecontext.Context) { if !cond(ctx) { - middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed") + exception("405", ctx) } } if v, ok := n.handlers.filters[BeforeRouter]; ok { diff --git a/router.go b/router.go index fadf2ec3..b9d649a2 100644 --- a/router.go +++ b/router.go @@ -30,7 +30,6 @@ import ( "time" beecontext "github.com/astaxie/beego/context" - "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" ) @@ -577,7 +576,6 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin // Implement http.Handler interface. func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { - defer p.recoverPanic(rw, r) starttime := time.Now() var runrouter reflect.Type var findrouter bool @@ -600,6 +598,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Output.Context = context context.Output.EnableGzip = EnableGzip + defer p.recoverPanic(context) + var urlPath string if !RouterCaseSensitive { urlPath = strings.ToLower(r.URL.Path) @@ -648,7 +648,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Input.CruSession, err = GlobalSessions.SessionStart(w, r) if err != nil { Error(err) - middleware.Exception("503", rw, r, "") + exception("503", context) return } defer func() { @@ -703,7 +703,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) //if no matches to url, throw a not found exception if !findrouter { - middleware.Exception("404", rw, r, "") + exception("404", context) goto Admin } @@ -719,7 +719,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) isRunable = true routerInfo.runfunction(context) } else { - middleware.Exception("405", rw, r, "Method Not Allowed") + exception("405", context) goto Admin } } else if routerInfo.routerType == routerTypeHandler { @@ -830,7 +830,7 @@ Admin: } } - if RunMode == "dev" { + if RunMode == "dev" || AccessLogs { var devinfo string if findrouter { if routerInfo != nil { @@ -852,27 +852,51 @@ Admin: } } -func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Request) { +func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { if err := recover(); err != nil { if err == USERSTOPRUN { return } - if he, ok := err.(middleware.HTTPException); ok { - rw.Write([]byte(he.Description)) - // catch intented errors, only for HTTP 4XX and 5XX - } else { - if RunMode == "dev" { - if !RecoverPanic { - panic(err) - } else { - if ErrorsShow { - if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok { - handler(rw, r) - return - } + if RunMode == "dev" { + if !RecoverPanic { + panic(err) + } else { + if ErrorsShow { + if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok { + executeError(handler, context) + return } - var stack string - Critical("the request url is ", r.URL.Path) + } + var stack string + Critical("the request url is ", context.Input.Url()) + Critical("Handler crashed with error", err) + for i := 1; ; i++ { + _, file, line, ok := runtime.Caller(i) + if !ok { + break + } + Critical(fmt.Sprintf("%s:%d", file, line)) + stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) + } + showErr(err, context, stack) + } + } else { + if !RecoverPanic { + panic(err) + } else { + // in production model show all infomation + if ErrorsShow { + if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok { + executeError(handler, context) + return + } else if handler, ok := ErrorMaps["503"]; ok { + executeError(handler, context) + return + } else { + context.WriteString(fmt.Sprint(err)) + } + } else { + Critical("the request url is ", context.Input.Url()) Critical("Handler crashed with error", err) for i := 1; ; i++ { _, file, line, ok := runtime.Caller(i) @@ -880,39 +904,9 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques break } Critical(fmt.Sprintf("%s:%d", file, line)) - stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) - } - middleware.ShowErr(err, rw, r, stack) - } - } else { - if !RecoverPanic { - panic(err) - } else { - // in production model show all infomation - if ErrorsShow { - 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) - Critical("Handler crashed with error", err) - for i := 1; ; i++ { - _, file, line, ok := runtime.Caller(i) - if !ok { - break - } - Critical(fmt.Sprintf("%s:%d", file, line)) - } } } } - } } } diff --git a/staticfile.go b/staticfile.go index d9855064..5ab853a3 100644 --- a/staticfile.go +++ b/staticfile.go @@ -22,7 +22,6 @@ import ( "strings" "github.com/astaxie/beego/context" - "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/utils" ) @@ -67,7 +66,7 @@ func serverStaticRouter(ctx *context.Context) { //if the request is dir and DirectoryIndex is false then if finfo.IsDir() { if !DirectoryIndex { - middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") + exception("403", ctx) 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) From 6e9d2dc965da9cc992c96beac0654c2f2e9d0e04 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 26 Feb 2015 23:49:24 +0800 Subject: [PATCH 34/39] add more error functions --- error.go | 162 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 121 insertions(+), 41 deletions(-) diff --git a/error.go b/error.go index 211c54e4..71be6916 100644 --- a/error.go +++ b/error.go @@ -210,8 +210,54 @@ func init() { ErrorMaps = make(map[string]*errorInfo) } +// show 401 unauthorized error. +func unauthorized(rw http.ResponseWriter, r *http.Request) { + t, _ := template.New("beegoerrortemp").Parse(errtpl) + data := make(map[string]interface{}) + data["Title"] = "Unauthorized" + data["Content"] = template.HTML("
The page you have requested can't be authorized." + + "
Perhaps you are here because:" + + "

    " + + "
    The credentials you supplied are incorrect" + + "
    There are errors in the website address" + + "
") + data["BeegoVersion"] = VERSION + t.Execute(rw, data) +} + +// show 402 Payment Required +func paymentRequired(rw http.ResponseWriter, r *http.Request) { + t, _ := template.New("beegoerrortemp").Parse(errtpl) + data := make(map[string]interface{}) + data["Title"] = "Payment Required" + data["Content"] = template.HTML("
The page you have requested Payment Required." + + "
Perhaps you are here because:" + + "

    " + + "
    The credentials you supplied are incorrect" + + "
    There are errors in the website address" + + "
") + data["BeegoVersion"] = VERSION + t.Execute(rw, data) +} + +// show 403 forbidden error. +func forbidden(rw http.ResponseWriter, r *http.Request) { + t, _ := template.New("beegoerrortemp").Parse(errtpl) + data := make(map[string]interface{}) + data["Title"] = "Forbidden" + data["Content"] = template.HTML("
The page you have requested is forbidden." + + "
Perhaps you are here because:" + + "

    " + + "
    Your address may be blocked" + + "
    The site may be disabled" + + "
    You need to log in" + + "
") + data["BeegoVersion"] = VERSION + t.Execute(rw, data) +} + // show 404 notfound error. -func NotFound(rw http.ResponseWriter, r *http.Request) { +func notFound(rw http.ResponseWriter, r *http.Request) { t, _ := template.New("beegoerrortemp").Parse(errtpl) data := make(map[string]interface{}) data["Title"] = "Page Not Found" @@ -224,45 +270,66 @@ func NotFound(rw http.ResponseWriter, r *http.Request) { "
You like 404 pages" + "") data["BeegoVersion"] = VERSION - //rw.WriteHeader(http.StatusNotFound) t.Execute(rw, data) } -// show 401 unauthorized error. -func Unauthorized(rw http.ResponseWriter, r *http.Request) { +// show 405 Method Not Allowed +func methodNotAllowed(rw http.ResponseWriter, r *http.Request) { t, _ := template.New("beegoerrortemp").Parse(errtpl) data := make(map[string]interface{}) - data["Title"] = "Unauthorized" - data["Content"] = template.HTML("
The page you have requested can't be authorized." + + data["Title"] = "Method Not Allowed" + data["Content"] = template.HTML("
The method you have requested Not Allowed." + "
Perhaps you are here because:" + "

    " + - "
    The credentials you supplied are incorrect" + - "
    There are errors in the website address" + + "
    The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" + + "
    The response MUST include an Allow header containing a list of valid methods for the requested resource." + "
") data["BeegoVersion"] = VERSION - //rw.WriteHeader(http.StatusUnauthorized) t.Execute(rw, data) } -// show 403 forbidden error. -func Forbidden(rw http.ResponseWriter, r *http.Request) { +// show 500 internal server error. +func internalServerError(rw http.ResponseWriter, r *http.Request) { t, _ := template.New("beegoerrortemp").Parse(errtpl) data := make(map[string]interface{}) - data["Title"] = "Forbidden" - data["Content"] = template.HTML("
The page you have requested is forbidden." + - "
Perhaps you are here because:" + + data["Title"] = "Internal Server Error" + data["Content"] = template.HTML("
The page you have requested is down right now." + "

    " + - "
    Your address may be blocked" + - "
    The site may be disabled" + - "
    You need to log in" + - "
") + "
Please try again later and report the error to the website administrator" + + "
") + data["BeegoVersion"] = VERSION + t.Execute(rw, data) +} + +// show 501 Not Implemented. +func notImplemented(rw http.ResponseWriter, r *http.Request) { + t, _ := template.New("beegoerrortemp").Parse(errtpl) + data := make(map[string]interface{}) + data["Title"] = "Not Implemented" + data["Content"] = template.HTML("
The page you have requested is Not Implemented." + + "

    " + + "
    Please try again later and report the error to the website administrator" + + "
") + data["BeegoVersion"] = VERSION + t.Execute(rw, data) +} + +// show 502 Bad Gateway. +func badGateway(rw http.ResponseWriter, r *http.Request) { + t, _ := template.New("beegoerrortemp").Parse(errtpl) + data := make(map[string]interface{}) + data["Title"] = "Bad Gateway" + data["Content"] = template.HTML("
The page you have requested is down right now." + + "

    " + + "
    The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request." + + "
    Please try again later and report the error to the website administrator" + + "
") data["BeegoVersion"] = VERSION - //rw.WriteHeader(http.StatusForbidden) t.Execute(rw, data) } // show 503 service unavailable error. -func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) { +func serviceUnavailable(rw http.ResponseWriter, r *http.Request) { t, _ := template.New("beegoerrortemp").Parse(errtpl) data := make(map[string]interface{}) data["Title"] = "Service Unavailable" @@ -273,49 +340,62 @@ func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) { "
Please try again later." + "") data["BeegoVersion"] = VERSION - //rw.WriteHeader(http.StatusServiceUnavailable) t.Execute(rw, data) } -// show 500 internal server error. -func InternalServerError(rw http.ResponseWriter, r *http.Request) { +// show 504 Gateway Timeout. +func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { t, _ := template.New("beegoerrortemp").Parse(errtpl) data := make(map[string]interface{}) - data["Title"] = "Internal Server Error" - data["Content"] = template.HTML("
The page you have requested is down right now." + + data["Title"] = "Gateway Timeout" + data["Content"] = template.HTML("
The page you have requested is unavailable." + + "
Perhaps you are here because:" + "

    " + - "
    Please try again later and report the error to the website administrator" + - "
") + "

The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI." + + "
Please try again later." + + "") data["BeegoVersion"] = VERSION - //rw.WriteHeader(http.StatusInternalServerError) t.Execute(rw, data) } -// show 500 internal error with simple text string. -func SimpleServerError(rw http.ResponseWriter, r *http.Request) { - http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) -} - // register default error http handlers, 404,401,403,500 and 503. func registerDefaultErrorHandler() { - if _, ok := ErrorMaps["404"]; !ok { - Errorhandler("404", NotFound) + if _, ok := ErrorMaps["401"]; !ok { + Errorhandler("401", unauthorized) } - if _, ok := ErrorMaps["401"]; !ok { - Errorhandler("401", Unauthorized) + if _, ok := ErrorMaps["402"]; !ok { + Errorhandler("402", paymentRequired) } if _, ok := ErrorMaps["403"]; !ok { - Errorhandler("403", Forbidden) + Errorhandler("403", forbidden) } - if _, ok := ErrorMaps["503"]; !ok { - Errorhandler("503", ServiceUnavailable) + if _, ok := ErrorMaps["404"]; !ok { + Errorhandler("404", notFound) + } + + if _, ok := ErrorMaps["405"]; !ok { + Errorhandler("405", methodNotAllowed) } if _, ok := ErrorMaps["500"]; !ok { - Errorhandler("500", InternalServerError) + Errorhandler("500", internalServerError) + } + if _, ok := ErrorMaps["501"]; !ok { + Errorhandler("501", notImplemented) + } + if _, ok := ErrorMaps["502"]; !ok { + Errorhandler("502", badGateway) + } + + if _, ok := ErrorMaps["503"]; !ok { + Errorhandler("503", serviceUnavailable) + } + + if _, ok := ErrorMaps["504"]; !ok { + Errorhandler("504", gatewayTimeout) } } From e938876c4a94669996087adb00f195156cd4376f Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 27 Feb 2015 00:12:10 +0800 Subject: [PATCH 35/39] fix the cycle import --- context/context.go | 9 +-------- controller.go | 16 +++++++++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/context/context.go b/context/context.go index 0cb8df6d..f6aa85d6 100644 --- a/context/context.go +++ b/context/context.go @@ -31,7 +31,6 @@ import ( "strings" "time" - "github.com/astaxie/beego" "github.com/astaxie/beego/utils" ) @@ -56,13 +55,7 @@ func (ctx *Context) Redirect(status int, localurl string) { // if beego.ErrorMaps exists, panic body. func (ctx *Context) Abort(status int, body string) { ctx.ResponseWriter.WriteHeader(status) - // first panic from ErrorMaps, is is user defined error functions. - if _, ok := beego.ErrorMaps[body]; ok { - panic(body) - } - // last panic user string - ctx.ResponseWriter.Write([]byte(body)) - panic(beego.USERSTOPRUN) + panic(body) } // Write string to response body. diff --git a/controller.go b/controller.go index 12c73e7f..e056f52d 100644 --- a/controller.go +++ b/controller.go @@ -270,16 +270,22 @@ func (c *Controller) Redirect(url string, code int) { // Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string. func (c *Controller) Abort(code string) { status, err := strconv.Atoi(code) - if err == nil { - c.Ctx.Abort(status, code) - } else { - c.Ctx.Abort(200, code) + if err != nil { + status = 200 } + c.CustomAbort(status, code) } // CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body. func (c *Controller) CustomAbort(status int, body string) { - c.Ctx.Abort(status, body) + c.Ctx.ResponseWriter.WriteHeader(status) + // first panic from ErrorMaps, is is user defined error functions. + if _, ok := ErrorMaps[body]; ok { + panic(body) + } + // last panic user string + c.Ctx.ResponseWriter.Write([]byte(body)) + panic(USERSTOPRUN) } // StopRun makes panic of USERSTOPRUN error and go to recover function if defined. From f96a6285bf215ff9f7a3e06bda9f4d74065fc205 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 27 Feb 2015 22:21:58 +0800 Subject: [PATCH 36/39] fix #978 --- app.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app.go b/app.go index d155c531..35040f33 100644 --- a/app.go +++ b/app.go @@ -19,7 +19,10 @@ import ( "net" "net/http" "net/http/fcgi" + "os" "time" + + "github.com/astaxie/beego/utils" ) // App defines beego application with a new PatternServeMux. @@ -59,6 +62,10 @@ func (app *App) Run() { } } else { if HttpPort == 0 { + // remove the Socket file before start + if utils.FileExists(addr) { + os.Remove(addr) + } l, err = net.Listen("unix", addr) } else { l, err = net.Listen("tcp", addr) From 2a4e2d4a7149633a56888b991f1a36bea384c737 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 27 Feb 2015 22:37:07 +0800 Subject: [PATCH 37/39] delete the group route, because we already has namespace --- beego.go | 67 -------------------------------------------------------- 1 file changed, 67 deletions(-) diff --git a/beego.go b/beego.go index ba28855d..8a626eae 100644 --- a/beego.go +++ b/beego.go @@ -42,73 +42,6 @@ const VERSION = "1.4.2" type hookfunc func() error //hook function to run var hooks []hookfunc //hook function slice to store the hookfunc -type groupRouter struct { - pattern string - controller ControllerInterface - mappingMethods string -} - -// RouterGroups which will store routers -type GroupRouters []groupRouter - -// Get a new GroupRouters -func NewGroupRouters() GroupRouters { - return make(GroupRouters, 0) -} - -// Add Router in the GroupRouters -// it is for plugin or module to register router -func (gr *GroupRouters) AddRouter(pattern string, c ControllerInterface, mappingMethod ...string) { - var newRG groupRouter - if len(mappingMethod) > 0 { - newRG = groupRouter{ - pattern, - c, - mappingMethod[0], - } - } else { - newRG = groupRouter{ - pattern, - c, - "", - } - } - *gr = append(*gr, newRG) -} - -func (gr *GroupRouters) AddAuto(c ControllerInterface) { - newRG := groupRouter{ - "", - c, - "", - } - *gr = append(*gr, newRG) -} - -// AddGroupRouter with the prefix -// it will register the router in BeeApp -// the follow code is write in modules: -// GR:=NewGroupRouters() -// GR.AddRouter("/login",&UserController,"get:Login") -// GR.AddRouter("/logout",&UserController,"get:Logout") -// GR.AddRouter("/register",&UserController,"get:Reg") -// the follow code is write in app: -// import "github.com/beego/modules/auth" -// AddRouterGroup("/admin", auth.GR) -func AddGroupRouter(prefix string, groups GroupRouters) *App { - for _, v := range groups { - if v.pattern == "" { - BeeApp.Handlers.AddAutoPrefix(prefix, v.controller) - } else if v.mappingMethods != "" { - BeeApp.Handlers.Add(prefix+v.pattern, v.controller, v.mappingMethods) - } else { - BeeApp.Handlers.Add(prefix+v.pattern, v.controller) - } - - } - return BeeApp -} - // Router adds a patterned controller handler to BeeApp. // it's an alias method of App.Router. // usage: From 6d313aa15fb5a67f1e86366781e042fd35cd3429 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 27 Feb 2015 22:37:41 +0800 Subject: [PATCH 38/39] fix #985 --- orm/orm.go | 4 ---- orm/types.go | 1 - 2 files changed, 5 deletions(-) diff --git a/orm/orm.go b/orm/orm.go index cdb7f27c..f881433b 100644 --- a/orm/orm.go +++ b/orm/orm.go @@ -489,10 +489,6 @@ func (o *orm) Driver() Driver { return driver(o.alias.Name) } -func (o *orm) GetDB() dbQuerier { - panic(ErrNotImplement) -} - // create new orm func NewOrm() Ormer { BootStrap() // execute only once diff --git a/orm/types.go b/orm/types.go index c342e1c2..b46be4fc 100644 --- a/orm/types.go +++ b/orm/types.go @@ -51,7 +51,6 @@ type Ormer interface { Rollback() error Raw(string, ...interface{}) RawSeter Driver() Driver - GetDB() dbQuerier } // insert prepared statement From 3f8252bffdd356fe01a8c4bd441052fdab64f4e9 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 27 Feb 2015 22:47:21 +0800 Subject: [PATCH 39/39] change the version from 1.4.2 to 1.4.3 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 8a626eae..9c34c9f9 100644 --- a/beego.go +++ b/beego.go @@ -37,7 +37,7 @@ import ( ) // beego web framework version. -const VERSION = "1.4.2" +const VERSION = "1.4.3" type hookfunc func() error //hook function to run var hooks []hookfunc //hook function slice to store the hookfunc