diff --git a/.gitignore b/.gitignore index 39ae5706..9806457b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .DS_Store *.swp *.swo +beego.iml diff --git a/README.md b/README.md index 7b650887..b1b48e75 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,36 @@ [![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, modular, full-stack web framework. +beego is used for rapid development of RESTful APIs, web apps and backend services in Go. +It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. More info [beego.me](http://beego.me) -## Installation +##Quick Start +######Download and install go get github.com/astaxie/beego +######Create file `hello.go` +```go +package main + +import "github.com/astaxie/beego" + +func main(){ + beego.Run() +} +``` +######Build and run +```bash + go build hello.go + ./hello +``` +######Congratulations! +You just built your first beego app. +Open your browser and visit `http://localhost:8000`. +Please see [Documentation](http://beego.me/docs) for more. + ## Features * RESTful support diff --git a/admin.go b/admin.go index 64d7fe34..93087e12 100644 --- a/admin.go +++ b/admin.go @@ -101,11 +101,11 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["AppConfigPath"] = AppConfigPath m["StaticDir"] = StaticDir m["StaticExtensionsToGzip"] = StaticExtensionsToGzip - m["HttpAddr"] = HttpAddr - m["HttpPort"] = HttpPort - m["HttpTLS"] = EnableHttpTLS - m["HttpCertFile"] = HttpCertFile - m["HttpKeyFile"] = HttpKeyFile + m["HTTPAddr"] = HTTPAddr + m["HTTPPort"] = HTTPPort + m["HTTPTLS"] = EnableHTTPTLS + m["HTTPCertFile"] = HTTPCertFile + m["HTTPKeyFile"] = HTTPKeyFile m["RecoverPanic"] = RecoverPanic m["AutoRender"] = AutoRender m["ViewsPath"] = ViewsPath @@ -114,14 +114,14 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["SessionProvider"] = SessionProvider m["SessionName"] = SessionName m["SessionGCMaxLifetime"] = SessionGCMaxLifetime - m["SessionSavePath"] = SessionSavePath + m["SessionProviderConfig"] = SessionProviderConfig m["SessionCookieLifeTime"] = SessionCookieLifeTime - m["UseFcgi"] = UseFcgi + m["EnabelFcgi"] = EnabelFcgi m["MaxMemory"] = MaxMemory m["EnableGzip"] = EnableGzip m["DirectoryIndex"] = DirectoryIndex - m["HttpServerTimeOut"] = HttpServerTimeOut - m["ErrorsShow"] = ErrorsShow + m["HTTPServerTimeOut"] = HTTPServerTimeOut + m["EnableErrorsShow"] = EnableErrorsShow m["XSRFKEY"] = XSRFKEY m["EnableXSRF"] = EnableXSRF m["XSRFExpire"] = XSRFExpire @@ -130,8 +130,8 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["TemplateRight"] = TemplateRight m["BeegoServerName"] = BeegoServerName m["EnableAdmin"] = EnableAdmin - m["AdminHttpAddr"] = AdminHttpAddr - m["AdminHttpPort"] = AdminHttpPort + m["AdminHTTPAddr"] = AdminHTTPAddr + m["AdminHTTPPort"] = AdminHTTPPort tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) tmpl = template.Must(tmpl.Parse(configTpl)) @@ -314,14 +314,14 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { data["Content"] = result.String() if format == "json" && command == "gc summary" { - dataJson, err := json.Marshal(data) + dataJSON, err := json.Marshal(data) if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } rw.Header().Set("Content-Type", "application/json") - rw.Write(dataJson) + rw.Write(dataJSON) return } @@ -451,10 +451,10 @@ func (admin *adminApp) Run() { if len(toolbox.AdminTaskList) > 0 { toolbox.StartTask() } - addr := AdminHttpAddr + addr := AdminHTTPAddr - if AdminHttpPort != 0 { - addr = fmt.Sprintf("%s:%d", AdminHttpAddr, AdminHttpPort) + if AdminHTTPPort != 0 { + addr = fmt.Sprintf("%s:%d", AdminHTTPAddr, AdminHTTPPort) } for p, f := range admin.routers { http.Handle(p, f) diff --git a/app.go b/app.go index 8fc320ad..d10d436c 100644 --- a/app.go +++ b/app.go @@ -20,15 +20,26 @@ import ( "net/http" "net/http/fcgi" "os" + "path" "time" "github.com/astaxie/beego/grace" "github.com/astaxie/beego/utils" ) +var ( + // BeeApp is an application instance + BeeApp *App +) + +func init() { + // create beego application + BeeApp = NewApp() +} + // App defines beego application with a new PatternServeMux. type App struct { - Handlers *ControllerRegistor + Handlers *ControllerRegister Server *http.Server } @@ -41,10 +52,10 @@ func NewApp() *App { // Run beego application. func (app *App) Run() { - addr := HttpAddr + addr := HTTPAddr - if HttpPort != 0 { - addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort) + if HTTPPort != 0 { + addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPPort) } var ( @@ -53,8 +64,8 @@ func (app *App) Run() { ) endRunning := make(chan bool, 1) - if UseFcgi { - if UseStdIo { + if EnabelFcgi { + if EnableStdIo { err = fcgi.Serve(nil, app.Handlers) // standard I/O if err == nil { BeeLogger.Info("Use FCGI via standard I/O") @@ -62,7 +73,7 @@ func (app *App) Run() { BeeLogger.Info("Cannot use FCGI via standard I/O", err) } } else { - if HttpPort == 0 { + if HTTPPort == 0 { // remove the Socket file before start if utils.FileExists(addr) { os.Remove(addr) @@ -80,18 +91,18 @@ func (app *App) Run() { if Graceful { app.Server.Addr = addr app.Server.Handler = app.Handlers - app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second - if EnableHttpTLS { + app.Server.ReadTimeout = time.Duration(HTTPServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HTTPServerTimeOut) * time.Second + if EnableHTTPTLS { go func() { time.Sleep(20 * time.Microsecond) - if HttpsPort != 0 { - addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + if HTTPSPort != 0 { + addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPSPort) app.Server.Addr = addr } server := grace.NewServer(addr, app.Handlers) server.Server = app.Server - err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + err := server.ListenAndServeTLS(HTTPCertFile, HTTPKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) @@ -99,11 +110,11 @@ func (app *App) Run() { } }() } - if EnableHttpListen { + if EnableHTTPListen { go func() { server := grace.NewServer(addr, app.Handlers) server.Server = app.Server - if ListenTCP4 && HttpAddr == "" { + if ListenTCP4 && HTTPAddr == "" { server.Network = "tcp4" } err := server.ListenAndServe() @@ -117,17 +128,17 @@ func (app *App) Run() { } else { app.Server.Addr = addr app.Server.Handler = app.Handlers - app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + app.Server.ReadTimeout = time.Duration(HTTPServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HTTPServerTimeOut) * time.Second - if EnableHttpTLS { + if EnableHTTPTLS { go func() { time.Sleep(20 * time.Microsecond) - if HttpsPort != 0 { - app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + if HTTPSPort != 0 { + app.Server.Addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPSPort) } BeeLogger.Info("https server Running on %s", app.Server.Addr) - err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + err := app.Server.ListenAndServeTLS(HTTPCertFile, HTTPKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) time.Sleep(100 * time.Microsecond) @@ -136,11 +147,11 @@ func (app *App) Run() { }() } - if EnableHttpListen { + if EnableHTTPListen { go func() { app.Server.Addr = addr BeeLogger.Info("http server Running on %s", app.Server.Addr) - if ListenTCP4 && HttpAddr == "" { + if ListenTCP4 && HTTPAddr == "" { ln, err := net.Listen("tcp4", app.Server.Addr) if err != nil { BeeLogger.Critical("ListenAndServe: ", err) @@ -170,3 +181,182 @@ func (app *App) Run() { } <-endRunning } + +// Router adds a patterned controller handler to BeeApp. +// it's an alias method of App.Router. +// usage: +// simple router +// beego.Router("/admin", &admin.UserController{}) +// beego.Router("/admin/index", &admin.ArticleController{}) +// +// regex router +// +// beego.Router("/api/:id([0-9]+)", &controllers.RController{}) +// +// custom rules +// beego.Router("/api/list",&RestController{},"*:ListFood") +// beego.Router("/api/create",&RestController{},"post:CreateFood") +// beego.Router("/api/update",&RestController{},"put:UpdateFood") +// beego.Router("/api/delete",&RestController{},"delete:DeleteFood") +func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { + BeeApp.Handlers.Add(rootpath, c, mappingMethods...) + return BeeApp +} + +// Include will generate router file in the router/xxx.go from the controller's comments +// usage: +// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) +// type BankAccount struct{ +// beego.Controller +// } +// +// register the function +// func (b *BankAccount)Mapping(){ +// b.Mapping("ShowAccount" , b.ShowAccount) +// b.Mapping("ModifyAccount", b.ModifyAccount) +//} +// +// //@router /account/:id [get] +// func (b *BankAccount) ShowAccount(){ +// //logic +// } +// +// +// //@router /account/:id [post] +// func (b *BankAccount) ModifyAccount(){ +// //logic +// } +// +// the comments @router url methodlist +// url support all the function Router's pattern +// methodlist [get post head put delete options *] +func Include(cList ...ControllerInterface) *App { + BeeApp.Handlers.Include(cList...) + return BeeApp +} + +// RESTRouter adds a restful controller handler to BeeApp. +// its' controller implements beego.ControllerInterface and +// defines a param "pattern/:objectId" to visit each resource. +func RESTRouter(rootpath string, c ControllerInterface) *App { + Router(rootpath, c) + Router(path.Join(rootpath, ":objectId"), c) + return BeeApp +} + +// AutoRouter adds defined controller handler to BeeApp. +// it's same to App.AutoRouter. +// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page, +// visit the url /main/list to exec List function or /main/page to exec Page function. +func AutoRouter(c ControllerInterface) *App { + BeeApp.Handlers.AddAuto(c) + return BeeApp +} + +// AutoPrefix adds controller handler to BeeApp with prefix. +// it's same to App.AutoRouterWithPrefix. +// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page, +// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function. +func AutoPrefix(prefix string, c ControllerInterface) *App { + BeeApp.Handlers.AddAutoPrefix(prefix, c) + return BeeApp +} + +// Get used to register router for Get method +// usage: +// beego.Get("/", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Get(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Get(rootpath, f) + return BeeApp +} + +// Post used to register router for Post method +// usage: +// beego.Post("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Post(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Post(rootpath, f) + return BeeApp +} + +// Delete used to register router for Delete method +// usage: +// beego.Delete("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Delete(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Delete(rootpath, f) + return BeeApp +} + +// Put used to register router for Put method +// usage: +// beego.Put("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Put(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Put(rootpath, f) + return BeeApp +} + +// Head used to register router for Head method +// usage: +// beego.Head("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Head(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Head(rootpath, f) + return BeeApp +} + +// Options used to register router for Options method +// usage: +// beego.Options("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Options(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Options(rootpath, f) + return BeeApp +} + +// Patch used to register router for Patch method +// usage: +// beego.Patch("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Patch(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Patch(rootpath, f) + return BeeApp +} + +// Any used to register router for all methods +// usage: +// beego.Any("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Any(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Any(rootpath, f) + return BeeApp +} + +// Handler used to register a Handler router +// usage: +// beego.Handler("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Handler(rootpath string, h http.Handler, options ...interface{}) *App { + BeeApp.Handlers.Handler(rootpath, h, options...) + return BeeApp +} + +// InsertFilter adds a FilterFunc with pattern condition and action constant. +// The pos means action constant including +// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. +// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) +func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App { + BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...) + return BeeApp +} diff --git a/beego.go b/beego.go index cfebfbea..329e0713 100644 --- a/beego.go +++ b/beego.go @@ -12,243 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -// beego is an open-source, high-performance, modularity, full-stack web framework -// -// package main -// -// import "github.com/astaxie/beego" -// -// func main() { -// beego.Run() -// } -// -// more infomation: http://beego.me package beego import ( - "net/http" "os" - "path" "path/filepath" "strconv" "strings" - - "github.com/astaxie/beego/session" ) // beego web framework version. const VERSION = "1.5.0" -type hookfunc func() error //hook function to run -var hooks []hookfunc //hook function slice to store the hookfunc +//hook function to run +type hookfunc func() error -// Router adds a patterned controller handler to BeeApp. -// it's an alias method of App.Router. -// usage: -// simple router -// beego.Router("/admin", &admin.UserController{}) -// beego.Router("/admin/index", &admin.ArticleController{}) -// -// regex router -// -// beego.Router("/api/:id([0-9]+)", &controllers.RController{}) -// -// custom rules -// beego.Router("/api/list",&RestController{},"*:ListFood") -// beego.Router("/api/create",&RestController{},"post:CreateFood") -// beego.Router("/api/update",&RestController{},"put:UpdateFood") -// beego.Router("/api/delete",&RestController{},"delete:DeleteFood") -func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { - BeeApp.Handlers.Add(rootpath, c, mappingMethods...) - return BeeApp -} +var ( + hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc +) -// Router add list from -// usage: -// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) -// type BankAccount struct{ -// beego.Controller -// } -// -// register the function -// func (b *BankAccount)Mapping(){ -// b.Mapping("ShowAccount" , b.ShowAccount) -// b.Mapping("ModifyAccount", b.ModifyAccount) -//} -// -// //@router /account/:id [get] -// func (b *BankAccount) ShowAccount(){ -// //logic -// } -// -// -// //@router /account/:id [post] -// func (b *BankAccount) ModifyAccount(){ -// //logic -// } -// -// the comments @router url methodlist -// url support all the function Router's pattern -// methodlist [get post head put delete options *] -func Include(cList ...ControllerInterface) *App { - BeeApp.Handlers.Include(cList...) - return BeeApp -} - -// RESTRouter adds a restful controller handler to BeeApp. -// its' controller implements beego.ControllerInterface and -// defines a param "pattern/:objectId" to visit each resource. -func RESTRouter(rootpath string, c ControllerInterface) *App { - Router(rootpath, c) - Router(path.Join(rootpath, ":objectId"), c) - return BeeApp -} - -// AutoRouter adds defined controller handler to BeeApp. -// it's same to App.AutoRouter. -// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page, -// visit the url /main/list to exec List function or /main/page to exec Page function. -func AutoRouter(c ControllerInterface) *App { - BeeApp.Handlers.AddAuto(c) - return BeeApp -} - -// AutoPrefix adds controller handler to BeeApp with prefix. -// it's same to App.AutoRouterWithPrefix. -// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page, -// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function. -func AutoPrefix(prefix string, c ControllerInterface) *App { - BeeApp.Handlers.AddAutoPrefix(prefix, c) - return BeeApp -} - -// register router for Get method -// usage: -// beego.Get("/", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Get(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Get(rootpath, f) - return BeeApp -} - -// register router for Post method -// usage: -// beego.Post("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Post(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Post(rootpath, f) - return BeeApp -} - -// register router for Delete method -// usage: -// beego.Delete("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Delete(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Delete(rootpath, f) - return BeeApp -} - -// register router for Put method -// usage: -// beego.Put("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Put(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Put(rootpath, f) - return BeeApp -} - -// register router for Head method -// usage: -// beego.Head("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Head(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Head(rootpath, f) - return BeeApp -} - -// register router for Options method -// usage: -// beego.Options("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Options(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Options(rootpath, f) - return BeeApp -} - -// register router for Patch method -// usage: -// beego.Patch("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Patch(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Patch(rootpath, f) - return BeeApp -} - -// register router for all method -// usage: -// beego.Any("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Any(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Any(rootpath, f) - return BeeApp -} - -// register router for own Handler -// usage: -// beego.Handler("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Handler(rootpath string, h http.Handler, options ...interface{}) *App { - BeeApp.Handlers.Handler(rootpath, h, options...) - return BeeApp -} - -// SetViewsPath sets view directory path in beego application. -func SetViewsPath(path string) *App { - ViewsPath = path - return BeeApp -} - -// SetStaticPath sets static directory path and proper url pattern in beego application. -// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public". -func SetStaticPath(url string, path string) *App { - if !strings.HasPrefix(url, "/") { - url = "/" + url - } - url = strings.TrimRight(url, "/") - StaticDir[url] = path - return BeeApp -} - -// DelStaticPath removes the static folder setting in this url pattern in beego application. -func DelStaticPath(url string) *App { - if !strings.HasPrefix(url, "/") { - url = "/" + url - } - url = strings.TrimRight(url, "/") - delete(StaticDir, url) - return BeeApp -} - -// InsertFilter adds a FilterFunc with pattern condition and action constant. -// The pos means action constant including -// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. -// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) -func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App { - BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...) - return BeeApp -} - -// The hookfunc will run in beego.Run() +// AddAPPStartHook is used to register the hookfunc +// The hookfuncs will run in beego.Run() // such as sessionInit, middlerware start, buildtemplate, admin start func AddAPPStartHook(hf hookfunc) { hooks = append(hooks, hf) @@ -259,25 +43,22 @@ func AddAPPStartHook(hf hookfunc) { // beego.Run(":8089") // beego.Run("127.0.0.1:8089") func Run(params ...string) { + initBeforeHTTPRun() + if len(params) > 0 && params[0] != "" { strs := strings.Split(params[0], ":") if len(strs) > 0 && strs[0] != "" { - HttpAddr = strs[0] + HTTPAddr = strs[0] } if len(strs) > 1 && strs[1] != "" { - HttpPort, _ = strconv.Atoi(strs[1]) + HTTPPort, _ = strconv.Atoi(strs[1]) } } - initBeforeHttpRun() - - if EnableAdmin { - go beeAdminApp.Run() - } BeeApp.Run() } -func initBeforeHttpRun() { +func initBeforeHTTPRun() { // if AppConfigPath not In the conf/app.conf reParse config if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { err := ParseConfig() @@ -287,53 +68,22 @@ func initBeforeHttpRun() { } } - //init mime - AddAPPStartHook(initMime) + //init hooks + AddAPPStartHook(registerMime) + AddAPPStartHook(registerDefaultErrorHandler) + AddAPPStartHook(registerSession) + AddAPPStartHook(registerDocs) + AddAPPStartHook(registerTemplate) + AddAPPStartHook(registerAdmin) - // do hooks function for _, hk := range hooks { - err := hk() - if err != nil { + if err := hk(); err != nil { panic(err) } } - - if SessionOn { - var err error - sessionConfig := AppConfig.String("sessionConfig") - if sessionConfig == "" { - sessionConfig = `{"cookieName":"` + SessionName + `",` + - `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + - `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + - `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + - `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + - `"domain":"` + SessionDomain + `",` + - `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` - } - GlobalSessions, err = session.NewManager(SessionProvider, - sessionConfig) - if err != nil { - panic(err) - } - go GlobalSessions.GC() - } - - err := BuildTemplate(ViewsPath) - if err != nil { - if RunMode == "dev" { - Warn(err) - } - } - - registerDefaultErrorHandler() - - if EnableDocs { - Get("/docs", serverDocs) - Get("/docs/*", serverDocs) - } } -// this function is for test package init +// TestBeegoInit is for test package init func TestBeegoInit(apppath string) { AppPath = apppath os.Setenv("BEEGO_RUNMODE", "test") @@ -344,9 +94,5 @@ func TestBeegoInit(apppath string) { Info(err) } os.Chdir(AppPath) - initBeforeHttpRun() -} - -func init() { - hooks = make([]hookfunc, 0) + initBeforeHTTPRun() } diff --git a/cache/README.md b/cache/README.md index 72d0d1c5..4152c57e 100644 --- a/cache/README.md +++ b/cache/README.md @@ -43,7 +43,7 @@ interval means the gc time. The cache will check at each time interval, whether ## Memcache adapter -Memcache adapter use the vitess's [Memcache](http://code.google.com/p/vitess/go/memcache) client. +Memcache adapter use the [gomemcache](http://github.com/bradfitz/gomemcache) client. Configure like this: @@ -52,7 +52,7 @@ Configure like this: ## Redis adapter -Redis adapter use the [redigo](http://github.com/garyburd/redigo/redis) client. +Redis adapter use the [redigo](http://github.com/garyburd/redigo) client. Configure like this: diff --git a/cache/cache.go b/cache/cache.go index 7ca87802..6413e210 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Package cache provide a Cache interface and some implemetn engine // Usage: // // import( @@ -80,7 +81,7 @@ func Register(name string, adapter Cache) { adapters[name] = adapter } -// Create a new cache driver by adapter name and config string. +// NewCache Create a new cache driver by adapter name and config string. // config need to be correct JSON as string: {"interval":360}. // it will start gc automatically. func NewCache(adapterName, config string) (adapter Cache, err error) { diff --git a/cache/conv.go b/cache/conv.go index 724abfd2..16ef1106 100644 --- a/cache/conv.go +++ b/cache/conv.go @@ -19,7 +19,7 @@ import ( "strconv" ) -// convert interface to string. +// GetString convert interface to string. func GetString(v interface{}) string { switch result := v.(type) { case string: @@ -34,7 +34,7 @@ func GetString(v interface{}) string { return "" } -// convert interface to int. +// GetInt convert interface to int. func GetInt(v interface{}) int { switch result := v.(type) { case int: @@ -52,7 +52,7 @@ func GetInt(v interface{}) int { return 0 } -// convert interface to int64. +// GetInt64 convert interface to int64. func GetInt64(v interface{}) int64 { switch result := v.(type) { case int: @@ -71,7 +71,7 @@ func GetInt64(v interface{}) int64 { return 0 } -// convert interface to float64. +// GetFloat64 convert interface to float64. func GetFloat64(v interface{}) float64 { switch result := v.(type) { case float64: @@ -85,7 +85,7 @@ func GetFloat64(v interface{}) float64 { return 0 } -// convert interface to bool. +// GetBool convert interface to bool. func GetBool(v interface{}) bool { switch result := v.(type) { case bool: @@ -99,7 +99,7 @@ func GetBool(v interface{}) bool { return false } -// convert interface to byte slice. +// getByteArray convert interface to byte slice. func getByteArray(v interface{}) []byte { switch result := v.(type) { case []byte: diff --git a/cache/conv_test.go b/cache/conv_test.go index 267bb0c9..c87ffe84 100644 --- a/cache/conv_test.go +++ b/cache/conv_test.go @@ -27,7 +27,7 @@ func TestGetString(t *testing.T) { if "test2" != GetString(t2) { t.Error("get string from byte array error") } - var t3 int = 1 + var t3 = 1 if "1" != GetString(t3) { t.Error("get string from int error") } @@ -35,7 +35,7 @@ func TestGetString(t *testing.T) { if "1" != GetString(t4) { t.Error("get string from int64 error") } - var t5 float64 = 1.1 + var t5 = 1.1 if "1.1" != GetString(t5) { t.Error("get string from float64 error") } @@ -46,7 +46,7 @@ func TestGetString(t *testing.T) { } func TestGetInt(t *testing.T) { - var t1 int = 1 + var t1 = 1 if 1 != GetInt(t1) { t.Error("get int from int error") } @@ -69,7 +69,7 @@ func TestGetInt(t *testing.T) { func TestGetInt64(t *testing.T) { var i int64 = 1 - var t1 int = 1 + var t1 = 1 if i != GetInt64(t1) { t.Error("get int64 from int error") } @@ -91,12 +91,12 @@ func TestGetInt64(t *testing.T) { } func TestGetFloat64(t *testing.T) { - var f float64 = 1.11 + var f = 1.11 var t1 float32 = 1.11 if f != GetFloat64(t1) { t.Error("get float64 from float32 error") } - var t2 float64 = 1.11 + var t2 = 1.11 if f != GetFloat64(t2) { t.Error("get float64 from float64 error") } @@ -106,7 +106,7 @@ func TestGetFloat64(t *testing.T) { } var f2 float64 = 1 - var t4 int = 1 + var t4 = 1 if f2 != GetFloat64(t4) { t.Error("get float64 from int error") } diff --git a/cache/file.go b/cache/file.go index 65f114f3..9233a852 100644 --- a/cache/file.go +++ b/cache/file.go @@ -41,11 +41,12 @@ type FileCacheItem struct { Expired int64 } +// FileCache Config var ( - FileCachePath string = "cache" // cache directory - FileCacheFileSuffix string = ".bin" // cache file suffix - FileCacheDirectoryLevel int = 2 // cache file deep level if auto generated cache files. - FileCacheEmbedExpiry int64 = 0 // cache expire time, default is no expire forever. + FileCachePath = "cache" // cache directory + FileCacheFileSuffix = ".bin" // cache file suffix + FileCacheDirectoryLevel = 2 // cache file deep level if auto generated cache files. + FileCacheEmbedExpiry int64 // cache expire time, default is no expire forever. ) // FileCache is cache adapter for file storage. @@ -56,14 +57,14 @@ type FileCache struct { EmbedExpiry int } -// Create new file cache with no config. +// NewFileCache Create new file cache with no config. // the level and expiry need set in method StartAndGC as config string. func NewFileCache() *FileCache { // return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix} return &FileCache{} } -// Start and begin gc for file cache. +// StartAndGC will start and begin gc for file cache. // the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0} func (fc *FileCache) StartAndGC(config string) error { @@ -120,12 +121,12 @@ func (fc *FileCache) getCacheFileName(key string) string { // Get value from file cache. // if non-exist or expired, return empty string. func (fc *FileCache) Get(key string) interface{} { - fileData, err := File_get_contents(fc.getCacheFileName(key)) + fileData, err := FileGetContents(fc.getCacheFileName(key)) if err != nil { return "" } var to FileCacheItem - Gob_decode(fileData, &to) + GobDecode(fileData, &to) if to.Expired < time.Now().Unix() { return "" } @@ -155,11 +156,11 @@ func (fc *FileCache) Put(key string, val interface{}, timeout int64) error { item.Expired = time.Now().Unix() + timeout } item.Lastaccess = time.Now().Unix() - data, err := Gob_encode(item) + data, err := GobEncode(item) if err != nil { return err } - return File_put_contents(fc.getCacheFileName(key), data) + return FilePutContents(fc.getCacheFileName(key), data) } // Delete file cache value. @@ -171,7 +172,7 @@ func (fc *FileCache) Delete(key string) error { return nil } -// Increase cached int value. +// Incr will increase cached int value. // fc value is saving forever unless Delete. func (fc *FileCache) Incr(key string) error { data := fc.Get(key) @@ -185,7 +186,7 @@ func (fc *FileCache) Incr(key string) error { return nil } -// Decrease cached int value. +// Decr will decrease cached int value. func (fc *FileCache) Decr(key string) error { data := fc.Get(key) var decr int @@ -198,13 +199,13 @@ func (fc *FileCache) Decr(key string) error { return nil } -// Check value is exist. +// IsExist check value is exist. func (fc *FileCache) IsExist(key string) bool { ret, _ := exists(fc.getCacheFileName(key)) return ret } -// Clean cached files. +// ClearAll will clean cached files. // not implemented. func (fc *FileCache) ClearAll() error { return nil @@ -222,9 +223,9 @@ func exists(path string) (bool, error) { return false, err } -// Get bytes to file. +// FileGetContents Get bytes to file. // if non-exist, create this file. -func File_get_contents(filename string) (data []byte, e error) { +func FileGetContents(filename string) (data []byte, e error) { f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm) if e != nil { return @@ -242,9 +243,9 @@ func File_get_contents(filename string) (data []byte, e error) { return } -// Put bytes to file. +// FilePutContents Put bytes to file. // if non-exist, create this file. -func File_put_contents(filename string, content []byte) error { +func FilePutContents(filename string, content []byte) error { fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { return err @@ -254,8 +255,8 @@ func File_put_contents(filename string, content []byte) error { return err } -// Gob encodes file cache item. -func Gob_encode(data interface{}) ([]byte, error) { +// GobEncode Gob encodes file cache item. +func GobEncode(data interface{}) ([]byte, error) { buf := bytes.NewBuffer(nil) enc := gob.NewEncoder(buf) err := enc.Encode(data) @@ -265,8 +266,8 @@ func Gob_encode(data interface{}) ([]byte, error) { return buf.Bytes(), err } -// Gob decodes file cache item. -func Gob_decode(data []byte, to *FileCacheItem) error { +// GobDecode Gob decodes file cache item. +func GobDecode(data []byte, to *FileCacheItem) error { buf := bytes.NewBuffer(data) dec := gob.NewDecoder(buf) return dec.Decode(&to) diff --git a/cache/memcache/memcache.go b/cache/memcache/memcache.go index c6829054..643fb130 100644 --- a/cache/memcache/memcache.go +++ b/cache/memcache/memcache.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// package memcahe for cache provider +// Package memcache for cache provider // // depend on github.com/bradfitz/gomemcache/memcache // @@ -39,19 +39,19 @@ import ( "github.com/astaxie/beego/cache" ) -// Memcache adapter. -type MemcacheCache struct { +// Cache Memcache adapter. +type Cache struct { conn *memcache.Client conninfo []string } -// create new memcache adapter. -func NewMemCache() *MemcacheCache { - return &MemcacheCache{} +// NewMemCache create new memcache adapter. +func NewMemCache() *Cache { + return &Cache{} } -// get value from memcache. -func (rc *MemcacheCache) Get(key string) interface{} { +// Get get value from memcache. +func (rc *Cache) Get(key string) interface{} { if rc.conn == nil { if err := rc.connectInit(); err != nil { return err @@ -63,8 +63,8 @@ func (rc *MemcacheCache) Get(key string) interface{} { return nil } -// get value from memcache. -func (rc *MemcacheCache) GetMulti(keys []string) []interface{} { +// GetMulti get value from memcache. +func (rc *Cache) GetMulti(keys []string) []interface{} { size := len(keys) var rv []interface{} if rc.conn == nil { @@ -81,16 +81,15 @@ func (rc *MemcacheCache) GetMulti(keys []string) []interface{} { rv = append(rv, string(v.Value)) } return rv - } else { - for i := 0; i < size; i++ { - rv = append(rv, err) - } - return rv } + for i := 0; i < size; i++ { + rv = append(rv, err) + } + return rv } -// put value to memcache. only support string. -func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error { +// Put put value to memcache. only support string. +func (rc *Cache) Put(key string, val interface{}, timeout int64) error { if rc.conn == nil { if err := rc.connectInit(); err != nil { return err @@ -104,8 +103,8 @@ func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error { return rc.conn.Set(&item) } -// delete value in memcache. -func (rc *MemcacheCache) Delete(key string) error { +// Delete delete value in memcache. +func (rc *Cache) Delete(key string) error { if rc.conn == nil { if err := rc.connectInit(); err != nil { return err @@ -114,8 +113,8 @@ func (rc *MemcacheCache) Delete(key string) error { return rc.conn.Delete(key) } -// increase counter. -func (rc *MemcacheCache) Incr(key string) error { +// Incr increase counter. +func (rc *Cache) Incr(key string) error { if rc.conn == nil { if err := rc.connectInit(); err != nil { return err @@ -125,8 +124,8 @@ func (rc *MemcacheCache) Incr(key string) error { return err } -// decrease counter. -func (rc *MemcacheCache) Decr(key string) error { +// Decr decrease counter. +func (rc *Cache) Decr(key string) error { if rc.conn == nil { if err := rc.connectInit(); err != nil { return err @@ -136,8 +135,8 @@ func (rc *MemcacheCache) Decr(key string) error { return err } -// check value exists in memcache. -func (rc *MemcacheCache) IsExist(key string) bool { +// IsExist check value exists in memcache. +func (rc *Cache) IsExist(key string) bool { if rc.conn == nil { if err := rc.connectInit(); err != nil { return false @@ -150,8 +149,8 @@ func (rc *MemcacheCache) IsExist(key string) bool { return true } -// clear all cached in memcache. -func (rc *MemcacheCache) ClearAll() error { +// ClearAll clear all cached in memcache. +func (rc *Cache) ClearAll() error { if rc.conn == nil { if err := rc.connectInit(); err != nil { return err @@ -160,10 +159,10 @@ func (rc *MemcacheCache) ClearAll() error { return rc.conn.FlushAll() } -// start memcache adapter. +// StartAndGC start memcache adapter. // config string is like {"conn":"connection info"}. // if connecting error, return. -func (rc *MemcacheCache) StartAndGC(config string) error { +func (rc *Cache) StartAndGC(config string) error { var cf map[string]string json.Unmarshal([]byte(config), &cf) if _, ok := cf["conn"]; !ok { @@ -179,7 +178,7 @@ func (rc *MemcacheCache) StartAndGC(config string) error { } // connect to memcache and keep the connection. -func (rc *MemcacheCache) connectInit() error { +func (rc *Cache) connectInit() error { rc.conn = memcache.New(rc.conninfo...) return nil } diff --git a/cache/memcache/memcache_test.go b/cache/memcache/memcache_test.go index 0523ae85..27b085e6 100644 --- a/cache/memcache/memcache_test.go +++ b/cache/memcache/memcache_test.go @@ -23,7 +23,7 @@ import ( "time" ) -func TestRedisCache(t *testing.T) { +func TestMemcacheCache(t *testing.T) { bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`) if err != nil { t.Error("init err") diff --git a/cache/memory.go b/cache/memory.go index b6657048..ce1afa5f 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -23,18 +23,18 @@ import ( ) var ( - // clock time of recycling the expired cache items in memory. - DefaultEvery int = 60 // 1 minute + // DefaultEvery means the clock time of recycling the expired cache items in memory. + DefaultEvery = 60 // 1 minute ) -// Memory cache item. +// MemoryItem store enery cache item. type MemoryItem struct { val interface{} Lastaccess time.Time expired int64 } -// Memory cache adapter. +// MemoryCache is Memory cache adapter. // it contains a RW locker for safe map storage. type MemoryCache struct { lock sync.RWMutex @@ -87,7 +87,7 @@ func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error return nil } -/// Delete cache in memory. +// Delete cache in memory. func (bc *MemoryCache) Delete(name string) error { bc.lock.Lock() defer bc.lock.Unlock() @@ -101,7 +101,7 @@ func (bc *MemoryCache) Delete(name string) error { return nil } -// Increase cache counter in memory. +// Incr increase cache counter in memory. // it supports int,int64,int32,uint,uint64,uint32. func (bc *MemoryCache) Incr(key string) error { bc.lock.RLock() @@ -129,7 +129,7 @@ func (bc *MemoryCache) Incr(key string) error { return nil } -// Decrease counter in memory. +// Decr decrease counter in memory. func (bc *MemoryCache) Decr(key string) error { bc.lock.RLock() defer bc.lock.RUnlock() @@ -168,7 +168,7 @@ func (bc *MemoryCache) Decr(key string) error { return nil } -// check cache exist in memory. +// IsExist check cache exist in memory. func (bc *MemoryCache) IsExist(name string) bool { bc.lock.RLock() defer bc.lock.RUnlock() @@ -176,7 +176,7 @@ func (bc *MemoryCache) IsExist(name string) bool { return ok } -// delete all cache in memory. +// ClearAll will delete all cache in memory. func (bc *MemoryCache) ClearAll() error { bc.lock.Lock() defer bc.lock.Unlock() @@ -184,7 +184,7 @@ func (bc *MemoryCache) ClearAll() error { return nil } -// start memory cache. it will check expiration in every clock time. +// StartAndGC start memory cache. it will check expiration in every clock time. func (bc *MemoryCache) StartAndGC(config string) error { var cf map[string]int json.Unmarshal([]byte(config), &cf) @@ -213,13 +213,13 @@ func (bc *MemoryCache) vaccuum() { return } for name := range bc.items { - bc.item_expired(name) + bc.itemExpired(name) } } } -// item_expired returns true if an item is expired. -func (bc *MemoryCache) item_expired(name string) bool { +// itemExpired returns true if an item is expired. +func (bc *MemoryCache) itemExpired(name string) bool { bc.lock.Lock() defer bc.lock.Unlock() itm, ok := bc.items[name] diff --git a/cache/redis/redis.go b/cache/redis/redis.go index d14b1ada..ed12cf26 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// package redis for cache provider +// Package redis for cache provider // // depend on github.com/garyburd/redigo/redis // @@ -41,12 +41,12 @@ import ( ) var ( - // the collection name of redis for cache adapter. - DefaultKey string = "beecacheRedis" + // DefaultKey the collection name of redis for cache adapter. + DefaultKey = "beecacheRedis" ) -// Redis cache adapter. -type RedisCache struct { +// Cache is Redis cache adapter. +type Cache struct { p *redis.Pool // redis connection pool conninfo string dbNum int @@ -54,13 +54,13 @@ type RedisCache struct { password string } -// create new redis cache with default collection name. -func NewRedisCache() *RedisCache { - return &RedisCache{key: DefaultKey} +// NewRedisCache create new redis cache with default collection name. +func NewRedisCache() *Cache { + return &Cache{key: DefaultKey} } // actually do the redis cmds -func (rc *RedisCache) do(commandName string, args ...interface{}) (reply interface{}, err error) { +func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) { c := rc.p.Get() defer c.Close() @@ -68,7 +68,7 @@ func (rc *RedisCache) do(commandName string, args ...interface{}) (reply interfa } // Get cache from redis. -func (rc *RedisCache) Get(key string) interface{} { +func (rc *Cache) Get(key string) interface{} { if v, err := rc.do("GET", key); err == nil { return v } @@ -76,7 +76,7 @@ func (rc *RedisCache) Get(key string) interface{} { } // GetMulti get cache from redis. -func (rc *RedisCache) GetMulti(keys []string) []interface{} { +func (rc *Cache) GetMulti(keys []string) []interface{} { size := len(keys) var rv []interface{} c := rc.p.Get() @@ -108,8 +108,8 @@ ERROR: return rv } -// put cache to redis. -func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { +// Put put cache to redis. +func (rc *Cache) Put(key string, val interface{}, timeout int64) error { var err error if _, err = rc.do("SETEX", key, timeout, val); err != nil { return err @@ -121,8 +121,8 @@ func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { return err } -// delete cache in redis. -func (rc *RedisCache) Delete(key string) error { +// Delete delete cache in redis. +func (rc *Cache) Delete(key string) error { var err error if _, err = rc.do("DEL", key); err != nil { return err @@ -131,8 +131,8 @@ func (rc *RedisCache) Delete(key string) error { return err } -// check cache's existence in redis. -func (rc *RedisCache) IsExist(key string) bool { +// IsExist check cache's existence in redis. +func (rc *Cache) IsExist(key string) bool { v, err := redis.Bool(rc.do("EXISTS", key)) if err != nil { return false @@ -145,20 +145,20 @@ func (rc *RedisCache) IsExist(key string) bool { return v } -// increase counter in redis. -func (rc *RedisCache) Incr(key string) error { +// Incr increase counter in redis. +func (rc *Cache) Incr(key string) error { _, err := redis.Bool(rc.do("INCRBY", key, 1)) return err } -// decrease counter in redis. -func (rc *RedisCache) Decr(key string) error { +// Decr decrease counter in redis. +func (rc *Cache) Decr(key string) error { _, err := redis.Bool(rc.do("INCRBY", key, -1)) return err } -// clean all cache in redis. delete this redis collection. -func (rc *RedisCache) ClearAll() error { +// ClearAll clean all cache in redis. delete this redis collection. +func (rc *Cache) ClearAll() error { cachedKeys, err := redis.Strings(rc.do("HKEYS", rc.key)) if err != nil { return err @@ -172,11 +172,11 @@ func (rc *RedisCache) ClearAll() error { return err } -// start redis cache adapter. +// StartAndGC start redis cache adapter. // 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 { +func (rc *Cache) StartAndGC(config string) error { var cf map[string]string json.Unmarshal([]byte(config), &cf) @@ -206,7 +206,7 @@ func (rc *RedisCache) StartAndGC(config string) error { } // connect to redis. -func (rc *RedisCache) connectInit() { +func (rc *Cache) connectInit() { dialFunc := func() (c redis.Conn, err error) { c, err = redis.Dial("tcp", rc.conninfo) if err != nil { diff --git a/config.go b/config.go index 09d0df24..ae334cd7 100644 --- a/config.go +++ b/config.go @@ -29,60 +29,115 @@ import ( ) var ( - BeeApp *App // beego application - AppName string - AppPath string - workPath string - AppConfigPath string - StaticDir map[string]string - TemplateCache map[string]*template.Template // template caching map - StaticExtensionsToGzip []string // files with should be compressed with gzip (.js,.css,etc) - EnableHttpListen bool - HttpAddr string - HttpPort int - ListenTCP4 bool - EnableHttpTLS bool - HttpsPort int - HttpCertFile string - HttpKeyFile string - RecoverPanic bool // flag of auto recover panic - AutoRender bool // flag of render template automatically - ViewsPath string - AppConfig *beegoAppConfig - RunMode string // run mode, "dev" or "prod" - GlobalSessions *session.Manager // global session mananger - SessionOn bool // flag of starting session auto. default is false. - SessionProvider string // default session provider, memory, mysql , redis ,etc. - 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. - SessionCookieLifeTime int // the life time of session id in cookie. - SessionAutoSetCookie bool // auto setcookie - SessionDomain string // the cookie domain default is empty - UseFcgi bool - UseStdIo bool - MaxMemory int64 - EnableGzip bool // flag of enable gzip - DirectoryIndex bool // flag of display directory index. default is false. - HttpServerTimeOut int64 - ErrorsShow bool // flag of show errors in page. if true, show error and trace info in page rendered with error template. - XSRFKEY string // xsrf hash salt string. - EnableXSRF bool // flag of enable xsrf. - XSRFExpire int // the expiry of xsrf value. - CopyRequestBody bool // flag of copy raw request body in context. - TemplateLeft string - TemplateRight string - BeegoServerName string // beego server name exported in response header. - EnableAdmin bool // flag of enable admin module to log every request info. - AdminHttpAddr string // http server configurations for admin module. - AdminHttpPort int - FlashName string // name of the flash variable found in response header and cookie - FlashSeperator string // used to seperate flash key:value - AppConfigProvider string // config provider - EnableDocs bool // enable generate docs & server docs API Swagger - RouterCaseSensitive bool // router case sensitive default is true - AccessLogs bool // print access logs, default is false - Graceful bool // use graceful start the server + // AccessLogs represent whether output the access logs, default is false + AccessLogs bool + // AdminHTTPAddr is address for admin + AdminHTTPAddr string + // AdminHTTPPort is listens port for admin + AdminHTTPPort int + // AppConfig is the instance of Config, store the config information from file + AppConfig *beegoAppConfig + // AppName represent Application name, always the project folder name + AppName string + // AppPath is the path to the application + AppPath string + // AppConfigPath is the path to the config files + AppConfigPath string + // AppConfigProvider is the provider for the config, default is ini + AppConfigProvider string + // AutoRender is a flag of render template automatically. It's always turn off in API application + // default is true + AutoRender bool + // BeegoServerName exported in response header. + BeegoServerName string + // CopyRequestBody is just useful for raw request body in context. default is false + CopyRequestBody bool + // DirectoryIndex wheather display directory index. default is false. + DirectoryIndex bool + // EnableAdmin means turn on admin module to log every request info. + EnableAdmin bool + // EnableDocs enable generate docs & server docs API Swagger + EnableDocs bool + // EnableErrorsShow wheather show errors in page. if true, show error and trace info in page rendered with error template. + EnableErrorsShow bool + // EnabelFcgi turn on the fcgi Listen, default is false + EnabelFcgi bool + // EnableGzip means gzip the response + EnableGzip bool + // EnableHTTPListen represent whether turn on the HTTP, default is true + EnableHTTPListen bool + // EnableHTTPTLS represent whether turn on the HTTPS, default is true + EnableHTTPTLS bool + // EnableStdIo works with EnabelFcgi Use FCGI via standard I/O + EnableStdIo bool + // EnableXSRF whether turn on xsrf. default is false + EnableXSRF bool + // FlashName is the name of the flash variable found in response header and cookie + FlashName string + // FlashSeperator used to seperate flash key:value, default is BEEGOFLASH + FlashSeperator string + // GlobalSessions is the instance for the session manager + GlobalSessions *session.Manager + // Graceful means use graceful module to start the server + Graceful bool + // workPath is always the same as AppPath, but sometime when it started with other + // program, like supervisor + workPath string + // ListenTCP4 represent only Listen in TCP4, default is false + ListenTCP4 bool + // MaxMemory The whole request body is parsed and up to a total of maxMemory + // bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files + MaxMemory int64 + // HTTPAddr is the TCP network address addr for HTTP + HTTPAddr string + // HTTPPort is listens port for HTTP + HTTPPort int + // HTTPSPort is listens port for HTTPS + HTTPSPort int + // HTTPCertFile is the path to certificate file + HTTPCertFile string + // HTTPKeyFile is the path to private key file + HTTPKeyFile string + // HTTPServerTimeOut HTTP server timeout. default is 0, no timeout + HTTPServerTimeOut int64 + // RecoverPanic is a flag for auto recover panic, default is true + RecoverPanic bool + // RouterCaseSensitive means whether router case sensitive, default is true + RouterCaseSensitive bool + // RunMode represent the staging, "dev" or "prod" + RunMode string + // SessionOn means whether turn on the session auto when application started. default is false. + SessionOn bool + // SessionProvider means session provider, e.q memory, mysql, redis,etc. + SessionProvider string + // SessionName is the cookie name when saving session id into cookie. + SessionName string + // SessionGCMaxLifetime for auto cleaning expired session. + SessionGCMaxLifetime int64 + // SessionProviderConfig is for the provider config, define save path or connection info. + SessionProviderConfig string + // SessionCookieLifeTime means the life time of session id in cookie. + SessionCookieLifeTime int + // SessionAutoSetCookie auto setcookie + SessionAutoSetCookie bool + // SessionDomain means the cookie domain default is empty + SessionDomain string + // StaticDir store the static path, key is path, value is the folder + StaticDir map[string]string + // StaticExtensionsToGzip stores the extensions which need to gzip(.js,.css,etc) + StaticExtensionsToGzip []string + // TemplateCache store the caching template + TemplateCache map[string]*template.Template + // TemplateLeft left delimiter + TemplateLeft string + // TemplateRight right delimiter + TemplateRight string + // ViewsPath means the template folder + ViewsPath string + // XSRFKEY xsrf hash salt string. + XSRFKEY string + // XSRFExpire is the expiry of xsrf value. + XSRFExpire int ) type beegoAppConfig struct { @@ -215,9 +270,6 @@ func (b *beegoAppConfig) SaveConfigFile(filename string) error { } func init() { - // create beego application - BeeApp = NewApp() - workPath, _ = os.Getwd() workPath, _ = filepath.Abs(workPath) // initialize default configurations @@ -243,12 +295,12 @@ func init() { TemplateCache = make(map[string]*template.Template) // set this to 0.0.0.0 to make this app available to externally - EnableHttpListen = true //default enable http Listen + EnableHTTPListen = true //default enable http Listen - HttpAddr = "" - HttpPort = 8080 + HTTPAddr = "" + HTTPPort = 8080 - HttpsPort = 10443 + HTTPSPort = 10443 AppName = "beego" @@ -264,20 +316,15 @@ func init() { SessionProvider = "memory" SessionName = "beegosessionID" SessionGCMaxLifetime = 3600 - SessionSavePath = "" + SessionProviderConfig = "" SessionCookieLifeTime = 0 //set cookie default is the brower life SessionAutoSetCookie = true - UseFcgi = false - UseStdIo = false - MaxMemory = 1 << 26 //64MB - EnableGzip = false + HTTPServerTimeOut = 0 - HttpServerTimeOut = 0 - - ErrorsShow = true + EnableErrorsShow = true XSRFKEY = "beegoxsrf" XSRFExpire = 0 @@ -288,8 +335,8 @@ func init() { BeegoServerName = "beegoServer:" + VERSION EnableAdmin = false - AdminHttpAddr = "127.0.0.1" - AdminHttpPort = 8088 + AdminHTTPAddr = "127.0.0.1" + AdminHTTPPort = 8088 FlashName = "BEEGO_FLASH" FlashSeperator = "BEEGOFLASH" @@ -330,18 +377,18 @@ func ParseConfig() (err error) { RunMode = runmode } - HttpAddr = AppConfig.String("HttpAddr") + HTTPAddr = AppConfig.String("HTTPAddr") - if v, err := AppConfig.Int("HttpPort"); err == nil { - HttpPort = v + if v, err := AppConfig.Int("HTTPPort"); err == nil { + HTTPPort = v } if v, err := AppConfig.Bool("ListenTCP4"); err == nil { ListenTCP4 = v } - if v, err := AppConfig.Bool("EnableHttpListen"); err == nil { - EnableHttpListen = v + if v, err := AppConfig.Bool("EnableHTTPListen"); err == nil { + EnableHTTPListen = v } if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil { @@ -376,8 +423,8 @@ func ParseConfig() (err error) { SessionName = sessName } - if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" { - SessionSavePath = sesssavepath + if sessProvConfig := AppConfig.String("SessionProviderConfig"); sessProvConfig != "" { + SessionProviderConfig = sessProvConfig } if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { @@ -388,8 +435,8 @@ func ParseConfig() (err error) { SessionCookieLifeTime = sesscookielifetime } - if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil { - UseFcgi = usefcgi + if enabelFcgi, err := AppConfig.Bool("EnabelFcgi"); err == nil { + EnabelFcgi = enabelFcgi } if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil { @@ -400,12 +447,12 @@ func ParseConfig() (err error) { DirectoryIndex = directoryindex } - if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil { - HttpServerTimeOut = timeout + if timeout, err := AppConfig.Int64("HTTPServerTimeOut"); err == nil { + HTTPServerTimeOut = timeout } - if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil { - ErrorsShow = errorsshow + if errorsshow, err := AppConfig.Bool("EnableErrorsShow"); err == nil { + EnableErrorsShow = errorsshow } if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil { @@ -432,20 +479,20 @@ func ParseConfig() (err error) { TemplateRight = tplright } - if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil { - EnableHttpTLS = httptls + if httptls, err := AppConfig.Bool("EnableHTTPTLS"); err == nil { + EnableHTTPTLS = httptls } - if httpsport, err := AppConfig.Int("HttpsPort"); err == nil { - HttpsPort = httpsport + if httpsport, err := AppConfig.Int("HTTPSPort"); err == nil { + HTTPSPort = httpsport } - if certfile := AppConfig.String("HttpCertFile"); certfile != "" { - HttpCertFile = certfile + if certfile := AppConfig.String("HTTPCertFile"); certfile != "" { + HTTPCertFile = certfile } - if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" { - HttpKeyFile = keyfile + if keyfile := AppConfig.String("HTTPKeyFile"); keyfile != "" { + HTTPKeyFile = keyfile } if serverName := AppConfig.String("BeegoServerName"); serverName != "" { @@ -495,12 +542,12 @@ func ParseConfig() (err error) { EnableAdmin = enableadmin } - if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" { - AdminHttpAddr = adminhttpaddr + if adminhttpaddr := AppConfig.String("AdminHTTPAddr"); adminhttpaddr != "" { + AdminHTTPAddr = adminhttpaddr } - if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil { - AdminHttpPort = adminhttpport + if adminhttpport, err := AppConfig.Int("AdminHTTPPort"); err == nil { + AdminHTTPPort = adminhttpport } if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil { diff --git a/config/config.go b/config/config.go index 8d9261b8..d0eca747 100644 --- a/config/config.go +++ b/config/config.go @@ -88,12 +88,12 @@ func Register(name string, adapter Config) { // adapterName is ini/json/xml/yaml. // filename is the config file path. -func NewConfig(adapterName, fileaname string) (ConfigContainer, error) { +func NewConfig(adapterName, filename string) (ConfigContainer, error) { adapter, ok := adapters[adapterName] if !ok { return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName) } - return adapter.Parse(fileaname) + return adapter.Parse(filename) } // adapterName is ini/json/xml/yaml. diff --git a/context/input.go b/context/input.go index 1985df21..c8a8aaa9 100644 --- a/context/input.go +++ b/context/input.go @@ -353,7 +353,7 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error { } func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { - rv := reflect.Zero(reflect.TypeOf(0)) + rv := reflect.Zero(typ) switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: val := input.Query(key) @@ -398,7 +398,7 @@ func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { } func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value { - rv := reflect.Zero(reflect.TypeOf(0)) + rv := reflect.Zero(typ) switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: rv = input.bindInt(val, typ) diff --git a/controller.go b/controller.go index a8b9e8d3..b225df03 100644 --- a/controller.go +++ b/controller.go @@ -34,18 +34,19 @@ import ( //commonly used mime-types const ( - applicationJson = "application/json" - applicationXml = "application/xml" - textXml = "text/xml" + applicationJSON = "application/json" + applicationXML = "application/xml" + textXML = "text/xml" ) var ( - // custom error when user stop request handler manually. - USERSTOPRUN = errors.New("User stop run") - GlobalControllerRouter map[string][]ControllerComments = make(map[string][]ControllerComments) //pkgpath+controller:comments + // ErrAbort custom error when user stop request handler manually. + ErrAbort = errors.New("User stop run") + // GlobalControllerRouter store comments with controller. pkgpath+controller:comments + GlobalControllerRouter = make(map[string][]ControllerComments) ) -// store the comment for the controller method +// ControllerComments store the comment for the controller method type ControllerComments struct { Method string Router string @@ -64,7 +65,7 @@ type Controller struct { Layout string LayoutSections map[string]string // the key is the section name and the value is the template name TplExt string - _xsrf_token string + _xsrfToken string gotofunc string CruSession session.SessionStore XSRFExpire int @@ -87,8 +88,8 @@ type ControllerInterface interface { Options() Finish() Render() error - XsrfToken() string - CheckXsrfCookie() bool + XSRFToken() string + CheckXSRFCookie() bool HandlerFunc(fn string) bool URLMapping() } @@ -153,20 +154,20 @@ func (c *Controller) Options() { http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405) } -// call function fn +// HandlerFunc call function with the name func (c *Controller) HandlerFunc(fnname string) bool { if v, ok := c.methodMapping[fnname]; ok { v() return true - } else { - return false } + return false } // URLMapping register the internal Controller router. func (c *Controller) URLMapping() { } +// Mapping the method to function func (c *Controller) Mapping(method string, fn func()) { c.methodMapping[method] = fn } @@ -177,13 +178,11 @@ func (c *Controller) Render() error { return nil } rb, err := c.RenderBytes() - if err != nil { return err - } else { - c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") - c.Ctx.Output.Body(rb) } + c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") + c.Ctx.Output.Body(rb) return nil } @@ -200,8 +199,19 @@ func (c *Controller) RenderBytes() ([]byte, error) { if c.TplNames == "" { c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt } + if RunMode == "dev" { - BuildTemplate(ViewsPath) + buildFiles := make([]string, 1) + buildFiles = append(buildFiles, c.TplNames) + if c.LayoutSections != nil { + for _, sectionTpl := range c.LayoutSections { + if sectionTpl == "" { + continue + } + buildFiles = append(buildFiles, sectionTpl) + } + } + BuildTemplate(ViewsPath, buildFiles...) } newbytes := bytes.NewBufferString("") if _, ok := BeeTemplates[c.TplNames]; !ok { @@ -241,25 +251,25 @@ func (c *Controller) RenderBytes() ([]byte, error) { } icontent, _ := ioutil.ReadAll(ibytes) return icontent, nil - } else { - if c.TplNames == "" { - c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt - } - if RunMode == "dev" { - BuildTemplate(ViewsPath) - } - ibytes := bytes.NewBufferString("") - if _, ok := BeeTemplates[c.TplNames]; !ok { - panic("can't find templatefile in the path:" + c.TplNames) - } - err := BeeTemplates[c.TplNames].ExecuteTemplate(ibytes, c.TplNames, c.Data) - if err != nil { - Trace("template Execute err:", err) - return nil, err - } - icontent, _ := ioutil.ReadAll(ibytes) - return icontent, nil } + + if c.TplNames == "" { + c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt + } + if RunMode == "dev" { + BuildTemplate(ViewsPath, c.TplNames) + } + ibytes := bytes.NewBufferString("") + if _, ok := BeeTemplates[c.TplNames]; !ok { + panic("can't find templatefile in the path:" + c.TplNames) + } + err := BeeTemplates[c.TplNames].ExecuteTemplate(ibytes, c.TplNames, c.Data) + if err != nil { + Trace("template Execute err:", err) + return nil, err + } + icontent, _ := ioutil.ReadAll(ibytes) + return icontent, nil } // Redirect sends the redirection response to url with status code. @@ -267,7 +277,7 @@ func (c *Controller) Redirect(url string, code int) { c.Ctx.Redirect(code, url) } -// Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string. +// Abort 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 { @@ -285,29 +295,28 @@ func (c *Controller) CustomAbort(status int, body string) { } // last panic user string c.Ctx.ResponseWriter.Write([]byte(body)) - panic(USERSTOPRUN) + panic(ErrAbort) } // StopRun makes panic of USERSTOPRUN error and go to recover function if defined. func (c *Controller) StopRun() { - panic(USERSTOPRUN) + panic(ErrAbort) } -// UrlFor does another controller handler in this request function. +// 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 ...interface{}) string { +func (c *Controller) URLFor(endpoint string, values ...interface{}) string { if len(endpoint) <= 0 { return "" } if endpoint[0] == '.' { - return UrlFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...) - } else { - return UrlFor(endpoint, values...) + return URLFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...) } + return URLFor(endpoint, values...) } -// ServeJson sends a json response with encoding charset. -func (c *Controller) ServeJson(encoding ...bool) { +// ServeJSON sends a json response with encoding charset. +func (c *Controller) ServeJSON(encoding ...bool) { var hasIndent bool var hasencoding bool if RunMode == "prod" { @@ -321,8 +330,8 @@ func (c *Controller) ServeJson(encoding ...bool) { c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding) } -// ServeJsonp sends a jsonp response. -func (c *Controller) ServeJsonp() { +// ServeJSONP sends a jsonp response. +func (c *Controller) ServeJSONP() { var hasIndent bool if RunMode == "prod" { hasIndent = false @@ -332,8 +341,8 @@ func (c *Controller) ServeJsonp() { c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent) } -// ServeXml sends xml response. -func (c *Controller) ServeXml() { +// ServeXML sends xml response. +func (c *Controller) ServeXML() { var hasIndent bool if RunMode == "prod" { hasIndent = false @@ -347,12 +356,12 @@ func (c *Controller) ServeXml() { func (c *Controller) ServeFormatted() { accept := c.Ctx.Input.Header("Accept") switch accept { - case applicationJson: - c.ServeJson() - case applicationXml, textXml: - c.ServeXml() + case applicationJSON: + c.ServeJSON() + case applicationXML, textXML: + c.ServeXML() default: - c.ServeJson() + c.ServeJSON() } } @@ -378,9 +387,8 @@ func (c *Controller) GetString(key string, def ...string) string { if v := c.Ctx.Input.Query(key); v != "" { return v - } else { - return defv } + return defv } // GetStrings returns the input string slice by key string or the default value while it's present and input is blank @@ -399,9 +407,8 @@ func (c *Controller) GetStrings(key string, def ...[]string) []string { vs := f[key] if len(vs) > 0 { return vs - } else { - return defv } + return defv } // GetInt returns input as an int or the default value while it's present and input is blank @@ -575,7 +582,7 @@ func (c *Controller) GetSession(name interface{}) interface{} { return c.CruSession.Get(name) } -// SetSession removes value from session. +// DelSession removes value from session. func (c *Controller) DelSession(name interface{}) { if c.CruSession == nil { c.StartSession() @@ -614,34 +621,34 @@ func (c *Controller) SetSecureCookie(Secret, name, value string, others ...inter c.Ctx.SetSecureCookie(Secret, name, value, others...) } -// XsrfToken creates a xsrf token string and returns. -func (c *Controller) XsrfToken() string { - if c._xsrf_token == "" { +// XSRFToken creates a CSRF token string and returns. +func (c *Controller) XSRFToken() string { + if c._xsrfToken == "" { var expire int64 if c.XSRFExpire > 0 { expire = int64(c.XSRFExpire) } else { expire = int64(XSRFExpire) } - c._xsrf_token = c.Ctx.XsrfToken(XSRFKEY, expire) + c._xsrfToken = c.Ctx.XsrfToken(XSRFKEY, expire) } - return c._xsrf_token + return c._xsrfToken } -// CheckXsrfCookie checks xsrf token in this request is valid or not. +// CheckXSRFCookie checks xsrf token in this request is valid or not. // the token can provided in request header "X-Xsrftoken" and "X-CsrfToken" // or in form field value named as "_xsrf". -func (c *Controller) CheckXsrfCookie() bool { +func (c *Controller) CheckXSRFCookie() bool { if !c.EnableXSRF { return true } return c.Ctx.CheckXsrfCookie() } -// XsrfFormHtml writes an input field contains xsrf token value. -func (c *Controller) XsrfFormHtml() string { +// XSRFFormHTML writes an input field contains xsrf token value. +func (c *Controller) XSRFFormHTML() string { return "" + c._xsrfToken + "\"/>" } // GetControllerAndAction gets the executing controller name and action name. diff --git a/doc.go b/doc.go new file mode 100644 index 00000000..4be305b3 --- /dev/null +++ b/doc.go @@ -0,0 +1,17 @@ +/* +Package beego provide a MVC framework +beego: an open-source, high-performance, modular, full-stack web framework + +It is used for rapid development of RESTful APIs, web apps and backend services in Go. +beego is inspired by Tornado, Sinatra and Flask with the added benefit of some Go-specific features such as interfaces and struct embedding. + + package main + import "github.com/astaxie/beego" + + func main() { + beego.Run() + } + +more infomation: http://beego.me +*/ +package beego diff --git a/docs.go b/docs.go index aaad205e..1dbed6cc 100644 --- a/docs.go +++ b/docs.go @@ -20,20 +20,21 @@ import ( "github.com/astaxie/beego/context" ) -var GlobalDocApi map[string]interface{} +// GlobalDocAPI store the swagger api documents +var GlobalDocAPI map[string]interface{} func init() { if EnableDocs { - GlobalDocApi = make(map[string]interface{}) + GlobalDocAPI = make(map[string]interface{}) } } func serverDocs(ctx *context.Context) { var obj interface{} if splat := ctx.Input.Param(":splat"); splat == "" { - obj = GlobalDocApi["Root"] + obj = GlobalDocAPI["Root"] } else { - if v, ok := GlobalDocApi[splat]; ok { + if v, ok := GlobalDocAPI[splat]; ok { obj = v } } diff --git a/error.go b/error.go index 99a1fcf3..b4708b30 100644 --- a/error.go +++ b/error.go @@ -358,52 +358,11 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { t.Execute(rw, data) } -// register default error http handlers, 404,401,403,500 and 503. -func registerDefaultErrorHandler() { - if _, ok := ErrorMaps["401"]; !ok { - Errorhandler("401", unauthorized) - } - - if _, ok := ErrorMaps["402"]; !ok { - Errorhandler("402", paymentRequired) - } - - if _, ok := ErrorMaps["403"]; !ok { - Errorhandler("403", forbidden) - } - - if _, ok := ErrorMaps["404"]; !ok { - Errorhandler("404", notFound) - } - - if _, ok := ErrorMaps["405"]; !ok { - Errorhandler("405", methodNotAllowed) - } - - if _, ok := ErrorMaps["500"]; !ok { - 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) - } -} - // 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 { +func ErrorHandler(code string, h http.HandlerFunc) *App { errinfo := &errorInfo{} errinfo.errorType = errorTypeHandler errinfo.handler = h @@ -414,7 +373,7 @@ func Errorhandler(code string, h http.HandlerFunc) *App { // ErrorController registers ControllerInterface to each http err code string. // usage: -// beego.ErrorHandler(&controllers.ErrorController{}) +// beego.ErrorController(&controllers.ErrorController{}) func ErrorController(c ControllerInterface) *App { reflectVal := reflect.ValueOf(c) rt := reflectVal.Type() @@ -453,7 +412,6 @@ func exception(errcode string, ctx *context.Context) { func executeError(err *errorInfo, ctx *context.Context, code int) { if err.errorType == errorTypeHandler { - ctx.ResponseWriter.WriteHeader(code) err.handler(ctx.ResponseWriter, ctx.Request) return } @@ -473,7 +431,7 @@ func executeError(err *errorInfo, ctx *context.Context, code int) { execController.URLMapping() - in := make([]reflect.Value, 0) + var in []reflect.Value method := vc.MethodByName(err.method) method.Call(in) diff --git a/example/chat/controllers/ws.go b/example/chat/controllers/ws.go index fc3917b3..9e405513 100644 --- a/example/chat/controllers/ws.go +++ b/example/chat/controllers/ws.go @@ -54,14 +54,13 @@ func (c *connection) readPump() { }() c.ws.SetReadLimit(maxMessageSize) c.ws.SetReadDeadline(time.Now().Add(readWait)) + c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(readWait)); return nil }) for { op, r, err := c.ws.NextReader() if err != nil { break } switch op { - case websocket.PongMessage: - c.ws.SetReadDeadline(time.Now().Add(readWait)) case websocket.TextMessage: message, err := ioutil.ReadAll(r) if err != nil { diff --git a/filter.go b/filter.go index f673ab66..7dbc7028 100644 --- a/filter.go +++ b/filter.go @@ -39,7 +39,6 @@ func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) { } if isok, ok := isok.(bool); ok { return isok, params - } else { - return false, nil } + return false, nil } diff --git a/flash_test.go b/flash_test.go index b655f552..640d54de 100644 --- a/flash_test.go +++ b/flash_test.go @@ -30,7 +30,7 @@ func (t *TestFlashController) TestWriteFlash() { flash.Notice("TestFlashString") flash.Store(&t.Controller) // we choose to serve json because we don't want to load a template html file - t.ServeJson(true) + t.ServeJSON(true) } func TestFlashHeader(t *testing.T) { diff --git a/grace/grace.go b/grace/grace.go index e5577267..33bf196c 100644 --- a/grace/grace.go +++ b/grace/grace.go @@ -32,7 +32,7 @@ // mux := http.NewServeMux() // mux.HandleFunc("/hello", handler) // -// err := grace.ListenAndServe("localhost:8080", mux1) +// err := grace.ListenAndServe("localhost:8080", mux) // if err != nil { // log.Println(err) // } diff --git a/hooks.go b/hooks.go new file mode 100644 index 00000000..178d8dc4 --- /dev/null +++ b/hooks.go @@ -0,0 +1,106 @@ +package beego + +import ( + "mime" + "path/filepath" + "strconv" + + "github.com/astaxie/beego/session" +) + +// +func registerMime() error { + for k, v := range mimemaps { + mime.AddExtensionType(k, v) + } + return nil +} + +// register default error http handlers, 404,401,403,500 and 503. +func registerDefaultErrorHandler() error { + if _, ok := ErrorMaps["401"]; !ok { + ErrorHandler("401", unauthorized) + } + + if _, ok := ErrorMaps["402"]; !ok { + ErrorHandler("402", paymentRequired) + } + + if _, ok := ErrorMaps["403"]; !ok { + ErrorHandler("403", forbidden) + } + + if _, ok := ErrorMaps["404"]; !ok { + ErrorHandler("404", notFound) + } + + if _, ok := ErrorMaps["405"]; !ok { + ErrorHandler("405", methodNotAllowed) + } + + if _, ok := ErrorMaps["500"]; !ok { + 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) + } + return nil +} + +func registerSession() error { + if SessionOn { + var err error + sessionConfig := AppConfig.String("sessionConfig") + if sessionConfig == "" { + sessionConfig = `{"cookieName":"` + SessionName + `",` + + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + + `"providerConfig":"` + filepath.ToSlash(SessionProviderConfig) + `",` + + `"secure":` + strconv.FormatBool(EnableHTTPTLS) + `,` + + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + + `"domain":"` + SessionDomain + `",` + + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` + } + GlobalSessions, err = session.NewManager(SessionProvider, sessionConfig) + if err != nil { + return err + } + go GlobalSessions.GC() + } + return nil +} + +func registerTemplate() error { + if AutoRender { + err := BuildTemplate(ViewsPath) + if err != nil && RunMode == "dev" { + Warn(err) + } + } + return nil +} + +func registerDocs() error { + if EnableDocs { + Get("/docs", serverDocs) + Get("/docs/*", serverDocs) + } + return nil +} + +func registerAdmin() error { + if EnableAdmin { + go beeAdminApp.Run() + } + return nil +} diff --git a/httplib/httplib.go b/httplib/httplib.go index 68c22d70..72f7a625 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -74,12 +74,6 @@ func SetDefaultSetting(setting BeegoHttpSettings) { settingMutex.Lock() defer settingMutex.Unlock() defaultSetting = setting - if defaultSetting.ConnectTimeout == 0 { - defaultSetting.ConnectTimeout = 60 * time.Second - } - if defaultSetting.ReadWriteTimeout == 0 { - defaultSetting.ReadWriteTimeout = 60 * time.Second - } } // return *BeegoHttpRequest with specific method @@ -100,7 +94,7 @@ func NewBeegoRequest(rawurl, method string) *BeegoHttpRequest { return &BeegoHttpRequest{ url: rawurl, req: &req, - params: map[string]string{}, + params: map[string][]string{}, files: map[string]string{}, setting: defaultSetting, resp: &resp, @@ -150,7 +144,7 @@ type BeegoHttpSettings struct { type BeegoHttpRequest struct { url string req *http.Request - params map[string]string + params map[string][]string files map[string]string setting BeegoHttpSettings resp *http.Response @@ -273,7 +267,11 @@ func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) // Param adds query param in to request. // params build query string as ?key1=value1&key2=value2... func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest { - b.params[key] = value + if param, ok := b.params[key]; ok { + b.params[key] = append(param, value) + } else { + b.params[key] = []string{value} + } return b } @@ -348,7 +346,9 @@ func (b *BeegoHttpRequest) buildUrl(paramBody string) { } } for k, v := range b.params { - bodyWriter.WriteField(k, v) + for _, vv := range v { + bodyWriter.WriteField(k, vv) + } } bodyWriter.Close() pw.Close() @@ -383,10 +383,12 @@ func (b *BeegoHttpRequest) SendOut() (*http.Response, error) { 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('&') + for _, vv := range v { + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(vv)) + buf.WriteByte('&') + } } paramBody = buf.String() paramBody = paramBody[0 : len(paramBody)-1] diff --git a/httplib/httplib_test.go b/httplib/httplib_test.go index 0b551c53..0b6b5779 100644 --- a/httplib/httplib_test.go +++ b/httplib/httplib_test.go @@ -19,6 +19,7 @@ import ( "os" "strings" "testing" + "time" ) func TestResponse(t *testing.T) { @@ -153,6 +154,7 @@ func TestWithSetting(t *testing.T) { setting.EnableCookie = true setting.UserAgent = v setting.Transport = nil + setting.ReadWriteTimeout = 5 * time.Second SetDefaultSetting(setting) str, err := Get("http://httpbin.org/get").String() diff --git a/log.go b/log.go index 7949ed96..68252236 100644 --- a/log.go +++ b/log.go @@ -32,18 +32,18 @@ const ( LevelDebug ) -// SetLogLevel sets the global log level used by the simple -// logger. +// SetLevel sets the global log level used by the simple logger. func SetLevel(l int) { BeeLogger.SetLevel(l) } +// SetLogFuncCall set the CallDepth, default is 3 func SetLogFuncCall(b bool) { BeeLogger.EnableFuncCallDepth(b) BeeLogger.SetLogFuncCallDepth(3) } -// logger references the used application logger. +// BeeLogger references the used application logger. var BeeLogger *logs.BeeLogger // SetLogger sets a new logger. @@ -55,10 +55,12 @@ func SetLogger(adaptername string, config string) error { return nil } +// Emergency logs a message at emergency level. func Emergency(v ...interface{}) { BeeLogger.Emergency(generateFmtStr(len(v)), v...) } +// Alert logs a message at alert level. func Alert(v ...interface{}) { BeeLogger.Alert(generateFmtStr(len(v)), v...) } @@ -78,21 +80,22 @@ func Warning(v ...interface{}) { BeeLogger.Warning(generateFmtStr(len(v)), v...) } -// compatibility alias for Warning() +// Warn compatibility alias for Warning() func Warn(v ...interface{}) { BeeLogger.Warn(generateFmtStr(len(v)), v...) } +// Notice logs a message at notice level. func Notice(v ...interface{}) { BeeLogger.Notice(generateFmtStr(len(v)), v...) } -// Info logs a message at info level. +// Informational logs a message at info level. func Informational(v ...interface{}) { BeeLogger.Informational(generateFmtStr(len(v)), v...) } -// compatibility alias for Warning() +// Info compatibility alias for Warning() func Info(v ...interface{}) { BeeLogger.Info(generateFmtStr(len(v)), v...) } diff --git a/logs/file.go b/logs/file.go index 2d3449ce..84a8c4f3 100644 --- a/logs/file.go +++ b/logs/file.go @@ -205,15 +205,20 @@ func (w *FileLogWriter) lines() (int, error) { } // DoRotate means it need to write file in new file. -// new file name like xx.log.2013-01-01.2 +// new file name like xx.2013-01-01.2.log func (w *FileLogWriter) DoRotate() error { _, err := os.Lstat(w.Filename) if err == nil { // file exists // Find the next available number num := 1 fname := "" + suffix := filepath.Ext(w.Filename) + filenameOnly := strings.TrimSuffix(w.Filename, suffix) + if suffix == "" { + suffix = ".log" + } for ; err == nil && num <= 999; num++ { - fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) + fname = filenameOnly + fmt.Sprintf(".%s.%03d%s", time.Now().Format("2006-01-02"), num, suffix) _, err = os.Lstat(fname) } // return error if the last file checked still existed diff --git a/logs/file_test.go b/logs/file_test.go index c71e9bb4..97260f12 100644 --- a/logs/file_test.go +++ b/logs/file_test.go @@ -103,7 +103,7 @@ func TestFileRotate(t *testing.T) { log.Critical("critical") log.Emergency("emergency") time.Sleep(time.Second * 4) - rotatename := "test3.log" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + rotatename := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log" b, err := exists(rotatename) if !b || err != nil { t.Fatal("rotate not generated") diff --git a/logs/smtp.go b/logs/smtp.go index 95123ebf..f9b292b7 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -31,9 +31,9 @@ const ( // smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server. type SmtpWriter struct { - Username string `json:"Username"` + Username string `json:"username"` Password string `json:"password"` - Host string `json:"Host"` + Host string `json:"host"` Subject string `json:"subject"` FromAddress string `json:"fromAddress"` RecipientAddresses []string `json:"sendTos"` diff --git a/memzipfile.go b/memzipfile.go index cc5e3851..0fff44b6 100644 --- a/memzipfile.go +++ b/memzipfile.go @@ -28,8 +28,10 @@ import ( "time" ) -var gmfim map[string]*memFileInfo = make(map[string]*memFileInfo) -var lock sync.RWMutex +var ( + gmfim = make(map[string]*memFileInfo) + lock sync.RWMutex +) // OpenMemZipFile returns MemFile object with a compressed static file. // it's used for serve static file if gzip enable. diff --git a/mime.go b/mime.go index 20246c21..e85fcb2a 100644 --- a/mime.go +++ b/mime.go @@ -14,11 +14,7 @@ package beego -import ( - "mime" -) - -var mimemaps map[string]string = map[string]string{ +var mimemaps = map[string]string{ ".3dm": "x-world/x-3dmf", ".3dmf": "x-world/x-3dmf", ".7z": "application/x-7z-compressed", @@ -558,10 +554,3 @@ var mimemaps map[string]string = map[string]string{ ".oex": "application/x-opera-extension", ".mustache": "text/html", } - -func initMime() error { - for k, v := range mimemaps { - mime.AddExtensionType(k, v) - } - return nil -} diff --git a/namespace.go b/namespace.go index ebb7c14f..0dfdd7af 100644 --- a/namespace.go +++ b/namespace.go @@ -23,16 +23,17 @@ import ( type namespaceCond func(*beecontext.Context) bool -type innnerNamespace func(*Namespace) +// LinkNamespace used as link action +type LinkNamespace func(*Namespace) // Namespace is store all the info type Namespace struct { prefix string - handlers *ControllerRegistor + handlers *ControllerRegister } -// get new Namespace -func NewNamespace(prefix string, params ...innnerNamespace) *Namespace { +// NewNamespace get new Namespace +func NewNamespace(prefix string, params ...LinkNamespace) *Namespace { ns := &Namespace{ prefix: prefix, handlers: NewControllerRegister(), @@ -43,7 +44,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace { return ns } -// set condtion function +// Cond set condtion function // if cond return true can run this namespace, else can't // usage: // ns.Cond(func (ctx *context.Context) bool{ @@ -72,7 +73,7 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace { return n } -// add filter in the Namespace +// Filter add filter in the Namespace // action has before & after // FilterFunc // usage: @@ -95,98 +96,98 @@ func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace { return n } -// same as beego.Rourer +// Router same as beego.Rourer // refer: https://godoc.org/github.com/astaxie/beego#Router func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { n.handlers.Add(rootpath, c, mappingMethods...) return n } -// same as beego.AutoRouter +// AutoRouter same as beego.AutoRouter // refer: https://godoc.org/github.com/astaxie/beego#AutoRouter func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace { n.handlers.AddAuto(c) return n } -// same as beego.AutoPrefix +// AutoPrefix same as beego.AutoPrefix // refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace { n.handlers.AddAutoPrefix(prefix, c) return n } -// same as beego.Get +// Get same as beego.Get // refer: https://godoc.org/github.com/astaxie/beego#Get func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace { n.handlers.Get(rootpath, f) return n } -// same as beego.Post +// Post same as beego.Post // refer: https://godoc.org/github.com/astaxie/beego#Post func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace { n.handlers.Post(rootpath, f) return n } -// same as beego.Delete +// Delete same as beego.Delete // refer: https://godoc.org/github.com/astaxie/beego#Delete func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace { n.handlers.Delete(rootpath, f) return n } -// same as beego.Put +// Put same as beego.Put // refer: https://godoc.org/github.com/astaxie/beego#Put func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace { n.handlers.Put(rootpath, f) return n } -// same as beego.Head +// Head same as beego.Head // refer: https://godoc.org/github.com/astaxie/beego#Head func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace { n.handlers.Head(rootpath, f) return n } -// same as beego.Options +// Options same as beego.Options // refer: https://godoc.org/github.com/astaxie/beego#Options func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace { n.handlers.Options(rootpath, f) return n } -// same as beego.Patch +// Patch same as beego.Patch // refer: https://godoc.org/github.com/astaxie/beego#Patch func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace { n.handlers.Patch(rootpath, f) return n } -// same as beego.Any +// Any same as beego.Any // refer: https://godoc.org/github.com/astaxie/beego#Any func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace { n.handlers.Any(rootpath, f) return n } -// same as beego.Handler +// Handler same as beego.Handler // refer: https://godoc.org/github.com/astaxie/beego#Handler func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace { n.handlers.Handler(rootpath, h) return n } -// add include class +// Include add include class // refer: https://godoc.org/github.com/astaxie/beego#Include func (n *Namespace) Include(cList ...ControllerInterface) *Namespace { n.handlers.Include(cList...) return n } -// nest Namespace +// Namespace add nest Namespace // usage: //ns := beego.NewNamespace(“/v1”). //Namespace( @@ -230,7 +231,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { return n } -// register Namespace into beego.Handler +// AddNamespace register Namespace into beego.Handler // support multi Namespace func AddNamespace(nl ...*Namespace) { for _, n := range nl { @@ -275,113 +276,113 @@ func addPrefix(t *Tree, prefix string) { } -// Namespace Condition -func NSCond(cond namespaceCond) innnerNamespace { +// NSCond is Namespace Condition +func NSCond(cond namespaceCond) LinkNamespace { return func(ns *Namespace) { ns.Cond(cond) } } -// Namespace BeforeRouter filter -func NSBefore(filiterList ...FilterFunc) innnerNamespace { +// NSBefore Namespace BeforeRouter filter +func NSBefore(filiterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Filter("before", filiterList...) } } -// Namespace FinishRouter filter -func NSAfter(filiterList ...FilterFunc) innnerNamespace { +// NSAfter add Namespace FinishRouter filter +func NSAfter(filiterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Filter("after", filiterList...) } } -// Namespace Include ControllerInterface -func NSInclude(cList ...ControllerInterface) innnerNamespace { +// NSInclude Namespace Include ControllerInterface +func NSInclude(cList ...ControllerInterface) LinkNamespace { return func(ns *Namespace) { ns.Include(cList...) } } -// Namespace Router -func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) innnerNamespace { +// NSRouter call Namespace Router +func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) LinkNamespace { return func(ns *Namespace) { ns.Router(rootpath, c, mappingMethods...) } } -// Namespace Get -func NSGet(rootpath string, f FilterFunc) innnerNamespace { +// NSGet call Namespace Get +func NSGet(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Get(rootpath, f) } } -// Namespace Post -func NSPost(rootpath string, f FilterFunc) innnerNamespace { +// NSPost call Namespace Post +func NSPost(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Post(rootpath, f) } } -// Namespace Head -func NSHead(rootpath string, f FilterFunc) innnerNamespace { +// NSHead call Namespace Head +func NSHead(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Head(rootpath, f) } } -// Namespace Put -func NSPut(rootpath string, f FilterFunc) innnerNamespace { +// NSPut call Namespace Put +func NSPut(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Put(rootpath, f) } } -// Namespace Delete -func NSDelete(rootpath string, f FilterFunc) innnerNamespace { +// NSDelete call Namespace Delete +func NSDelete(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Delete(rootpath, f) } } -// Namespace Any -func NSAny(rootpath string, f FilterFunc) innnerNamespace { +// NSAny call Namespace Any +func NSAny(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Any(rootpath, f) } } -// Namespace Options -func NSOptions(rootpath string, f FilterFunc) innnerNamespace { +// NSOptions call Namespace Options +func NSOptions(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Options(rootpath, f) } } -// Namespace Patch -func NSPatch(rootpath string, f FilterFunc) innnerNamespace { +// NSPatch call Namespace Patch +func NSPatch(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Patch(rootpath, f) } } -//Namespace AutoRouter -func NSAutoRouter(c ControllerInterface) innnerNamespace { +// NSAutoRouter call Namespace AutoRouter +func NSAutoRouter(c ControllerInterface) LinkNamespace { return func(ns *Namespace) { ns.AutoRouter(c) } } -// Namespace AutoPrefix -func NSAutoPrefix(prefix string, c ControllerInterface) innnerNamespace { +// NSAutoPrefix call Namespace AutoPrefix +func NSAutoPrefix(prefix string, c ControllerInterface) LinkNamespace { return func(ns *Namespace) { ns.AutoPrefix(prefix, c) } } -// Namespace add sub Namespace -func NSNamespace(prefix string, params ...innnerNamespace) innnerNamespace { +// NSNamespace add sub Namespace +func NSNamespace(prefix string, params ...LinkNamespace) LinkNamespace { return func(ns *Namespace) { n := NewNamespace(prefix, params...) ns.Namespace(n) diff --git a/orm/db.go b/orm/db.go index 20dc80f2..1803db4f 100644 --- a/orm/db.go +++ b/orm/db.go @@ -802,6 +802,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() @@ -814,7 +815,11 @@ 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) + sqlSelect := "SELECT" + if qs.distinct { + sqlSelect += " DISTINCT" + } + query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) d.ins.ReplaceMarks(&query) @@ -1444,13 +1449,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..245be891 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 5cc47617..af16b694 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -60,7 +60,9 @@ type querySet struct { relDepth int limit int64 offset int64 + groups []string orders []string + distinct bool orm *orm } @@ -105,6 +107,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 { @@ -112,6 +120,12 @@ func (o querySet) OrderBy(exprs ...string) QuerySeter { return &o } +// add DISTINCT to SELECT +func (o querySet) Distinct() QuerySeter { + o.distinct = true + return &o +} + // set relation model to query together. // it will query relation models and assign to parent model. func (o querySet) RelatedSel(params ...interface{}) QuerySeter { diff --git a/orm/types.go b/orm/types.go index b46be4fc..95a3fee3 100644 --- a/orm/types.go +++ b/orm/types.go @@ -66,7 +66,9 @@ type QuerySeter interface { SetCond(*Condition) QuerySeter Limit(interface{}, ...interface{}) QuerySeter Offset(interface{}) QuerySeter + GroupBy(...string) QuerySeter OrderBy(...string) QuerySeter + Distinct() QuerySeter RelatedSel(...interface{}) QuerySeter Count() (int64, error) Exist() bool diff --git a/parser.go b/parser.go index 7d55ffe3..7e8670cb 100644 --- a/parser.go +++ b/parser.go @@ -42,13 +42,13 @@ func init() { ` var ( - lastupdateFilename string = "lastupdate.tmp" + lastupdateFilename = "lastupdate.tmp" commentFilename string pkgLastupdate map[string]int64 genInfoList map[string][]ControllerComments ) -const COMMENTFL = "commentsRouter_" +const coomentPrefix = "commentsRouter_" func init() { pkgLastupdate = make(map[string]int64) @@ -56,7 +56,7 @@ func init() { func parserPkg(pkgRealpath, pkgpath string) error { rep := strings.NewReplacer("/", "_", ".", "_") - commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go" + commentFilename = coomentPrefix + rep.Replace(pkgpath) + ".go" if !compareFile(pkgRealpath) { Info(pkgRealpath + " has not changed, not reloading") return nil @@ -132,9 +132,11 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat func genRouterCode() { os.Mkdir(path.Join(workPath, "routers"), 0755) Info("generate router from comments") - var globalinfo string - sortKey := make([]string, 0) - for k, _ := range genInfoList { + var ( + globalinfo string + sortKey []string + ) + for k := range genInfoList { sortKey = append(sortKey, k) } sort.Strings(sortKey) diff --git a/plugins/apiauth/apiauth.go b/plugins/apiauth/apiauth.go index bbae7def..56a92d25 100644 --- a/plugins/apiauth/apiauth.go +++ b/plugins/apiauth/apiauth.go @@ -83,41 +83,41 @@ func APIBaiscAuth(appid, appkey string) beego.FilterFunc { func APIAuthWithFunc(f AppIdToAppSecret, timeout int) beego.FilterFunc { return func(ctx *context.Context) { if ctx.Input.Query("appid") == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("miss query param: appid") return } appsecret := f(ctx.Input.Query("appid")) if appsecret == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("not exist this appid") return } if ctx.Input.Query("signature") == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("miss query param: signature") return } if ctx.Input.Query("timestamp") == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("miss query param: timestamp") return } u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp")) if err != nil { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05") return } t := time.Now() if t.Sub(u).Seconds() > float64(timeout) { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("timeout! the request time is long ago, please try again") return } if ctx.Input.Query("signature") != Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.Uri()) { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("auth failed") } } diff --git a/plugins/cors/cors.go b/plugins/cors/cors.go index 052d3bc6..1e973a40 100644 --- a/plugins/cors/cors.go +++ b/plugins/cors/cors.go @@ -24,7 +24,7 @@ // // - PUT and PATCH methods // // - Origin header // // - Credentials share -// beego.InsertFilter("*", beego.BeforeRouter,cors.Allow(&cors.Options{ +// beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ // AllowOrigins: []string{"https://*.foo.com"}, // AllowMethods: []string{"PUT", "PATCH"}, // AllowHeaders: []string{"Origin"}, @@ -36,7 +36,6 @@ package cors import ( - "net/http" "regexp" "strconv" "strings" @@ -216,8 +215,6 @@ func Allow(opts *Options) beego.FilterFunc { for key, value := range headers { ctx.Output.Header(key, value) } - ctx.Output.SetStatus(http.StatusOK) - ctx.WriteString("") return } headers = opts.Header(origin) diff --git a/plugins/jwt/jwt.go b/plugins/jwt/jwt.go new file mode 100644 index 00000000..fd064686 --- /dev/null +++ b/plugins/jwt/jwt.go @@ -0,0 +1,135 @@ +// 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 jwt provides JWT (Json Web Token) authentication +// +// Usage +// In file main.go +// +// import ( +// "github.com/astaxie/beego" +// "github.com/astaxie/beego/plugins/jwt" +// ) +// +// func main() { +// // JWT for Url matching /v1/* +// // PrivateKeyPath: The path for the private RSA key used by JWT +// // PublicKeyPath: The path for the public RSA key used by JWT +// // The list of Urls should be excluded from the JWT Auth +// beego.InsertFilter("/v1/*", beego.BeforeRouter, jwt.AuthRequest(&jwt.Options{ +// PrivateKeyPath: "conf/beeblog.rsa", +// PublicKeyPath: "conf/beeblog.rsa.pub", +// WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, +// })) +// beego.Run() +// } +// +// In file routers/router.go +// +// import ( +// "github.com/astaxie/beego" +// "github.com/astaxie/beego/plugins/jwt" +// ) +// func init() { +// ns := beego.NSNamespace("/jwt", +// beego.NSInclude( +// &jwt.JwtController{}, +// ), +// ) +// beego.AddNamespace(ns) +// } +// + +package jwt + +import ( + "github.com/astaxie/beego" + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" + goJwt "github.com/dgrijalva/jwt-go" + "io/ioutil" + "net/http" + "time" +) + +// Options for the JWT Auth +type Options struct { + PrivateKeyPath string + PublicKeyPath string + WhiteList []string +} + +var RSAKeys struct { + PrivateKey []byte + PublicKey []byte +} + +func AuthRequest(o *Options) beego.FilterFunc { + RSAKeys.PrivateKey, _ = ioutil.ReadFile(o.PrivateKeyPath) + RSAKeys.PublicKey, _ = ioutil.ReadFile(o.PublicKeyPath) + + return func(ctx *context.Context) { + // :TODO the url patterns should be considered here. + // Shouldn't only use the string equal + for _, method := range o.WhiteList { + if method == ctx.Request.URL.Path { + return + } + } + + parsedToken, err := goJwt.ParseFromRequest(ctx.Request, func(t *goJwt.Token) (interface{}, error) { + return RSAKeys.PublicKey, nil + }) + + if err == nil && parsedToken.Valid { + ctx.Output.SetStatus(http.StatusOK) + } else { + ctx.Output.SetStatus(http.StatusUnauthorized) + } + + } +} + +// oprations for Jwt +type JwtController struct { + beego.Controller +} + +func (this *JwtController) URLMapping() { + this.Mapping("IssueToken", this.IssueToken) +} + +// @Title IssueToken +// @Description Issue a Json Web Token +// @Success 200 string +// @Failure 403 no privilege to access +// @Failure 500 server inner error +// @router /issue-token [get] +func (this *JwtController) IssueToken() { + this.Data["json"] = CreateToken() + this.ServeJson() +} + +func CreateToken() map[string]string { + log := logs.NewLogger(10000) + log.SetLogger("console", "") + + token := goJwt.New(goJwt.GetSigningMethod("RS256")) // Create a Token that will be signed with RSA 256. + token.Claims["ID"] = "This is my super fake ID" + token.Claims["exp"] = time.Now().Unix() + 36000 + // The claims object allows you to store information in the actual token. + tokenString, _ := token.SignedString(RSAKeys.PrivateKey) + // tokenString Contains the actual token you should share with your client. + return map[string]string{"token": tokenString} +} diff --git a/plugins/jwt/jwt_test.go b/plugins/jwt/jwt_test.go new file mode 100644 index 00000000..dd8cafe7 --- /dev/null +++ b/plugins/jwt/jwt_test.go @@ -0,0 +1,88 @@ +package jwt + +import ( + "github.com/astaxie/beego" + "net/http" + "net/http/httptest" + "testing" +) + +func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request) { + request, _ := http.NewRequest(method, path, nil) + recorder := httptest.NewRecorder() + + return recorder, request +} + +func Test_IssueTokenAction(t *testing.T) { + url := "/v1/jwt/issue-token" + + mux := beego.NewControllerRegister() + + mux.InsertFilter("*", beego.BeforeRouter, AuthRequest(&Options{ + PrivateKeyPath: "test/jwt.rsa", + PublicKeyPath: "test/jwt.rsa.pub", + WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, + })) + + mux.Add("/v1/jwt/issue-token", &JwtController{}, "get:IssueToken") + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if rw.Code != http.StatusOK { + t.Errorf("Shoud return 200") + } +} + +func (tc *JwtController) Foo() { + tc.Ctx.Output.Body([]byte("ok")) +} + +func Test_AuthRequestWithAuthorizationHeader(t *testing.T) { + + url := "/foo" + + mux := beego.NewControllerRegister() + + mux.InsertFilter("*", beego.BeforeRouter, AuthRequest(&Options{ + PrivateKeyPath: "test/jwt.rsa", + PublicKeyPath: "test/jwt.rsa.pub", + WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, + })) + + mux.Add("/foo", &JwtController{}, "get:Foo") + newToken := CreateToken() + + rw, r := testRequest("GET", url) + r.Header.Add("Authorization", "Bearer "+newToken["token"]) + mux.ServeHTTP(rw, r) + + if rw.Code != http.StatusOK { + t.Errorf("Shoud return 200") + } + if rw.Body.String() != "ok" { + t.Errorf("Should output ok") + } +} + +func Test_AuthRequestWithoutAuthorizationHeader(t *testing.T) { + url := "/foo" + + mux := beego.NewControllerRegister() + + mux.InsertFilter("*", beego.BeforeRouter, AuthRequest(&Options{ + PrivateKeyPath: "test/jwt.rsa", + PublicKeyPath: "test/jwt.rsa.pub", + WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, + })) + + mux.Add("/foo", &JwtController{}, "get:Foo") + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if rw.Code != http.StatusUnauthorized { + t.Errorf("Shoud return 401") + } +} diff --git a/plugins/jwt/test/jwt.rsa b/plugins/jwt/test/jwt.rsa new file mode 100644 index 00000000..8d584fc6 --- /dev/null +++ b/plugins/jwt/test/jwt.rsa @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCdu+23Y/0J/FTQKnIPnxupoOo9/OYCv90DPXN/KLLRAMjYzgcC +DsBST2xVR5jlimI/gyfCpVB62dwpSzzr0cA3MoDhbaGWuTdQUX9zmiLoQ4I7X6h0 +dwyiihOz+CzOMlAg5+qBhiTGcKvIFlfEc1FUcn/tB3PVRG9j6B1Ibz5CnQIDAQAB +AoGAFGg+/i4ai9MwqeoD7c95Bb5C8BgrLgnir0uhCL+cOvwuABbPw01jRoLuEi58 +Mp5vzaXLXByFSA+ts03/qMbvZkDGac5g5kLli5TjHIONMxVBrdfGQ1+OApnaPayN +N+HYjZKs6xao6J5iFqfA0FqzDR9kQhUoeosdQoo1GlxDckECQQDO/0LJrFiLzYWe +qS/DxfAnFu2BlClKZjxRJ3vIkRRaON6HPl8BeJW901bFKG5+WSfO+OwQ9egWaf3X +fFm/oEHRAkEAwxMor4fOkBZbL4KPW7sen169vwnXuYusqj0t3dIeiIVrCigkOMT4 +OvX/63u4CTdXh1D5u/4Z/1HTYH92VCP7DQJAJPxbNKnE0IYSf/z++d4eQP3JxkNw +9Ug7Msz5QycZGd3bdRLh6uNe7iIa+PN2esD3afX0SDuIEqkxoBUp/CFoYQJAUmi3 +mV+/7bLkFrALK+9iwmTdt+TKk4HkEY8C32CysW3biFDo7GqZix79XFfJqWsNuQaG +WdrA1NGWgH+YV3dTyQJAIWEZGAuUXRkQB20LfjGzpsKgQFbqjTisMS0qe3JjnDwu +0JR8sYXapgeEEEisH+OtkZKIfyeFOwoUyNC83bcvgw== +-----END RSA PRIVATE KEY----- diff --git a/plugins/jwt/test/jwt.rsa.pub b/plugins/jwt/test/jwt.rsa.pub new file mode 100644 index 00000000..3fdd8ce7 --- /dev/null +++ b/plugins/jwt/test/jwt.rsa.pub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdu+23Y/0J/FTQKnIPnxupoOo9 +/OYCv90DPXN/KLLRAMjYzgcCDsBST2xVR5jlimI/gyfCpVB62dwpSzzr0cA3MoDh +baGWuTdQUX9zmiLoQ4I7X6h0dwyiihOz+CzOMlAg5+qBhiTGcKvIFlfEc1FUcn/t +B3PVRG9j6B1Ibz5CnQIDAQAB +-----END PUBLIC KEY----- diff --git a/router.go b/router.go index 3e1ebab3..12b5f6ae 100644 --- a/router.go +++ b/router.go @@ -34,8 +34,8 @@ import ( "github.com/astaxie/beego/utils" ) +// default filter execution points const ( - // default filter execution points BeforeStatic = iota BeforeRouter BeforeExec @@ -50,7 +50,7 @@ const ( ) var ( - // supported http methods. + // HTTPMETHOD list the supported http methods. HTTPMETHOD = map[string]string{ "GET": "GET", "POST": "POST", @@ -71,10 +71,12 @@ var ( "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", "GetControllerAndAction"} - url_placeholder = "{{placeholder}}" - DefaultLogFilter FilterHandler = &logFilter{} + urlPlaceholder = "{{placeholder}}" + // DefaultAccessLogFilter will skip the accesslog if return true + DefaultAccessLogFilter FilterHandler = &logFilter{} ) +// FilterHandler is an interface for type FilterHandler interface { Filter(*beecontext.Context) bool } @@ -96,7 +98,7 @@ func (l *logFilter) Filter(ctx *beecontext.Context) bool { return false } -// To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter +// ExceptMethodAppend 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) } @@ -110,22 +112,22 @@ type controllerInfo struct { routerType int } -// ControllerRegistor containers registered router rules, controller handlers and filters. -type ControllerRegistor struct { +// ControllerRegister containers registered router rules, controller handlers and filters. +type ControllerRegister struct { routers map[string]*Tree enableFilter bool filters map[int][]*FilterRouter } -// NewControllerRegister returns a new ControllerRegistor. -func NewControllerRegister() *ControllerRegistor { - return &ControllerRegistor{ +// NewControllerRegister returns a new ControllerRegister. +func NewControllerRegister() *ControllerRegister { + return &ControllerRegister{ routers: make(map[string]*Tree), filters: make(map[int][]*FilterRouter), } } -// Add controller handler and pattern rules to ControllerRegistor. +// Add controller handler and pattern rules to ControllerRegister. // usage: // default methods is the same name as method // Add("/user",&UserController{}) @@ -135,7 +137,7 @@ func NewControllerRegister() *ControllerRegistor { // Add("/api/delete",&RestController{},"delete:DeleteFood") // Add("/api",&RestController{},"get,post:ApiFunc") // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") -func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingMethods ...string) { +func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() methods := make(map[string]string) @@ -183,7 +185,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM } } -func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) { +func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerInfo) { if !RouterCaseSensitive { pattern = strings.ToLower(pattern) } @@ -196,9 +198,9 @@ func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerIn } } -// only when the Runmode is dev will generate router file in the router/auto.go from the controller +// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller // Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) -func (p *ControllerRegistor) Include(cList ...ControllerInterface) { +func (p *ControllerRegister) Include(cList ...ControllerInterface) { if RunMode == "dev" { skip := make(map[string]bool, 10) for _, c := range cList { @@ -238,84 +240,84 @@ func (p *ControllerRegistor) Include(cList ...ControllerInterface) { } } -// add get method +// Get add get method // usage: // Get("/", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Get(pattern string, f FilterFunc) { +func (p *ControllerRegister) Get(pattern string, f FilterFunc) { p.AddMethod("get", pattern, f) } -// add post method +// Post add post method // usage: // Post("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Post(pattern string, f FilterFunc) { +func (p *ControllerRegister) Post(pattern string, f FilterFunc) { p.AddMethod("post", pattern, f) } -// add put method +// Put add put method // usage: // Put("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Put(pattern string, f FilterFunc) { +func (p *ControllerRegister) Put(pattern string, f FilterFunc) { p.AddMethod("put", pattern, f) } -// add delete method +// Delete add delete method // usage: // Delete("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Delete(pattern string, f FilterFunc) { +func (p *ControllerRegister) Delete(pattern string, f FilterFunc) { p.AddMethod("delete", pattern, f) } -// add head method +// Head add head method // usage: // Head("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Head(pattern string, f FilterFunc) { +func (p *ControllerRegister) Head(pattern string, f FilterFunc) { p.AddMethod("head", pattern, f) } -// add patch method +// Patch add patch method // usage: // Patch("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Patch(pattern string, f FilterFunc) { +func (p *ControllerRegister) Patch(pattern string, f FilterFunc) { p.AddMethod("patch", pattern, f) } -// add options method +// Options add options method // usage: // Options("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Options(pattern string, f FilterFunc) { +func (p *ControllerRegister) Options(pattern string, f FilterFunc) { p.AddMethod("options", pattern, f) } -// add all method +// Any add all method // usage: // Any("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Any(pattern string, f FilterFunc) { +func (p *ControllerRegister) Any(pattern string, f FilterFunc) { p.AddMethod("*", pattern, f) } -// add http method router +// AddMethod add http method router // usage: // AddMethod("get","/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { +func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok { panic("not support http method: " + method) } @@ -343,8 +345,8 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { } } -// add user defined Handler -func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) { +// Handler add user defined Handler +func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { route := &controllerInfo{} route.pattern = pattern route.routerType = routerTypeHandler @@ -359,21 +361,21 @@ func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ... } } -// Add auto router to ControllerRegistor. +// AddAuto router to ControllerRegister. // example beego.AddAuto(&MainContorlller{}), // MainController has method List and Page. // visit the url /main/list to execute List function // /main/page to execute Page function. -func (p *ControllerRegistor) AddAuto(c ControllerInterface) { +func (p *ControllerRegister) AddAuto(c ControllerInterface) { p.AddAutoPrefix("/", c) } -// Add auto router to ControllerRegistor with prefix. +// AddAutoPrefix Add auto router to ControllerRegister with prefix. // example beego.AddAutoPrefix("/admin",&MainContorlller{}), // MainController has method List and Page. // visit the url /admin/main/list to execute List function // /admin/main/page to execute Page function. -func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) { +func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) { reflectVal := reflect.ValueOf(c) rt := reflectVal.Type() ct := reflect.Indirect(reflectVal).Type() @@ -399,9 +401,9 @@ func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) } } -// Add a FilterFunc with pattern rule and action constant. +// InsertFilter Add a FilterFunc with pattern rule and action constant. // The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) -func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error { +func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error { mr := new(FilterRouter) mr.tree = NewTree() @@ -420,15 +422,15 @@ func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter Filter } // add Filter into -func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error { +func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error { p.filters[pos] = append(p.filters[pos], mr) p.enableFilter = true return nil } -// UrlFor does another controller handler in this request function. +// URLFor does another controller handler in this request function. // it can access any controller method. -func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) string { +func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string { paths := strings.Split(endpoint, ".") if len(paths) <= 1 { Warn("urlfor endpoint must like path.controller.method") @@ -460,7 +462,7 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) stri return "" } -func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) { +func (p *ControllerRegister) 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, httpMethod) @@ -469,7 +471,7 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin } } if t.wildcard != nil { - u := path.Join(url, url_placeholder) + u := path.Join(url, urlPlaceholder) ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod) if ok { return ok, u @@ -499,22 +501,21 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin if find { if l.regexps == nil { if len(l.wildcards) == 0 { - return true, strings.Replace(url, "/"+url_placeholder, "", 1) + tourl(params) + return true, strings.Replace(url, "/"+urlPlaceholder, "", 1) + tourl(params) } if len(l.wildcards) == 1 { if v, ok := params[l.wildcards[0]]; ok { delete(params, l.wildcards[0]) - return true, strings.Replace(url, url_placeholder, v, 1) + tourl(params) - } else { - return false, "" + return true, strings.Replace(url, urlPlaceholder, v, 1) + tourl(params) } + return false, "" } if len(l.wildcards) == 3 && l.wildcards[0] == "." { if p, ok := params[":path"]; ok { if e, isok := params[":ext"]; isok { delete(params, ":path") delete(params, ":ext") - return true, strings.Replace(url, url_placeholder, p+"."+e, -1) + tourl(params) + return true, strings.Replace(url, urlPlaceholder, p+"."+e, -1) + tourl(params) } } } @@ -526,45 +527,43 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin } if u, ok := params[v]; ok { delete(params, v) - url = strings.Replace(url, url_placeholder, u, 1) + url = strings.Replace(url, urlPlaceholder, u, 1) } else { if canskip { canskip = false continue - } else { - return false, "" } + return false, "" } } return true, url + tourl(params) - } else { - var i int - var startreg bool - regurl := "" - for _, v := range strings.Trim(l.regexps.String(), "^$") { - if v == '(' { - startreg = true - continue - } else if v == ')' { - startreg = false - if v, ok := params[l.wildcards[i]]; ok { - delete(params, l.wildcards[i]) - regurl = regurl + v - i++ - } else { - break - } - } else if !startreg { - regurl = string(append([]rune(regurl), v)) + } + var i int + var startreg bool + regurl := "" + for _, v := range strings.Trim(l.regexps.String(), "^$") { + if v == '(' { + startreg = true + continue + } else if v == ')' { + startreg = false + if v, ok := params[l.wildcards[i]]; ok { + delete(params, l.wildcards[i]) + regurl = regurl + v + i++ + } else { + break } + } else if !startreg { + regurl = string(append([]rune(regurl), v)) } - if l.regexps.MatchString(regurl) { - ps := strings.Split(regurl, "/") - for _, p := range ps { - url = strings.Replace(url, url_placeholder, p, 1) - } - return true, url + tourl(params) + } + if l.regexps.MatchString(regurl) { + ps := strings.Split(regurl, "/") + for _, p := range ps { + url = strings.Replace(url, urlPlaceholder, p, 1) } + return true, url + tourl(params) } } } @@ -575,7 +574,7 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin } // Implement http.Handler interface. -func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { +func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { starttime := time.Now() var runrouter reflect.Type var findrouter bool @@ -607,7 +606,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) urlPath = r.URL.Path } // defined filter function - do_filter := func(pos int) (started bool) { + doFilter := func(pos int) (started bool) { if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { @@ -617,7 +616,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { if context.Input.Params == nil { - context.Input.Params = make(map[string]string) + context.Input.Params = make(map[string]string) } context.Input.Params[k] = v } @@ -639,7 +638,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } // filter for static file - if do_filter(BeforeStatic) { + if doFilter(BeforeStatic) { goto Admin } @@ -670,7 +669,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Input.ParseFormOrMulitForm(MaxMemory) } - if do_filter(BeforeRouter) { + if doFilter(BeforeRouter) { goto Admin } @@ -681,17 +680,17 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } if !findrouter { - http_method := r.Method + httpMethod := r.Method - if http_method == "POST" && context.Input.Query("_method") == "PUT" { - http_method = "PUT" + if httpMethod == "POST" && context.Input.Query("_method") == "PUT" { + httpMethod = "PUT" } - if http_method == "POST" && context.Input.Query("_method") == "DELETE" { - http_method = "DELETE" + if httpMethod == "POST" && context.Input.Query("_method") == "DELETE" { + httpMethod = "DELETE" } - if t, ok := p.routers[http_method]; ok { + if t, ok := p.routers[httpMethod]; ok { runObject, p := t.Match(urlPath) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r @@ -718,7 +717,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if findrouter { //execute middleware filters - if do_filter(BeforeExec) { + if doFilter(BeforeExec) { goto Admin } isRunable := false @@ -770,10 +769,10 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if EnableXSRF { - execController.XsrfToken() + execController.XSRFToken() if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { - execController.CheckXsrfCookie() + execController.CheckXSRFCookie() } } @@ -798,7 +797,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) execController.Options() default: if !execController.HandlerFunc(runMethod) { - in := make([]reflect.Value, 0) + var in []reflect.Value method := vc.MethodByName(runMethod) method.Call(in) } @@ -819,12 +818,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } //execute middleware filters - if do_filter(AfterExec) { + if doFilter(AfterExec) { goto Admin } } - do_filter(FinishRouter) + doFilter(FinishRouter) Admin: timeend := time.Since(starttime) @@ -850,7 +849,7 @@ Admin: } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } - if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) { + if DefaultAccessLogFilter == nil || !DefaultAccessLogFilter.Filter(context) { Debug(devinfo) } } @@ -861,15 +860,15 @@ Admin: } } -func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { +func (p *ControllerRegister) recoverPanic(context *beecontext.Context) { if err := recover(); err != nil { - if err == USERSTOPRUN { + if err == ErrAbort { return } if !RecoverPanic { panic(err) } else { - if ErrorsShow { + if EnableErrorsShow { if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { exception(fmt.Sprint(err), context) return @@ -931,6 +930,13 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return hj.Hijack() } +func (w *responseWriter) Flush() { + f, ok := w.writer.(http.Flusher) + if ok { + f.Flush() + } +} + func tourl(params map[string]string) string { if len(params) == 0 { return "" diff --git a/router_test.go b/router_test.go index 005f32d6..c6ec9c92 100644 --- a/router_test.go +++ b/router_test.go @@ -52,17 +52,17 @@ func (tc *TestController) Myext() { tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext"))) } -func (tc *TestController) GetUrl() { - tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext"))) +func (tc *TestController) GetURL() { + tc.Ctx.Output.Body([]byte(tc.URLFor(".Myext"))) } -func (t *TestController) GetParams() { - t.Ctx.WriteString(t.Ctx.Input.Query(":last") + "+" + - t.Ctx.Input.Query(":first") + "+" + t.Ctx.Input.Query("learn")) +func (tc *TestController) GetParams() { + tc.Ctx.WriteString(tc.Ctx.Input.Query(":last") + "+" + + tc.Ctx.Input.Query(":first") + "+" + tc.Ctx.Input.Query("learn")) } -func (t *TestController) GetManyRouter() { - t.Ctx.WriteString(t.Ctx.Input.Query(":id") + t.Ctx.Input.Query(":page")) +func (tc *TestController) GetManyRouter() { + tc.Ctx.WriteString(tc.Ctx.Input.Query(":id") + tc.Ctx.Input.Query(":page")) } type ResStatus struct { @@ -70,29 +70,29 @@ type ResStatus struct { Msg string } -type JsonController struct { +type JSONController struct { Controller } -func (this *JsonController) Prepare() { - this.Data["json"] = "prepare" - this.ServeJson(true) +func (jc *JSONController) Prepare() { + jc.Data["json"] = "prepare" + jc.ServeJSON(true) } -func (this *JsonController) Get() { - this.Data["Username"] = "astaxie" - this.Ctx.Output.Body([]byte("ok")) +func (jc *JSONController) Get() { + jc.Data["Username"] = "astaxie" + jc.Ctx.Output.Body([]byte("ok")) } func TestUrlFor(t *testing.T) { handler := NewControllerRegister() handler.Add("/api/list", &TestController{}, "*:List") handler.Add("/person/:last/:first", &TestController{}, "*:Param") - if a := handler.UrlFor("TestController.List"); a != "/api/list" { + if a := handler.URLFor("TestController.List"); a != "/api/list" { Info(a) t.Errorf("TestController.List must equal to /api/list") } - if a := handler.UrlFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" { + if a := handler.URLFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" { t.Errorf("TestController.Param must equal to /person/xie/asta, but get " + a) } } @@ -100,39 +100,39 @@ func TestUrlFor(t *testing.T) { func TestUrlFor3(t *testing.T) { handler := NewControllerRegister() handler.AddAuto(&TestController{}) - if a := handler.UrlFor("TestController.Myext"); a != "/test/myext" && a != "/Test/Myext" { + if a := handler.URLFor("TestController.Myext"); a != "/test/myext" && a != "/Test/Myext" { t.Errorf("TestController.Myext must equal to /test/myext, but get " + a) } - if a := handler.UrlFor("TestController.GetUrl"); a != "/test/geturl" && a != "/Test/GetUrl" { - t.Errorf("TestController.GetUrl must equal to /test/geturl, but get " + a) + if a := handler.URLFor("TestController.GetURL"); a != "/test/geturl" && a != "/Test/GetURL" { + t.Errorf("TestController.GetURL must equal to /test/geturl, but get " + a) } } func TestUrlFor2(t *testing.T) { handler := NewControllerRegister() handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, "*:List") - handler.Add("/v1/:username/edit", &TestController{}, "get:GetUrl") + handler.Add("/v1/:username/edit", &TestController{}, "get:GetURL") handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param") handler.Add("/:year:int/:month:int/:title/:entid", &TestController{}) - if handler.UrlFor("TestController.GetUrl", ":username", "astaxie") != "/v1/astaxie/edit" { - Info(handler.UrlFor("TestController.GetUrl")) + if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" { + Info(handler.URLFor("TestController.GetURL")) t.Errorf("TestController.List must equal to /v1/astaxie/edit") } - if handler.UrlFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") != + if handler.URLFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") != "/v1/za/cms_12_123.html" { - Info(handler.UrlFor("TestController.List")) + Info(handler.URLFor("TestController.List")) t.Errorf("TestController.List must equal to /v1/za/cms_12_123.html") } - if handler.UrlFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") != + if handler.URLFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") != "/v1/za_cms/ttt_12_123.html" { - Info(handler.UrlFor("TestController.Param")) + Info(handler.URLFor("TestController.Param")) t.Errorf("TestController.List must equal to /v1/za_cms/ttt_12_123.html") } - if handler.UrlFor("TestController.Get", ":year", "1111", ":month", "11", + if handler.URLFor("TestController.Get", ":year", "1111", ":month", "11", ":title", "aaaa", ":entid", "aaaa") != "/1111/11/aaaa/aaaa" { - Info(handler.UrlFor("TestController.Get")) + Info(handler.URLFor("TestController.Get")) t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa") } } @@ -270,7 +270,7 @@ func TestPrepare(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/json/list", &JsonController{}) + handler.Add("/json/list", &JSONController{}) handler.ServeHTTP(w, r) if w.Body.String() != `"prepare"` { t.Errorf(w.Body.String() + "user define func can't run") diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 76a13932..26237e95 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -51,7 +51,10 @@ import ( _ "github.com/go-sql-driver/mysql" ) -var mysqlpder = &MysqlProvider{} +var ( + TableName = "session" + mysqlpder = &MysqlProvider{} +) // mysql session store type MysqlSessionStore struct { @@ -110,7 +113,7 @@ func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) { if err != nil { return } - st.c.Exec("UPDATE session set `session_data`=?, `session_expiry`=? where session_key=?", + st.c.Exec("UPDATE "+TableName+" set `session_data`=?, `session_expiry`=? where session_key=?", b, time.Now().Unix(), st.sid) } @@ -141,11 +144,11 @@ func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error { // get mysql session by sid func (mp *MysqlProvider) SessionRead(sid string) (session.SessionStore, error) { c := mp.connectInit() - row := c.QueryRow("select session_data from session where session_key=?", sid) + row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) if err == sql.ErrNoRows { - c.Exec("insert into session(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", + c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", sid, "", time.Now().Unix()) } var kv map[interface{}]interface{} @@ -165,7 +168,7 @@ func (mp *MysqlProvider) SessionRead(sid string) (session.SessionStore, error) { func (mp *MysqlProvider) SessionExist(sid string) bool { c := mp.connectInit() defer c.Close() - row := c.QueryRow("select session_data from session where session_key=?", sid) + row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) if err == sql.ErrNoRows { @@ -178,13 +181,13 @@ func (mp *MysqlProvider) SessionExist(sid string) bool { // generate new sid for mysql session func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) { c := mp.connectInit() - row := c.QueryRow("select session_data from session where session_key=?", oldsid) + row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid) var sessiondata []byte err := row.Scan(&sessiondata) if err == sql.ErrNoRows { - c.Exec("insert into session(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", oldsid, "", time.Now().Unix()) + c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", oldsid, "", time.Now().Unix()) } - c.Exec("update session set `session_key`=? where session_key=?", sid, oldsid) + c.Exec("update "+TableName+" set `session_key`=? where session_key=?", sid, oldsid) var kv map[interface{}]interface{} if len(sessiondata) == 0 { kv = make(map[interface{}]interface{}) @@ -201,7 +204,7 @@ func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.SessionS // delete mysql session by sid func (mp *MysqlProvider) SessionDestroy(sid string) error { c := mp.connectInit() - c.Exec("DELETE FROM session where session_key=?", sid) + c.Exec("DELETE FROM "+TableName+" where session_key=?", sid) c.Close() return nil } @@ -209,7 +212,7 @@ func (mp *MysqlProvider) SessionDestroy(sid string) error { // delete expired values in mysql session func (mp *MysqlProvider) SessionGC() { c := mp.connectInit() - c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) + c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) c.Close() return } @@ -219,7 +222,7 @@ func (mp *MysqlProvider) SessionAll() int { c := mp.connectInit() defer c.Close() var total int - err := c.QueryRow("SELECT count(*) as num from session").Scan(&total) + err := c.QueryRow("SELECT count(*) as num from " + TableName).Scan(&total) if err != nil { return 0 } diff --git a/staticfile.go b/staticfile.go index 7c1ed98c..c85f17e0 100644 --- a/staticfile.go +++ b/staticfile.go @@ -40,15 +40,13 @@ func serverStaticRouter(ctx *context.Context) { if utils.FileExists(file) { http.ServeFile(ctx.ResponseWriter, ctx.Request, file) return - } else { - i++ - if i == len(StaticDir) { - http.NotFound(ctx.ResponseWriter, ctx.Request) - return - } else { - continue - } } + i++ + if i == len(StaticDir) { + http.NotFound(ctx.ResponseWriter, ctx.Request) + return + } + continue } if strings.HasPrefix(requestPath, prefix) { if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' { diff --git a/template.go b/template.go index 64b1939e..8822c514 100644 --- a/template.go +++ b/template.go @@ -28,17 +28,14 @@ import ( ) var ( - beegoTplFuncMap template.FuncMap - // beego template caching map and supported template file extensions. - BeeTemplates map[string]*template.Template - BeeTemplateExt []string + beegoTplFuncMap = make(template.FuncMap) + // BeeTemplates caching map and supported template file extensions. + BeeTemplates = make(map[string]*template.Template) + // BeeTemplateExt stores the template extention which will build + BeeTemplateExt = []string{"tpl", "html"} ) func init() { - BeeTemplates = make(map[string]*template.Template) - beegoTplFuncMap = make(template.FuncMap) - BeeTemplateExt = make([]string, 0) - BeeTemplateExt = append(BeeTemplateExt, "tpl", "html") beegoTplFuncMap["dateformat"] = DateFormat beegoTplFuncMap["date"] = Date beegoTplFuncMap["compare"] = Compare @@ -46,14 +43,15 @@ func init() { beegoTplFuncMap["not_nil"] = NotNil beegoTplFuncMap["not_null"] = NotNil beegoTplFuncMap["substr"] = Substr - beegoTplFuncMap["html2str"] = Html2str + beegoTplFuncMap["html2str"] = HTML2str beegoTplFuncMap["str2html"] = Str2html beegoTplFuncMap["htmlquote"] = Htmlquote beegoTplFuncMap["htmlunquote"] = Htmlunquote beegoTplFuncMap["renderform"] = RenderForm beegoTplFuncMap["assets_js"] = AssetsJs - beegoTplFuncMap["assets_css"] = AssetsCss + beegoTplFuncMap["assets_css"] = AssetsCSS beegoTplFuncMap["config"] = Config + beegoTplFuncMap["map_get"] = MapGet // go1.2 added template funcs // Comparisons @@ -64,7 +62,7 @@ func init() { beegoTplFuncMap["lt"] = lt // < beegoTplFuncMap["ne"] = ne // != - beegoTplFuncMap["urlfor"] = UrlFor // != + beegoTplFuncMap["urlfor"] = URLFor // != } // AddFuncMap let user to register a func in the template. @@ -78,7 +76,7 @@ type templatefile struct { files map[string][]string } -func (self *templatefile) visit(paths string, f os.FileInfo, err error) error { +func (tf *templatefile) visit(paths string, f os.FileInfo, err error) error { if f == nil { return err } @@ -91,21 +89,21 @@ func (self *templatefile) visit(paths string, f os.FileInfo, err error) error { replace := strings.NewReplacer("\\", "/") a := []byte(paths) - a = a[len([]byte(self.root)):] + a = a[len([]byte(tf.root)):] file := strings.TrimLeft(replace.Replace(string(a)), "/") subdir := filepath.Dir(file) - if _, ok := self.files[subdir]; ok { - self.files[subdir] = append(self.files[subdir], file) + if _, ok := tf.files[subdir]; ok { + tf.files[subdir] = append(tf.files[subdir], file) } else { m := make([]string, 1) m[0] = file - self.files[subdir] = m + tf.files[subdir] = m } return nil } -// return this path contains supported template extension of beego or not. +// HasTemplateExt return this path contains supported template extension of beego or not. func HasTemplateExt(paths string) bool { for _, v := range BeeTemplateExt { if strings.HasSuffix(paths, "."+v) { @@ -115,7 +113,7 @@ func HasTemplateExt(paths string) bool { return false } -// add new extension for template. +// AddTemplateExt add new extension for template. func AddTemplateExt(ext string) { for _, v := range BeeTemplateExt { if v == ext { @@ -125,15 +123,14 @@ func AddTemplateExt(ext string) { BeeTemplateExt = append(BeeTemplateExt, ext) } -// build all template files in a directory. +// BuildTemplate will build all template files in a directory. // it makes beego can render any template file in view directory. -func BuildTemplate(dir string) error { +func BuildTemplate(dir string, files ...string) error { if _, err := os.Stat(dir); err != nil { if os.IsNotExist(err) { return nil - } else { - return errors.New("dir open err") } + return errors.New("dir open err") } self := &templatefile{ root: dir, @@ -148,11 +145,13 @@ func BuildTemplate(dir string) error { } for _, v := range self.files { for _, file := range v { - t, err := getTemplate(self.root, file, v...) - if err != nil { - Trace("parse template err:", file, err) - } else { - BeeTemplates[file] = t + if len(files) == 0 || utils.InSlice(file, files) { + t, err := getTemplate(self.root, file, v...) + if err != nil { + Trace("parse template err:", file, err) + } else { + BeeTemplates[file] = t + } } } } @@ -260,3 +259,30 @@ func _getTemplate(t0 *template.Template, root string, submods [][]string, others } return } + +// SetViewsPath sets view directory path in beego application. +func SetViewsPath(path string) *App { + ViewsPath = path + return BeeApp +} + +// SetStaticPath sets static directory path and proper url pattern in beego application. +// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public". +func SetStaticPath(url string, path string) *App { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + url = strings.TrimRight(url, "/") + StaticDir[url] = path + return BeeApp +} + +// DelStaticPath removes the static folder setting in this url pattern in beego application. +func DelStaticPath(url string) *App { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + url = strings.TrimRight(url, "/") + delete(StaticDir, url) + return BeeApp +} diff --git a/template_test.go b/template_test.go index b35da5ce..2e222efc 100644 --- a/template_test.go +++ b/template_test.go @@ -20,11 +20,11 @@ import ( "testing" ) -var header string = `{{define "header"}} +var header = `{{define "header"}}

Hello, astaxie!

{{end}}` -var index string = ` +var index = ` beego welcome template @@ -37,7 +37,7 @@ var index string = ` ` -var block string = `{{define "block"}} +var block = `{{define "block"}}

Hello, blocks!

{{end}}` @@ -82,7 +82,7 @@ func TestTemplate(t *testing.T) { os.RemoveAll(dir) } -var menu string = `