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

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

This commit is contained in:
David V. Wallin 2015-09-17 17:07:06 +02:00
commit edbad60782
56 changed files with 1571 additions and 995 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
.DS_Store .DS_Store
*.swp *.swp
*.swo *.swo
beego.iml

View File

@ -3,14 +3,36 @@
[![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest) [![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) [![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) More info [beego.me](http://beego.me)
## Installation ##Quick Start
######Download and install
go get github.com/astaxie/beego 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 ## Features
* RESTful support * RESTful support

View File

@ -101,11 +101,11 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
m["AppConfigPath"] = AppConfigPath m["AppConfigPath"] = AppConfigPath
m["StaticDir"] = StaticDir m["StaticDir"] = StaticDir
m["StaticExtensionsToGzip"] = StaticExtensionsToGzip m["StaticExtensionsToGzip"] = StaticExtensionsToGzip
m["HttpAddr"] = HttpAddr m["HTTPAddr"] = HTTPAddr
m["HttpPort"] = HttpPort m["HTTPPort"] = HTTPPort
m["HttpTLS"] = EnableHttpTLS m["HTTPTLS"] = EnableHTTPTLS
m["HttpCertFile"] = HttpCertFile m["HTTPCertFile"] = HTTPCertFile
m["HttpKeyFile"] = HttpKeyFile m["HTTPKeyFile"] = HTTPKeyFile
m["RecoverPanic"] = RecoverPanic m["RecoverPanic"] = RecoverPanic
m["AutoRender"] = AutoRender m["AutoRender"] = AutoRender
m["ViewsPath"] = ViewsPath m["ViewsPath"] = ViewsPath
@ -114,14 +114,14 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
m["SessionProvider"] = SessionProvider m["SessionProvider"] = SessionProvider
m["SessionName"] = SessionName m["SessionName"] = SessionName
m["SessionGCMaxLifetime"] = SessionGCMaxLifetime m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
m["SessionSavePath"] = SessionSavePath m["SessionProviderConfig"] = SessionProviderConfig
m["SessionCookieLifeTime"] = SessionCookieLifeTime m["SessionCookieLifeTime"] = SessionCookieLifeTime
m["UseFcgi"] = UseFcgi m["EnabelFcgi"] = EnabelFcgi
m["MaxMemory"] = MaxMemory m["MaxMemory"] = MaxMemory
m["EnableGzip"] = EnableGzip m["EnableGzip"] = EnableGzip
m["DirectoryIndex"] = DirectoryIndex m["DirectoryIndex"] = DirectoryIndex
m["HttpServerTimeOut"] = HttpServerTimeOut m["HTTPServerTimeOut"] = HTTPServerTimeOut
m["ErrorsShow"] = ErrorsShow m["EnableErrorsShow"] = EnableErrorsShow
m["XSRFKEY"] = XSRFKEY m["XSRFKEY"] = XSRFKEY
m["EnableXSRF"] = EnableXSRF m["EnableXSRF"] = EnableXSRF
m["XSRFExpire"] = XSRFExpire m["XSRFExpire"] = XSRFExpire
@ -130,8 +130,8 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
m["TemplateRight"] = TemplateRight m["TemplateRight"] = TemplateRight
m["BeegoServerName"] = BeegoServerName m["BeegoServerName"] = BeegoServerName
m["EnableAdmin"] = EnableAdmin m["EnableAdmin"] = EnableAdmin
m["AdminHttpAddr"] = AdminHttpAddr m["AdminHTTPAddr"] = AdminHTTPAddr
m["AdminHttpPort"] = AdminHttpPort m["AdminHTTPPort"] = AdminHTTPPort
tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl))
tmpl = template.Must(tmpl.Parse(configTpl)) tmpl = template.Must(tmpl.Parse(configTpl))
@ -314,14 +314,14 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
data["Content"] = result.String() data["Content"] = result.String()
if format == "json" && command == "gc summary" { if format == "json" && command == "gc summary" {
dataJson, err := json.Marshal(data) dataJSON, err := json.Marshal(data)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
return return
} }
rw.Header().Set("Content-Type", "application/json") rw.Header().Set("Content-Type", "application/json")
rw.Write(dataJson) rw.Write(dataJSON)
return return
} }
@ -451,10 +451,10 @@ func (admin *adminApp) Run() {
if len(toolbox.AdminTaskList) > 0 { if len(toolbox.AdminTaskList) > 0 {
toolbox.StartTask() toolbox.StartTask()
} }
addr := AdminHttpAddr addr := AdminHTTPAddr
if AdminHttpPort != 0 { if AdminHTTPPort != 0 {
addr = fmt.Sprintf("%s:%d", AdminHttpAddr, AdminHttpPort) addr = fmt.Sprintf("%s:%d", AdminHTTPAddr, AdminHTTPPort)
} }
for p, f := range admin.routers { for p, f := range admin.routers {
http.Handle(p, f) http.Handle(p, f)

236
app.go
View File

@ -20,15 +20,26 @@ import (
"net/http" "net/http"
"net/http/fcgi" "net/http/fcgi"
"os" "os"
"path"
"time" "time"
"github.com/astaxie/beego/grace" "github.com/astaxie/beego/grace"
"github.com/astaxie/beego/utils" "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. // App defines beego application with a new PatternServeMux.
type App struct { type App struct {
Handlers *ControllerRegistor Handlers *ControllerRegister
Server *http.Server Server *http.Server
} }
@ -41,10 +52,10 @@ func NewApp() *App {
// Run beego application. // Run beego application.
func (app *App) Run() { func (app *App) Run() {
addr := HttpAddr addr := HTTPAddr
if HttpPort != 0 { if HTTPPort != 0 {
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort) addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPPort)
} }
var ( var (
@ -53,8 +64,8 @@ func (app *App) Run() {
) )
endRunning := make(chan bool, 1) endRunning := make(chan bool, 1)
if UseFcgi { if EnabelFcgi {
if UseStdIo { if EnableStdIo {
err = fcgi.Serve(nil, app.Handlers) // standard I/O err = fcgi.Serve(nil, app.Handlers) // standard I/O
if err == nil { if err == nil {
BeeLogger.Info("Use FCGI via standard I/O") 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) BeeLogger.Info("Cannot use FCGI via standard I/O", err)
} }
} else { } else {
if HttpPort == 0 { if HTTPPort == 0 {
// remove the Socket file before start // remove the Socket file before start
if utils.FileExists(addr) { if utils.FileExists(addr) {
os.Remove(addr) os.Remove(addr)
@ -80,18 +91,18 @@ func (app *App) Run() {
if Graceful { if Graceful {
app.Server.Addr = addr app.Server.Addr = addr
app.Server.Handler = app.Handlers app.Server.Handler = app.Handlers
app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second app.Server.ReadTimeout = time.Duration(HTTPServerTimeOut) * time.Second
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second app.Server.WriteTimeout = time.Duration(HTTPServerTimeOut) * time.Second
if EnableHttpTLS { if EnableHTTPTLS {
go func() { go func() {
time.Sleep(20 * time.Microsecond) time.Sleep(20 * time.Microsecond)
if HttpsPort != 0 { if HTTPSPort != 0 {
addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPSPort)
app.Server.Addr = addr app.Server.Addr = addr
} }
server := grace.NewServer(addr, app.Handlers) server := grace.NewServer(addr, app.Handlers)
server.Server = app.Server server.Server = app.Server
err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) err := server.ListenAndServeTLS(HTTPCertFile, HTTPKeyFile)
if err != nil { if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
time.Sleep(100 * time.Microsecond) time.Sleep(100 * time.Microsecond)
@ -99,11 +110,11 @@ func (app *App) Run() {
} }
}() }()
} }
if EnableHttpListen { if EnableHTTPListen {
go func() { go func() {
server := grace.NewServer(addr, app.Handlers) server := grace.NewServer(addr, app.Handlers)
server.Server = app.Server server.Server = app.Server
if ListenTCP4 && HttpAddr == "" { if ListenTCP4 && HTTPAddr == "" {
server.Network = "tcp4" server.Network = "tcp4"
} }
err := server.ListenAndServe() err := server.ListenAndServe()
@ -117,17 +128,17 @@ func (app *App) Run() {
} else { } else {
app.Server.Addr = addr app.Server.Addr = addr
app.Server.Handler = app.Handlers app.Server.Handler = app.Handlers
app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second app.Server.ReadTimeout = time.Duration(HTTPServerTimeOut) * time.Second
app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second app.Server.WriteTimeout = time.Duration(HTTPServerTimeOut) * time.Second
if EnableHttpTLS { if EnableHTTPTLS {
go func() { go func() {
time.Sleep(20 * time.Microsecond) time.Sleep(20 * time.Microsecond)
if HttpsPort != 0 { if HTTPSPort != 0 {
app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) app.Server.Addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPSPort)
} }
BeeLogger.Info("https server Running on %s", app.Server.Addr) 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 { if err != nil {
BeeLogger.Critical("ListenAndServeTLS: ", err) BeeLogger.Critical("ListenAndServeTLS: ", err)
time.Sleep(100 * time.Microsecond) time.Sleep(100 * time.Microsecond)
@ -136,11 +147,11 @@ func (app *App) Run() {
}() }()
} }
if EnableHttpListen { if EnableHTTPListen {
go func() { go func() {
app.Server.Addr = addr app.Server.Addr = addr
BeeLogger.Info("http server Running on %s", app.Server.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) ln, err := net.Listen("tcp4", app.Server.Addr)
if err != nil { if err != nil {
BeeLogger.Critical("ListenAndServe: ", err) BeeLogger.Critical("ListenAndServe: ", err)
@ -170,3 +181,182 @@ func (app *App) Run() {
} }
<-endRunning <-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
}

298
beego.go
View File

@ -12,243 +12,27 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // 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 package beego
import ( import (
"net/http"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"github.com/astaxie/beego/session"
) )
// beego web framework version. // beego web framework version.
const VERSION = "1.5.0" const VERSION = "1.5.0"
type hookfunc func() error //hook function to run //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc type hookfunc func() error
// Router adds a patterned controller handler to BeeApp. var (
// it's an alias method of App.Router. hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc
// 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
}
// Router add list from // AddAPPStartHook is used to register the hookfunc
// usage: // The hookfuncs will run in beego.Run()
// 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()
// such as sessionInit, middlerware start, buildtemplate, admin start // such as sessionInit, middlerware start, buildtemplate, admin start
func AddAPPStartHook(hf hookfunc) { func AddAPPStartHook(hf hookfunc) {
hooks = append(hooks, hf) hooks = append(hooks, hf)
@ -259,25 +43,22 @@ func AddAPPStartHook(hf hookfunc) {
// beego.Run(":8089") // beego.Run(":8089")
// beego.Run("127.0.0.1:8089") // beego.Run("127.0.0.1:8089")
func Run(params ...string) { func Run(params ...string) {
initBeforeHTTPRun()
if len(params) > 0 && params[0] != "" { if len(params) > 0 && params[0] != "" {
strs := strings.Split(params[0], ":") strs := strings.Split(params[0], ":")
if len(strs) > 0 && strs[0] != "" { if len(strs) > 0 && strs[0] != "" {
HttpAddr = strs[0] HTTPAddr = strs[0]
} }
if len(strs) > 1 && strs[1] != "" { if len(strs) > 1 && strs[1] != "" {
HttpPort, _ = strconv.Atoi(strs[1]) HTTPPort, _ = strconv.Atoi(strs[1])
} }
} }
initBeforeHttpRun()
if EnableAdmin {
go beeAdminApp.Run()
}
BeeApp.Run() BeeApp.Run()
} }
func initBeforeHttpRun() { func initBeforeHTTPRun() {
// if AppConfigPath not In the conf/app.conf reParse config // if AppConfigPath not In the conf/app.conf reParse config
if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") {
err := ParseConfig() err := ParseConfig()
@ -287,53 +68,22 @@ func initBeforeHttpRun() {
} }
} }
//init mime //init hooks
AddAPPStartHook(initMime) AddAPPStartHook(registerMime)
AddAPPStartHook(registerDefaultErrorHandler)
AddAPPStartHook(registerSession)
AddAPPStartHook(registerDocs)
AddAPPStartHook(registerTemplate)
AddAPPStartHook(registerAdmin)
// do hooks function
for _, hk := range hooks { for _, hk := range hooks {
err := hk() if err := hk(); err != nil {
if err != nil {
panic(err) 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) { func TestBeegoInit(apppath string) {
AppPath = apppath AppPath = apppath
os.Setenv("BEEGO_RUNMODE", "test") os.Setenv("BEEGO_RUNMODE", "test")
@ -344,9 +94,5 @@ func TestBeegoInit(apppath string) {
Info(err) Info(err)
} }
os.Chdir(AppPath) os.Chdir(AppPath)
initBeforeHttpRun() initBeforeHTTPRun()
}
func init() {
hooks = make([]hookfunc, 0)
} }

4
cache/README.md vendored
View File

@ -43,7 +43,7 @@ interval means the gc time. The cache will check at each time interval, whether
## Memcache adapter ## 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: Configure like this:
@ -52,7 +52,7 @@ Configure like this:
## Redis adapter ## 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: Configure like this:

3
cache/cache.go vendored
View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package cache provide a Cache interface and some implemetn engine
// Usage: // Usage:
// //
// import( // import(
@ -80,7 +81,7 @@ func Register(name string, adapter Cache) {
adapters[name] = adapter 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}. // config need to be correct JSON as string: {"interval":360}.
// it will start gc automatically. // it will start gc automatically.
func NewCache(adapterName, config string) (adapter Cache, err error) { func NewCache(adapterName, config string) (adapter Cache, err error) {

12
cache/conv.go vendored
View File

@ -19,7 +19,7 @@ import (
"strconv" "strconv"
) )
// convert interface to string. // GetString convert interface to string.
func GetString(v interface{}) string { func GetString(v interface{}) string {
switch result := v.(type) { switch result := v.(type) {
case string: case string:
@ -34,7 +34,7 @@ func GetString(v interface{}) string {
return "" return ""
} }
// convert interface to int. // GetInt convert interface to int.
func GetInt(v interface{}) int { func GetInt(v interface{}) int {
switch result := v.(type) { switch result := v.(type) {
case int: case int:
@ -52,7 +52,7 @@ func GetInt(v interface{}) int {
return 0 return 0
} }
// convert interface to int64. // GetInt64 convert interface to int64.
func GetInt64(v interface{}) int64 { func GetInt64(v interface{}) int64 {
switch result := v.(type) { switch result := v.(type) {
case int: case int:
@ -71,7 +71,7 @@ func GetInt64(v interface{}) int64 {
return 0 return 0
} }
// convert interface to float64. // GetFloat64 convert interface to float64.
func GetFloat64(v interface{}) float64 { func GetFloat64(v interface{}) float64 {
switch result := v.(type) { switch result := v.(type) {
case float64: case float64:
@ -85,7 +85,7 @@ func GetFloat64(v interface{}) float64 {
return 0 return 0
} }
// convert interface to bool. // GetBool convert interface to bool.
func GetBool(v interface{}) bool { func GetBool(v interface{}) bool {
switch result := v.(type) { switch result := v.(type) {
case bool: case bool:
@ -99,7 +99,7 @@ func GetBool(v interface{}) bool {
return false return false
} }
// convert interface to byte slice. // getByteArray convert interface to byte slice.
func getByteArray(v interface{}) []byte { func getByteArray(v interface{}) []byte {
switch result := v.(type) { switch result := v.(type) {
case []byte: case []byte:

14
cache/conv_test.go vendored
View File

@ -27,7 +27,7 @@ func TestGetString(t *testing.T) {
if "test2" != GetString(t2) { if "test2" != GetString(t2) {
t.Error("get string from byte array error") t.Error("get string from byte array error")
} }
var t3 int = 1 var t3 = 1
if "1" != GetString(t3) { if "1" != GetString(t3) {
t.Error("get string from int error") t.Error("get string from int error")
} }
@ -35,7 +35,7 @@ func TestGetString(t *testing.T) {
if "1" != GetString(t4) { if "1" != GetString(t4) {
t.Error("get string from int64 error") t.Error("get string from int64 error")
} }
var t5 float64 = 1.1 var t5 = 1.1
if "1.1" != GetString(t5) { if "1.1" != GetString(t5) {
t.Error("get string from float64 error") t.Error("get string from float64 error")
} }
@ -46,7 +46,7 @@ func TestGetString(t *testing.T) {
} }
func TestGetInt(t *testing.T) { func TestGetInt(t *testing.T) {
var t1 int = 1 var t1 = 1
if 1 != GetInt(t1) { if 1 != GetInt(t1) {
t.Error("get int from int error") t.Error("get int from int error")
} }
@ -69,7 +69,7 @@ func TestGetInt(t *testing.T) {
func TestGetInt64(t *testing.T) { func TestGetInt64(t *testing.T) {
var i int64 = 1 var i int64 = 1
var t1 int = 1 var t1 = 1
if i != GetInt64(t1) { if i != GetInt64(t1) {
t.Error("get int64 from int error") t.Error("get int64 from int error")
} }
@ -91,12 +91,12 @@ func TestGetInt64(t *testing.T) {
} }
func TestGetFloat64(t *testing.T) { func TestGetFloat64(t *testing.T) {
var f float64 = 1.11 var f = 1.11
var t1 float32 = 1.11 var t1 float32 = 1.11
if f != GetFloat64(t1) { if f != GetFloat64(t1) {
t.Error("get float64 from float32 error") t.Error("get float64 from float32 error")
} }
var t2 float64 = 1.11 var t2 = 1.11
if f != GetFloat64(t2) { if f != GetFloat64(t2) {
t.Error("get float64 from float64 error") t.Error("get float64 from float64 error")
} }
@ -106,7 +106,7 @@ func TestGetFloat64(t *testing.T) {
} }
var f2 float64 = 1 var f2 float64 = 1
var t4 int = 1 var t4 = 1
if f2 != GetFloat64(t4) { if f2 != GetFloat64(t4) {
t.Error("get float64 from int error") t.Error("get float64 from int error")
} }

45
cache/file.go vendored
View File

@ -41,11 +41,12 @@ type FileCacheItem struct {
Expired int64 Expired int64
} }
// FileCache Config
var ( var (
FileCachePath string = "cache" // cache directory FileCachePath = "cache" // cache directory
FileCacheFileSuffix string = ".bin" // cache file suffix FileCacheFileSuffix = ".bin" // cache file suffix
FileCacheDirectoryLevel int = 2 // cache file deep level if auto generated cache files. FileCacheDirectoryLevel = 2 // cache file deep level if auto generated cache files.
FileCacheEmbedExpiry int64 = 0 // cache expire time, default is no expire forever. FileCacheEmbedExpiry int64 // cache expire time, default is no expire forever.
) )
// FileCache is cache adapter for file storage. // FileCache is cache adapter for file storage.
@ -56,14 +57,14 @@ type FileCache struct {
EmbedExpiry int 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. // the level and expiry need set in method StartAndGC as config string.
func NewFileCache() *FileCache { func NewFileCache() *FileCache {
// return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix} // return &FileCache{CachePath:FileCachePath, FileSuffix:FileCacheFileSuffix}
return &FileCache{} 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} // the config need to be like {CachePath:"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}
func (fc *FileCache) StartAndGC(config string) error { func (fc *FileCache) StartAndGC(config string) error {
@ -120,12 +121,12 @@ func (fc *FileCache) getCacheFileName(key string) string {
// Get value from file cache. // Get value from file cache.
// if non-exist or expired, return empty string. // if non-exist or expired, return empty string.
func (fc *FileCache) Get(key string) interface{} { func (fc *FileCache) Get(key string) interface{} {
fileData, err := File_get_contents(fc.getCacheFileName(key)) fileData, err := FileGetContents(fc.getCacheFileName(key))
if err != nil { if err != nil {
return "" return ""
} }
var to FileCacheItem var to FileCacheItem
Gob_decode(fileData, &to) GobDecode(fileData, &to)
if to.Expired < time.Now().Unix() { if to.Expired < time.Now().Unix() {
return "" return ""
} }
@ -155,11 +156,11 @@ func (fc *FileCache) Put(key string, val interface{}, timeout int64) error {
item.Expired = time.Now().Unix() + timeout item.Expired = time.Now().Unix() + timeout
} }
item.Lastaccess = time.Now().Unix() item.Lastaccess = time.Now().Unix()
data, err := Gob_encode(item) data, err := GobEncode(item)
if err != nil { if err != nil {
return err return err
} }
return File_put_contents(fc.getCacheFileName(key), data) return FilePutContents(fc.getCacheFileName(key), data)
} }
// Delete file cache value. // Delete file cache value.
@ -171,7 +172,7 @@ func (fc *FileCache) Delete(key string) error {
return nil return nil
} }
// Increase cached int value. // Incr will increase cached int value.
// fc value is saving forever unless Delete. // fc value is saving forever unless Delete.
func (fc *FileCache) Incr(key string) error { func (fc *FileCache) Incr(key string) error {
data := fc.Get(key) data := fc.Get(key)
@ -185,7 +186,7 @@ func (fc *FileCache) Incr(key string) error {
return nil return nil
} }
// Decrease cached int value. // Decr will decrease cached int value.
func (fc *FileCache) Decr(key string) error { func (fc *FileCache) Decr(key string) error {
data := fc.Get(key) data := fc.Get(key)
var decr int var decr int
@ -198,13 +199,13 @@ func (fc *FileCache) Decr(key string) error {
return nil return nil
} }
// Check value is exist. // IsExist check value is exist.
func (fc *FileCache) IsExist(key string) bool { func (fc *FileCache) IsExist(key string) bool {
ret, _ := exists(fc.getCacheFileName(key)) ret, _ := exists(fc.getCacheFileName(key))
return ret return ret
} }
// Clean cached files. // ClearAll will clean cached files.
// not implemented. // not implemented.
func (fc *FileCache) ClearAll() error { func (fc *FileCache) ClearAll() error {
return nil return nil
@ -222,9 +223,9 @@ func exists(path string) (bool, error) {
return false, err return false, err
} }
// Get bytes to file. // FileGetContents Get bytes to file.
// if non-exist, create this 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) f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
if e != nil { if e != nil {
return return
@ -242,9 +243,9 @@ func File_get_contents(filename string) (data []byte, e error) {
return return
} }
// Put bytes to file. // FilePutContents Put bytes to file.
// if non-exist, create this 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) fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil { if err != nil {
return err return err
@ -254,8 +255,8 @@ func File_put_contents(filename string, content []byte) error {
return err return err
} }
// Gob encodes file cache item. // GobEncode Gob encodes file cache item.
func Gob_encode(data interface{}) ([]byte, error) { func GobEncode(data interface{}) ([]byte, error) {
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
enc := gob.NewEncoder(buf) enc := gob.NewEncoder(buf)
err := enc.Encode(data) err := enc.Encode(data)
@ -265,8 +266,8 @@ func Gob_encode(data interface{}) ([]byte, error) {
return buf.Bytes(), err return buf.Bytes(), err
} }
// Gob decodes file cache item. // GobDecode Gob decodes file cache item.
func Gob_decode(data []byte, to *FileCacheItem) error { func GobDecode(data []byte, to *FileCacheItem) error {
buf := bytes.NewBuffer(data) buf := bytes.NewBuffer(data)
dec := gob.NewDecoder(buf) dec := gob.NewDecoder(buf)
return dec.Decode(&to) return dec.Decode(&to)

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// package memcahe for cache provider // Package memcache for cache provider
// //
// depend on github.com/bradfitz/gomemcache/memcache // depend on github.com/bradfitz/gomemcache/memcache
// //
@ -39,19 +39,19 @@ import (
"github.com/astaxie/beego/cache" "github.com/astaxie/beego/cache"
) )
// Memcache adapter. // Cache Memcache adapter.
type MemcacheCache struct { type Cache struct {
conn *memcache.Client conn *memcache.Client
conninfo []string conninfo []string
} }
// create new memcache adapter. // NewMemCache create new memcache adapter.
func NewMemCache() *MemcacheCache { func NewMemCache() *Cache {
return &MemcacheCache{} return &Cache{}
} }
// get value from memcache. // Get get value from memcache.
func (rc *MemcacheCache) Get(key string) interface{} { func (rc *Cache) Get(key string) interface{} {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return err return err
@ -63,8 +63,8 @@ func (rc *MemcacheCache) Get(key string) interface{} {
return nil return nil
} }
// get value from memcache. // GetMulti get value from memcache.
func (rc *MemcacheCache) GetMulti(keys []string) []interface{} { func (rc *Cache) GetMulti(keys []string) []interface{} {
size := len(keys) size := len(keys)
var rv []interface{} var rv []interface{}
if rc.conn == nil { if rc.conn == nil {
@ -81,16 +81,15 @@ func (rc *MemcacheCache) GetMulti(keys []string) []interface{} {
rv = append(rv, string(v.Value)) rv = append(rv, string(v.Value))
} }
return rv 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. // Put put value to memcache. only support string.
func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error { func (rc *Cache) Put(key string, val interface{}, timeout int64) error {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return err return err
@ -104,8 +103,8 @@ func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error {
return rc.conn.Set(&item) return rc.conn.Set(&item)
} }
// delete value in memcache. // Delete delete value in memcache.
func (rc *MemcacheCache) Delete(key string) error { func (rc *Cache) Delete(key string) error {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return err return err
@ -114,8 +113,8 @@ func (rc *MemcacheCache) Delete(key string) error {
return rc.conn.Delete(key) return rc.conn.Delete(key)
} }
// increase counter. // Incr increase counter.
func (rc *MemcacheCache) Incr(key string) error { func (rc *Cache) Incr(key string) error {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return err return err
@ -125,8 +124,8 @@ func (rc *MemcacheCache) Incr(key string) error {
return err return err
} }
// decrease counter. // Decr decrease counter.
func (rc *MemcacheCache) Decr(key string) error { func (rc *Cache) Decr(key string) error {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return err return err
@ -136,8 +135,8 @@ func (rc *MemcacheCache) Decr(key string) error {
return err return err
} }
// check value exists in memcache. // IsExist check value exists in memcache.
func (rc *MemcacheCache) IsExist(key string) bool { func (rc *Cache) IsExist(key string) bool {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return false return false
@ -150,8 +149,8 @@ func (rc *MemcacheCache) IsExist(key string) bool {
return true return true
} }
// clear all cached in memcache. // ClearAll clear all cached in memcache.
func (rc *MemcacheCache) ClearAll() error { func (rc *Cache) ClearAll() error {
if rc.conn == nil { if rc.conn == nil {
if err := rc.connectInit(); err != nil { if err := rc.connectInit(); err != nil {
return err return err
@ -160,10 +159,10 @@ func (rc *MemcacheCache) ClearAll() error {
return rc.conn.FlushAll() return rc.conn.FlushAll()
} }
// start memcache adapter. // StartAndGC start memcache adapter.
// config string is like {"conn":"connection info"}. // config string is like {"conn":"connection info"}.
// if connecting error, return. // if connecting error, return.
func (rc *MemcacheCache) StartAndGC(config string) error { func (rc *Cache) StartAndGC(config string) error {
var cf map[string]string var cf map[string]string
json.Unmarshal([]byte(config), &cf) json.Unmarshal([]byte(config), &cf)
if _, ok := cf["conn"]; !ok { if _, ok := cf["conn"]; !ok {
@ -179,7 +178,7 @@ func (rc *MemcacheCache) StartAndGC(config string) error {
} }
// connect to memcache and keep the connection. // connect to memcache and keep the connection.
func (rc *MemcacheCache) connectInit() error { func (rc *Cache) connectInit() error {
rc.conn = memcache.New(rc.conninfo...) rc.conn = memcache.New(rc.conninfo...)
return nil return nil
} }

View File

@ -23,7 +23,7 @@ import (
"time" "time"
) )
func TestRedisCache(t *testing.T) { func TestMemcacheCache(t *testing.T) {
bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`) bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`)
if err != nil { if err != nil {
t.Error("init err") t.Error("init err")

26
cache/memory.go vendored
View File

@ -23,18 +23,18 @@ import (
) )
var ( var (
// clock time of recycling the expired cache items in memory. // DefaultEvery means the clock time of recycling the expired cache items in memory.
DefaultEvery int = 60 // 1 minute DefaultEvery = 60 // 1 minute
) )
// Memory cache item. // MemoryItem store enery cache item.
type MemoryItem struct { type MemoryItem struct {
val interface{} val interface{}
Lastaccess time.Time Lastaccess time.Time
expired int64 expired int64
} }
// Memory cache adapter. // MemoryCache is Memory cache adapter.
// it contains a RW locker for safe map storage. // it contains a RW locker for safe map storage.
type MemoryCache struct { type MemoryCache struct {
lock sync.RWMutex lock sync.RWMutex
@ -87,7 +87,7 @@ func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error
return nil return nil
} }
/// Delete cache in memory. // Delete cache in memory.
func (bc *MemoryCache) Delete(name string) error { func (bc *MemoryCache) Delete(name string) error {
bc.lock.Lock() bc.lock.Lock()
defer bc.lock.Unlock() defer bc.lock.Unlock()
@ -101,7 +101,7 @@ func (bc *MemoryCache) Delete(name string) error {
return nil return nil
} }
// Increase cache counter in memory. // Incr increase cache counter in memory.
// it supports int,int64,int32,uint,uint64,uint32. // it supports int,int64,int32,uint,uint64,uint32.
func (bc *MemoryCache) Incr(key string) error { func (bc *MemoryCache) Incr(key string) error {
bc.lock.RLock() bc.lock.RLock()
@ -129,7 +129,7 @@ func (bc *MemoryCache) Incr(key string) error {
return nil return nil
} }
// Decrease counter in memory. // Decr decrease counter in memory.
func (bc *MemoryCache) Decr(key string) error { func (bc *MemoryCache) Decr(key string) error {
bc.lock.RLock() bc.lock.RLock()
defer bc.lock.RUnlock() defer bc.lock.RUnlock()
@ -168,7 +168,7 @@ func (bc *MemoryCache) Decr(key string) error {
return nil return nil
} }
// check cache exist in memory. // IsExist check cache exist in memory.
func (bc *MemoryCache) IsExist(name string) bool { func (bc *MemoryCache) IsExist(name string) bool {
bc.lock.RLock() bc.lock.RLock()
defer bc.lock.RUnlock() defer bc.lock.RUnlock()
@ -176,7 +176,7 @@ func (bc *MemoryCache) IsExist(name string) bool {
return ok return ok
} }
// delete all cache in memory. // ClearAll will delete all cache in memory.
func (bc *MemoryCache) ClearAll() error { func (bc *MemoryCache) ClearAll() error {
bc.lock.Lock() bc.lock.Lock()
defer bc.lock.Unlock() defer bc.lock.Unlock()
@ -184,7 +184,7 @@ func (bc *MemoryCache) ClearAll() error {
return nil 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 { func (bc *MemoryCache) StartAndGC(config string) error {
var cf map[string]int var cf map[string]int
json.Unmarshal([]byte(config), &cf) json.Unmarshal([]byte(config), &cf)
@ -213,13 +213,13 @@ func (bc *MemoryCache) vaccuum() {
return return
} }
for name := range bc.items { for name := range bc.items {
bc.item_expired(name) bc.itemExpired(name)
} }
} }
} }
// item_expired returns true if an item is expired. // itemExpired returns true if an item is expired.
func (bc *MemoryCache) item_expired(name string) bool { func (bc *MemoryCache) itemExpired(name string) bool {
bc.lock.Lock() bc.lock.Lock()
defer bc.lock.Unlock() defer bc.lock.Unlock()
itm, ok := bc.items[name] itm, ok := bc.items[name]

52
cache/redis/redis.go vendored
View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// package redis for cache provider // Package redis for cache provider
// //
// depend on github.com/garyburd/redigo/redis // depend on github.com/garyburd/redigo/redis
// //
@ -41,12 +41,12 @@ import (
) )
var ( var (
// the collection name of redis for cache adapter. // DefaultKey the collection name of redis for cache adapter.
DefaultKey string = "beecacheRedis" DefaultKey = "beecacheRedis"
) )
// Redis cache adapter. // Cache is Redis cache adapter.
type RedisCache struct { type Cache struct {
p *redis.Pool // redis connection pool p *redis.Pool // redis connection pool
conninfo string conninfo string
dbNum int dbNum int
@ -54,13 +54,13 @@ type RedisCache struct {
password string password string
} }
// create new redis cache with default collection name. // NewRedisCache create new redis cache with default collection name.
func NewRedisCache() *RedisCache { func NewRedisCache() *Cache {
return &RedisCache{key: DefaultKey} return &Cache{key: DefaultKey}
} }
// actually do the redis cmds // 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() c := rc.p.Get()
defer c.Close() defer c.Close()
@ -68,7 +68,7 @@ func (rc *RedisCache) do(commandName string, args ...interface{}) (reply interfa
} }
// Get cache from redis. // 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 { if v, err := rc.do("GET", key); err == nil {
return v return v
} }
@ -76,7 +76,7 @@ func (rc *RedisCache) Get(key string) interface{} {
} }
// GetMulti get cache from redis. // GetMulti get cache from redis.
func (rc *RedisCache) GetMulti(keys []string) []interface{} { func (rc *Cache) GetMulti(keys []string) []interface{} {
size := len(keys) size := len(keys)
var rv []interface{} var rv []interface{}
c := rc.p.Get() c := rc.p.Get()
@ -108,8 +108,8 @@ ERROR:
return rv return rv
} }
// put cache to redis. // Put put cache to redis.
func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { func (rc *Cache) Put(key string, val interface{}, timeout int64) error {
var err error var err error
if _, err = rc.do("SETEX", key, timeout, val); err != nil { if _, err = rc.do("SETEX", key, timeout, val); err != nil {
return err return err
@ -121,8 +121,8 @@ func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
return err return err
} }
// delete cache in redis. // Delete delete cache in redis.
func (rc *RedisCache) Delete(key string) error { func (rc *Cache) Delete(key string) error {
var err error var err error
if _, err = rc.do("DEL", key); err != nil { if _, err = rc.do("DEL", key); err != nil {
return err return err
@ -131,8 +131,8 @@ func (rc *RedisCache) Delete(key string) error {
return err return err
} }
// check cache's existence in redis. // IsExist check cache's existence in redis.
func (rc *RedisCache) IsExist(key string) bool { func (rc *Cache) IsExist(key string) bool {
v, err := redis.Bool(rc.do("EXISTS", key)) v, err := redis.Bool(rc.do("EXISTS", key))
if err != nil { if err != nil {
return false return false
@ -145,20 +145,20 @@ func (rc *RedisCache) IsExist(key string) bool {
return v return v
} }
// increase counter in redis. // Incr increase counter in redis.
func (rc *RedisCache) Incr(key string) error { func (rc *Cache) Incr(key string) error {
_, err := redis.Bool(rc.do("INCRBY", key, 1)) _, err := redis.Bool(rc.do("INCRBY", key, 1))
return err return err
} }
// decrease counter in redis. // Decr decrease counter in redis.
func (rc *RedisCache) Decr(key string) error { func (rc *Cache) Decr(key string) error {
_, err := redis.Bool(rc.do("INCRBY", key, -1)) _, err := redis.Bool(rc.do("INCRBY", key, -1))
return err return err
} }
// clean all cache in redis. delete this redis collection. // ClearAll clean all cache in redis. delete this redis collection.
func (rc *RedisCache) ClearAll() error { func (rc *Cache) ClearAll() error {
cachedKeys, err := redis.Strings(rc.do("HKEYS", rc.key)) cachedKeys, err := redis.Strings(rc.do("HKEYS", rc.key))
if err != nil { if err != nil {
return err return err
@ -172,11 +172,11 @@ func (rc *RedisCache) ClearAll() error {
return err return err
} }
// start redis cache adapter. // StartAndGC start redis cache adapter.
// config is like {"key":"collection key","conn":"connection info","dbNum":"0"} // config is like {"key":"collection key","conn":"connection info","dbNum":"0"}
// the cache item in redis are stored forever, // the cache item in redis are stored forever,
// so no gc operation. // so no gc operation.
func (rc *RedisCache) StartAndGC(config string) error { func (rc *Cache) StartAndGC(config string) error {
var cf map[string]string var cf map[string]string
json.Unmarshal([]byte(config), &cf) json.Unmarshal([]byte(config), &cf)
@ -206,7 +206,7 @@ func (rc *RedisCache) StartAndGC(config string) error {
} }
// connect to redis. // connect to redis.
func (rc *RedisCache) connectInit() { func (rc *Cache) connectInit() {
dialFunc := func() (c redis.Conn, err error) { dialFunc := func() (c redis.Conn, err error) {
c, err = redis.Dial("tcp", rc.conninfo) c, err = redis.Dial("tcp", rc.conninfo)
if err != nil { if err != nil {

239
config.go
View File

@ -29,60 +29,115 @@ import (
) )
var ( var (
BeeApp *App // beego application // AccessLogs represent whether output the access logs, default is false
AppName string AccessLogs bool
AppPath string // AdminHTTPAddr is address for admin
workPath string AdminHTTPAddr string
AppConfigPath string // AdminHTTPPort is listens port for admin
StaticDir map[string]string AdminHTTPPort int
TemplateCache map[string]*template.Template // template caching map // AppConfig is the instance of Config, store the config information from file
StaticExtensionsToGzip []string // files with should be compressed with gzip (.js,.css,etc) AppConfig *beegoAppConfig
EnableHttpListen bool // AppName represent Application name, always the project folder name
HttpAddr string AppName string
HttpPort int // AppPath is the path to the application
ListenTCP4 bool AppPath string
EnableHttpTLS bool // AppConfigPath is the path to the config files
HttpsPort int AppConfigPath string
HttpCertFile string // AppConfigProvider is the provider for the config, default is ini
HttpKeyFile string AppConfigProvider string
RecoverPanic bool // flag of auto recover panic // AutoRender is a flag of render template automatically. It's always turn off in API application
AutoRender bool // flag of render template automatically // default is true
ViewsPath string AutoRender bool
AppConfig *beegoAppConfig // BeegoServerName exported in response header.
RunMode string // run mode, "dev" or "prod" BeegoServerName string
GlobalSessions *session.Manager // global session mananger // CopyRequestBody is just useful for raw request body in context. default is false
SessionOn bool // flag of starting session auto. default is false. CopyRequestBody bool
SessionProvider string // default session provider, memory, mysql , redis ,etc. // DirectoryIndex wheather display directory index. default is false.
SessionName string // the cookie name when saving session id into cookie. DirectoryIndex bool
SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session. // EnableAdmin means turn on admin module to log every request info.
SessionSavePath string // if use mysql/redis/file provider, define save path to connection info. EnableAdmin bool
SessionCookieLifeTime int // the life time of session id in cookie. // EnableDocs enable generate docs & server docs API Swagger
SessionAutoSetCookie bool // auto setcookie EnableDocs bool
SessionDomain string // the cookie domain default is empty // EnableErrorsShow wheather show errors in page. if true, show error and trace info in page rendered with error template.
UseFcgi bool EnableErrorsShow bool
UseStdIo bool // EnabelFcgi turn on the fcgi Listen, default is false
MaxMemory int64 EnabelFcgi bool
EnableGzip bool // flag of enable gzip // EnableGzip means gzip the response
DirectoryIndex bool // flag of display directory index. default is false. EnableGzip bool
HttpServerTimeOut int64 // EnableHTTPListen represent whether turn on the HTTP, default is true
ErrorsShow bool // flag of show errors in page. if true, show error and trace info in page rendered with error template. EnableHTTPListen bool
XSRFKEY string // xsrf hash salt string. // EnableHTTPTLS represent whether turn on the HTTPS, default is true
EnableXSRF bool // flag of enable xsrf. EnableHTTPTLS bool
XSRFExpire int // the expiry of xsrf value. // EnableStdIo works with EnabelFcgi Use FCGI via standard I/O
CopyRequestBody bool // flag of copy raw request body in context. EnableStdIo bool
TemplateLeft string // EnableXSRF whether turn on xsrf. default is false
TemplateRight string EnableXSRF bool
BeegoServerName string // beego server name exported in response header. // FlashName is the name of the flash variable found in response header and cookie
EnableAdmin bool // flag of enable admin module to log every request info. FlashName string
AdminHttpAddr string // http server configurations for admin module. // FlashSeperator used to seperate flash key:value, default is BEEGOFLASH
AdminHttpPort int FlashSeperator string
FlashName string // name of the flash variable found in response header and cookie // GlobalSessions is the instance for the session manager
FlashSeperator string // used to seperate flash key:value GlobalSessions *session.Manager
AppConfigProvider string // config provider // Graceful means use graceful module to start the server
EnableDocs bool // enable generate docs & server docs API Swagger Graceful bool
RouterCaseSensitive bool // router case sensitive default is true // workPath is always the same as AppPath, but sometime when it started with other
AccessLogs bool // print access logs, default is false // program, like supervisor
Graceful bool // use graceful start the server 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 { type beegoAppConfig struct {
@ -215,9 +270,6 @@ func (b *beegoAppConfig) SaveConfigFile(filename string) error {
} }
func init() { func init() {
// create beego application
BeeApp = NewApp()
workPath, _ = os.Getwd() workPath, _ = os.Getwd()
workPath, _ = filepath.Abs(workPath) workPath, _ = filepath.Abs(workPath)
// initialize default configurations // initialize default configurations
@ -243,12 +295,12 @@ func init() {
TemplateCache = make(map[string]*template.Template) TemplateCache = make(map[string]*template.Template)
// set this to 0.0.0.0 to make this app available to externally // 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 = "" HTTPAddr = ""
HttpPort = 8080 HTTPPort = 8080
HttpsPort = 10443 HTTPSPort = 10443
AppName = "beego" AppName = "beego"
@ -264,20 +316,15 @@ func init() {
SessionProvider = "memory" SessionProvider = "memory"
SessionName = "beegosessionID" SessionName = "beegosessionID"
SessionGCMaxLifetime = 3600 SessionGCMaxLifetime = 3600
SessionSavePath = "" SessionProviderConfig = ""
SessionCookieLifeTime = 0 //set cookie default is the brower life SessionCookieLifeTime = 0 //set cookie default is the brower life
SessionAutoSetCookie = true SessionAutoSetCookie = true
UseFcgi = false
UseStdIo = false
MaxMemory = 1 << 26 //64MB MaxMemory = 1 << 26 //64MB
EnableGzip = false HTTPServerTimeOut = 0
HttpServerTimeOut = 0 EnableErrorsShow = true
ErrorsShow = true
XSRFKEY = "beegoxsrf" XSRFKEY = "beegoxsrf"
XSRFExpire = 0 XSRFExpire = 0
@ -288,8 +335,8 @@ func init() {
BeegoServerName = "beegoServer:" + VERSION BeegoServerName = "beegoServer:" + VERSION
EnableAdmin = false EnableAdmin = false
AdminHttpAddr = "127.0.0.1" AdminHTTPAddr = "127.0.0.1"
AdminHttpPort = 8088 AdminHTTPPort = 8088
FlashName = "BEEGO_FLASH" FlashName = "BEEGO_FLASH"
FlashSeperator = "BEEGOFLASH" FlashSeperator = "BEEGOFLASH"
@ -330,18 +377,18 @@ func ParseConfig() (err error) {
RunMode = runmode RunMode = runmode
} }
HttpAddr = AppConfig.String("HttpAddr") HTTPAddr = AppConfig.String("HTTPAddr")
if v, err := AppConfig.Int("HttpPort"); err == nil { if v, err := AppConfig.Int("HTTPPort"); err == nil {
HttpPort = v HTTPPort = v
} }
if v, err := AppConfig.Bool("ListenTCP4"); err == nil { if v, err := AppConfig.Bool("ListenTCP4"); err == nil {
ListenTCP4 = v ListenTCP4 = v
} }
if v, err := AppConfig.Bool("EnableHttpListen"); err == nil { if v, err := AppConfig.Bool("EnableHTTPListen"); err == nil {
EnableHttpListen = v EnableHTTPListen = v
} }
if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil { if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil {
@ -376,8 +423,8 @@ func ParseConfig() (err error) {
SessionName = sessName SessionName = sessName
} }
if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" { if sessProvConfig := AppConfig.String("SessionProviderConfig"); sessProvConfig != "" {
SessionSavePath = sesssavepath SessionProviderConfig = sessProvConfig
} }
if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
@ -388,8 +435,8 @@ func ParseConfig() (err error) {
SessionCookieLifeTime = sesscookielifetime SessionCookieLifeTime = sesscookielifetime
} }
if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil { if enabelFcgi, err := AppConfig.Bool("EnabelFcgi"); err == nil {
UseFcgi = usefcgi EnabelFcgi = enabelFcgi
} }
if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil { if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil {
@ -400,12 +447,12 @@ func ParseConfig() (err error) {
DirectoryIndex = directoryindex DirectoryIndex = directoryindex
} }
if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil { if timeout, err := AppConfig.Int64("HTTPServerTimeOut"); err == nil {
HttpServerTimeOut = timeout HTTPServerTimeOut = timeout
} }
if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil { if errorsshow, err := AppConfig.Bool("EnableErrorsShow"); err == nil {
ErrorsShow = errorsshow EnableErrorsShow = errorsshow
} }
if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil { if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil {
@ -432,20 +479,20 @@ func ParseConfig() (err error) {
TemplateRight = tplright TemplateRight = tplright
} }
if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil { if httptls, err := AppConfig.Bool("EnableHTTPTLS"); err == nil {
EnableHttpTLS = httptls EnableHTTPTLS = httptls
} }
if httpsport, err := AppConfig.Int("HttpsPort"); err == nil { if httpsport, err := AppConfig.Int("HTTPSPort"); err == nil {
HttpsPort = httpsport HTTPSPort = httpsport
} }
if certfile := AppConfig.String("HttpCertFile"); certfile != "" { if certfile := AppConfig.String("HTTPCertFile"); certfile != "" {
HttpCertFile = certfile HTTPCertFile = certfile
} }
if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" { if keyfile := AppConfig.String("HTTPKeyFile"); keyfile != "" {
HttpKeyFile = keyfile HTTPKeyFile = keyfile
} }
if serverName := AppConfig.String("BeegoServerName"); serverName != "" { if serverName := AppConfig.String("BeegoServerName"); serverName != "" {
@ -495,12 +542,12 @@ func ParseConfig() (err error) {
EnableAdmin = enableadmin EnableAdmin = enableadmin
} }
if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" { if adminhttpaddr := AppConfig.String("AdminHTTPAddr"); adminhttpaddr != "" {
AdminHttpAddr = adminhttpaddr AdminHTTPAddr = adminhttpaddr
} }
if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil { if adminhttpport, err := AppConfig.Int("AdminHTTPPort"); err == nil {
AdminHttpPort = adminhttpport AdminHTTPPort = adminhttpport
} }
if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil { if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil {

View File

@ -88,12 +88,12 @@ func Register(name string, adapter Config) {
// adapterName is ini/json/xml/yaml. // adapterName is ini/json/xml/yaml.
// filename is the config file path. // filename is the config file path.
func NewConfig(adapterName, fileaname string) (ConfigContainer, error) { func NewConfig(adapterName, filename string) (ConfigContainer, error) {
adapter, ok := adapters[adapterName] adapter, ok := adapters[adapterName]
if !ok { if !ok {
return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName) 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. // adapterName is ini/json/xml/yaml.

View File

@ -353,7 +353,7 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error {
} }
func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value {
rv := reflect.Zero(reflect.TypeOf(0)) rv := reflect.Zero(typ)
switch typ.Kind() { switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val := input.Query(key) 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 { func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value {
rv := reflect.Zero(reflect.TypeOf(0)) rv := reflect.Zero(typ)
switch typ.Kind() { switch typ.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
rv = input.bindInt(val, typ) rv = input.bindInt(val, typ)

View File

@ -34,18 +34,19 @@ import (
//commonly used mime-types //commonly used mime-types
const ( const (
applicationJson = "application/json" applicationJSON = "application/json"
applicationXml = "application/xml" applicationXML = "application/xml"
textXml = "text/xml" textXML = "text/xml"
) )
var ( var (
// custom error when user stop request handler manually. // ErrAbort custom error when user stop request handler manually.
USERSTOPRUN = errors.New("User stop run") ErrAbort = errors.New("User stop run")
GlobalControllerRouter map[string][]ControllerComments = make(map[string][]ControllerComments) //pkgpath+controller:comments // 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 { type ControllerComments struct {
Method string Method string
Router string Router string
@ -64,7 +65,7 @@ type Controller struct {
Layout string Layout string
LayoutSections map[string]string // the key is the section name and the value is the template name LayoutSections map[string]string // the key is the section name and the value is the template name
TplExt string TplExt string
_xsrf_token string _xsrfToken string
gotofunc string gotofunc string
CruSession session.SessionStore CruSession session.SessionStore
XSRFExpire int XSRFExpire int
@ -87,8 +88,8 @@ type ControllerInterface interface {
Options() Options()
Finish() Finish()
Render() error Render() error
XsrfToken() string XSRFToken() string
CheckXsrfCookie() bool CheckXSRFCookie() bool
HandlerFunc(fn string) bool HandlerFunc(fn string) bool
URLMapping() URLMapping()
} }
@ -153,20 +154,20 @@ func (c *Controller) Options() {
http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405) 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 { func (c *Controller) HandlerFunc(fnname string) bool {
if v, ok := c.methodMapping[fnname]; ok { if v, ok := c.methodMapping[fnname]; ok {
v() v()
return true return true
} else {
return false
} }
return false
} }
// URLMapping register the internal Controller router. // URLMapping register the internal Controller router.
func (c *Controller) URLMapping() { func (c *Controller) URLMapping() {
} }
// Mapping the method to function
func (c *Controller) Mapping(method string, fn func()) { func (c *Controller) Mapping(method string, fn func()) {
c.methodMapping[method] = fn c.methodMapping[method] = fn
} }
@ -177,13 +178,11 @@ func (c *Controller) Render() error {
return nil return nil
} }
rb, err := c.RenderBytes() rb, err := c.RenderBytes()
if err != nil { if err != nil {
return err 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 return nil
} }
@ -200,8 +199,19 @@ func (c *Controller) RenderBytes() ([]byte, error) {
if c.TplNames == "" { if c.TplNames == "" {
c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt
} }
if RunMode == "dev" { 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("") newbytes := bytes.NewBufferString("")
if _, ok := BeeTemplates[c.TplNames]; !ok { if _, ok := BeeTemplates[c.TplNames]; !ok {
@ -241,25 +251,25 @@ func (c *Controller) RenderBytes() ([]byte, error) {
} }
icontent, _ := ioutil.ReadAll(ibytes) icontent, _ := ioutil.ReadAll(ibytes)
return icontent, nil 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. // 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) 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) { func (c *Controller) Abort(code string) {
status, err := strconv.Atoi(code) status, err := strconv.Atoi(code)
if err != nil { if err != nil {
@ -285,29 +295,28 @@ func (c *Controller) CustomAbort(status int, body string) {
} }
// last panic user string // last panic user string
c.Ctx.ResponseWriter.Write([]byte(body)) c.Ctx.ResponseWriter.Write([]byte(body))
panic(USERSTOPRUN) panic(ErrAbort)
} }
// StopRun makes panic of USERSTOPRUN error and go to recover function if defined. // StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
func (c *Controller) StopRun() { 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. // 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 { if len(endpoint) <= 0 {
return "" return ""
} }
if endpoint[0] == '.' { if endpoint[0] == '.' {
return UrlFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...) return URLFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...)
} else {
return UrlFor(endpoint, values...)
} }
return URLFor(endpoint, values...)
} }
// ServeJson sends a json response with encoding charset. // ServeJSON sends a json response with encoding charset.
func (c *Controller) ServeJson(encoding ...bool) { func (c *Controller) ServeJSON(encoding ...bool) {
var hasIndent bool var hasIndent bool
var hasencoding bool var hasencoding bool
if RunMode == "prod" { if RunMode == "prod" {
@ -321,8 +330,8 @@ func (c *Controller) ServeJson(encoding ...bool) {
c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding) c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding)
} }
// ServeJsonp sends a jsonp response. // ServeJSONP sends a jsonp response.
func (c *Controller) ServeJsonp() { func (c *Controller) ServeJSONP() {
var hasIndent bool var hasIndent bool
if RunMode == "prod" { if RunMode == "prod" {
hasIndent = false hasIndent = false
@ -332,8 +341,8 @@ func (c *Controller) ServeJsonp() {
c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent) c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent)
} }
// ServeXml sends xml response. // ServeXML sends xml response.
func (c *Controller) ServeXml() { func (c *Controller) ServeXML() {
var hasIndent bool var hasIndent bool
if RunMode == "prod" { if RunMode == "prod" {
hasIndent = false hasIndent = false
@ -347,12 +356,12 @@ func (c *Controller) ServeXml() {
func (c *Controller) ServeFormatted() { func (c *Controller) ServeFormatted() {
accept := c.Ctx.Input.Header("Accept") accept := c.Ctx.Input.Header("Accept")
switch accept { switch accept {
case applicationJson: case applicationJSON:
c.ServeJson() c.ServeJSON()
case applicationXml, textXml: case applicationXML, textXML:
c.ServeXml() c.ServeXML()
default: 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 != "" { if v := c.Ctx.Input.Query(key); v != "" {
return 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 // 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] vs := f[key]
if len(vs) > 0 { if len(vs) > 0 {
return vs 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 // 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) return c.CruSession.Get(name)
} }
// SetSession removes value from session. // DelSession removes value from session.
func (c *Controller) DelSession(name interface{}) { func (c *Controller) DelSession(name interface{}) {
if c.CruSession == nil { if c.CruSession == nil {
c.StartSession() c.StartSession()
@ -614,34 +621,34 @@ func (c *Controller) SetSecureCookie(Secret, name, value string, others ...inter
c.Ctx.SetSecureCookie(Secret, name, value, others...) c.Ctx.SetSecureCookie(Secret, name, value, others...)
} }
// XsrfToken creates a xsrf token string and returns. // XSRFToken creates a CSRF token string and returns.
func (c *Controller) XsrfToken() string { func (c *Controller) XSRFToken() string {
if c._xsrf_token == "" { if c._xsrfToken == "" {
var expire int64 var expire int64
if c.XSRFExpire > 0 { if c.XSRFExpire > 0 {
expire = int64(c.XSRFExpire) expire = int64(c.XSRFExpire)
} else { } else {
expire = int64(XSRFExpire) 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" // the token can provided in request header "X-Xsrftoken" and "X-CsrfToken"
// or in form field value named as "_xsrf". // or in form field value named as "_xsrf".
func (c *Controller) CheckXsrfCookie() bool { func (c *Controller) CheckXSRFCookie() bool {
if !c.EnableXSRF { if !c.EnableXSRF {
return true return true
} }
return c.Ctx.CheckXsrfCookie() return c.Ctx.CheckXsrfCookie()
} }
// XsrfFormHtml writes an input field contains xsrf token value. // XSRFFormHTML writes an input field contains xsrf token value.
func (c *Controller) XsrfFormHtml() string { func (c *Controller) XSRFFormHTML() string {
return "<input type=\"hidden\" name=\"_xsrf\" value=\"" + return "<input type=\"hidden\" name=\"_xsrf\" value=\"" +
c._xsrf_token + "\"/>" c._xsrfToken + "\"/>"
} }
// GetControllerAndAction gets the executing controller name and action name. // GetControllerAndAction gets the executing controller name and action name.

17
doc.go Normal file
View File

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

View File

@ -20,20 +20,21 @@ import (
"github.com/astaxie/beego/context" "github.com/astaxie/beego/context"
) )
var GlobalDocApi map[string]interface{} // GlobalDocAPI store the swagger api documents
var GlobalDocAPI map[string]interface{}
func init() { func init() {
if EnableDocs { if EnableDocs {
GlobalDocApi = make(map[string]interface{}) GlobalDocAPI = make(map[string]interface{})
} }
} }
func serverDocs(ctx *context.Context) { func serverDocs(ctx *context.Context) {
var obj interface{} var obj interface{}
if splat := ctx.Input.Param(":splat"); splat == "" { if splat := ctx.Input.Param(":splat"); splat == "" {
obj = GlobalDocApi["Root"] obj = GlobalDocAPI["Root"]
} else { } else {
if v, ok := GlobalDocApi[splat]; ok { if v, ok := GlobalDocAPI[splat]; ok {
obj = v obj = v
} }
} }

View File

@ -358,52 +358,11 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
t.Execute(rw, data) 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. // ErrorHandler registers http.HandlerFunc to each http err code string.
// usage: // usage:
// beego.ErrorHandler("404",NotFound) // beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError) // beego.ErrorHandler("500",InternalServerError)
func Errorhandler(code string, h http.HandlerFunc) *App { func ErrorHandler(code string, h http.HandlerFunc) *App {
errinfo := &errorInfo{} errinfo := &errorInfo{}
errinfo.errorType = errorTypeHandler errinfo.errorType = errorTypeHandler
errinfo.handler = h errinfo.handler = h
@ -414,7 +373,7 @@ func Errorhandler(code string, h http.HandlerFunc) *App {
// ErrorController registers ControllerInterface to each http err code string. // ErrorController registers ControllerInterface to each http err code string.
// usage: // usage:
// beego.ErrorHandler(&controllers.ErrorController{}) // beego.ErrorController(&controllers.ErrorController{})
func ErrorController(c ControllerInterface) *App { func ErrorController(c ControllerInterface) *App {
reflectVal := reflect.ValueOf(c) reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type() rt := reflectVal.Type()
@ -453,7 +412,6 @@ func exception(errcode string, ctx *context.Context) {
func executeError(err *errorInfo, ctx *context.Context, code int) { func executeError(err *errorInfo, ctx *context.Context, code int) {
if err.errorType == errorTypeHandler { if err.errorType == errorTypeHandler {
ctx.ResponseWriter.WriteHeader(code)
err.handler(ctx.ResponseWriter, ctx.Request) err.handler(ctx.ResponseWriter, ctx.Request)
return return
} }
@ -473,7 +431,7 @@ func executeError(err *errorInfo, ctx *context.Context, code int) {
execController.URLMapping() execController.URLMapping()
in := make([]reflect.Value, 0) var in []reflect.Value
method := vc.MethodByName(err.method) method := vc.MethodByName(err.method)
method.Call(in) method.Call(in)

View File

@ -54,14 +54,13 @@ func (c *connection) readPump() {
}() }()
c.ws.SetReadLimit(maxMessageSize) c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(readWait)) c.ws.SetReadDeadline(time.Now().Add(readWait))
c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(readWait)); return nil })
for { for {
op, r, err := c.ws.NextReader() op, r, err := c.ws.NextReader()
if err != nil { if err != nil {
break break
} }
switch op { switch op {
case websocket.PongMessage:
c.ws.SetReadDeadline(time.Now().Add(readWait))
case websocket.TextMessage: case websocket.TextMessage:
message, err := ioutil.ReadAll(r) message, err := ioutil.ReadAll(r)
if err != nil { if err != nil {

View File

@ -39,7 +39,6 @@ func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) {
} }
if isok, ok := isok.(bool); ok { if isok, ok := isok.(bool); ok {
return isok, params return isok, params
} else {
return false, nil
} }
return false, nil
} }

View File

@ -30,7 +30,7 @@ func (t *TestFlashController) TestWriteFlash() {
flash.Notice("TestFlashString") flash.Notice("TestFlashString")
flash.Store(&t.Controller) flash.Store(&t.Controller)
// we choose to serve json because we don't want to load a template html file // 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) { func TestFlashHeader(t *testing.T) {

View File

@ -32,7 +32,7 @@
// mux := http.NewServeMux() // mux := http.NewServeMux()
// mux.HandleFunc("/hello", handler) // mux.HandleFunc("/hello", handler)
// //
// err := grace.ListenAndServe("localhost:8080", mux1) // err := grace.ListenAndServe("localhost:8080", mux)
// if err != nil { // if err != nil {
// log.Println(err) // log.Println(err)
// } // }

106
hooks.go Normal file
View File

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

View File

@ -74,12 +74,6 @@ func SetDefaultSetting(setting BeegoHttpSettings) {
settingMutex.Lock() settingMutex.Lock()
defer settingMutex.Unlock() defer settingMutex.Unlock()
defaultSetting = setting 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 // return *BeegoHttpRequest with specific method
@ -100,7 +94,7 @@ func NewBeegoRequest(rawurl, method string) *BeegoHttpRequest {
return &BeegoHttpRequest{ return &BeegoHttpRequest{
url: rawurl, url: rawurl,
req: &req, req: &req,
params: map[string]string{}, params: map[string][]string{},
files: map[string]string{}, files: map[string]string{},
setting: defaultSetting, setting: defaultSetting,
resp: &resp, resp: &resp,
@ -150,7 +144,7 @@ type BeegoHttpSettings struct {
type BeegoHttpRequest struct { type BeegoHttpRequest struct {
url string url string
req *http.Request req *http.Request
params map[string]string params map[string][]string
files map[string]string files map[string]string
setting BeegoHttpSettings setting BeegoHttpSettings
resp *http.Response 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. // Param adds query param in to request.
// params build query string as ?key1=value1&key2=value2... // params build query string as ?key1=value1&key2=value2...
func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest { 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 return b
} }
@ -348,7 +346,9 @@ func (b *BeegoHttpRequest) buildUrl(paramBody string) {
} }
} }
for k, v := range b.params { for k, v := range b.params {
bodyWriter.WriteField(k, v) for _, vv := range v {
bodyWriter.WriteField(k, vv)
}
} }
bodyWriter.Close() bodyWriter.Close()
pw.Close() pw.Close()
@ -383,10 +383,12 @@ func (b *BeegoHttpRequest) SendOut() (*http.Response, error) {
if len(b.params) > 0 { if len(b.params) > 0 {
var buf bytes.Buffer var buf bytes.Buffer
for k, v := range b.params { for k, v := range b.params {
buf.WriteString(url.QueryEscape(k)) for _, vv := range v {
buf.WriteByte('=') buf.WriteString(url.QueryEscape(k))
buf.WriteString(url.QueryEscape(v)) buf.WriteByte('=')
buf.WriteByte('&') buf.WriteString(url.QueryEscape(vv))
buf.WriteByte('&')
}
} }
paramBody = buf.String() paramBody = buf.String()
paramBody = paramBody[0 : len(paramBody)-1] paramBody = paramBody[0 : len(paramBody)-1]

View File

@ -19,6 +19,7 @@ import (
"os" "os"
"strings" "strings"
"testing" "testing"
"time"
) )
func TestResponse(t *testing.T) { func TestResponse(t *testing.T) {
@ -153,6 +154,7 @@ func TestWithSetting(t *testing.T) {
setting.EnableCookie = true setting.EnableCookie = true
setting.UserAgent = v setting.UserAgent = v
setting.Transport = nil setting.Transport = nil
setting.ReadWriteTimeout = 5 * time.Second
SetDefaultSetting(setting) SetDefaultSetting(setting)
str, err := Get("http://httpbin.org/get").String() str, err := Get("http://httpbin.org/get").String()

15
log.go
View File

@ -32,18 +32,18 @@ const (
LevelDebug LevelDebug
) )
// SetLogLevel sets the global log level used by the simple // SetLevel sets the global log level used by the simple logger.
// logger.
func SetLevel(l int) { func SetLevel(l int) {
BeeLogger.SetLevel(l) BeeLogger.SetLevel(l)
} }
// SetLogFuncCall set the CallDepth, default is 3
func SetLogFuncCall(b bool) { func SetLogFuncCall(b bool) {
BeeLogger.EnableFuncCallDepth(b) BeeLogger.EnableFuncCallDepth(b)
BeeLogger.SetLogFuncCallDepth(3) BeeLogger.SetLogFuncCallDepth(3)
} }
// logger references the used application logger. // BeeLogger references the used application logger.
var BeeLogger *logs.BeeLogger var BeeLogger *logs.BeeLogger
// SetLogger sets a new logger. // SetLogger sets a new logger.
@ -55,10 +55,12 @@ func SetLogger(adaptername string, config string) error {
return nil return nil
} }
// Emergency logs a message at emergency level.
func Emergency(v ...interface{}) { func Emergency(v ...interface{}) {
BeeLogger.Emergency(generateFmtStr(len(v)), v...) BeeLogger.Emergency(generateFmtStr(len(v)), v...)
} }
// Alert logs a message at alert level.
func Alert(v ...interface{}) { func Alert(v ...interface{}) {
BeeLogger.Alert(generateFmtStr(len(v)), v...) BeeLogger.Alert(generateFmtStr(len(v)), v...)
} }
@ -78,21 +80,22 @@ func Warning(v ...interface{}) {
BeeLogger.Warning(generateFmtStr(len(v)), v...) BeeLogger.Warning(generateFmtStr(len(v)), v...)
} }
// compatibility alias for Warning() // Warn compatibility alias for Warning()
func Warn(v ...interface{}) { func Warn(v ...interface{}) {
BeeLogger.Warn(generateFmtStr(len(v)), v...) BeeLogger.Warn(generateFmtStr(len(v)), v...)
} }
// Notice logs a message at notice level.
func Notice(v ...interface{}) { func Notice(v ...interface{}) {
BeeLogger.Notice(generateFmtStr(len(v)), v...) BeeLogger.Notice(generateFmtStr(len(v)), v...)
} }
// Info logs a message at info level. // Informational logs a message at info level.
func Informational(v ...interface{}) { func Informational(v ...interface{}) {
BeeLogger.Informational(generateFmtStr(len(v)), v...) BeeLogger.Informational(generateFmtStr(len(v)), v...)
} }
// compatibility alias for Warning() // Info compatibility alias for Warning()
func Info(v ...interface{}) { func Info(v ...interface{}) {
BeeLogger.Info(generateFmtStr(len(v)), v...) BeeLogger.Info(generateFmtStr(len(v)), v...)
} }

View File

@ -205,15 +205,20 @@ func (w *FileLogWriter) lines() (int, error) {
} }
// DoRotate means it need to write file in new file. // 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 { func (w *FileLogWriter) DoRotate() error {
_, err := os.Lstat(w.Filename) _, err := os.Lstat(w.Filename)
if err == nil { // file exists if err == nil { // file exists
// Find the next available number // Find the next available number
num := 1 num := 1
fname := "" fname := ""
suffix := filepath.Ext(w.Filename)
filenameOnly := strings.TrimSuffix(w.Filename, suffix)
if suffix == "" {
suffix = ".log"
}
for ; err == nil && num <= 999; num++ { 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) _, err = os.Lstat(fname)
} }
// return error if the last file checked still existed // return error if the last file checked still existed

View File

@ -103,7 +103,7 @@ func TestFileRotate(t *testing.T) {
log.Critical("critical") log.Critical("critical")
log.Emergency("emergency") log.Emergency("emergency")
time.Sleep(time.Second * 4) 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) b, err := exists(rotatename)
if !b || err != nil { if !b || err != nil {
t.Fatal("rotate not generated") t.Fatal("rotate not generated")

View File

@ -31,9 +31,9 @@ const (
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server. // smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
type SmtpWriter struct { type SmtpWriter struct {
Username string `json:"Username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Host string `json:"Host"` Host string `json:"host"`
Subject string `json:"subject"` Subject string `json:"subject"`
FromAddress string `json:"fromAddress"` FromAddress string `json:"fromAddress"`
RecipientAddresses []string `json:"sendTos"` RecipientAddresses []string `json:"sendTos"`

View File

@ -28,8 +28,10 @@ import (
"time" "time"
) )
var gmfim map[string]*memFileInfo = make(map[string]*memFileInfo) var (
var lock sync.RWMutex gmfim = make(map[string]*memFileInfo)
lock sync.RWMutex
)
// OpenMemZipFile returns MemFile object with a compressed static file. // OpenMemZipFile returns MemFile object with a compressed static file.
// it's used for serve static file if gzip enable. // it's used for serve static file if gzip enable.

13
mime.go
View File

@ -14,11 +14,7 @@
package beego package beego
import ( var mimemaps = map[string]string{
"mime"
)
var mimemaps map[string]string = map[string]string{
".3dm": "x-world/x-3dmf", ".3dm": "x-world/x-3dmf",
".3dmf": "x-world/x-3dmf", ".3dmf": "x-world/x-3dmf",
".7z": "application/x-7z-compressed", ".7z": "application/x-7z-compressed",
@ -558,10 +554,3 @@ var mimemaps map[string]string = map[string]string{
".oex": "application/x-opera-extension", ".oex": "application/x-opera-extension",
".mustache": "text/html", ".mustache": "text/html",
} }
func initMime() error {
for k, v := range mimemaps {
mime.AddExtensionType(k, v)
}
return nil
}

View File

@ -23,16 +23,17 @@ import (
type namespaceCond func(*beecontext.Context) bool 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 // Namespace is store all the info
type Namespace struct { type Namespace struct {
prefix string prefix string
handlers *ControllerRegistor handlers *ControllerRegister
} }
// get new Namespace // NewNamespace get new Namespace
func NewNamespace(prefix string, params ...innnerNamespace) *Namespace { func NewNamespace(prefix string, params ...LinkNamespace) *Namespace {
ns := &Namespace{ ns := &Namespace{
prefix: prefix, prefix: prefix,
handlers: NewControllerRegister(), handlers: NewControllerRegister(),
@ -43,7 +44,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
return ns return ns
} }
// set condtion function // Cond set condtion function
// if cond return true can run this namespace, else can't // if cond return true can run this namespace, else can't
// usage: // usage:
// ns.Cond(func (ctx *context.Context) bool{ // ns.Cond(func (ctx *context.Context) bool{
@ -72,7 +73,7 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace {
return n return n
} }
// add filter in the Namespace // Filter add filter in the Namespace
// action has before & after // action has before & after
// FilterFunc // FilterFunc
// usage: // usage:
@ -95,98 +96,98 @@ func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace {
return n return n
} }
// same as beego.Rourer // Router same as beego.Rourer
// refer: https://godoc.org/github.com/astaxie/beego#Router // refer: https://godoc.org/github.com/astaxie/beego#Router
func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace {
n.handlers.Add(rootpath, c, mappingMethods...) n.handlers.Add(rootpath, c, mappingMethods...)
return n return n
} }
// same as beego.AutoRouter // AutoRouter same as beego.AutoRouter
// refer: https://godoc.org/github.com/astaxie/beego#AutoRouter // refer: https://godoc.org/github.com/astaxie/beego#AutoRouter
func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace { func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace {
n.handlers.AddAuto(c) n.handlers.AddAuto(c)
return n return n
} }
// same as beego.AutoPrefix // AutoPrefix same as beego.AutoPrefix
// refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix // refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix
func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace { func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace {
n.handlers.AddAutoPrefix(prefix, c) n.handlers.AddAutoPrefix(prefix, c)
return n return n
} }
// same as beego.Get // Get same as beego.Get
// refer: https://godoc.org/github.com/astaxie/beego#Get // refer: https://godoc.org/github.com/astaxie/beego#Get
func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace {
n.handlers.Get(rootpath, f) n.handlers.Get(rootpath, f)
return n return n
} }
// same as beego.Post // Post same as beego.Post
// refer: https://godoc.org/github.com/astaxie/beego#Post // refer: https://godoc.org/github.com/astaxie/beego#Post
func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace {
n.handlers.Post(rootpath, f) n.handlers.Post(rootpath, f)
return n return n
} }
// same as beego.Delete // Delete same as beego.Delete
// refer: https://godoc.org/github.com/astaxie/beego#Delete // refer: https://godoc.org/github.com/astaxie/beego#Delete
func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace {
n.handlers.Delete(rootpath, f) n.handlers.Delete(rootpath, f)
return n return n
} }
// same as beego.Put // Put same as beego.Put
// refer: https://godoc.org/github.com/astaxie/beego#Put // refer: https://godoc.org/github.com/astaxie/beego#Put
func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace {
n.handlers.Put(rootpath, f) n.handlers.Put(rootpath, f)
return n return n
} }
// same as beego.Head // Head same as beego.Head
// refer: https://godoc.org/github.com/astaxie/beego#Head // refer: https://godoc.org/github.com/astaxie/beego#Head
func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace {
n.handlers.Head(rootpath, f) n.handlers.Head(rootpath, f)
return n return n
} }
// same as beego.Options // Options same as beego.Options
// refer: https://godoc.org/github.com/astaxie/beego#Options // refer: https://godoc.org/github.com/astaxie/beego#Options
func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace {
n.handlers.Options(rootpath, f) n.handlers.Options(rootpath, f)
return n return n
} }
// same as beego.Patch // Patch same as beego.Patch
// refer: https://godoc.org/github.com/astaxie/beego#Patch // refer: https://godoc.org/github.com/astaxie/beego#Patch
func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace {
n.handlers.Patch(rootpath, f) n.handlers.Patch(rootpath, f)
return n return n
} }
// same as beego.Any // Any same as beego.Any
// refer: https://godoc.org/github.com/astaxie/beego#Any // refer: https://godoc.org/github.com/astaxie/beego#Any
func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace { func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace {
n.handlers.Any(rootpath, f) n.handlers.Any(rootpath, f)
return n return n
} }
// same as beego.Handler // Handler same as beego.Handler
// refer: https://godoc.org/github.com/astaxie/beego#Handler // refer: https://godoc.org/github.com/astaxie/beego#Handler
func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace { func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace {
n.handlers.Handler(rootpath, h) n.handlers.Handler(rootpath, h)
return n return n
} }
// add include class // Include add include class
// refer: https://godoc.org/github.com/astaxie/beego#Include // refer: https://godoc.org/github.com/astaxie/beego#Include
func (n *Namespace) Include(cList ...ControllerInterface) *Namespace { func (n *Namespace) Include(cList ...ControllerInterface) *Namespace {
n.handlers.Include(cList...) n.handlers.Include(cList...)
return n return n
} }
// nest Namespace // Namespace add nest Namespace
// usage: // usage:
//ns := beego.NewNamespace(“/v1”). //ns := beego.NewNamespace(“/v1”).
//Namespace( //Namespace(
@ -230,7 +231,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
return n return n
} }
// register Namespace into beego.Handler // AddNamespace register Namespace into beego.Handler
// support multi Namespace // support multi Namespace
func AddNamespace(nl ...*Namespace) { func AddNamespace(nl ...*Namespace) {
for _, n := range nl { for _, n := range nl {
@ -275,113 +276,113 @@ func addPrefix(t *Tree, prefix string) {
} }
// Namespace Condition // NSCond is Namespace Condition
func NSCond(cond namespaceCond) innnerNamespace { func NSCond(cond namespaceCond) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Cond(cond) ns.Cond(cond)
} }
} }
// Namespace BeforeRouter filter // NSBefore Namespace BeforeRouter filter
func NSBefore(filiterList ...FilterFunc) innnerNamespace { func NSBefore(filiterList ...FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Filter("before", filiterList...) ns.Filter("before", filiterList...)
} }
} }
// Namespace FinishRouter filter // NSAfter add Namespace FinishRouter filter
func NSAfter(filiterList ...FilterFunc) innnerNamespace { func NSAfter(filiterList ...FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Filter("after", filiterList...) ns.Filter("after", filiterList...)
} }
} }
// Namespace Include ControllerInterface // NSInclude Namespace Include ControllerInterface
func NSInclude(cList ...ControllerInterface) innnerNamespace { func NSInclude(cList ...ControllerInterface) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Include(cList...) ns.Include(cList...)
} }
} }
// Namespace Router // NSRouter call Namespace Router
func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) innnerNamespace { func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Router(rootpath, c, mappingMethods...) ns.Router(rootpath, c, mappingMethods...)
} }
} }
// Namespace Get // NSGet call Namespace Get
func NSGet(rootpath string, f FilterFunc) innnerNamespace { func NSGet(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Get(rootpath, f) ns.Get(rootpath, f)
} }
} }
// Namespace Post // NSPost call Namespace Post
func NSPost(rootpath string, f FilterFunc) innnerNamespace { func NSPost(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Post(rootpath, f) ns.Post(rootpath, f)
} }
} }
// Namespace Head // NSHead call Namespace Head
func NSHead(rootpath string, f FilterFunc) innnerNamespace { func NSHead(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Head(rootpath, f) ns.Head(rootpath, f)
} }
} }
// Namespace Put // NSPut call Namespace Put
func NSPut(rootpath string, f FilterFunc) innnerNamespace { func NSPut(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Put(rootpath, f) ns.Put(rootpath, f)
} }
} }
// Namespace Delete // NSDelete call Namespace Delete
func NSDelete(rootpath string, f FilterFunc) innnerNamespace { func NSDelete(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Delete(rootpath, f) ns.Delete(rootpath, f)
} }
} }
// Namespace Any // NSAny call Namespace Any
func NSAny(rootpath string, f FilterFunc) innnerNamespace { func NSAny(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Any(rootpath, f) ns.Any(rootpath, f)
} }
} }
// Namespace Options // NSOptions call Namespace Options
func NSOptions(rootpath string, f FilterFunc) innnerNamespace { func NSOptions(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Options(rootpath, f) ns.Options(rootpath, f)
} }
} }
// Namespace Patch // NSPatch call Namespace Patch
func NSPatch(rootpath string, f FilterFunc) innnerNamespace { func NSPatch(rootpath string, f FilterFunc) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.Patch(rootpath, f) ns.Patch(rootpath, f)
} }
} }
//Namespace AutoRouter // NSAutoRouter call Namespace AutoRouter
func NSAutoRouter(c ControllerInterface) innnerNamespace { func NSAutoRouter(c ControllerInterface) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.AutoRouter(c) ns.AutoRouter(c)
} }
} }
// Namespace AutoPrefix // NSAutoPrefix call Namespace AutoPrefix
func NSAutoPrefix(prefix string, c ControllerInterface) innnerNamespace { func NSAutoPrefix(prefix string, c ControllerInterface) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
ns.AutoPrefix(prefix, c) ns.AutoPrefix(prefix, c)
} }
} }
// Namespace add sub Namespace // NSNamespace add sub Namespace
func NSNamespace(prefix string, params ...innnerNamespace) innnerNamespace { func NSNamespace(prefix string, params ...LinkNamespace) LinkNamespace {
return func(ns *Namespace) { return func(ns *Namespace) {
n := NewNamespace(prefix, params...) n := NewNamespace(prefix, params...)
ns.Namespace(n) ns.Namespace(n)

View File

@ -802,6 +802,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
tables.parseRelated(qs.related, qs.relDepth) tables.parseRelated(qs.related, qs.relDepth)
where, args := tables.getCondSql(cond, false, tz) where, args := tables.getCondSql(cond, false, tz)
groupBy := tables.getGroupSql(qs.groups)
orderBy := tables.getOrderSql(qs.orders) orderBy := tables.getOrderSql(qs.orders)
limit := tables.getLimitSql(mi, offset, rlimit) limit := tables.getLimitSql(mi, offset, rlimit)
join := tables.getJoinSql() 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) 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) where, args := tables.getCondSql(cond, false, tz)
groupBy := tables.getGroupSql(qs.groups)
orderBy := tables.getOrderSql(qs.orders) orderBy := tables.getOrderSql(qs.orders)
limit := tables.getLimitSql(mi, qs.offset, qs.limit) limit := tables.getLimitSql(mi, qs.offset, qs.limit)
join := tables.getJoinSql() join := tables.getJoinSql()
sels := strings.Join(cols, ", ") 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) d.ins.ReplaceMarks(&query)

View File

@ -390,6 +390,30 @@ func (t *dbTables) getCondSql(cond *Condition, sub bool, tz *time.Location) (whe
return 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. // generate order sql.
func (t *dbTables) getOrderSql(orders []string) (orderSql string) { func (t *dbTables) getOrderSql(orders []string) (orderSql string) {
if len(orders) == 0 { if len(orders) == 0 {

View File

@ -60,7 +60,9 @@ type querySet struct {
relDepth int relDepth int
limit int64 limit int64
offset int64 offset int64
groups []string
orders []string orders []string
distinct bool
orm *orm orm *orm
} }
@ -105,6 +107,12 @@ func (o querySet) Offset(offset interface{}) QuerySeter {
return &o return &o
} }
// add GROUP expression
func (o querySet) GroupBy(exprs ...string) QuerySeter {
o.groups = exprs
return &o
}
// add ORDER expression. // add ORDER expression.
// "column" means ASC, "-column" means DESC. // "column" means ASC, "-column" means DESC.
func (o querySet) OrderBy(exprs ...string) QuerySeter { func (o querySet) OrderBy(exprs ...string) QuerySeter {
@ -112,6 +120,12 @@ func (o querySet) OrderBy(exprs ...string) QuerySeter {
return &o return &o
} }
// add DISTINCT to SELECT
func (o querySet) Distinct() QuerySeter {
o.distinct = true
return &o
}
// set relation model to query together. // set relation model to query together.
// it will query relation models and assign to parent model. // it will query relation models and assign to parent model.
func (o querySet) RelatedSel(params ...interface{}) QuerySeter { func (o querySet) RelatedSel(params ...interface{}) QuerySeter {

View File

@ -66,7 +66,9 @@ type QuerySeter interface {
SetCond(*Condition) QuerySeter SetCond(*Condition) QuerySeter
Limit(interface{}, ...interface{}) QuerySeter Limit(interface{}, ...interface{}) QuerySeter
Offset(interface{}) QuerySeter Offset(interface{}) QuerySeter
GroupBy(...string) QuerySeter
OrderBy(...string) QuerySeter OrderBy(...string) QuerySeter
Distinct() QuerySeter
RelatedSel(...interface{}) QuerySeter RelatedSel(...interface{}) QuerySeter
Count() (int64, error) Count() (int64, error)
Exist() bool Exist() bool

View File

@ -42,13 +42,13 @@ func init() {
` `
var ( var (
lastupdateFilename string = "lastupdate.tmp" lastupdateFilename = "lastupdate.tmp"
commentFilename string commentFilename string
pkgLastupdate map[string]int64 pkgLastupdate map[string]int64
genInfoList map[string][]ControllerComments genInfoList map[string][]ControllerComments
) )
const COMMENTFL = "commentsRouter_" const coomentPrefix = "commentsRouter_"
func init() { func init() {
pkgLastupdate = make(map[string]int64) pkgLastupdate = make(map[string]int64)
@ -56,7 +56,7 @@ func init() {
func parserPkg(pkgRealpath, pkgpath string) error { func parserPkg(pkgRealpath, pkgpath string) error {
rep := strings.NewReplacer("/", "_", ".", "_") rep := strings.NewReplacer("/", "_", ".", "_")
commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go" commentFilename = coomentPrefix + rep.Replace(pkgpath) + ".go"
if !compareFile(pkgRealpath) { if !compareFile(pkgRealpath) {
Info(pkgRealpath + " has not changed, not reloading") Info(pkgRealpath + " has not changed, not reloading")
return nil return nil
@ -132,9 +132,11 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
func genRouterCode() { func genRouterCode() {
os.Mkdir(path.Join(workPath, "routers"), 0755) os.Mkdir(path.Join(workPath, "routers"), 0755)
Info("generate router from comments") Info("generate router from comments")
var globalinfo string var (
sortKey := make([]string, 0) globalinfo string
for k, _ := range genInfoList { sortKey []string
)
for k := range genInfoList {
sortKey = append(sortKey, k) sortKey = append(sortKey, k)
} }
sort.Strings(sortKey) sort.Strings(sortKey)

View File

@ -83,41 +83,41 @@ func APIBaiscAuth(appid, appkey string) beego.FilterFunc {
func APIAuthWithFunc(f AppIdToAppSecret, timeout int) beego.FilterFunc { func APIAuthWithFunc(f AppIdToAppSecret, timeout int) beego.FilterFunc {
return func(ctx *context.Context) { return func(ctx *context.Context) {
if ctx.Input.Query("appid") == "" { if ctx.Input.Query("appid") == "" {
ctx.Output.SetStatus(403) ctx.ResponseWriter.WriteHeader(403)
ctx.WriteString("miss query param: appid") ctx.WriteString("miss query param: appid")
return return
} }
appsecret := f(ctx.Input.Query("appid")) appsecret := f(ctx.Input.Query("appid"))
if appsecret == "" { if appsecret == "" {
ctx.Output.SetStatus(403) ctx.ResponseWriter.WriteHeader(403)
ctx.WriteString("not exist this appid") ctx.WriteString("not exist this appid")
return return
} }
if ctx.Input.Query("signature") == "" { if ctx.Input.Query("signature") == "" {
ctx.Output.SetStatus(403) ctx.ResponseWriter.WriteHeader(403)
ctx.WriteString("miss query param: signature") ctx.WriteString("miss query param: signature")
return return
} }
if ctx.Input.Query("timestamp") == "" { if ctx.Input.Query("timestamp") == "" {
ctx.Output.SetStatus(403) ctx.ResponseWriter.WriteHeader(403)
ctx.WriteString("miss query param: timestamp") ctx.WriteString("miss query param: timestamp")
return return
} }
u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp")) u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp"))
if err != nil { if err != nil {
ctx.Output.SetStatus(403) ctx.ResponseWriter.WriteHeader(403)
ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05") ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05")
return return
} }
t := time.Now() t := time.Now()
if t.Sub(u).Seconds() > float64(timeout) { 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") ctx.WriteString("timeout! the request time is long ago, please try again")
return return
} }
if ctx.Input.Query("signature") != if ctx.Input.Query("signature") !=
Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.Uri()) { Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.Uri()) {
ctx.Output.SetStatus(403) ctx.ResponseWriter.WriteHeader(403)
ctx.WriteString("auth failed") ctx.WriteString("auth failed")
} }
} }

View File

@ -24,7 +24,7 @@
// // - PUT and PATCH methods // // - PUT and PATCH methods
// // - Origin header // // - Origin header
// // - Credentials share // // - Credentials share
// beego.InsertFilter("*", beego.BeforeRouter,cors.Allow(&cors.Options{ // beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
// AllowOrigins: []string{"https://*.foo.com"}, // AllowOrigins: []string{"https://*.foo.com"},
// AllowMethods: []string{"PUT", "PATCH"}, // AllowMethods: []string{"PUT", "PATCH"},
// AllowHeaders: []string{"Origin"}, // AllowHeaders: []string{"Origin"},
@ -36,7 +36,6 @@
package cors package cors
import ( import (
"net/http"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -216,8 +215,6 @@ func Allow(opts *Options) beego.FilterFunc {
for key, value := range headers { for key, value := range headers {
ctx.Output.Header(key, value) ctx.Output.Header(key, value)
} }
ctx.Output.SetStatus(http.StatusOK)
ctx.WriteString("")
return return
} }
headers = opts.Header(origin) headers = opts.Header(origin)

135
plugins/jwt/jwt.go Normal file
View File

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

88
plugins/jwt/jwt_test.go Normal file
View File

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

15
plugins/jwt/test/jwt.rsa Normal file
View File

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

View File

@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdu+23Y/0J/FTQKnIPnxupoOo9
/OYCv90DPXN/KLLRAMjYzgcCDsBST2xVR5jlimI/gyfCpVB62dwpSzzr0cA3MoDh
baGWuTdQUX9zmiLoQ4I7X6h0dwyiihOz+CzOMlAg5+qBhiTGcKvIFlfEc1FUcn/t
B3PVRG9j6B1Ibz5CnQIDAQAB
-----END PUBLIC KEY-----

206
router.go
View File

@ -34,8 +34,8 @@ import (
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
// default filter execution points
const ( const (
// default filter execution points
BeforeStatic = iota BeforeStatic = iota
BeforeRouter BeforeRouter
BeforeExec BeforeExec
@ -50,7 +50,7 @@ const (
) )
var ( var (
// supported http methods. // HTTPMETHOD list the supported http methods.
HTTPMETHOD = map[string]string{ HTTPMETHOD = map[string]string{
"GET": "GET", "GET": "GET",
"POST": "POST", "POST": "POST",
@ -71,10 +71,12 @@ var (
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
"GetControllerAndAction"} "GetControllerAndAction"}
url_placeholder = "{{placeholder}}" urlPlaceholder = "{{placeholder}}"
DefaultLogFilter FilterHandler = &logFilter{} // DefaultAccessLogFilter will skip the accesslog if return true
DefaultAccessLogFilter FilterHandler = &logFilter{}
) )
// FilterHandler is an interface for
type FilterHandler interface { type FilterHandler interface {
Filter(*beecontext.Context) bool Filter(*beecontext.Context) bool
} }
@ -96,7 +98,7 @@ func (l *logFilter) Filter(ctx *beecontext.Context) bool {
return false 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) { func ExceptMethodAppend(action string) {
exceptMethod = append(exceptMethod, action) exceptMethod = append(exceptMethod, action)
} }
@ -110,22 +112,22 @@ type controllerInfo struct {
routerType int routerType int
} }
// ControllerRegistor containers registered router rules, controller handlers and filters. // ControllerRegister containers registered router rules, controller handlers and filters.
type ControllerRegistor struct { type ControllerRegister struct {
routers map[string]*Tree routers map[string]*Tree
enableFilter bool enableFilter bool
filters map[int][]*FilterRouter filters map[int][]*FilterRouter
} }
// NewControllerRegister returns a new ControllerRegistor. // NewControllerRegister returns a new ControllerRegister.
func NewControllerRegister() *ControllerRegistor { func NewControllerRegister() *ControllerRegister {
return &ControllerRegistor{ return &ControllerRegister{
routers: make(map[string]*Tree), routers: make(map[string]*Tree),
filters: make(map[int][]*FilterRouter), filters: make(map[int][]*FilterRouter),
} }
} }
// Add controller handler and pattern rules to ControllerRegistor. // Add controller handler and pattern rules to ControllerRegister.
// usage: // usage:
// default methods is the same name as method // default methods is the same name as method
// Add("/user",&UserController{}) // Add("/user",&UserController{})
@ -135,7 +137,7 @@ func NewControllerRegister() *ControllerRegistor {
// Add("/api/delete",&RestController{},"delete:DeleteFood") // Add("/api/delete",&RestController{},"delete:DeleteFood")
// Add("/api",&RestController{},"get,post:ApiFunc") // Add("/api",&RestController{},"get,post:ApiFunc")
// Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") // 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) reflectVal := reflect.ValueOf(c)
t := reflect.Indirect(reflectVal).Type() t := reflect.Indirect(reflectVal).Type()
methods := make(map[string]string) 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 { if !RouterCaseSensitive {
pattern = strings.ToLower(pattern) 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{}) // Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{})
func (p *ControllerRegistor) Include(cList ...ControllerInterface) { func (p *ControllerRegister) Include(cList ...ControllerInterface) {
if RunMode == "dev" { if RunMode == "dev" {
skip := make(map[string]bool, 10) skip := make(map[string]bool, 10)
for _, c := range cList { for _, c := range cList {
@ -238,84 +240,84 @@ func (p *ControllerRegistor) Include(cList ...ControllerInterface) {
} }
} }
// add get method // Get add get method
// usage: // usage:
// Get("/", func(ctx *context.Context){ // Get("/", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("get", pattern, f)
} }
// add post method // Post add post method
// usage: // usage:
// Post("/api", func(ctx *context.Context){ // Post("/api", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("post", pattern, f)
} }
// add put method // Put add put method
// usage: // usage:
// Put("/api/:id", func(ctx *context.Context){ // Put("/api/:id", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("put", pattern, f)
} }
// add delete method // Delete add delete method
// usage: // usage:
// Delete("/api/:id", func(ctx *context.Context){ // Delete("/api/:id", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("delete", pattern, f)
} }
// add head method // Head add head method
// usage: // usage:
// Head("/api/:id", func(ctx *context.Context){ // Head("/api/:id", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("head", pattern, f)
} }
// add patch method // Patch add patch method
// usage: // usage:
// Patch("/api/:id", func(ctx *context.Context){ // Patch("/api/:id", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("patch", pattern, f)
} }
// add options method // Options add options method
// usage: // usage:
// Options("/api/:id", func(ctx *context.Context){ // Options("/api/:id", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("options", pattern, f)
} }
// add all method // Any add all method
// usage: // usage:
// Any("/api/:id", func(ctx *context.Context){ // Any("/api/:id", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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) p.AddMethod("*", pattern, f)
} }
// add http method router // AddMethod add http method router
// usage: // usage:
// AddMethod("get","/api/:id", func(ctx *context.Context){ // AddMethod("get","/api/:id", func(ctx *context.Context){
// ctx.Output.Body("hello world") // 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 { if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok {
panic("not support http method: " + method) panic("not support http method: " + method)
} }
@ -343,8 +345,8 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) {
} }
} }
// add user defined Handler // Handler add user defined Handler
func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) { func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) {
route := &controllerInfo{} route := &controllerInfo{}
route.pattern = pattern route.pattern = pattern
route.routerType = routerTypeHandler 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{}), // example beego.AddAuto(&MainContorlller{}),
// MainController has method List and Page. // MainController has method List and Page.
// visit the url /main/list to execute List function // visit the url /main/list to execute List function
// /main/page to execute Page function. // /main/page to execute Page function.
func (p *ControllerRegistor) AddAuto(c ControllerInterface) { func (p *ControllerRegister) AddAuto(c ControllerInterface) {
p.AddAutoPrefix("/", c) p.AddAutoPrefix("/", c)
} }
// Add auto router to ControllerRegistor with prefix. // AddAutoPrefix Add auto router to ControllerRegister with prefix.
// example beego.AddAutoPrefix("/admin",&MainContorlller{}), // example beego.AddAutoPrefix("/admin",&MainContorlller{}),
// MainController has method List and Page. // MainController has method List and Page.
// visit the url /admin/main/list to execute List function // visit the url /admin/main/list to execute List function
// /admin/main/page to execute Page 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) reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type() rt := reflectVal.Type()
ct := reflect.Indirect(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) // 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 := new(FilterRouter)
mr.tree = NewTree() mr.tree = NewTree()
@ -420,15 +422,15 @@ func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter Filter
} }
// add Filter into // 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.filters[pos] = append(p.filters[pos], mr)
p.enableFilter = true p.enableFilter = true
return nil 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. // 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, ".") paths := strings.Split(endpoint, ".")
if len(paths) <= 1 { if len(paths) <= 1 {
Warn("urlfor endpoint must like path.controller.method") Warn("urlfor endpoint must like path.controller.method")
@ -460,7 +462,7 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) stri
return "" 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 { for k, subtree := range t.fixrouters {
u := path.Join(url, k) u := path.Join(url, k)
ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod) 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 { 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) ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
if ok { if ok {
return ok, u return ok, u
@ -499,22 +501,21 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
if find { if find {
if l.regexps == nil { if l.regexps == nil {
if len(l.wildcards) == 0 { 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 len(l.wildcards) == 1 {
if v, ok := params[l.wildcards[0]]; ok { if v, ok := params[l.wildcards[0]]; ok {
delete(params, l.wildcards[0]) delete(params, l.wildcards[0])
return true, strings.Replace(url, url_placeholder, v, 1) + tourl(params) return true, strings.Replace(url, urlPlaceholder, v, 1) + tourl(params)
} else {
return false, ""
} }
return false, ""
} }
if len(l.wildcards) == 3 && l.wildcards[0] == "." { if len(l.wildcards) == 3 && l.wildcards[0] == "." {
if p, ok := params[":path"]; ok { if p, ok := params[":path"]; ok {
if e, isok := params[":ext"]; isok { if e, isok := params[":ext"]; isok {
delete(params, ":path") delete(params, ":path")
delete(params, ":ext") 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 { if u, ok := params[v]; ok {
delete(params, v) delete(params, v)
url = strings.Replace(url, url_placeholder, u, 1) url = strings.Replace(url, urlPlaceholder, u, 1)
} else { } else {
if canskip { if canskip {
canskip = false canskip = false
continue continue
} else {
return false, ""
} }
return false, ""
} }
} }
return true, url + tourl(params) return true, url + tourl(params)
} else { }
var i int var i int
var startreg bool var startreg bool
regurl := "" regurl := ""
for _, v := range strings.Trim(l.regexps.String(), "^$") { for _, v := range strings.Trim(l.regexps.String(), "^$") {
if v == '(' { if v == '(' {
startreg = true startreg = true
continue continue
} else if v == ')' { } else if v == ')' {
startreg = false startreg = false
if v, ok := params[l.wildcards[i]]; ok { if v, ok := params[l.wildcards[i]]; ok {
delete(params, l.wildcards[i]) delete(params, l.wildcards[i])
regurl = regurl + v regurl = regurl + v
i++ i++
} else { } else {
break break
}
} else if !startreg {
regurl = string(append([]rune(regurl), v))
} }
} else if !startreg {
regurl = string(append([]rune(regurl), v))
} }
if l.regexps.MatchString(regurl) { }
ps := strings.Split(regurl, "/") if l.regexps.MatchString(regurl) {
for _, p := range ps { ps := strings.Split(regurl, "/")
url = strings.Replace(url, url_placeholder, p, 1) for _, p := range ps {
} url = strings.Replace(url, urlPlaceholder, p, 1)
return true, url + tourl(params)
} }
return true, url + tourl(params)
} }
} }
} }
@ -575,7 +574,7 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
} }
// Implement http.Handler interface. // 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() starttime := time.Now()
var runrouter reflect.Type var runrouter reflect.Type
var findrouter bool var findrouter bool
@ -607,7 +606,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
urlPath = r.URL.Path urlPath = r.URL.Path
} }
// defined filter function // defined filter function
do_filter := func(pos int) (started bool) { doFilter := func(pos int) (started bool) {
if p.enableFilter { if p.enableFilter {
if l, ok := p.filters[pos]; ok { if l, ok := p.filters[pos]; ok {
for _, filterR := range l { 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 { if ok, params := filterR.ValidRouter(urlPath); ok {
for k, v := range params { for k, v := range params {
if context.Input.Params == nil { if context.Input.Params == nil {
context.Input.Params = make(map[string]string) context.Input.Params = make(map[string]string)
} }
context.Input.Params[k] = v context.Input.Params[k] = v
} }
@ -639,7 +638,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
// filter for static file // filter for static file
if do_filter(BeforeStatic) { if doFilter(BeforeStatic) {
goto Admin goto Admin
} }
@ -670,7 +669,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
context.Input.ParseFormOrMulitForm(MaxMemory) context.Input.ParseFormOrMulitForm(MaxMemory)
} }
if do_filter(BeforeRouter) { if doFilter(BeforeRouter) {
goto Admin goto Admin
} }
@ -681,17 +680,17 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
if !findrouter { if !findrouter {
http_method := r.Method httpMethod := r.Method
if http_method == "POST" && context.Input.Query("_method") == "PUT" { if httpMethod == "POST" && context.Input.Query("_method") == "PUT" {
http_method = "PUT" httpMethod = "PUT"
} }
if http_method == "POST" && context.Input.Query("_method") == "DELETE" { if httpMethod == "POST" && context.Input.Query("_method") == "DELETE" {
http_method = "DELETE" httpMethod = "DELETE"
} }
if t, ok := p.routers[http_method]; ok { if t, ok := p.routers[httpMethod]; ok {
runObject, p := t.Match(urlPath) runObject, p := t.Match(urlPath)
if r, ok := runObject.(*controllerInfo); ok { if r, ok := runObject.(*controllerInfo); ok {
routerInfo = r routerInfo = r
@ -718,7 +717,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if findrouter { if findrouter {
//execute middleware filters //execute middleware filters
if do_filter(BeforeExec) { if doFilter(BeforeExec) {
goto Admin goto Admin
} }
isRunable := false 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 XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf
if EnableXSRF { if EnableXSRF {
execController.XsrfToken() execController.XSRFToken()
if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" ||
(r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_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() execController.Options()
default: default:
if !execController.HandlerFunc(runMethod) { if !execController.HandlerFunc(runMethod) {
in := make([]reflect.Value, 0) var in []reflect.Value
method := vc.MethodByName(runMethod) method := vc.MethodByName(runMethod)
method.Call(in) method.Call(in)
} }
@ -819,12 +818,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
//execute middleware filters //execute middleware filters
if do_filter(AfterExec) { if doFilter(AfterExec) {
goto Admin goto Admin
} }
} }
do_filter(FinishRouter) doFilter(FinishRouter)
Admin: Admin:
timeend := time.Since(starttime) timeend := time.Since(starttime)
@ -850,7 +849,7 @@ Admin:
} else { } else {
devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") 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) 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 := recover(); err != nil {
if err == USERSTOPRUN { if err == ErrAbort {
return return
} }
if !RecoverPanic { if !RecoverPanic {
panic(err) panic(err)
} else { } else {
if ErrorsShow { if EnableErrorsShow {
if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
exception(fmt.Sprint(err), context) exception(fmt.Sprint(err), context)
return return
@ -931,6 +930,13 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return hj.Hijack() return hj.Hijack()
} }
func (w *responseWriter) Flush() {
f, ok := w.writer.(http.Flusher)
if ok {
f.Flush()
}
}
func tourl(params map[string]string) string { func tourl(params map[string]string) string {
if len(params) == 0 { if len(params) == 0 {
return "" return ""

View File

@ -52,17 +52,17 @@ func (tc *TestController) Myext() {
tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext"))) tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
} }
func (tc *TestController) GetUrl() { func (tc *TestController) GetURL() {
tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext"))) tc.Ctx.Output.Body([]byte(tc.URLFor(".Myext")))
} }
func (t *TestController) GetParams() { func (tc *TestController) GetParams() {
t.Ctx.WriteString(t.Ctx.Input.Query(":last") + "+" + tc.Ctx.WriteString(tc.Ctx.Input.Query(":last") + "+" +
t.Ctx.Input.Query(":first") + "+" + t.Ctx.Input.Query("learn")) tc.Ctx.Input.Query(":first") + "+" + tc.Ctx.Input.Query("learn"))
} }
func (t *TestController) GetManyRouter() { func (tc *TestController) GetManyRouter() {
t.Ctx.WriteString(t.Ctx.Input.Query(":id") + t.Ctx.Input.Query(":page")) tc.Ctx.WriteString(tc.Ctx.Input.Query(":id") + tc.Ctx.Input.Query(":page"))
} }
type ResStatus struct { type ResStatus struct {
@ -70,29 +70,29 @@ type ResStatus struct {
Msg string Msg string
} }
type JsonController struct { type JSONController struct {
Controller Controller
} }
func (this *JsonController) Prepare() { func (jc *JSONController) Prepare() {
this.Data["json"] = "prepare" jc.Data["json"] = "prepare"
this.ServeJson(true) jc.ServeJSON(true)
} }
func (this *JsonController) Get() { func (jc *JSONController) Get() {
this.Data["Username"] = "astaxie" jc.Data["Username"] = "astaxie"
this.Ctx.Output.Body([]byte("ok")) jc.Ctx.Output.Body([]byte("ok"))
} }
func TestUrlFor(t *testing.T) { func TestUrlFor(t *testing.T) {
handler := NewControllerRegister() handler := NewControllerRegister()
handler.Add("/api/list", &TestController{}, "*:List") handler.Add("/api/list", &TestController{}, "*:List")
handler.Add("/person/:last/:first", &TestController{}, "*:Param") 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) Info(a)
t.Errorf("TestController.List must equal to /api/list") 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) 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) { func TestUrlFor3(t *testing.T) {
handler := NewControllerRegister() handler := NewControllerRegister()
handler.AddAuto(&TestController{}) 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) t.Errorf("TestController.Myext must equal to /test/myext, but get " + a)
} }
if a := handler.UrlFor("TestController.GetUrl"); a != "/test/geturl" && a != "/Test/GetUrl" { if a := handler.URLFor("TestController.GetURL"); a != "/test/geturl" && a != "/Test/GetURL" {
t.Errorf("TestController.GetUrl must equal to /test/geturl, but get " + a) t.Errorf("TestController.GetURL must equal to /test/geturl, but get " + a)
} }
} }
func TestUrlFor2(t *testing.T) { func TestUrlFor2(t *testing.T) {
handler := NewControllerRegister() handler := NewControllerRegister()
handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, "*:List") 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("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param")
handler.Add("/:year:int/:month:int/:title/:entid", &TestController{}) handler.Add("/:year:int/:month:int/:title/:entid", &TestController{})
if handler.UrlFor("TestController.GetUrl", ":username", "astaxie") != "/v1/astaxie/edit" { if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" {
Info(handler.UrlFor("TestController.GetUrl")) Info(handler.URLFor("TestController.GetURL"))
t.Errorf("TestController.List must equal to /v1/astaxie/edit") 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" { "/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") 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" { "/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") 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") != ":title", "aaaa", ":entid", "aaaa") !=
"/1111/11/aaaa/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") t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa")
} }
} }
@ -270,7 +270,7 @@ func TestPrepare(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler := NewControllerRegister() handler := NewControllerRegister()
handler.Add("/json/list", &JsonController{}) handler.Add("/json/list", &JSONController{})
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
if w.Body.String() != `"prepare"` { if w.Body.String() != `"prepare"` {
t.Errorf(w.Body.String() + "user define func can't run") t.Errorf(w.Body.String() + "user define func can't run")

View File

@ -51,7 +51,10 @@ import (
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
) )
var mysqlpder = &MysqlProvider{} var (
TableName = "session"
mysqlpder = &MysqlProvider{}
)
// mysql session store // mysql session store
type MysqlSessionStore struct { type MysqlSessionStore struct {
@ -110,7 +113,7 @@ func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) {
if err != nil { if err != nil {
return 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) b, time.Now().Unix(), st.sid)
} }
@ -141,11 +144,11 @@ func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error {
// get mysql session by sid // get mysql session by sid
func (mp *MysqlProvider) SessionRead(sid string) (session.SessionStore, error) { func (mp *MysqlProvider) SessionRead(sid string) (session.SessionStore, error) {
c := mp.connectInit() 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 var sessiondata []byte
err := row.Scan(&sessiondata) err := row.Scan(&sessiondata)
if err == sql.ErrNoRows { 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()) sid, "", time.Now().Unix())
} }
var kv map[interface{}]interface{} 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 { func (mp *MysqlProvider) SessionExist(sid string) bool {
c := mp.connectInit() c := mp.connectInit()
defer c.Close() 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 var sessiondata []byte
err := row.Scan(&sessiondata) err := row.Scan(&sessiondata)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
@ -178,13 +181,13 @@ func (mp *MysqlProvider) SessionExist(sid string) bool {
// generate new sid for mysql session // generate new sid for mysql session
func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) { func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) {
c := mp.connectInit() 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 var sessiondata []byte
err := row.Scan(&sessiondata) err := row.Scan(&sessiondata)
if err == sql.ErrNoRows { 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{} var kv map[interface{}]interface{}
if len(sessiondata) == 0 { if len(sessiondata) == 0 {
kv = make(map[interface{}]interface{}) kv = make(map[interface{}]interface{})
@ -201,7 +204,7 @@ func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.SessionS
// delete mysql session by sid // delete mysql session by sid
func (mp *MysqlProvider) SessionDestroy(sid string) error { func (mp *MysqlProvider) SessionDestroy(sid string) error {
c := mp.connectInit() c := mp.connectInit()
c.Exec("DELETE FROM session where session_key=?", sid) c.Exec("DELETE FROM "+TableName+" where session_key=?", sid)
c.Close() c.Close()
return nil return nil
} }
@ -209,7 +212,7 @@ func (mp *MysqlProvider) SessionDestroy(sid string) error {
// delete expired values in mysql session // delete expired values in mysql session
func (mp *MysqlProvider) SessionGC() { func (mp *MysqlProvider) SessionGC() {
c := mp.connectInit() 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() c.Close()
return return
} }
@ -219,7 +222,7 @@ func (mp *MysqlProvider) SessionAll() int {
c := mp.connectInit() c := mp.connectInit()
defer c.Close() defer c.Close()
var total int 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 { if err != nil {
return 0 return 0
} }

View File

@ -40,15 +40,13 @@ func serverStaticRouter(ctx *context.Context) {
if utils.FileExists(file) { if utils.FileExists(file) {
http.ServeFile(ctx.ResponseWriter, ctx.Request, file) http.ServeFile(ctx.ResponseWriter, ctx.Request, file)
return 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 strings.HasPrefix(requestPath, prefix) {
if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' { if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' {

View File

@ -28,17 +28,14 @@ import (
) )
var ( var (
beegoTplFuncMap template.FuncMap beegoTplFuncMap = make(template.FuncMap)
// beego template caching map and supported template file extensions. // BeeTemplates caching map and supported template file extensions.
BeeTemplates map[string]*template.Template BeeTemplates = make(map[string]*template.Template)
BeeTemplateExt []string // BeeTemplateExt stores the template extention which will build
BeeTemplateExt = []string{"tpl", "html"}
) )
func init() { 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["dateformat"] = DateFormat
beegoTplFuncMap["date"] = Date beegoTplFuncMap["date"] = Date
beegoTplFuncMap["compare"] = Compare beegoTplFuncMap["compare"] = Compare
@ -46,14 +43,15 @@ func init() {
beegoTplFuncMap["not_nil"] = NotNil beegoTplFuncMap["not_nil"] = NotNil
beegoTplFuncMap["not_null"] = NotNil beegoTplFuncMap["not_null"] = NotNil
beegoTplFuncMap["substr"] = Substr beegoTplFuncMap["substr"] = Substr
beegoTplFuncMap["html2str"] = Html2str beegoTplFuncMap["html2str"] = HTML2str
beegoTplFuncMap["str2html"] = Str2html beegoTplFuncMap["str2html"] = Str2html
beegoTplFuncMap["htmlquote"] = Htmlquote beegoTplFuncMap["htmlquote"] = Htmlquote
beegoTplFuncMap["htmlunquote"] = Htmlunquote beegoTplFuncMap["htmlunquote"] = Htmlunquote
beegoTplFuncMap["renderform"] = RenderForm beegoTplFuncMap["renderform"] = RenderForm
beegoTplFuncMap["assets_js"] = AssetsJs beegoTplFuncMap["assets_js"] = AssetsJs
beegoTplFuncMap["assets_css"] = AssetsCss beegoTplFuncMap["assets_css"] = AssetsCSS
beegoTplFuncMap["config"] = Config beegoTplFuncMap["config"] = Config
beegoTplFuncMap["map_get"] = MapGet
// go1.2 added template funcs // go1.2 added template funcs
// Comparisons // Comparisons
@ -64,7 +62,7 @@ func init() {
beegoTplFuncMap["lt"] = lt // < beegoTplFuncMap["lt"] = lt // <
beegoTplFuncMap["ne"] = ne // != beegoTplFuncMap["ne"] = ne // !=
beegoTplFuncMap["urlfor"] = UrlFor // != beegoTplFuncMap["urlfor"] = URLFor // !=
} }
// AddFuncMap let user to register a func in the template. // AddFuncMap let user to register a func in the template.
@ -78,7 +76,7 @@ type templatefile struct {
files map[string][]string 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 { if f == nil {
return err return err
} }
@ -91,21 +89,21 @@ func (self *templatefile) visit(paths string, f os.FileInfo, err error) error {
replace := strings.NewReplacer("\\", "/") replace := strings.NewReplacer("\\", "/")
a := []byte(paths) a := []byte(paths)
a = a[len([]byte(self.root)):] a = a[len([]byte(tf.root)):]
file := strings.TrimLeft(replace.Replace(string(a)), "/") file := strings.TrimLeft(replace.Replace(string(a)), "/")
subdir := filepath.Dir(file) subdir := filepath.Dir(file)
if _, ok := self.files[subdir]; ok { if _, ok := tf.files[subdir]; ok {
self.files[subdir] = append(self.files[subdir], file) tf.files[subdir] = append(tf.files[subdir], file)
} else { } else {
m := make([]string, 1) m := make([]string, 1)
m[0] = file m[0] = file
self.files[subdir] = m tf.files[subdir] = m
} }
return nil 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 { func HasTemplateExt(paths string) bool {
for _, v := range BeeTemplateExt { for _, v := range BeeTemplateExt {
if strings.HasSuffix(paths, "."+v) { if strings.HasSuffix(paths, "."+v) {
@ -115,7 +113,7 @@ func HasTemplateExt(paths string) bool {
return false return false
} }
// add new extension for template. // AddTemplateExt add new extension for template.
func AddTemplateExt(ext string) { func AddTemplateExt(ext string) {
for _, v := range BeeTemplateExt { for _, v := range BeeTemplateExt {
if v == ext { if v == ext {
@ -125,15 +123,14 @@ func AddTemplateExt(ext string) {
BeeTemplateExt = append(BeeTemplateExt, ext) 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. // 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 _, err := os.Stat(dir); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil return nil
} else {
return errors.New("dir open err")
} }
return errors.New("dir open err")
} }
self := &templatefile{ self := &templatefile{
root: dir, root: dir,
@ -148,11 +145,13 @@ func BuildTemplate(dir string) error {
} }
for _, v := range self.files { for _, v := range self.files {
for _, file := range v { for _, file := range v {
t, err := getTemplate(self.root, file, v...) if len(files) == 0 || utils.InSlice(file, files) {
if err != nil { t, err := getTemplate(self.root, file, v...)
Trace("parse template err:", file, err) if err != nil {
} else { Trace("parse template err:", file, err)
BeeTemplates[file] = t } else {
BeeTemplates[file] = t
}
} }
} }
} }
@ -260,3 +259,30 @@ func _getTemplate(t0 *template.Template, root string, submods [][]string, others
} }
return 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
}

View File

@ -20,11 +20,11 @@ import (
"testing" "testing"
) )
var header string = `{{define "header"}} var header = `{{define "header"}}
<h1>Hello, astaxie!</h1> <h1>Hello, astaxie!</h1>
{{end}}` {{end}}`
var index string = `<!DOCTYPE html> var index = `<!DOCTYPE html>
<html> <html>
<head> <head>
<title>beego welcome template</title> <title>beego welcome template</title>
@ -37,7 +37,7 @@ var index string = `<!DOCTYPE html>
</html> </html>
` `
var block string = `{{define "block"}} var block = `{{define "block"}}
<h1>Hello, blocks!</h1> <h1>Hello, blocks!</h1>
{{end}}` {{end}}`
@ -82,7 +82,7 @@ func TestTemplate(t *testing.T) {
os.RemoveAll(dir) os.RemoveAll(dir)
} }
var menu string = `<div class="menu"> var menu = `<div class="menu">
<ul> <ul>
<li>menu1</li> <li>menu1</li>
<li>menu2</li> <li>menu2</li>
@ -90,7 +90,7 @@ var menu string = `<div class="menu">
</ul> </ul>
</div> </div>
` `
var user string = `<!DOCTYPE html> var user = `<!DOCTYPE html>
<html> <html>
<head> <head>
<title>beego welcome template</title> <title>beego welcome template</title>
@ -123,7 +123,7 @@ func TestRelativeTemplate(t *testing.T) {
f.Close() f.Close()
} }
} }
if err := BuildTemplate(dir); err != nil { if err := BuildTemplate(dir, files[1]); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := BeeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil { if err := BeeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil {

View File

@ -44,8 +44,8 @@ func Substr(s string, start, length int) string {
return string(bt[start:end]) return string(bt[start:end])
} }
// Html2str returns escaping text convert from html. // HTML2str returns escaping text convert from html.
func Html2str(html string) string { func HTML2str(html string) string {
src := string(html) src := string(html)
re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
@ -115,7 +115,7 @@ var datePatterns = []string{
"r", time.RFC1123Z, "r", time.RFC1123Z,
} }
// Parse Date use PHP time format. // DateParse Parse Date use PHP time format.
func DateParse(dateString, format string) (time.Time, error) { func DateParse(dateString, format string) (time.Time, error) {
replacer := strings.NewReplacer(datePatterns...) replacer := strings.NewReplacer(datePatterns...)
format = replacer.Replace(format) format = replacer.Replace(format)
@ -139,14 +139,17 @@ func Compare(a, b interface{}) (equal bool) {
return return
} }
// CompareNot !Compare
func CompareNot(a, b interface{}) (equal bool) { func CompareNot(a, b interface{}) (equal bool) {
return ! Compare(a, b) return !Compare(a, b)
} }
func NotNil(a interface{}) (is_nil bool) { // NotNil the same as CompareNot
return CompareNot(a, nil) func NotNil(a interface{}) (isNil bool) {
return CompareNot(a, nil)
} }
// Config get the Appconfig
func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) { func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) {
switch returnType { switch returnType {
case "String": case "String":
@ -162,12 +165,12 @@ func Config(returnType, key string, defaultVal interface{}) (value interface{},
case "DIY": case "DIY":
value, err = AppConfig.DIY(key) value, err = AppConfig.DIY(key)
default: default:
err = errors.New("Config keys must be of type String, Bool, Int, Int64, Float, or DIY!") err = errors.New("Config keys must be of type String, Bool, Int, Int64, Float, or DIY")
} }
if err != nil { if err != nil {
if reflect.TypeOf(returnType) != reflect.TypeOf(defaultVal) { if reflect.TypeOf(returnType) != reflect.TypeOf(defaultVal) {
err = errors.New("defaultVal type does not match returnType!") err = errors.New("defaultVal type does not match returnType")
} else { } else {
value, err = defaultVal, nil value, err = defaultVal, nil
} }
@ -184,7 +187,7 @@ func Config(returnType, key string, defaultVal interface{}) (value interface{},
return return
} }
// Convert string to template.HTML type. // Str2html Convert string to template.HTML type.
func Str2html(raw string) template.HTML { func Str2html(raw string) template.HTML {
return template.HTML(raw) return template.HTML(raw)
} }
@ -237,14 +240,14 @@ func Htmlunquote(src string) string {
return strings.TrimSpace(text) return strings.TrimSpace(text)
} }
// UrlFor returns url string with another registered controller handler with params. // URLFor returns url string with another registered controller handler with params.
// usage: // usage:
// //
// UrlFor(".index") // URLFor(".index")
// print UrlFor("index") // print URLFor("index")
// router /login // router /login
// print UrlFor("login") // print URLFor("login")
// print UrlFor("login", "next","/"") // print URLFor("login", "next","/"")
// router /profile/:username // router /profile/:username
// print UrlFor("profile", ":username","John Doe") // print UrlFor("profile", ":username","John Doe")
// result: // result:
@ -254,11 +257,11 @@ func Htmlunquote(src string) string {
// /user/John%20Doe // /user/John%20Doe
// //
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md // more detail http://beego.me/docs/mvc/controller/urlbuilding.md
func UrlFor(endpoint string, values ...interface{}) string { func URLFor(endpoint string, values ...interface{}) string {
return BeeApp.Handlers.UrlFor(endpoint, values...) return BeeApp.Handlers.URLFor(endpoint, values...)
} }
// returns script tag with src string. // AssetsJs returns script tag with src string.
func AssetsJs(src string) template.HTML { func AssetsJs(src string) template.HTML {
text := string(src) text := string(src)
@ -267,8 +270,8 @@ func AssetsJs(src string) template.HTML {
return template.HTML(text) return template.HTML(text)
} }
// returns stylesheet link tag with src string. // AssetsCSS returns stylesheet link tag with src string.
func AssetsCss(src string) template.HTML { func AssetsCSS(src string) template.HTML {
text := string(src) text := string(src)
text = "<link href=\"" + src + "\" rel=\"stylesheet\" />" text = "<link href=\"" + src + "\" rel=\"stylesheet\" />"
@ -276,7 +279,7 @@ func AssetsCss(src string) template.HTML {
return template.HTML(text) return template.HTML(text)
} }
// parse form values to struct via tag. // ParseForm will parse form values to struct via tag.
func ParseForm(form url.Values, obj interface{}) error { func ParseForm(form url.Values, obj interface{}) error {
objT := reflect.TypeOf(obj) objT := reflect.TypeOf(obj)
objV := reflect.ValueOf(obj) objV := reflect.ValueOf(obj)
@ -352,7 +355,7 @@ func ParseForm(form url.Values, obj interface{}) error {
if len(tags) > 1 { if len(tags) > 1 {
format = tags[1] format = tags[1]
} }
t, err := time.Parse(format, value) t, err := time.ParseInLocation(format, value, time.Local)
if err != nil { if err != nil {
return err return err
} }
@ -398,7 +401,7 @@ var unKind = map[reflect.Kind]bool{
reflect.UnsafePointer: true, reflect.UnsafePointer: true,
} }
// render object to form html. // RenderForm will render object to form html.
// obj must be a struct pointer. // obj must be a struct pointer.
func RenderForm(obj interface{}) template.HTML { func RenderForm(obj interface{}) template.HTML {
objT := reflect.TypeOf(obj) objT := reflect.TypeOf(obj)
@ -651,4 +654,77 @@ func ge(arg1, arg2 interface{}) (bool, error) {
return !lessThan, nil return !lessThan, nil
} }
// go1.2 added template funcs. end // MapGet getting value from map by keys
// usage:
// Data["m"] = map[string]interface{} {
// "a": 1,
// "1": map[string]float64{
// "c": 4,
// },
// }
//
// {{ map_get m "a" }} // return 1
// {{ map_get m 1 "c" }} // return 4
func MapGet(arg1 interface{}, arg2 ...interface{}) (interface{}, error) {
arg1Type := reflect.TypeOf(arg1)
arg1Val := reflect.ValueOf(arg1)
if arg1Type.Kind() == reflect.Map && len(arg2) > 0 {
// check whether arg2[0] type equals to arg1 key type
// if they are different, make convertion
arg2Val := reflect.ValueOf(arg2[0])
arg2Type := reflect.TypeOf(arg2[0])
if arg2Type.Kind() != arg1Type.Key().Kind() {
// convert arg2Value to string
var arg2ConvertedVal interface{}
arg2String := fmt.Sprintf("%v", arg2[0])
// convert string representation to any other type
switch arg1Type.Key().Kind() {
case reflect.Bool:
arg2ConvertedVal, _ = strconv.ParseBool(arg2String)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
arg2ConvertedVal, _ = strconv.ParseInt(arg2String, 0, 64)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
arg2ConvertedVal, _ = strconv.ParseUint(arg2String, 0, 64)
case reflect.Float32, reflect.Float64:
arg2ConvertedVal, _ = strconv.ParseFloat(arg2String, 64)
case reflect.String:
arg2ConvertedVal = arg2String
default:
arg2ConvertedVal = arg2Val.Interface()
}
arg2Val = reflect.ValueOf(arg2ConvertedVal)
}
storedVal := arg1Val.MapIndex(arg2Val)
if storedVal.IsValid() {
var result interface{}
switch arg1Type.Elem().Kind() {
case reflect.Bool:
result = storedVal.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
result = storedVal.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
result = storedVal.Uint()
case reflect.Float32, reflect.Float64:
result = storedVal.Float()
case reflect.String:
result = storedVal.String()
default:
result = storedVal.Interface()
}
// if there is more keys, handle this recursively
if len(arg2) > 1 {
return MapGet(result, arg2[1:]...)
}
return result, nil
}
return nil, nil
}
return nil, nil
}

View File

@ -40,7 +40,7 @@ func TestHtml2str(t *testing.T) {
\n` \n`
if Html2str(h) != "123\\n\n\\n" { if HTML2str(h) != "123\\n\n\\n" {
t.Error("should be equal") t.Error("should be equal")
} }
} }
@ -82,15 +82,15 @@ func TestCompareRelated(t *testing.T) {
if !Compare("1", 1) { if !Compare("1", 1) {
t.Error("should be equal") t.Error("should be equal")
} }
if CompareNot("abc", "abc") { if CompareNot("abc", "abc") {
t.Error("should be equal") t.Error("should be equal")
} }
if !CompareNot("abc", "aBc") { if !CompareNot("abc", "aBc") {
t.Error("should be not equal") t.Error("should be not equal")
} }
if !NotNil("a string") { if !NotNil("a string") {
t.Error("should not be nil") t.Error("should not be nil")
} }
} }
func TestHtmlquote(t *testing.T) { func TestHtmlquote(t *testing.T) {
@ -111,7 +111,7 @@ func TestHtmlunquote(t *testing.T) {
func TestParseForm(t *testing.T) { func TestParseForm(t *testing.T) {
type user struct { type user struct {
Id int `form:"-"` ID int `form:"-"`
tag string `form:"tag"` tag string `form:"tag"`
Name interface{} `form:"username"` Name interface{} `form:"username"`
Age int `form:"age,text"` Age int `form:"age,text"`
@ -123,7 +123,7 @@ func TestParseForm(t *testing.T) {
u := user{} u := user{}
form := url.Values{ form := url.Values{
"Id": []string{"1"}, "ID": []string{"1"},
"-": []string{"1"}, "-": []string{"1"},
"tag": []string{"no"}, "tag": []string{"no"},
"username": []string{"test"}, "username": []string{"test"},
@ -139,8 +139,8 @@ func TestParseForm(t *testing.T) {
if err := ParseForm(form, &u); err != nil { if err := ParseForm(form, &u); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if u.Id != 0 { if u.ID != 0 {
t.Errorf("Id should equal 0 but got %v", u.Id) t.Errorf("ID should equal 0 but got %v", u.ID)
} }
if len(u.tag) != 0 { if len(u.tag) != 0 {
t.Errorf("tag's length should equal 0 but got %v", len(u.tag)) t.Errorf("tag's length should equal 0 but got %v", len(u.tag))
@ -168,7 +168,7 @@ func TestParseForm(t *testing.T) {
func TestRenderForm(t *testing.T) { func TestRenderForm(t *testing.T) {
type user struct { type user struct {
Id int `form:"-"` ID int `form:"-"`
tag string `form:"tag"` tag string `form:"tag"`
Name interface{} `form:"username"` Name interface{} `form:"username"`
Age int `form:"age,text,年龄:"` Age int `form:"age,text,年龄:"`
@ -244,3 +244,80 @@ func TestParseFormTag(t *testing.T) {
t.Errorf("Form Tag that should be ignored was not correctly parsed.") t.Errorf("Form Tag that should be ignored was not correctly parsed.")
} }
} }
func TestMapGet(t *testing.T) {
// test one level map
m1 := map[string]int64{
"a": 1,
"1": 2,
}
if res, err := MapGet(m1, "a"); err == nil {
if res.(int64) != 1 {
t.Errorf("Should return 1, but return %v", res)
}
} else {
t.Errorf("Error happens %v", err)
}
if res, err := MapGet(m1, "1"); err == nil {
if res.(int64) != 2 {
t.Errorf("Should return 2, but return %v", res)
}
} else {
t.Errorf("Error happens %v", err)
}
if res, err := MapGet(m1, 1); err == nil {
if res.(int64) != 2 {
t.Errorf("Should return 2, but return %v", res)
}
} else {
t.Errorf("Error happens %v", err)
}
// test 2 level map
m2 := map[string]interface{}{
"1": map[string]float64{
"2": 3.5,
},
}
if res, err := MapGet(m2, 1, 2); err == nil {
if res.(float64) != 3.5 {
t.Errorf("Should return 3.5, but return %v", res)
}
} else {
t.Errorf("Error happens %v", err)
}
// test 5 level map
m5 := map[string]interface{}{
"1": map[string]interface{}{
"2": map[string]interface{}{
"3": map[string]interface{}{
"4": map[string]interface{}{
"5": 1.2,
},
},
},
},
}
if res, err := MapGet(m5, 1, 2, 3, 4, 5); err == nil {
if res.(float64) != 1.2 {
t.Errorf("Should return 1.2, but return %v", res)
}
} else {
t.Errorf("Error happens %v", err)
}
// check whether element not exists in map
if res, err := MapGet(m5, 5, 4, 3, 2, 1); err == nil {
if res != nil {
t.Errorf("Should return nil, but return %v", res)
}
} else {
t.Errorf("Error happens %v", err)
}
}

31
tree.go
View File

@ -22,6 +22,10 @@ import (
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
// Tree has three elements: FixRouter/wildcard/leaves
// fixRouter sotres Fixed Router
// wildcard stores params
// leaves store the endpoint information
type Tree struct { type Tree struct {
//search fix route first //search fix route first
fixrouters map[string]*Tree fixrouters map[string]*Tree
@ -33,13 +37,14 @@ type Tree struct {
leaves []*leafInfo leaves []*leafInfo
} }
// NewTree return a new Tree
func NewTree() *Tree { func NewTree() *Tree {
return &Tree{ return &Tree{
fixrouters: make(map[string]*Tree), fixrouters: make(map[string]*Tree),
} }
} }
// add Tree to the exist Tree // AddTree will add tree to the exist Tree
// prefix should has no params // prefix should has no params
func (t *Tree) AddTree(prefix string, tree *Tree) { func (t *Tree) AddTree(prefix string, tree *Tree) {
t.addtree(splitPath(prefix), tree, nil, "") t.addtree(splitPath(prefix), tree, nil, "")
@ -187,7 +192,7 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
} }
} }
// call addseg function // AddRouter call addseg function
func (t *Tree) AddRouter(pattern string, runObject interface{}) { func (t *Tree) AddRouter(pattern string, runObject interface{}) {
t.addseg(splitPath(pattern), runObject, nil, "") t.addseg(splitPath(pattern), runObject, nil, "")
} }
@ -266,7 +271,7 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string,
} }
} }
// match router to runObject & params // Match router to runObject & params
func (t *Tree) Match(pattern string) (runObject interface{}, params map[string]string) { func (t *Tree) Match(pattern string) (runObject interface{}, params map[string]string) {
if len(pattern) == 0 || pattern[0] != '/' { if len(pattern) == 0 || pattern[0] != '/' {
return nil, nil return nil, nil
@ -349,7 +354,7 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
continue continue
} }
params[v] = "" params[v] = ""
j += 1 j++
} }
return true, params return true, params
} }
@ -402,7 +407,7 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
return false, nil return false, nil
} }
params[v] = wildcardValues[j] params[v] = wildcardValues[j]
j += 1 j++
} }
if len(params) != len(wildcardValues) { if len(params) != len(wildcardValues) {
return false, nil return false, nil
@ -453,9 +458,8 @@ func splitSegment(key string) (bool, []string, string) {
if strings.HasPrefix(key, "*") { if strings.HasPrefix(key, "*") {
if key == "*.*" { if key == "*.*" {
return true, []string{".", ":path", ":ext"}, "" return true, []string{".", ":path", ":ext"}, ""
} else {
return true, []string{":splat"}, ""
} }
return true, []string{":splat"}, ""
} }
if strings.ContainsAny(key, ":") { if strings.ContainsAny(key, ":") {
var paramsNum int var paramsNum int
@ -469,7 +473,7 @@ func splitSegment(key string) (bool, []string, string) {
reg := regexp.MustCompile(`[a-zA-Z0-9_]+`) reg := regexp.MustCompile(`[a-zA-Z0-9_]+`)
for i, v := range key { for i, v := range key {
if skipnum > 0 { if skipnum > 0 {
skipnum -= 1 skipnum--
continue continue
} }
if start { if start {
@ -483,7 +487,7 @@ func splitSegment(key string) (bool, []string, string) {
startexp = false startexp = false
skipnum = 3 skipnum = 3
param = make([]rune, 0) param = make([]rune, 0)
paramsNum += 1 paramsNum++
continue continue
} }
} }
@ -491,7 +495,7 @@ func splitSegment(key string) (bool, []string, string) {
if key[i+1:i+7] == "string" { if key[i+1:i+7] == "string" {
out = append(out, []rune(`([\w]+)`)...) out = append(out, []rune(`([\w]+)`)...)
params = append(params, ":"+string(param)) params = append(params, ":"+string(param))
paramsNum += 1 paramsNum++
start = false start = false
startexp = false startexp = false
skipnum = 6 skipnum = 6
@ -509,7 +513,7 @@ func splitSegment(key string) (bool, []string, string) {
out = append(out, []rune(`(.+)`)...) out = append(out, []rune(`(.+)`)...)
params = append(params, ":"+string(param)) params = append(params, ":"+string(param))
param = make([]rune, 0) param = make([]rune, 0)
paramsNum += 1 paramsNum++
start = false start = false
startexp = false startexp = false
} }
@ -527,7 +531,7 @@ func splitSegment(key string) (bool, []string, string) {
startexp = true startexp = true
start = false start = false
params = append(params, ":"+string(param)) params = append(params, ":"+string(param))
paramsNum += 1 paramsNum++
expt = make([]rune, 0) expt = make([]rune, 0)
expt = append(expt, '(') expt = append(expt, '(')
} else if v == ')' { } else if v == ')' {
@ -548,7 +552,6 @@ func splitSegment(key string) (bool, []string, string) {
params = append(params, ":"+string(param)) params = append(params, ":"+string(param))
} }
return true, params, string(out) return true, params, string(out)
} else {
return false, nil, ""
} }
return false, nil, ""
} }

View File

@ -457,7 +457,7 @@ func (b Base64) GetLimitValue() interface{} {
} }
// just for chinese mobile phone number // just for chinese mobile phone number
var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][67]|[4][579]))\\d{8}$") var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][067]|[4][579]))\\d{8}$")
type Mobile struct { type Mobile struct {
Match Match