From 0bcd828d7375079d5255d3bf142223e662663407 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Sun, 26 Oct 2014 22:56:00 -0700 Subject: [PATCH 01/83] add session store in ledis --- session/ledis/ledis_session.go | 168 +++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 session/ledis/ledis_session.go diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go new file mode 100644 index 00000000..3ada47ac --- /dev/null +++ b/session/ledis/ledis_session.go @@ -0,0 +1,168 @@ +package session + +import ( + "net/http" + "sync" + + "github.com/astaxie/beego/session" + "github.com/siddontang/ledisdb/config" + "github.com/siddontang/ledisdb/ledis" +) + +var ledispder = &LedisProvider{} +var c *ledis.DB + +// ledis session store +type LedisSessionStore struct { + sid string + lock sync.RWMutex + values map[interface{}]interface{} + maxlifetime int64 +} + +// set value in ledis session +func (ls *LedisSessionStore) Set(key, value interface{}) error { + ls.lock.Lock() + defer ls.lock.Unlock() + ls.values[key] = value + return nil +} + +// get value in ledis session +func (ls *LedisSessionStore) Get(key interface{}) interface{} { + ls.lock.RLock() + defer ls.lock.RUnlock() + if v, ok := ls.values[key]; ok { + return v + } else { + return nil + } +} + +// delete value in ledis session +func (ls *LedisSessionStore) Delete(key interface{}) error { + ls.lock.Lock() + defer ls.lock.Unlock() + delete(ls.values, key) + return nil +} + +// clear all values in ledis session +func (ls *LedisSessionStore) Flush() error { + ls.lock.Lock() + defer ls.lock.Unlock() + ls.values = make(map[interface{}]interface{}) + return nil +} + +// get ledis session id +func (ls *LedisSessionStore) SessionID() string { + return ls.sid +} + +// save session values to ledis +func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) { + b, err := session.EncodeGob(ls.values) + if err != nil { + return + } + c.Set([]byte(ls.sid), b) + c.Expire([]byte(ls.sid), ls.maxlifetime) +} + +// ledis session provider +type LedisProvider struct { + maxlifetime int64 + savePath string +} + +// init ledis session +// savepath like ledis server saveDataPath,pool size +// e.g. 127.0.0.1:6379,100,astaxie +func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error { + lp.maxlifetime = maxlifetime + lp.savePath = savePath + cfg := new(config.Config) + cfg.DataDir = lp.savePath + var err error + nowLedis, err := ledis.Open(cfg) + c, err = nowLedis.Select(0) + if err != nil { + println(err) + return nil + } + return nil +} + +// read ledis session by sid +func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) { + kvs, err := c.Get([]byte(sid)) + var kv map[interface{}]interface{} + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(kvs) + if err != nil { + return nil, err + } + } + ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} + return ls, nil +} + +// check ledis session exist by sid +func (lp *LedisProvider) SessionExist(sid string) bool { + count, _ := c.Exists([]byte(sid)) + if count == 0 { + return false + } else { + return true + } +} + +// generate new sid for ledis session +func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) { + count, _ := c.Exists([]byte(sid)) + if count == 0 { + // oldsid doesn't exists, set the new sid directly + // ignore error here, since if it return error + // the existed value will be 0 + c.Set([]byte(sid), []byte("")) + c.Expire([]byte(sid), lp.maxlifetime) + } else { + data, _ := c.Get([]byte(oldsid)) + c.Set([]byte(sid), data) + c.Expire([]byte(sid), lp.maxlifetime) + } + kvs, err := c.Get([]byte(sid)) + var kv map[interface{}]interface{} + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(kvs)) + if err != nil { + return nil, err + } + } + ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} + return ls, nil +} + +// delete ledis session by id +func (lp *LedisProvider) SessionDestroy(sid string) error { + c.Del([]byte(sid)) + return nil +} + +// Impelment method, no used. +func (lp *LedisProvider) SessionGC() { + return +} + +// @todo +func (lp *LedisProvider) SessionAll() int { + return 0 +} +func init() { + session.Register("ledis", ledispder) +} From 1f26852610c8cf24ff054d8e62bb7a6416140992 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 28 Oct 2014 19:33:14 +0800 Subject: [PATCH 02/83] logs:default support fileline --- log.go | 5 ----- logs/conn.go | 2 +- logs/console.go | 2 +- logs/file.go | 2 +- logs/log.go | 36 ++++++------------------------------ 5 files changed, 9 insertions(+), 38 deletions(-) diff --git a/log.go b/log.go index 5afba8ed..c159b6cc 100644 --- a/log.go +++ b/log.go @@ -38,11 +38,6 @@ func SetLevel(l int) { BeeLogger.SetLevel(l) } -func SetLogFuncCall(b bool) { - BeeLogger.EnableFuncCallDepth(b) - BeeLogger.SetLogFuncCallDepth(3) -} - // logger references the used application logger. var BeeLogger *logs.BeeLogger diff --git a/logs/conn.go b/logs/conn.go index 612634fa..e3d60eed 100644 --- a/logs/conn.go +++ b/logs/conn.go @@ -99,7 +99,7 @@ func (c *ConnWriter) connect() error { } c.innerWriter = conn - c.lg = log.New(conn, "", log.Ldate|log.Ltime) + c.lg = log.New(conn, "", log.Ldate|log.Ltime|log.Lshortfile) return nil } diff --git a/logs/console.go b/logs/console.go index 461291c2..aa34be45 100644 --- a/logs/console.go +++ b/logs/console.go @@ -51,7 +51,7 @@ type ConsoleWriter struct { // create ConsoleWriter returning as LoggerInterface. func NewConsole() LoggerInterface { cw := new(ConsoleWriter) - cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime) + cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) cw.Level = LevelDebug return cw } diff --git a/logs/file.go b/logs/file.go index 39220cc0..0381d1c1 100644 --- a/logs/file.go +++ b/logs/file.go @@ -89,7 +89,7 @@ func NewFileWriter() LoggerInterface { // use MuxWriter instead direct use os.File for lock write when rotate w.mw = new(MuxWriter) // set MuxWriter as Logger's io.Writer - w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) + w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime|log.Lshortfile) return w } diff --git a/logs/log.go b/logs/log.go index 341df572..3d66429d 100644 --- a/logs/log.go +++ b/logs/log.go @@ -34,8 +34,7 @@ package logs import ( "fmt" - "path" - "runtime" + "sync" ) @@ -88,12 +87,10 @@ func Register(name string, log loggerType) { // BeeLogger is default logger in beego application. // it can contain several providers and log message into all providers. type BeeLogger struct { - lock sync.Mutex - level int - enableFuncCallDepth bool - loggerFuncCallDepth int - msg chan *logMsg - outputs map[string]LoggerInterface + lock sync.Mutex + level int + msg chan *logMsg + outputs map[string]LoggerInterface } type logMsg struct { @@ -107,7 +104,6 @@ type logMsg struct { func NewLogger(channellen int64) *BeeLogger { bl := new(BeeLogger) bl.level = LevelDebug - bl.loggerFuncCallDepth = 2 bl.msg = make(chan *logMsg, channellen) bl.outputs = make(map[string]LoggerInterface) //bl.SetLogger("console", "") // default output to console @@ -153,17 +149,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { } lm := new(logMsg) lm.level = loglevel - if bl.enableFuncCallDepth { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if ok { - _, filename := path.Split(file) - lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) - } else { - lm.msg = msg - } - } else { - lm.msg = msg - } + lm.msg = msg bl.msg <- lm return nil } @@ -176,16 +162,6 @@ func (bl *BeeLogger) SetLevel(l int) { bl.level = l } -// set log funcCallDepth -func (bl *BeeLogger) SetLogFuncCallDepth(d int) { - bl.loggerFuncCallDepth = d -} - -// enable log funcCallDepth -func (bl *BeeLogger) EnableFuncCallDepth(b bool) { - bl.enableFuncCallDepth = b -} - // start logger chan reading. // when chan is not empty, write logs. func (bl *BeeLogger) startLogger() { From ddbfc25e56900fe03ff7c8f6d3886802f3a880dd Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 28 Oct 2014 19:34:11 +0800 Subject: [PATCH 03/83] fix the log test --- logs/console_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/logs/console_test.go b/logs/console_test.go index 2fad7241..167a007a 100644 --- a/logs/console_test.go +++ b/logs/console_test.go @@ -34,7 +34,6 @@ func testConsoleCalls(bl *BeeLogger) { // without a log level specification. func TestConsole(t *testing.T) { log1 := NewLogger(10000) - log1.EnableFuncCallDepth(true) log1.SetLogger("console", "") testConsoleCalls(log1) @@ -45,7 +44,6 @@ func TestConsole(t *testing.T) { func BenchmarkConsole(b *testing.B) { log := NewLogger(10000) - log.EnableFuncCallDepth(true) log.SetLogger("console", "") for i := 0; i < b.N; i++ { log.Debug("debug") From 824e3f8f5b1c788801ffcb3425ffd32059f9c682 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 29 Oct 2014 16:00:04 +0800 Subject: [PATCH 04/83] fix the only file upload param --- httplib/httplib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index dbcb2fed..bced9163 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -275,7 +275,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } else { b.url = b.url + "?" + paramBody } - } else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 { + } else if b.req.Method == "POST" && b.req.Body == nil { if len(b.files) > 0 { bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) @@ -303,7 +303,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { b.Header("Content-Type", contentType) b.req.Body = ioutil.NopCloser(bodyBuf) b.req.ContentLength = int64(bodyBuf.Len()) - } else { + } else if len(paramBody) > 0 { b.Header("Content-Type", "application/x-www-form-urlencoded") b.Body(paramBody) } From 57e62e5e5793788698a24bcb76e3c271379a81c9 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 11:16:09 +0800 Subject: [PATCH 05/83] update the file upload to io.Pipe --- httplib/httplib.go | 50 +++++++++++++++++++++-------------------- httplib/httplib_test.go | 31 ++++++++++++------------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index bced9163..37ba3b33 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -37,6 +37,7 @@ import ( "encoding/xml" "io" "io/ioutil" + "log" "mime/multipart" "net" "net/http" @@ -277,32 +278,33 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } } else if b.req.Method == "POST" && b.req.Body == nil { if len(b.files) > 0 { - bodyBuf := &bytes.Buffer{} - bodyWriter := multipart.NewWriter(bodyBuf) - for formname, filename := range b.files { - fileWriter, err := bodyWriter.CreateFormFile(formname, filename) - if err != nil { - return nil, err + pr, pw := io.Pipe() + bodyWriter := multipart.NewWriter(pw) + go func() { + for formname, filename := range b.files { + fileWriter, err := bodyWriter.CreateFormFile(formname, filename) + if err != nil { + log.Fatal(err) + } + fh, err := os.Open(filename) + if err != nil { + log.Fatal(err) + } + //iocopy + _, err = io.Copy(fileWriter, fh) + fh.Close() + if err != nil { + log.Fatal(err) + } } - fh, err := os.Open(filename) - if err != nil { - return nil, err + for k, v := range b.params { + bodyWriter.WriteField(k, v) } - //iocopy - _, err = io.Copy(fileWriter, fh) - fh.Close() - if err != nil { - return nil, err - } - } - for k, v := range b.params { - bodyWriter.WriteField(k, v) - } - contentType := bodyWriter.FormDataContentType() - bodyWriter.Close() - b.Header("Content-Type", contentType) - b.req.Body = ioutil.NopCloser(bodyBuf) - b.req.ContentLength = int64(bodyBuf.Len()) + bodyWriter.Close() + pw.Close() + }() + b.Header("Content-Type", bodyWriter.FormDataContentType()) + b.req.Body = ioutil.NopCloser(pr) } else if len(paramBody) > 0 { b.Header("Content-Type", "application/x-www-form-urlencoded") b.Body(paramBody) diff --git a/httplib/httplib_test.go b/httplib/httplib_test.go index 976ce498..0b551c53 100644 --- a/httplib/httplib_test.go +++ b/httplib/httplib_test.go @@ -66,23 +66,24 @@ func TestSimplePost(t *testing.T) { } } -func TestPostFile(t *testing.T) { - v := "smallfish" - req := Post("http://httpbin.org/post") - req.Param("username", v) - req.PostFile("uploadfile", "httplib_test.go") +//func TestPostFile(t *testing.T) { +// v := "smallfish" +// req := Post("http://httpbin.org/post") +// req.Debug(true) +// req.Param("username", v) +// req.PostFile("uploadfile", "httplib_test.go") - str, err := req.String() - if err != nil { - t.Fatal(err) - } - t.Log(str) +// str, err := req.String() +// if err != nil { +// t.Fatal(err) +// } +// t.Log(str) - n := strings.Index(str, v) - if n == -1 { - t.Fatal(v + " not found in post") - } -} +// n := strings.Index(str, v) +// if n == -1 { +// t.Fatal(v + " not found in post") +// } +//} func TestSimplePut(t *testing.T) { str, err := Put("http://httpbin.org/put").String() From fda841208d6c8c6934332587cb235fbc2996fab0 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:05:48 +0800 Subject: [PATCH 06/83] fix #893 --- context/context.go | 4 +++- router.go | 29 +++++++---------------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/context/context.go b/context/context.go index d31076d4..8569be14 100644 --- a/context/context.go +++ b/context/context.go @@ -31,6 +31,7 @@ import ( "strings" "time" + "github.com/astaxie/beego" "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/utils" ) @@ -69,7 +70,8 @@ func (ctx *Context) Abort(status int, body string) { panic(e) } // last panic user string - panic(body) + ctx.ResponseWriter.Write([]byte(body)) + panic(beego.USERSTOPRUN) } // Write string to response body. diff --git a/router.go b/router.go index 2a88490d..17415ce3 100644 --- a/router.go +++ b/router.go @@ -831,7 +831,9 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques if err == USERSTOPRUN { return } - if _, ok := err.(middleware.HTTPException); ok { + if he, ok := err.(middleware.HTTPException); ok { + rw.WriteHeader(he.StatusCode) + rw.Write([]byte(he.Description)) // catch intented errors, only for HTTP 4XX and 5XX } else { if RunMode == "dev" { @@ -863,9 +865,10 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques } else { // in production model show all infomation if ErrorsShow { - handler := p.getErrorHandler(fmt.Sprint(err)) - handler(rw, r) - return + if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok { + handler(rw, r) + return + } } else { Critical("the request url is ", r.URL.Path) Critical("Handler crashed with error", err) @@ -884,24 +887,6 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques } } -// there always should be error handler that sets error code accordingly for all unhandled errors. -// in order to have custom UI for error page it's necessary to override "500" error. -func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) { - handler := middleware.SimpleServerError - ok := true - if errorCode != "" { - handler, ok = middleware.ErrorMaps[errorCode] - if !ok { - handler, ok = middleware.ErrorMaps["500"] - } - if !ok || handler == nil { - handler = middleware.SimpleServerError - } - } - - return handler -} - //responseWriter is a wrapper for the http.ResponseWriter //started set to true if response was written to then don't execute other handler type responseWriter struct { From ecd0a5487e7f4bda60848ee90b84dc9435c2e83c Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:12:54 +0800 Subject: [PATCH 07/83] fix the import cycle not allowed --- context/context.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/context/context.go b/context/context.go index 8569be14..89b5ffe4 100644 --- a/context/context.go +++ b/context/context.go @@ -31,7 +31,6 @@ import ( "strings" "time" - "github.com/astaxie/beego" "github.com/astaxie/beego/middleware" "github.com/astaxie/beego/utils" ) @@ -71,7 +70,7 @@ func (ctx *Context) Abort(status int, body string) { } // last panic user string ctx.ResponseWriter.Write([]byte(body)) - panic(beego.USERSTOPRUN) + panic("User stop run") } // Write string to response body. From 68c3bdfdd40b6feac1f2c00c07a751f95912c718 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:57:48 +0800 Subject: [PATCH 08/83] Revert "logs:default support fileline" This reverts commit 1f26852610c8cf24ff054d8e62bb7a6416140992. --- log.go | 5 +++++ logs/conn.go | 2 +- logs/console.go | 2 +- logs/file.go | 2 +- logs/log.go | 36 ++++++++++++++++++++++++++++++------ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/log.go b/log.go index c159b6cc..5afba8ed 100644 --- a/log.go +++ b/log.go @@ -38,6 +38,11 @@ func SetLevel(l int) { BeeLogger.SetLevel(l) } +func SetLogFuncCall(b bool) { + BeeLogger.EnableFuncCallDepth(b) + BeeLogger.SetLogFuncCallDepth(3) +} + // logger references the used application logger. var BeeLogger *logs.BeeLogger diff --git a/logs/conn.go b/logs/conn.go index e3d60eed..612634fa 100644 --- a/logs/conn.go +++ b/logs/conn.go @@ -99,7 +99,7 @@ func (c *ConnWriter) connect() error { } c.innerWriter = conn - c.lg = log.New(conn, "", log.Ldate|log.Ltime|log.Lshortfile) + c.lg = log.New(conn, "", log.Ldate|log.Ltime) return nil } diff --git a/logs/console.go b/logs/console.go index aa34be45..461291c2 100644 --- a/logs/console.go +++ b/logs/console.go @@ -51,7 +51,7 @@ type ConsoleWriter struct { // create ConsoleWriter returning as LoggerInterface. func NewConsole() LoggerInterface { cw := new(ConsoleWriter) - cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile) + cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime) cw.Level = LevelDebug return cw } diff --git a/logs/file.go b/logs/file.go index 0381d1c1..39220cc0 100644 --- a/logs/file.go +++ b/logs/file.go @@ -89,7 +89,7 @@ func NewFileWriter() LoggerInterface { // use MuxWriter instead direct use os.File for lock write when rotate w.mw = new(MuxWriter) // set MuxWriter as Logger's io.Writer - w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime|log.Lshortfile) + w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) return w } diff --git a/logs/log.go b/logs/log.go index 3d66429d..341df572 100644 --- a/logs/log.go +++ b/logs/log.go @@ -34,7 +34,8 @@ package logs import ( "fmt" - + "path" + "runtime" "sync" ) @@ -87,10 +88,12 @@ func Register(name string, log loggerType) { // BeeLogger is default logger in beego application. // it can contain several providers and log message into all providers. type BeeLogger struct { - lock sync.Mutex - level int - msg chan *logMsg - outputs map[string]LoggerInterface + lock sync.Mutex + level int + enableFuncCallDepth bool + loggerFuncCallDepth int + msg chan *logMsg + outputs map[string]LoggerInterface } type logMsg struct { @@ -104,6 +107,7 @@ type logMsg struct { func NewLogger(channellen int64) *BeeLogger { bl := new(BeeLogger) bl.level = LevelDebug + bl.loggerFuncCallDepth = 2 bl.msg = make(chan *logMsg, channellen) bl.outputs = make(map[string]LoggerInterface) //bl.SetLogger("console", "") // default output to console @@ -149,7 +153,17 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { } lm := new(logMsg) lm.level = loglevel - lm.msg = msg + if bl.enableFuncCallDepth { + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) + if ok { + _, filename := path.Split(file) + lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) + } else { + lm.msg = msg + } + } else { + lm.msg = msg + } bl.msg <- lm return nil } @@ -162,6 +176,16 @@ func (bl *BeeLogger) SetLevel(l int) { bl.level = l } +// set log funcCallDepth +func (bl *BeeLogger) SetLogFuncCallDepth(d int) { + bl.loggerFuncCallDepth = d +} + +// enable log funcCallDepth +func (bl *BeeLogger) EnableFuncCallDepth(b bool) { + bl.enableFuncCallDepth = b +} + // start logger chan reading. // when chan is not empty, write logs. func (bl *BeeLogger) startLogger() { From 1636a7271c2d49fe7b5a9f6753db0eb46c1eedc1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 16:57:55 +0800 Subject: [PATCH 09/83] Revert "fix the log test" This reverts commit ddbfc25e56900fe03ff7c8f6d3886802f3a880dd. --- logs/console_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/logs/console_test.go b/logs/console_test.go index 167a007a..2fad7241 100644 --- a/logs/console_test.go +++ b/logs/console_test.go @@ -34,6 +34,7 @@ func testConsoleCalls(bl *BeeLogger) { // without a log level specification. func TestConsole(t *testing.T) { log1 := NewLogger(10000) + log1.EnableFuncCallDepth(true) log1.SetLogger("console", "") testConsoleCalls(log1) @@ -44,6 +45,7 @@ func TestConsole(t *testing.T) { func BenchmarkConsole(b *testing.B) { log := NewLogger(10000) + log.EnableFuncCallDepth(true) log.SetLogger("console", "") for i := 0; i < b.N; i++ { log.Debug("debug") From 71149218d17b7f5843c9d5c17ce95be22739e26e Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 30 Oct 2014 17:43:32 +0800 Subject: [PATCH 10/83] fix the log level --- config.go | 1 + logs/log.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/config.go b/config.go index 84f8cf39..8a819dad 100644 --- a/config.go +++ b/config.go @@ -277,6 +277,7 @@ func init() { if err != nil { fmt.Println("init console log error:", err) } + BeeLogger.SetLogFuncCallDepth(true) err = ParseConfig() if err != nil && !os.IsNotExist(err) { diff --git a/logs/log.go b/logs/log.go index 341df572..b61388e6 100644 --- a/logs/log.go +++ b/logs/log.go @@ -154,7 +154,10 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm := new(logMsg) lm.level = loglevel if bl.enableFuncCallDepth { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + if !ok { + _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth) + } if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From db43892fe675db58a1a33ac0e221a60ce3d7827c Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 00:28:51 +0800 Subject: [PATCH 11/83] improve the Put #896 --- cache/redis/redis.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 35cf88cd..b205545d 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -75,14 +75,13 @@ func (rc *RedisCache) Get(key string) interface{} { // put cache to redis. func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { var err error - if _, err = rc.do("SET", key, val); err != nil { + if _, err = rc.do("SETEX", key, timeout, val); err != nil { return err } if _, err = rc.do("HSET", rc.key, key, true); err != nil { return err } - _, err = rc.do("EXPIRE", key, timeout) return err } From 94c84b846fe84fddad10e1751c8a5519f4841a6f Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 09:02:16 +0800 Subject: [PATCH 12/83] fix the init logger --- config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.go b/config.go index 8a819dad..fd3d5de1 100644 --- a/config.go +++ b/config.go @@ -277,7 +277,7 @@ func init() { if err != nil { fmt.Println("init console log error:", err) } - BeeLogger.SetLogFuncCallDepth(true) + SetLogFuncCall(true) err = ParseConfig() if err != nil && !os.IsNotExist(err) { From 945b1da3a8f1b87d5d2d731f8e224a22f23b45f5 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 15:48:57 +0800 Subject: [PATCH 13/83] fix the gofmt --- example/chat/controllers/ws.go | 6 ++--- migration/ddl.go | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 migration/ddl.go diff --git a/example/chat/controllers/ws.go b/example/chat/controllers/ws.go index 862f89c9..9ec5b418 100644 --- a/example/chat/controllers/ws.go +++ b/example/chat/controllers/ws.go @@ -150,12 +150,12 @@ type WSController struct { } var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, + ReadBufferSize: 1024, + WriteBufferSize: 1024, } func (this *WSController) Get() { - ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil) + ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request, nil) if _, ok := err.(websocket.HandshakeError); ok { http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) return diff --git a/migration/ddl.go b/migration/ddl.go new file mode 100644 index 00000000..f9b60117 --- /dev/null +++ b/migration/ddl.go @@ -0,0 +1,46 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migration + +type Table struct { + TableName string + Columns []*Column +} + +func (t *Table) Create() string { + return "" +} + +func (t *Table) Drop() string { + return "" +} + +type Column struct { + Name string + Type string + Default interface{} +} + +func Create(tbname string, columns ...Column) string { + return "" +} + +func Drop(tbname string, columns ...Column) string { + return "" +} + +func TableDDL(tbname string, columns ...Column) string { + return "" +} From da127bbc229e4285d59041db209c5afa84c6d61a Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 31 Oct 2014 16:31:23 +0800 Subject: [PATCH 14/83] fix #855 #859 --- namespace.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/namespace.go b/namespace.go index d0109291..4e2632e5 100644 --- a/namespace.go +++ b/namespace.go @@ -217,7 +217,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { n.handlers.routers[k] = t } } - if n.handlers.enableFilter { + if ni.handlers.enableFilter { for pos, filterList := range ni.handlers.filters { for _, mr := range filterList { t := NewTree() From 90cff5f042393ff098862f228b5fd4e565161172 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 2 Nov 2014 21:01:51 +0800 Subject: [PATCH 15/83] fix #824 --- tree.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tree.go b/tree.go index 9f86dd48..25947442 100644 --- a/tree.go +++ b/tree.go @@ -394,6 +394,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string } return true, params } + if len(wildcardValues) <= j { + return false, nil + } params[v] = wildcardValues[j] j += 1 } From 716962672fa215d65d39d4ecb3b4f9470cf3bdb8 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 15:06:25 +0800 Subject: [PATCH 16/83] fix #751 add config ListenTCP4 when user want to listen on the TCP4, because now almost use the ipv4. but default lister on the ipv6 --- app.go | 36 ++++++++++++++++++++++++------------ config.go | 5 +++++ filter.go | 5 +++++ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/app.go b/app.go index 0b89d89e..d155c531 100644 --- a/app.go +++ b/app.go @@ -20,13 +20,8 @@ import ( "net/http" "net/http/fcgi" "time" - - "github.com/astaxie/beego/context" ) -// FilterFunc defines filter function type. -type FilterFunc func(*context.Context) - // App defines beego application with a new PatternServeMux. type App struct { Handlers *ControllerRegistor @@ -85,7 +80,7 @@ func (app *App) Run() { if HttpsPort != 0 { app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) } - BeeLogger.Info("Running on %s", app.Server.Addr) + BeeLogger.Info("https server Running on %s", app.Server.Addr) err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) @@ -98,12 +93,29 @@ func (app *App) Run() { if EnableHttpListen { go func() { app.Server.Addr = addr - BeeLogger.Info("Running on %s", app.Server.Addr) - err := app.Server.ListenAndServe() - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true + BeeLogger.Info("http server Running on %s", app.Server.Addr) + if ListenTCP4 && HttpAddr == "" { + ln, err := net.Listen("tcp4", app.Server.Addr) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + err = app.Server.Serve(ln) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + } else { + err := app.Server.ListenAndServe() + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } } }() } diff --git a/config.go b/config.go index fd3d5de1..ed91647f 100644 --- a/config.go +++ b/config.go @@ -40,6 +40,7 @@ var ( EnableHttpListen bool HttpAddr string HttpPort int + ListenTCP4 bool EnableHttpTLS bool HttpsPort int HttpCertFile string @@ -309,6 +310,10 @@ func ParseConfig() (err error) { HttpPort = v } + if v, err := AppConfig.Bool("ListenTCP4"); err == nil { + ListenTCP4 = v + } + if v, err := AppConfig.Bool("EnableHttpListen"); err == nil { EnableHttpListen = v } diff --git a/filter.go b/filter.go index eebad329..ddd61094 100644 --- a/filter.go +++ b/filter.go @@ -14,6 +14,11 @@ package beego +import "github.com/astaxie/beego/context" + +// FilterFunc defines filter function type. +type FilterFunc func(*context.Context) + // FilterRouter defines filter operation before controller handler execution. // it can match patterned url and do filter function when action arrives. type FilterRouter struct { From 10db97b193b40956948bf39c010462a4a629be18 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 15:08:51 +0800 Subject: [PATCH 17/83] add some tips for the admin server start --- admin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin.go b/admin.go index a0905dd3..273e666e 100644 --- a/admin.go +++ b/admin.go @@ -458,7 +458,7 @@ func (admin *adminApp) Run() { for p, f := range admin.routers { http.Handle(p, f) } - BeeLogger.Info("Running on %s", addr) + BeeLogger.Info("Admin server Running on %s", addr) err := http.ListenAndServe(addr, nil) if err != nil { BeeLogger.Critical("Admin ListenAndServe: ", err) From 8d797a4a5ee7788695ffa1f765520b45eb412226 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:14:40 +0800 Subject: [PATCH 18/83] file the static filter --- router.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/router.go b/router.go index 17415ce3..9b11be53 100644 --- a/router.go +++ b/router.go @@ -72,11 +72,31 @@ var ( "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", "GetControllerAndAction"} - url_placeholder = "{{placeholder}}" - - FilterRouterLog func(*beecontext.Context) bool + url_placeholder = "{{placeholder}}" + DefaultLogFilter FilterHandler = &logFilter{} ) +type FilterHandler interface { + Filter(*beecontext.Context) bool +} + +// default log filter static file will not show +type logFilter struct { +} + +func (l *logFilter) Filter(ctx *beecontext.Context) bool { + requestPath := path.Clean(ctx.Input.Request.URL.Path) + if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { + return true + } + for prefix, _ := range StaticDir { + if strings.HasPrefix(requestPath, prefix) { + return true + } + } + return false +} + // To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter func ExceptMethodAppend(action string) { exceptMethod = append(exceptMethod, action) @@ -815,7 +835,7 @@ Admin: } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } - if FilterRouterLog == nil || !FilterRouterLog(context) { + if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) { Debug(devinfo) } } From 2288ac868c49cadb170fb8876e14a2a188001d96 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:34:36 +0800 Subject: [PATCH 19/83] remove the deep Caller --- logs/log.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/logs/log.go b/logs/log.go index b61388e6..d24e21d2 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,9 +155,6 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) - if !ok { - _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth) - } if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From 304beaf89ffe65e2f4a6d4a19ec80d5303f24f04 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:40:08 +0800 Subject: [PATCH 20/83] update the log call deep --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index d24e21d2..341df572 100644 --- a/logs/log.go +++ b/logs/log.go @@ -154,7 +154,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm := new(logMsg) lm.level = loglevel if bl.enableFuncCallDepth { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From 88caf1ed70b7f5afb9a56128154e7bf5407420f1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:43:07 +0800 Subject: [PATCH 21/83] if read the log.go then calldepth add 1 --- logs/log.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logs/log.go b/logs/log.go index 341df572..a1964892 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,6 +155,9 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) + if file == "log.go" && line == 97 { + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + } if ok { _, filename := path.Split(file) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) From ef3c7c127b3bb505d65d9351129102bf8351c7ba Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:44:05 +0800 Subject: [PATCH 22/83] fix the variable --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index a1964892..89574769 100644 --- a/logs/log.go +++ b/logs/log.go @@ -156,7 +156,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) if file == "log.go" && line == 97 { - _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth + 1) + _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) } if ok { _, filename := path.Split(file) From 90a7ce5c6ad5ab3e1076596b558b3bfa9f8fa9fe Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:45:42 +0800 Subject: [PATCH 23/83] split the file for logs --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index 89574769..6251f573 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,7 +155,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if file == "log.go" && line == 97 { + if _, filename := path.Split(file); filename == "log.go" && line == 97 { _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) } if ok { From a2428af8a79abe437b6bcef254b218c1a30b8f1c Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 16:48:45 +0800 Subject: [PATCH 24/83] compatibility for warn & info function add one more depth --- logs/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/log.go b/logs/log.go index 6251f573..6abfb005 100644 --- a/logs/log.go +++ b/logs/log.go @@ -155,7 +155,7 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if _, filename := path.Split(file); filename == "log.go" && line == 97 { + if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) { _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) } if ok { From 8b747f54bc14206ceb8beef5a5c91fba36ea006b Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 3 Nov 2014 23:33:11 +0800 Subject: [PATCH 25/83] fix #770 --- parser.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index d29470dc..adb176f1 100644 --- a/parser.go +++ b/parser.go @@ -42,6 +42,7 @@ func init() { var ( lastupdateFilename string = "lastupdate.tmp" + commentFilename string = "commentsRouter.go" pkgLastupdate map[string]int64 genInfoList map[string][]ControllerComments ) @@ -52,6 +53,7 @@ func init() { } func parserPkg(pkgRealpath, pkgpath string) error { + commentFilename = strings.Replace(pkgpath, "/", "_", -1) + "_" + commentFilename if !compareFile(pkgRealpath) { Info(pkgRealpath + " don't has updated") return nil @@ -155,7 +157,7 @@ func genRouterCode() { } } if globalinfo != "" { - f, err := os.Create(path.Join(workPath, "routers", "commentsRouter.go")) + f, err := os.Create(path.Join(workPath, "routers", commentFilename)) if err != nil { panic(err) } @@ -165,7 +167,7 @@ func genRouterCode() { } func compareFile(pkgRealpath string) bool { - if !utils.FileExists(path.Join(workPath, "routers", "commentsRouter.go")) { + if !utils.FileExists(path.Join(workPath, "routers", commentFilename)) { return true } if utils.FileExists(path.Join(workPath, lastupdateFilename)) { From 9d4ec508bb8658e0fdf89de39d0ee89b895658b7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 10:19:30 +0800 Subject: [PATCH 26/83] parse for github.com replace the . to _ --- parser.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parser.go b/parser.go index adb176f1..0acdc8f7 100644 --- a/parser.go +++ b/parser.go @@ -53,7 +53,8 @@ func init() { } func parserPkg(pkgRealpath, pkgpath string) error { - commentFilename = strings.Replace(pkgpath, "/", "_", -1) + "_" + commentFilename + rep := strings.NewReplacer("/", "_", ".", "_") + commentFilename = rep.Replace(pkgpath) + "_" + commentFilename if !compareFile(pkgRealpath) { Info(pkgRealpath + " don't has updated") return nil From c4d8e4a244588330a4f658d41852ce9c89735f19 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 15:29:33 +0800 Subject: [PATCH 27/83] fix #759 --- router.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/router.go b/router.go index 9b11be53..4a4a934b 100644 --- a/router.go +++ b/router.go @@ -452,8 +452,8 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { } controllName := strings.Join(paths[:len(paths)-1], "/") methodName := paths[len(paths)-1] - for _, t := range p.routers { - ok, url := p.geturl(t, "/", controllName, methodName, params) + for m, t := range p.routers { + ok, url := p.geturl(t, "/", controllName, methodName, params, m) if ok { return url } @@ -461,17 +461,17 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string { return "" } -func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) { +func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) { for k, subtree := range t.fixrouters { u := path.Join(url, k) - ok, u := p.geturl(subtree, u, controllName, methodName, params) + ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod) if ok { return ok, u } } if t.wildcard != nil { - url = path.Join(url, url_placeholder) - ok, u := p.geturl(t.wildcard, url, controllName, methodName, params) + u := path.Join(url, url_placeholder) + ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod) if ok { return ok, u } @@ -491,8 +491,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin } } if !find { - for _, md := range c.methods { - if md == methodName { + for m, md := range c.methods { + if (m == "*" || m == httpMethod) && md == methodName { find = true } } From 7743eecfd4723b3e963de3e62da83c11c9d083e5 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 16:19:46 +0800 Subject: [PATCH 28/83] support #761 type Test struct { Date time.Time `form:"Date, 2006-01-02"` Save bool `form:"Save"` } --- templatefunc.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/templatefunc.go b/templatefunc.go index 99f0a3e8..16067613 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -302,6 +302,14 @@ func ParseForm(form url.Values, obj interface{}) error { switch fieldT.Type.Kind() { case reflect.Bool: + if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" { + fieldV.SetBool(true) + continue + } + if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" { + fieldV.SetBool(false) + continue + } b, err := strconv.ParseBool(value) if err != nil { return err @@ -329,6 +337,19 @@ func ParseForm(form url.Values, obj interface{}) error { fieldV.Set(reflect.ValueOf(value)) case reflect.String: fieldV.SetString(value) + case reflect.Struct: + switch fieldT.Type.String() { + case "time.Time": + format := time.RFC3339 + if len(tags) > 1 { + format = tags[1] + } + t, err := time.Parse(format, value) + if err != nil { + return err + } + fieldV.Set(reflect.ValueOf(t)) + } } } return nil From b9fdd67519a451c487fc5f15800dab5cf52d94f7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 16:39:17 +0800 Subject: [PATCH 29/83] add test case fot date & stringbool --- templatefunc_test.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/templatefunc_test.go b/templatefunc_test.go index 9a461c9f..3692a821 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -102,12 +102,14 @@ func TestHtmlunquote(t *testing.T) { func TestParseForm(t *testing.T) { type user struct { - Id int `form:"-"` - tag string `form:"tag"` - Name interface{} `form:"username"` - Age int `form:"age,text"` - Email string - Intro string `form:",textarea"` + Id int `form:"-"` + tag string `form:"tag"` + Name interface{} `form:"username"` + Age int `form:"age,text"` + Email string + Intro string `form:",textarea"` + StrBool bool `form:"strbool"` + Date time.Time `form:"date,2006-01-02"` } u := user{} @@ -119,6 +121,8 @@ func TestParseForm(t *testing.T) { "age": []string{"40"}, "Email": []string{"test@gmail.com"}, "Intro": []string{"I am an engineer!"}, + "strbool": []string{"yes"}, + "date": []string{"2014-11-12"}, } if err := ParseForm(form, u); err == nil { t.Fatal("nothing will be changed") @@ -144,6 +148,13 @@ func TestParseForm(t *testing.T) { if u.Intro != "I am an engineer!" { t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro) } + if u.StrBool != true { + t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool) + } + y, m, d := u.Date.Date() + if y != 2014 || m.String() != "November" || d != 12 { + t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String()) + } } func TestRenderForm(t *testing.T) { From fc6b9ce00913017cb73c84c3f5a4b16bc0e3e273 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 19:04:26 +0800 Subject: [PATCH 30/83] fix #620 simple the sessionID generate --- session/session.go | 97 +++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 57 deletions(-) diff --git a/session/session.go b/session/session.go index 88e94d59..3cbd2b05 100644 --- a/session/session.go +++ b/session/session.go @@ -28,19 +28,13 @@ package session import ( - "crypto/hmac" - "crypto/md5" "crypto/rand" - "crypto/sha1" "encoding/hex" "encoding/json" "fmt" - "io" "net/http" "net/url" "time" - - "github.com/astaxie/beego/utils" ) // SessionStore contains all data for one session process with specific id. @@ -81,16 +75,15 @@ func Register(name string, provide Provider) { } type managerConfig struct { - CookieName string `json:"cookieName"` - EnableSetCookie bool `json:"enableSetCookie,omitempty"` - Gclifetime int64 `json:"gclifetime"` - Maxlifetime int64 `json:"maxLifetime"` - Secure bool `json:"secure"` - SessionIDHashFunc string `json:"sessionIDHashFunc"` - SessionIDHashKey string `json:"sessionIDHashKey"` - CookieLifeTime int `json:"cookieLifeTime"` - ProviderConfig string `json:"providerConfig"` - Domain string `json:"domain"` + CookieName string `json:"cookieName"` + EnableSetCookie bool `json:"enableSetCookie,omitempty"` + Gclifetime int64 `json:"gclifetime"` + Maxlifetime int64 `json:"maxLifetime"` + Secure bool `json:"secure"` + CookieLifeTime int `json:"cookieLifeTime"` + ProviderConfig string `json:"providerConfig"` + Domain string `json:"domain"` + SessionIdLength int64 `json:"sessionIdLength"` } // Manager contains Provider and its configuration. @@ -129,11 +122,9 @@ func NewManager(provideName, config string) (*Manager, error) { if err != nil { return nil, err } - if cf.SessionIDHashFunc == "" { - cf.SessionIDHashFunc = "sha1" - } - if cf.SessionIDHashKey == "" { - cf.SessionIDHashKey = string(generateRandomKey(16)) + + if cf.SessionIdLength == 0 { + cf.SessionIdLength = 16 } return &Manager{ @@ -144,11 +135,14 @@ func NewManager(provideName, config string) (*Manager, error) { // Start session. generate or read the session id from http request. // if session id exists, return SessionStore with this id. -func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) { - cookie, err := r.Cookie(manager.config.CookieName) - if err != nil || cookie.Value == "" { - sid := manager.sessionId(r) - session, _ = manager.provider.SessionRead(sid) +func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore, err error) { + cookie, errs := r.Cookie(manager.config.CookieName) + if errs != nil || cookie.Value == "" { + sid, errs := manager.sessionId(r) + if errs != nil { + return nil, errs + } + session, err = manager.provider.SessionRead(sid) cookie = &http.Cookie{Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", @@ -163,12 +157,18 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se } r.AddCookie(cookie) } else { - sid, _ := url.QueryUnescape(cookie.Value) + sid, errs := url.QueryUnescape(cookie.Value) + if errs != nil { + return nil, errs + } if manager.provider.SessionExist(sid) { - session, _ = manager.provider.SessionRead(sid) + session, err = manager.provider.SessionRead(sid) } else { - sid = manager.sessionId(r) - session, _ = manager.provider.SessionRead(sid) + sid, err = manager.sessionId(r) + if err != nil { + return nil, err + } + session, err = manager.provider.SessionRead(sid) cookie = &http.Cookie{Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", @@ -219,7 +219,10 @@ func (manager *Manager) GC() { // Regenerate a session id for this SessionStore who's id is saving in http request. func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) { - sid := manager.sessionId(r) + sid, err := manager.sessionId(r) + if err != nil { + return + } cookie, err := r.Cookie(manager.config.CookieName) if err != nil && cookie.Value == "" { //delete old cookie @@ -251,36 +254,16 @@ func (manager *Manager) GetActiveSession() int { return manager.provider.SessionAll() } -// Set hash function for generating session id. -func (manager *Manager) SetHashFunc(hasfunc, hashkey string) { - manager.config.SessionIDHashFunc = hasfunc - manager.config.SessionIDHashKey = hashkey -} - // Set cookie with https. func (manager *Manager) SetSecure(secure bool) { manager.config.Secure = secure } -// generate session id with rand string, unix nano time, remote addr by hash function. -func (manager *Manager) sessionId(r *http.Request) (sid string) { - bs := make([]byte, 32) - if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil { - bs = utils.RandomCreateBytes(32) +func (manager *Manager) sessionId(r *http.Request) (string, error) { + b := make([]byte, manager.config.SessionIdLength) + n, err := rand.Read(b) + if n != len(b) || err != nil { + return "", fmt.Errorf("Could not successfully read from the system CSPRNG.") } - sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs) - if manager.config.SessionIDHashFunc == "md5" { - h := md5.New() - h.Write([]byte(sig)) - sid = hex.EncodeToString(h.Sum(nil)) - } else if manager.config.SessionIDHashFunc == "sha1" { - h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey)) - fmt.Fprintf(h, "%s", sig) - sid = hex.EncodeToString(h.Sum(nil)) - } else { - h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey)) - fmt.Fprintf(h, "%s", sig) - sid = hex.EncodeToString(h.Sum(nil)) - } - return + return hex.EncodeToString(b), nil } From 76522d43af8ac4b1d5d4c9788616263e4c956cdf Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 19:07:49 +0800 Subject: [PATCH 31/83] simple the session --- router.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/router.go b/router.go index 4a4a934b..1f47c907 100644 --- a/router.go +++ b/router.go @@ -644,7 +644,13 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) // session init if SessionOn { - context.Input.CruSession = GlobalSessions.SessionStart(w, r) + var err error + context.Input.CruSession, err = GlobalSessions.SessionStart(w, r) + if err != nil { + Error(err) + middleware.Exception("503", rw, r, "") + return + } defer func() { context.Input.CruSession.SessionRelease(w) }() @@ -888,6 +894,11 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok { handler(rw, r) return + } else if handler, ok := middleware.ErrorMaps["503"]; ok { + handler(rw, r) + return + } else { + rw.Write([]byte(fmt.Sprint(err))) } } else { Critical("the request url is ", r.URL.Path) From 15242d89ce16b2f96573a63f13d53f8a592ffd40 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 19:08:06 +0800 Subject: [PATCH 32/83] simple the session init --- admin.go | 2 -- beego.go | 2 -- config.go | 12 ------------ 3 files changed, 16 deletions(-) diff --git a/admin.go b/admin.go index 273e666e..d918b595 100644 --- a/admin.go +++ b/admin.go @@ -113,8 +113,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["SessionName"] = SessionName m["SessionGCMaxLifetime"] = SessionGCMaxLifetime m["SessionSavePath"] = SessionSavePath - m["SessionHashFunc"] = SessionHashFunc - m["SessionHashKey"] = SessionHashKey m["SessionCookieLifeTime"] = SessionCookieLifeTime m["UseFcgi"] = UseFcgi m["MaxMemory"] = MaxMemory diff --git a/beego.go b/beego.go index f23174c7..8bfcf617 100644 --- a/beego.go +++ b/beego.go @@ -383,8 +383,6 @@ func initBeforeHttpRun() { `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + - `"sessionIDHashFunc":"` + SessionHashFunc + `",` + - `"sessionIDHashKey":"` + SessionHashKey + `",` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"domain":"` + SessionDomain + `",` + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` diff --git a/config.go b/config.go index ed91647f..f4d0aba2 100644 --- a/config.go +++ b/config.go @@ -56,8 +56,6 @@ var ( SessionName string // the cookie name when saving session id into cookie. SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session. SessionSavePath string // if use mysql/redis/file provider, define save path to connection info. - SessionHashFunc string // session hash generation func. - SessionHashKey string // session hash salt string. SessionCookieLifeTime int // the life time of session id in cookie. SessionAutoSetCookie bool // auto setcookie SessionDomain string // the cookie domain default is empty @@ -237,8 +235,6 @@ func init() { SessionName = "beegosessionID" SessionGCMaxLifetime = 3600 SessionSavePath = "" - SessionHashFunc = "sha1" - SessionHashKey = "beegoserversessionkey" SessionCookieLifeTime = 0 //set cookie default is the brower life SessionAutoSetCookie = true @@ -354,14 +350,6 @@ func ParseConfig() (err error) { SessionSavePath = sesssavepath } - if sesshashfunc := AppConfig.String("SessionHashFunc"); sesshashfunc != "" { - SessionHashFunc = sesshashfunc - } - - if sesshashkey := AppConfig.String("SessionHashKey"); sesshashkey != "" { - SessionHashKey = sesshashkey - } - if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { SessionGCMaxLifetime = sessMaxLifeTime } From 000033e2a7e053d3ac12e12a7c50283c37d75d9b Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 22:07:38 +0800 Subject: [PATCH 33/83] update the test case --- session/sess_cookie_test.go | 5 ++++- session/sess_mem_test.go | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/session/sess_cookie_test.go b/session/sess_cookie_test.go index 4f40a7ba..fe3ac806 100644 --- a/session/sess_cookie_test.go +++ b/session/sess_cookie_test.go @@ -29,7 +29,10 @@ func TestCookie(t *testing.T) { } r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() - sess := globalSessions.SessionStart(w, r) + sess, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("set error,", err) + } err = sess.Set("username", "astaxie") if err != nil { t.Fatal("set error,", err) diff --git a/session/sess_mem_test.go b/session/sess_mem_test.go index 03927c76..43f5b0a9 100644 --- a/session/sess_mem_test.go +++ b/session/sess_mem_test.go @@ -26,9 +26,12 @@ func TestMem(t *testing.T) { go globalSessions.GC() r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() - sess := globalSessions.SessionStart(w, r) + sess, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("set error,", err) + } defer sess.SessionRelease(w) - err := sess.Set("username", "astaxie") + err = sess.Set("username", "astaxie") if err != nil { t.Fatal("set error,", err) } From fe50269b3fd07daafb5644ba33f3dff23d7731b1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Nov 2014 22:38:40 +0800 Subject: [PATCH 34/83] change 1.4.1 to 1.4.2 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 8bfcf617..ab70a182 100644 --- a/beego.go +++ b/beego.go @@ -38,7 +38,7 @@ import ( ) // beego web framework version. -const VERSION = "1.4.1" +const VERSION = "1.4.2" type hookfunc func() error //hook function to run var hooks []hookfunc //hook function slice to store the hookfunc From 950ff91d87b83b325cc99a6efd002900c6c1f535 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 5 Nov 2014 22:23:54 +0800 Subject: [PATCH 35/83] hotfix for parsefiel --- parser.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index 0acdc8f7..63655cd2 100644 --- a/parser.go +++ b/parser.go @@ -42,11 +42,13 @@ func init() { var ( lastupdateFilename string = "lastupdate.tmp" - commentFilename string = "commentsRouter.go" + commentFilename string pkgLastupdate map[string]int64 genInfoList map[string][]ControllerComments ) +const COMMENTFL = "commentsRouter.go" + func init() { pkgLastupdate = make(map[string]int64) genInfoList = make(map[string][]ControllerComments) @@ -54,7 +56,7 @@ func init() { func parserPkg(pkgRealpath, pkgpath string) error { rep := strings.NewReplacer("/", "_", ".", "_") - commentFilename = rep.Replace(pkgpath) + "_" + commentFilename + commentFilename = rep.Replace(pkgpath) + "_" + COMMENTFL if !compareFile(pkgRealpath) { Info(pkgRealpath + " don't has updated") return nil From 54ba307f7fa1e956723dd00b2c4fcc2994759d9f Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 5 Nov 2014 22:40:31 +0800 Subject: [PATCH 36/83] change this to short name --- example/beeapi/controllers/default.go | 40 +++++++++++++-------------- example/chat/controllers/default.go | 6 ++-- example/chat/controllers/ws.go | 6 ++-- flash_test.go | 6 ++-- router_test.go | 30 ++++++++++---------- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/example/beeapi/controllers/default.go b/example/beeapi/controllers/default.go index c6e53e74..a2184075 100644 --- a/example/beeapi/controllers/default.go +++ b/example/beeapi/controllers/default.go @@ -17,47 +17,47 @@ type ObjectController struct { beego.Controller } -func (this *ObjectController) Post() { +func (o *ObjectController) Post() { var ob models.Object - json.Unmarshal(this.Ctx.Input.RequestBody, &ob) + json.Unmarshal(o.Ctx.Input.RequestBody, &ob) objectid := models.AddOne(ob) - this.Data["json"] = map[string]string{"ObjectId": objectid} - this.ServeJson() + o.Data["json"] = map[string]string{"ObjectId": objectid} + o.ServeJson() } -func (this *ObjectController) Get() { - objectId := this.Ctx.Input.Params[":objectId"] +func (o *ObjectController) Get() { + objectId := o.Ctx.Input.Params[":objectId"] if objectId != "" { ob, err := models.GetOne(objectId) if err != nil { - this.Data["json"] = err + o.Data["json"] = err } else { - this.Data["json"] = ob + o.Data["json"] = ob } } else { obs := models.GetAll() - this.Data["json"] = obs + o.Data["json"] = obs } - this.ServeJson() + o.ServeJson() } -func (this *ObjectController) Put() { - objectId := this.Ctx.Input.Params[":objectId"] +func (o *ObjectController) Put() { + objectId := o.Ctx.Input.Params[":objectId"] var ob models.Object - json.Unmarshal(this.Ctx.Input.RequestBody, &ob) + json.Unmarshal(o.Ctx.Input.RequestBody, &ob) err := models.Update(objectId, ob.Score) if err != nil { - this.Data["json"] = err + o.Data["json"] = err } else { - this.Data["json"] = "update success!" + o.Data["json"] = "update success!" } - this.ServeJson() + o.ServeJson() } -func (this *ObjectController) Delete() { - objectId := this.Ctx.Input.Params[":objectId"] +func (o *ObjectController) Delete() { + objectId := o.Ctx.Input.Params[":objectId"] models.Delete(objectId) - this.Data["json"] = "delete success!" - this.ServeJson() + o.Data["json"] = "delete success!" + o.ServeJson() } diff --git a/example/chat/controllers/default.go b/example/chat/controllers/default.go index 370e6db1..95ef8a9b 100644 --- a/example/chat/controllers/default.go +++ b/example/chat/controllers/default.go @@ -14,7 +14,7 @@ type MainController struct { beego.Controller } -func (this *MainController) Get() { - this.Data["host"] = this.Ctx.Request.Host - this.TplNames = "index.tpl" +func (m *MainController) Get() { + m.Data["host"] = m.Ctx.Request.Host + m.TplNames = "index.tpl" } diff --git a/example/chat/controllers/ws.go b/example/chat/controllers/ws.go index 9ec5b418..fc3917b3 100644 --- a/example/chat/controllers/ws.go +++ b/example/chat/controllers/ws.go @@ -154,10 +154,10 @@ var upgrader = websocket.Upgrader{ WriteBufferSize: 1024, } -func (this *WSController) Get() { - ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request, nil) +func (w *WSController) Get() { + ws, err := upgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil) if _, ok := err.(websocket.HandshakeError); ok { - http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) + http.Error(w.Ctx.ResponseWriter, "Not a websocket handshake", 400) return } else if err != nil { return diff --git a/flash_test.go b/flash_test.go index 7c581e6a..b655f552 100644 --- a/flash_test.go +++ b/flash_test.go @@ -25,12 +25,12 @@ type TestFlashController struct { Controller } -func (this *TestFlashController) TestWriteFlash() { +func (t *TestFlashController) TestWriteFlash() { flash := NewFlash() flash.Notice("TestFlashString") - flash.Store(&this.Controller) + flash.Store(&t.Controller) // we choose to serve json because we don't want to load a template html file - this.ServeJson(true) + t.ServeJson(true) } func TestFlashHeader(t *testing.T) { diff --git a/router_test.go b/router_test.go index c64bb191..ee712167 100644 --- a/router_test.go +++ b/router_test.go @@ -27,33 +27,33 @@ type TestController struct { Controller } -func (this *TestController) Get() { - this.Data["Username"] = "astaxie" - this.Ctx.Output.Body([]byte("ok")) +func (tc *TestController) Get() { + tc.Data["Username"] = "astaxie" + tc.Ctx.Output.Body([]byte("ok")) } -func (this *TestController) Post() { - this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name"))) +func (tc *TestController) Post() { + tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name"))) } -func (this *TestController) Param() { - this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name"))) +func (tc *TestController) Param() { + tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name"))) } -func (this *TestController) List() { - this.Ctx.Output.Body([]byte("i am list")) +func (tc *TestController) List() { + tc.Ctx.Output.Body([]byte("i am list")) } -func (this *TestController) Params() { - this.Ctx.Output.Body([]byte(this.Ctx.Input.Params["0"] + this.Ctx.Input.Params["1"] + this.Ctx.Input.Params["2"])) +func (tc *TestController) Params() { + tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Params["0"] + tc.Ctx.Input.Params["1"] + tc.Ctx.Input.Params["2"])) } -func (this *TestController) Myext() { - this.Ctx.Output.Body([]byte(this.Ctx.Input.Param(":ext"))) +func (tc *TestController) Myext() { + tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext"))) } -func (this *TestController) GetUrl() { - this.Ctx.Output.Body([]byte(this.UrlFor(".Myext"))) +func (tc *TestController) GetUrl() { + tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext"))) } func (t *TestController) GetParams() { From bb795847da19e2be91c3a5af05b31f52c5e52dab Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 6 Nov 2014 11:12:00 +0800 Subject: [PATCH 37/83] fix the not exist config file application --- config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.go b/config.go index f4d0aba2..7d2a94de 100644 --- a/config.go +++ b/config.go @@ -277,7 +277,7 @@ func init() { SetLogFuncCall(true) err = ParseConfig() - if err != nil && !os.IsNotExist(err) { + if err != nil && os.IsNotExist(err) { // for init if doesn't have app.conf will not panic ac := config.NewFakeConfig() AppConfig = &beegoAppConfig{ac} From 1e92d17605b74555e4671a43e81fba79e5a2fae8 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 6 Nov 2014 16:25:47 +0800 Subject: [PATCH 38/83] fix the repeat commentsRouters --- parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.go b/parser.go index 63655cd2..e7e324ba 100644 --- a/parser.go +++ b/parser.go @@ -51,7 +51,6 @@ const COMMENTFL = "commentsRouter.go" func init() { pkgLastupdate = make(map[string]int64) - genInfoList = make(map[string][]ControllerComments) } func parserPkg(pkgRealpath, pkgpath string) error { @@ -61,6 +60,7 @@ func parserPkg(pkgRealpath, pkgpath string) error { Info(pkgRealpath + " don't has updated") return nil } + genInfoList = make(map[string][]ControllerComments) fileSet := token.NewFileSet() astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool { name := info.Name() From 07c628c7e9f813efc3f7e1785f667e08f2340d53 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 6 Nov 2014 17:30:50 +0800 Subject: [PATCH 39/83] fix the commentsRouter init sequence --- parser.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index e7e324ba..bd673044 100644 --- a/parser.go +++ b/parser.go @@ -47,7 +47,7 @@ var ( genInfoList map[string][]ControllerComments ) -const COMMENTFL = "commentsRouter.go" +const COMMENTFL = "commentsRouter_" func init() { pkgLastupdate = make(map[string]int64) @@ -55,7 +55,7 @@ func init() { func parserPkg(pkgRealpath, pkgpath string) error { rep := strings.NewReplacer("/", "_", ".", "_") - commentFilename = rep.Replace(pkgpath) + "_" + COMMENTFL + commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go" if !compareFile(pkgRealpath) { Info(pkgRealpath + " don't has updated") return nil From 1a1b0c14b98f44922c8310f5a3a09759fce047c3 Mon Sep 17 00:00:00 2001 From: supar Date: Thu, 6 Nov 2014 16:01:22 +0300 Subject: [PATCH 40/83] 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 41/83] 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 42/83] 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 43/83] 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 44/83] 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 45/83] 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 46/83] 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 47/83] 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 48/83] 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 49/83] 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 50/83] 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 51/83] 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 52/83] 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 53/83] =?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 54/83] =?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 55/83] 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 56/83] 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 57/83] 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 58/83] 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 59/83] 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 60/83] 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 61/83] 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 62/83] 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 8bd902814f5a7689c18ed4c6504b4991147edffc Mon Sep 17 00:00:00 2001 From: Hubery Date: Tue, 13 Jan 2015 11:09:43 +0800 Subject: [PATCH 63/83] Transaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit err处理写反了 --- orm/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/README.md b/orm/README.md index 356407c1..fa7fdca1 100644 --- a/orm/README.md +++ b/orm/README.md @@ -117,7 +117,7 @@ o.Begin() ... user := User{Name: "slene"} id, err := o.Insert(&user) -if err != nil { +if err == nil { o.Commit() } else { o.Rollback() From 73370ade90c53e477b6b9cb17d0c42cf3bd5d350 Mon Sep 17 00:00:00 2001 From: Kevin Mulvey Date: Mon, 2 Feb 2015 09:33:27 -0500 Subject: [PATCH 64/83] modular --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d7414b8..99c98792 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) -beego is an open-source, high-performance, modularity, full-stack web framework. +beego is an open-source, high-performance, modular, full-stack web framework. More info [beego.me](http://beego.me) From 1cc1d57f55cc5a56a4fae65fbb92db139d477bd1 Mon Sep 17 00:00:00 2001 From: Kevin Mulvey Date: Mon, 2 Feb 2015 09:33:59 -0500 Subject: [PATCH 65/83] development --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99c98792..7b650887 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ More info [beego.me](http://beego.me) * Auto API documents * Annotation router * Namespace -* Powerful develop tools +* Powerful development tools * Full stack for Web & API ## Documentation 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 66/83] 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 67/83] 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 68/83] 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 69/83] 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 70/83] 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 71/83] 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 72/83] 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 73/83] 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 74/83] 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 75/83] 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 76/83] 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 77/83] 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 78/83] 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 79/83] 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 80/83] 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 81/83] 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 From 2cee46ab2b23a716a450c378facc1c87665d7c70 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 27 Feb 2015 22:50:25 +0800 Subject: [PATCH 82/83] change the jQuery URL --- example/chat/views/index.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/chat/views/index.tpl b/example/chat/views/index.tpl index 75bffa62..31c11aa3 100644 --- a/example/chat/views/index.tpl +++ b/example/chat/views/index.tpl @@ -2,7 +2,7 @@ Chat Example - + +