1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-25 21:51:28 +00:00

Merge remote-tracking branch 'upstream/master' into develop

This commit is contained in:
Meaglith Ma 2015-03-01 11:47:02 +08:00
commit 9f70561f21
57 changed files with 1271 additions and 681 deletions

View File

@ -3,7 +3,7 @@
[![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, modularity, full-stack web framework. beego is an open-source, high-performance, modular, full-stack web framework.
More info [beego.me](http://beego.me) More info [beego.me](http://beego.me)
@ -19,7 +19,7 @@ More info [beego.me](http://beego.me)
* Auto API documents * Auto API documents
* Annotation router * Annotation router
* Namespace * Namespace
* Powerful develop tools * Powerful development tools
* Full stack for Web & API * Full stack for Web & API
## Documentation ## Documentation

View File

@ -113,8 +113,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
m["SessionName"] = SessionName m["SessionName"] = SessionName
m["SessionGCMaxLifetime"] = SessionGCMaxLifetime m["SessionGCMaxLifetime"] = SessionGCMaxLifetime
m["SessionSavePath"] = SessionSavePath m["SessionSavePath"] = SessionSavePath
m["SessionHashFunc"] = SessionHashFunc
m["SessionHashKey"] = SessionHashKey
m["SessionCookieLifeTime"] = SessionCookieLifeTime m["SessionCookieLifeTime"] = SessionCookieLifeTime
m["UseFcgi"] = UseFcgi m["UseFcgi"] = UseFcgi
m["MaxMemory"] = MaxMemory m["MaxMemory"] = MaxMemory
@ -399,7 +397,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
if err != nil { if err != nil {
data["Message"] = []string{"error", fmt.Sprintf("%s", err)} data["Message"] = []string{"error", fmt.Sprintf("%s", err)}
} }
data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is %s", taskname, t.GetStatus())} data["Message"] = []string{"success", fmt.Sprintf("%s run success,Now the Status is <br>%s", taskname, t.GetStatus())}
} else { } else {
data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)} data["Message"] = []string{"warning", fmt.Sprintf("there's no task which named: %s", taskname)}
} }
@ -412,12 +410,14 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
var fields = []string{ var fields = []string{
fmt.Sprintf("Task Name"), fmt.Sprintf("Task Name"),
fmt.Sprintf("Task Spec"), fmt.Sprintf("Task Spec"),
fmt.Sprintf("Task Function"), fmt.Sprintf("Task Status"),
fmt.Sprintf("Last Time"),
fmt.Sprintf(""), fmt.Sprintf(""),
} }
for tname, tk := range toolbox.AdminTaskList { for tname, tk := range toolbox.AdminTaskList {
result = []string{ result = []string{
fmt.Sprintf("%s", tname), fmt.Sprintf("%s", tname),
fmt.Sprintf("%s", tk.GetSpec()),
fmt.Sprintf("%s", tk.GetStatus()), fmt.Sprintf("%s", tk.GetStatus()),
fmt.Sprintf("%s", tk.GetPrev().String()), fmt.Sprintf("%s", tk.GetPrev().String()),
} }
@ -458,7 +458,7 @@ func (admin *adminApp) Run() {
for p, f := range admin.routers { for p, f := range admin.routers {
http.Handle(p, f) http.Handle(p, f)
} }
BeeLogger.Info("Running on %s", addr) BeeLogger.Info("Admin server Running on %s", addr)
err := http.ListenAndServe(addr, nil) err := http.ListenAndServe(addr, nil)
if err != nil { if err != nil {
BeeLogger.Critical("Admin ListenAndServe: ", err) BeeLogger.Critical("Admin ListenAndServe: ", err)

View File

@ -186,7 +186,7 @@ bg-warning
</td> </td>
{{end}} {{end}}
<td> <td>
<a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 1}}">Run</a> <a class="btn btn-primary btn-sm" href="/task?taskname={{index $slice 0}}">Run</a>
</td> </td>
</tr> </tr>
{{end}} {{end}}

41
app.go
View File

@ -19,14 +19,12 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/fcgi" "net/http/fcgi"
"os"
"time" "time"
"github.com/astaxie/beego/context" "github.com/astaxie/beego/utils"
) )
// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)
// 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 *ControllerRegistor
@ -64,6 +62,10 @@ func (app *App) Run() {
} }
} else { } else {
if HttpPort == 0 { if HttpPort == 0 {
// remove the Socket file before start
if utils.FileExists(addr) {
os.Remove(addr)
}
l, err = net.Listen("unix", addr) l, err = net.Listen("unix", addr)
} else { } else {
l, err = net.Listen("tcp", addr) l, err = net.Listen("tcp", addr)
@ -85,7 +87,7 @@ func (app *App) Run() {
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("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)
@ -98,12 +100,29 @@ func (app *App) Run() {
if EnableHttpListen { if EnableHttpListen {
go func() { go func() {
app.Server.Addr = addr app.Server.Addr = addr
BeeLogger.Info("Running on %s", app.Server.Addr) BeeLogger.Info("http server Running on %s", app.Server.Addr)
err := app.Server.ListenAndServe() if ListenTCP4 && HttpAddr == "" {
if err != nil { ln, err := net.Listen("tcp4", app.Server.Addr)
BeeLogger.Critical("ListenAndServe: ", err) if err != nil {
time.Sleep(100 * time.Microsecond) BeeLogger.Critical("ListenAndServe: ", err)
endRunning <- true time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
err = app.Server.Serve(ln)
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
return
}
} else {
err := app.Server.ListenAndServe()
if err != nil {
BeeLogger.Critical("ListenAndServe: ", err)
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
} }
}() }()
} }

View File

@ -33,83 +33,15 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
// beego web framework version. // beego web framework version.
const VERSION = "1.4.1" const VERSION = "1.4.3"
type hookfunc func() error //hook function to run type hookfunc func() error //hook function to run
var hooks []hookfunc //hook function slice to store the hookfunc var hooks []hookfunc //hook function slice to store the hookfunc
type groupRouter struct {
pattern string
controller ControllerInterface
mappingMethods string
}
// RouterGroups which will store routers
type GroupRouters []groupRouter
// Get a new GroupRouters
func NewGroupRouters() GroupRouters {
return make(GroupRouters, 0)
}
// Add Router in the GroupRouters
// it is for plugin or module to register router
func (gr *GroupRouters) AddRouter(pattern string, c ControllerInterface, mappingMethod ...string) {
var newRG groupRouter
if len(mappingMethod) > 0 {
newRG = groupRouter{
pattern,
c,
mappingMethod[0],
}
} else {
newRG = groupRouter{
pattern,
c,
"",
}
}
*gr = append(*gr, newRG)
}
func (gr *GroupRouters) AddAuto(c ControllerInterface) {
newRG := groupRouter{
"",
c,
"",
}
*gr = append(*gr, newRG)
}
// AddGroupRouter with the prefix
// it will register the router in BeeApp
// the follow code is write in modules:
// GR:=NewGroupRouters()
// GR.AddRouter("/login",&UserController,"get:Login")
// GR.AddRouter("/logout",&UserController,"get:Logout")
// GR.AddRouter("/register",&UserController,"get:Reg")
// the follow code is write in app:
// import "github.com/beego/modules/auth"
// AddRouterGroup("/admin", auth.GR)
func AddGroupRouter(prefix string, groups GroupRouters) *App {
for _, v := range groups {
if v.pattern == "" {
BeeApp.Handlers.AddAutoPrefix(prefix, v.controller)
} else if v.mappingMethods != "" {
BeeApp.Handlers.Add(prefix+v.pattern, v.controller, v.mappingMethods)
} else {
BeeApp.Handlers.Add(prefix+v.pattern, v.controller)
}
}
return BeeApp
}
// Router adds a patterned controller handler to BeeApp. // Router adds a patterned controller handler to BeeApp.
// it's an alias method of App.Router. // it's an alias method of App.Router.
// usage: // usage:
@ -280,15 +212,6 @@ func Handler(rootpath string, h http.Handler, options ...interface{}) *App {
return BeeApp return BeeApp
} }
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
func Errorhandler(err string, h http.HandlerFunc) *App {
middleware.Errorhandler(err, h)
return BeeApp
}
// SetViewsPath sets view directory path in beego application. // SetViewsPath sets view directory path in beego application.
func SetViewsPath(path string) *App { func SetViewsPath(path string) *App {
ViewsPath = path ViewsPath = path
@ -383,8 +306,6 @@ func initBeforeHttpRun() {
`"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
`"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` +
`"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
`"sessionIDHashFunc":"` + SessionHashFunc + `",` +
`"sessionIDHashKey":"` + SessionHashKey + `",` +
`"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
`"domain":"` + SessionDomain + `",` + `"domain":"` + SessionDomain + `",` +
`"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
@ -404,9 +325,7 @@ func initBeforeHttpRun() {
} }
} }
middleware.VERSION = VERSION registerDefaultErrorHandler()
middleware.AppName = AppName
middleware.RegisterErrorHandler()
if EnableDocs { if EnableDocs {
Get("/docs", serverDocs) Get("/docs", serverDocs)

4
cache/cache_test.go vendored
View File

@ -15,6 +15,7 @@
package cache package cache
import ( import (
"os"
"testing" "testing"
"time" "time"
) )
@ -67,7 +68,7 @@ func TestCache(t *testing.T) {
} }
func TestFileCache(t *testing.T) { func TestFileCache(t *testing.T) {
bm, err := NewCache("file", `{"CachePath":"/cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`) bm, err := NewCache("file", `{"CachePath":"cache","FileSuffix":".bin","DirectoryLevel":2,"EmbedExpiry":0}`)
if err != nil { if err != nil {
t.Error("init err") t.Error("init err")
} }
@ -112,4 +113,5 @@ func TestFileCache(t *testing.T) {
if v := bm.Get("astaxie"); v.(string) != "author" { if v := bm.Get("astaxie"); v.(string) != "author" {
t.Error("get err") t.Error("get err")
} }
os.RemoveAll("cache")
} }

2
cache/file.go vendored
View File

@ -92,8 +92,6 @@ func (fc *FileCache) StartAndGC(config string) error {
// Init will make new dir for file cache if not exist. // Init will make new dir for file cache if not exist.
func (fc *FileCache) Init() { func (fc *FileCache) Init() {
app := filepath.Dir(os.Args[0])
fc.CachePath = filepath.Join(app, fc.CachePath)
if ok, _ := exists(fc.CachePath); !ok { // todo : error handle if ok, _ := exists(fc.CachePath); !ok { // todo : error handle
_ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle _ = os.MkdirAll(fc.CachePath, os.ModePerm) // todo : error handle
} }

17
cache/redis/redis.go vendored
View File

@ -32,6 +32,7 @@ package redis
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"strconv"
"time" "time"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
@ -48,6 +49,7 @@ var (
type RedisCache struct { type RedisCache struct {
p *redis.Pool // redis connection pool p *redis.Pool // redis connection pool
conninfo string conninfo string
dbNum int
key string key string
} }
@ -75,14 +77,13 @@ func (rc *RedisCache) Get(key string) interface{} {
// put cache to redis. // put cache to redis.
func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error {
var err error var err error
if _, err = rc.do("SET", key, val); err != nil { if _, err = rc.do("SETEX", key, timeout, val); err != nil {
return err return err
} }
if _, err = rc.do("HSET", rc.key, key, true); err != nil { if _, err = rc.do("HSET", rc.key, key, true); err != nil {
return err return err
} }
_, err = rc.do("EXPIRE", key, timeout)
return err return err
} }
@ -138,7 +139,7 @@ func (rc *RedisCache) ClearAll() error {
} }
// start redis cache adapter. // start redis cache adapter.
// config is like {"key":"collection key","conn":"connection info"} // config is like {"key":"collection key","conn":"connection info","dbNum":"0"}
// the cache item in redis are stored forever, // 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 *RedisCache) StartAndGC(config string) error {
@ -152,9 +153,12 @@ func (rc *RedisCache) StartAndGC(config string) error {
if _, ok := cf["conn"]; !ok { if _, ok := cf["conn"]; !ok {
return errors.New("config has no conn key") return errors.New("config has no conn key")
} }
if _, ok := cf["dbNum"]; !ok {
cf["dbNum"] = "0"
}
rc.key = cf["key"] rc.key = cf["key"]
rc.conninfo = cf["conn"] rc.conninfo = cf["conn"]
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
rc.connectInit() rc.connectInit()
c := rc.p.Get() c := rc.p.Get()
@ -167,6 +171,11 @@ func (rc *RedisCache) StartAndGC(config string) error {
func (rc *RedisCache) connectInit() { func (rc *RedisCache) 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)
_, selecterr := c.Do("SELECT", rc.dbNum)
if selecterr != nil {
c.Close()
return nil, selecterr
}
return return
} }
// initialize a new pool // initialize a new pool

View File

@ -40,6 +40,7 @@ var (
EnableHttpListen bool EnableHttpListen bool
HttpAddr string HttpAddr string
HttpPort int HttpPort int
ListenTCP4 bool
EnableHttpTLS bool EnableHttpTLS bool
HttpsPort int HttpsPort int
HttpCertFile string HttpCertFile string
@ -55,8 +56,6 @@ var (
SessionName string // the cookie name when saving session id into cookie. SessionName string // the cookie name when saving session id into cookie.
SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session. SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session.
SessionSavePath string // if use mysql/redis/file provider, define save path to connection info. SessionSavePath string // if use mysql/redis/file provider, define save path to connection info.
SessionHashFunc string // session hash generation func.
SessionHashKey string // session hash salt string.
SessionCookieLifeTime int // the life time of session id in cookie. SessionCookieLifeTime int // the life time of session id in cookie.
SessionAutoSetCookie bool // auto setcookie SessionAutoSetCookie bool // auto setcookie
SessionDomain string // the cookie domain default is empty SessionDomain string // the cookie domain default is empty
@ -82,6 +81,7 @@ var (
AppConfigProvider string // config provider AppConfigProvider string // config provider
EnableDocs bool // enable generate docs & server docs API Swagger EnableDocs bool // enable generate docs & server docs API Swagger
RouterCaseSensitive bool // router case sensitive default is true RouterCaseSensitive bool // router case sensitive default is true
AccessLogs bool // print access logs, default is false
) )
type beegoAppConfig struct { type beegoAppConfig struct {
@ -111,7 +111,7 @@ func (b *beegoAppConfig) String(key string) string {
func (b *beegoAppConfig) Strings(key string) []string { func (b *beegoAppConfig) Strings(key string) []string {
v := b.innerConfig.Strings(RunMode + "::" + key) v := b.innerConfig.Strings(RunMode + "::" + key)
if len(v) == 0 { if v[0] == "" {
return b.innerConfig.Strings(key) return b.innerConfig.Strings(key)
} }
return v return v
@ -236,8 +236,6 @@ func init() {
SessionName = "beegosessionID" SessionName = "beegosessionID"
SessionGCMaxLifetime = 3600 SessionGCMaxLifetime = 3600
SessionSavePath = "" SessionSavePath = ""
SessionHashFunc = "sha1"
SessionHashKey = "beegoserversessionkey"
SessionCookieLifeTime = 0 //set cookie default is the brower life SessionCookieLifeTime = 0 //set cookie default is the brower life
SessionAutoSetCookie = true SessionAutoSetCookie = true
@ -277,9 +275,10 @@ func init() {
if err != nil { if err != nil {
fmt.Println("init console log error:", err) fmt.Println("init console log error:", err)
} }
SetLogFuncCall(true)
err = ParseConfig() err = ParseConfig()
if err != nil && !os.IsNotExist(err) { if err != nil && os.IsNotExist(err) {
// for init if doesn't have app.conf will not panic // for init if doesn't have app.conf will not panic
ac := config.NewFakeConfig() ac := config.NewFakeConfig()
AppConfig = &beegoAppConfig{ac} AppConfig = &beegoAppConfig{ac}
@ -308,6 +307,10 @@ func ParseConfig() (err error) {
HttpPort = v HttpPort = v
} }
if v, err := AppConfig.Bool("ListenTCP4"); err == nil {
ListenTCP4 = v
}
if v, err := AppConfig.Bool("EnableHttpListen"); err == nil { if v, err := AppConfig.Bool("EnableHttpListen"); err == nil {
EnableHttpListen = v EnableHttpListen = v
} }
@ -348,14 +351,6 @@ func ParseConfig() (err error) {
SessionSavePath = sesssavepath SessionSavePath = sesssavepath
} }
if sesshashfunc := AppConfig.String("SessionHashFunc"); sesshashfunc != "" {
SessionHashFunc = sesshashfunc
}
if sesshashkey := AppConfig.String("SessionHashKey"); sesshashkey != "" {
SessionHashKey = sesshashkey
}
if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 {
SessionGCMaxLifetime = sessMaxLifeTime SessionGCMaxLifetime = sessMaxLifeTime
} }

View File

@ -17,13 +17,10 @@ package config
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"strings" "strings"
"sync" "sync"
"time"
) )
// JsonConfig is a json config parser and implements Config interface. // JsonConfig is a json config parser and implements Config interface.
@ -41,13 +38,19 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return js.ParseData(content)
}
// ParseData returns a ConfigContainer with json string
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
x := &JsonConfigContainer{ x := &JsonConfigContainer{
data: make(map[string]interface{}), data: make(map[string]interface{}),
} }
err = json.Unmarshal(content, &x.data) err := json.Unmarshal(data, &x.data)
if err != nil { if err != nil {
var wrappingArray []interface{} var wrappingArray []interface{}
err2 := json.Unmarshal(content, &wrappingArray) err2 := json.Unmarshal(data, &wrappingArray)
if err2 != nil { if err2 != nil {
return nil, err return nil, err
} }
@ -56,16 +59,6 @@ func (js *JsonConfig) Parse(filename string) (ConfigContainer, error) {
return x, nil return x, nil
} }
func (js *JsonConfig) ParseData(data []byte) (ConfigContainer, error) {
// Save memory data to temporary file
tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond()))
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
if err := ioutil.WriteFile(tmpName, data, 0655); err != nil {
return nil, err
}
return js.Parse(tmpName)
}
// A Config represents the json configuration. // A Config represents the json configuration.
// Only when get value, support key as section:name type. // Only when get value, support key as section:name type.
type JsonConfigContainer struct { type JsonConfigContainer struct {
@ -88,11 +81,10 @@ func (c *JsonConfigContainer) Bool(key string) (bool, error) {
// DefaultBool return the bool value if has no error // DefaultBool return the bool value if has no error
// otherwise return the defaultval // otherwise return the defaultval
func (c *JsonConfigContainer) DefaultBool(key string, defaultval bool) bool { func (c *JsonConfigContainer) DefaultBool(key string, defaultval bool) bool {
if v, err := c.Bool(key); err != nil { if v, err := c.Bool(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// Int returns the integer value for a given key. // Int returns the integer value for a given key.
@ -110,11 +102,10 @@ func (c *JsonConfigContainer) Int(key string) (int, error) {
// DefaultInt returns the integer value for a given key. // DefaultInt returns the integer value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultInt(key string, defaultval int) int { func (c *JsonConfigContainer) DefaultInt(key string, defaultval int) int {
if v, err := c.Int(key); err != nil { if v, err := c.Int(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// Int64 returns the int64 value for a given key. // Int64 returns the int64 value for a given key.
@ -132,11 +123,10 @@ func (c *JsonConfigContainer) Int64(key string) (int64, error) {
// DefaultInt64 returns the int64 value for a given key. // DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultInt64(key string, defaultval int64) int64 { func (c *JsonConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
if v, err := c.Int64(key); err != nil { if v, err := c.Int64(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// Float returns the float value for a given key. // Float returns the float value for a given key.
@ -154,11 +144,10 @@ func (c *JsonConfigContainer) Float(key string) (float64, error) {
// DefaultFloat returns the float64 value for a given key. // DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultFloat(key string, defaultval float64) float64 { func (c *JsonConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
if v, err := c.Float(key); err != nil { if v, err := c.Float(key); err == nil {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// String returns the string value for a given key. // String returns the string value for a given key.
@ -175,35 +164,37 @@ func (c *JsonConfigContainer) String(key string) string {
// DefaultString returns the string value for a given key. // DefaultString returns the string value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string { func (c *JsonConfigContainer) DefaultString(key string, defaultval string) string {
if v := c.String(key); v == "" { // TODO FIXME should not use "" to replace non existance
return defaultval if v := c.String(key); v != "" {
} else {
return v return v
} }
return defaultval
} }
// Strings returns the []string value for a given key. // Strings returns the []string value for a given key.
func (c *JsonConfigContainer) Strings(key string) []string { func (c *JsonConfigContainer) Strings(key string) []string {
stringVal := c.String(key)
if stringVal == "" {
return []string{}
}
return strings.Split(c.String(key), ";") return strings.Split(c.String(key), ";")
} }
// DefaultStrings returns the []string value for a given key. // DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval // if err != nil return defaltval
func (c *JsonConfigContainer) DefaultStrings(key string, defaultval []string) []string { func (c *JsonConfigContainer) DefaultStrings(key string, defaultval []string) []string {
if v := c.Strings(key); len(v) == 0 { if v := c.Strings(key); len(v) > 0 {
return defaultval
} else {
return v return v
} }
return defaultval
} }
// GetSection returns map for the given section // GetSection returns map for the given section
func (c *JsonConfigContainer) GetSection(section string) (map[string]string, error) { func (c *JsonConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok { if v, ok := c.data[section]; ok {
return v.(map[string]string), nil return v.(map[string]string), nil
} else {
return nil, errors.New("not exist setction")
} }
return nil, errors.New("nonexist section " + section)
} }
// SaveConfigFile save the config into file // SaveConfigFile save the config into file
@ -222,7 +213,7 @@ func (c *JsonConfigContainer) SaveConfigFile(filename string) (err error) {
return err return err
} }
// WriteValue writes a new value for key. // Set writes a new value for key.
func (c *JsonConfigContainer) Set(key, val string) error { func (c *JsonConfigContainer) Set(key, val string) error {
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
@ -241,18 +232,20 @@ func (c *JsonConfigContainer) DIY(key string) (v interface{}, err error) {
// section.key or key // section.key or key
func (c *JsonConfigContainer) getData(key string) interface{} { func (c *JsonConfigContainer) getData(key string) interface{} {
c.RLock()
defer c.RUnlock()
if len(key) == 0 { if len(key) == 0 {
return nil return nil
} }
sectionKey := strings.Split(key, "::")
if len(sectionKey) >= 2 { c.RLock()
curValue, ok := c.data[sectionKey[0]] defer c.RUnlock()
sectionKeys := strings.Split(key, "::")
if len(sectionKeys) >= 2 {
curValue, ok := c.data[sectionKeys[0]]
if !ok { if !ok {
return nil return nil
} }
for _, key := range sectionKey[1:] { for _, key := range sectionKeys[1:] {
if v, ok := curValue.(map[string]interface{}); ok { if v, ok := curValue.(map[string]interface{}); ok {
if curValue, ok = v[key]; !ok { if curValue, ok = v[key]; !ok {
return nil return nil

View File

@ -21,6 +21,7 @@ import (
var jsoncontext = `{ var jsoncontext = `{
"appname": "beeapi", "appname": "beeapi",
"testnames": "foo;bar",
"httpport": 8080, "httpport": 8080,
"mysqlport": 3600, "mysqlport": 3600,
"PI": 3.1415976, "PI": 3.1415976,
@ -28,8 +29,8 @@ var jsoncontext = `{
"autorender": false, "autorender": false,
"copyrequestbody": true, "copyrequestbody": true,
"database": { "database": {
"host": "host", "host": "host",
"port": "port", "port": "port",
"database": "database", "database": "database",
"username": "username", "username": "username",
"password": "password", "password": "password",
@ -122,6 +123,12 @@ func TestJson(t *testing.T) {
if jsonconf.String("runmode") != "dev" { if jsonconf.String("runmode") != "dev" {
t.Fatal("runmode not equal to dev") t.Fatal("runmode not equal to dev")
} }
if v := jsonconf.Strings("unknown"); len(v) > 0 {
t.Fatal("unknown strings, the length should be 0")
}
if v := jsonconf.Strings("testnames"); len(v) != 2 {
t.Fatal("testnames length should be 2")
}
if v, err := jsonconf.Bool("autorender"); err != nil || v != false { if v, err := jsonconf.Bool("autorender"); err != nil || v != false {
t.Error(v) t.Error(v)
t.Fatal(err) t.Fatal(err)
@ -179,4 +186,8 @@ func TestJson(t *testing.T) {
if _, err := jsonconf.Bool("unknown"); err == nil { if _, err := jsonconf.Bool("unknown"); err == nil {
t.Error("unknown keys should return an error when expecting a Bool") t.Error("unknown keys should return an error when expecting a Bool")
} }
if !jsonconf.DefaultBool("unknow", true) {
t.Error("unknown keys with default value wrong")
}
} }

View File

@ -31,7 +31,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
@ -53,22 +52,9 @@ func (ctx *Context) Redirect(status int, localurl string) {
} }
// Abort stops this request. // Abort stops this request.
// if middleware.ErrorMaps exists, panic body. // if beego.ErrorMaps exists, panic body.
// if middleware.HTTPExceptionMaps exists, panic HTTPException struct with status and body string.
func (ctx *Context) Abort(status int, body string) { func (ctx *Context) Abort(status int, body string) {
ctx.ResponseWriter.WriteHeader(status) ctx.ResponseWriter.WriteHeader(status)
// first panic from ErrorMaps, is is user defined error functions.
if _, ok := middleware.ErrorMaps[body]; ok {
panic(body)
}
// second panic from HTTPExceptionMaps, it is system defined functions.
if e, ok := middleware.HTTPExceptionMaps[status]; ok {
if len(body) >= 1 {
e.Description = body
}
panic(e)
}
// last panic user string
panic(body) panic(body)
} }
@ -154,8 +140,11 @@ func (ctx *Context) CheckXsrfCookie() bool {
} }
if token == "" { if token == "" {
ctx.Abort(403, "'_xsrf' argument missing from POST") ctx.Abort(403, "'_xsrf' argument missing from POST")
} else if ctx._xsrf_token != token { return false
}
if ctx._xsrf_token != token {
ctx.Abort(403, "XSRF cookie does not match POST argument") ctx.Abort(403, "XSRF cookie does not match POST argument")
return false
} }
return true return true
} }

View File

@ -27,7 +27,7 @@ import (
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
// BeegoInput operates the http request header ,data ,cookie and body. // BeegoInput operates the http request header, data, cookie and body.
// it also contains router params and current session. // it also contains router params and current session.
type BeegoInput struct { type BeegoInput struct {
CruSession session.SessionStore CruSession session.SessionStore
@ -72,11 +72,11 @@ func (input *BeegoInput) Site() string {
func (input *BeegoInput) Scheme() string { func (input *BeegoInput) Scheme() string {
if input.Request.URL.Scheme != "" { if input.Request.URL.Scheme != "" {
return input.Request.URL.Scheme return input.Request.URL.Scheme
} else if input.Request.TLS == nil {
return "http"
} else {
return "https"
} }
if input.Request.TLS == nil {
return "http"
}
return "https"
} }
// Domain returns host name. // Domain returns host name.
@ -153,12 +153,12 @@ func (input *BeegoInput) IsSecure() bool {
return input.Scheme() == "https" return input.Scheme() == "https"
} }
// IsSecure returns boolean of this request is in webSocket. // IsWebsocket returns boolean of this request is in webSocket.
func (input *BeegoInput) IsWebsocket() bool { func (input *BeegoInput) IsWebsocket() bool {
return input.Header("Upgrade") == "websocket" return input.Header("Upgrade") == "websocket"
} }
// IsSecure returns boolean of whether file uploads in this request or not.. // IsUpload returns boolean of whether file uploads in this request or not..
func (input *BeegoInput) IsUpload() bool { func (input *BeegoInput) IsUpload() bool {
return strings.Contains(input.Header("Content-Type"), "multipart/form-data") return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
} }
@ -189,16 +189,24 @@ func (input *BeegoInput) Proxy() []string {
return []string{} return []string{}
} }
// Referer returns http referer header.
func (input *BeegoInput) Referer() string {
return input.Header("Referer")
}
// Refer returns http referer header. // Refer returns http referer header.
func (input *BeegoInput) Refer() string { func (input *BeegoInput) Refer() string {
return input.Header("Referer") return input.Referer()
} }
// SubDomains returns sub domain string. // SubDomains returns sub domain string.
// if aa.bb.domain.com, returns aa.bb . // if aa.bb.domain.com, returns aa.bb .
func (input *BeegoInput) SubDomains() string { func (input *BeegoInput) SubDomains() string {
parts := strings.Split(input.Host(), ".") parts := strings.Split(input.Host(), ".")
return strings.Join(parts[len(parts)-2:], ".") if len(parts) >= 3 {
return strings.Join(parts[:len(parts)-2], ".")
}
return ""
} }
// Port returns request client port. // Port returns request client port.
@ -237,6 +245,7 @@ func (input *BeegoInput) Query(key string) string {
} }
// Header returns request header item string by a given string. // Header returns request header item string by a given string.
// if non-existed, return empty string.
func (input *BeegoInput) Header(key string) string { func (input *BeegoInput) Header(key string) string {
return input.Request.Header.Get(key) return input.Request.Header.Get(key)
} }
@ -252,11 +261,12 @@ func (input *BeegoInput) Cookie(key string) string {
} }
// Session returns current session item value by a given key. // Session returns current session item value by a given key.
// if non-existed, return empty string.
func (input *BeegoInput) Session(key interface{}) interface{} { func (input *BeegoInput) Session(key interface{}) interface{} {
return input.CruSession.Get(key) return input.CruSession.Get(key)
} }
// Body returns the raw request body data as bytes. // CopyBody returns the raw request body data as bytes.
func (input *BeegoInput) CopyBody() []byte { func (input *BeegoInput) CopyBody() []byte {
requestbody, _ := ioutil.ReadAll(input.Request.Body) requestbody, _ := ioutil.ReadAll(input.Request.Body)
input.Request.Body.Close() input.Request.Body.Close()

View File

@ -70,3 +70,45 @@ func TestParse(t *testing.T) {
} }
fmt.Println(user) fmt.Println(user)
} }
func TestSubDomain(t *testing.T) {
r, _ := http.NewRequest("GET", "http://www.example.com/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil)
beegoInput := NewInput(r)
subdomain := beegoInput.SubDomains()
if subdomain != "www" {
t.Fatal("Subdomain parse error, got" + subdomain)
}
r, _ = http.NewRequest("GET", "http://localhost/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, should be empty, got " + beegoInput.SubDomains())
}
r, _ = http.NewRequest("GET", "http://aa.bb.example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "aa.bb" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
/* TODO Fix this
r, _ = http.NewRequest("GET", "http://127.0.0.1/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
*/
r, _ = http.NewRequest("GET", "http://example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
r, _ = http.NewRequest("GET", "http://aa.bb.cc.dd.example.com/", nil)
beegoInput.Request = r
if beegoInput.SubDomains() != "aa.bb.cc.dd" {
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
}
}

View File

@ -188,7 +188,7 @@ func sanitizeValue(v string) string {
// Json writes json to response body. // Json writes json to response body.
// if coding is true, it converts utf-8 to \u0000 type. // if coding is true, it converts utf-8 to \u0000 type.
func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error { func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
output.Header("Content-Type", "application/json;charset=UTF-8") output.Header("Content-Type", "application/json; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {
@ -209,7 +209,7 @@ func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) e
// Jsonp writes jsonp to response body. // Jsonp writes jsonp to response body.
func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error { func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/javascript;charset=UTF-8") output.Header("Content-Type", "application/javascript; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {
@ -235,7 +235,7 @@ func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
// Xml writes xml string to response body. // Xml writes xml string to response body.
func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error { func (output *BeegoOutput) Xml(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/xml;charset=UTF-8") output.Header("Content-Type", "application/xml; charset=utf-8")
var content []byte var content []byte
var err error var err error
if hasIndent { if hasIndent {

View File

@ -270,16 +270,22 @@ func (c *Controller) Redirect(url string, code int) {
// Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string. // Aborts 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 {
c.Ctx.Abort(status, code) status = 200
} else {
c.Ctx.Abort(200, code)
} }
c.CustomAbort(status, code)
} }
// CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body. // CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body.
func (c *Controller) CustomAbort(status int, body string) { func (c *Controller) CustomAbort(status int, body string) {
c.Ctx.Abort(status, body) c.Ctx.ResponseWriter.WriteHeader(status)
// first panic from ErrorMaps, is is user defined error functions.
if _, ok := ErrorMaps[body]; ok {
panic(body)
}
// last panic user string
c.Ctx.ResponseWriter.Write([]byte(body))
panic(USERSTOPRUN)
} }
// StopRun makes panic of USERSTOPRUN error and go to recover function if defined. // StopRun makes panic of USERSTOPRUN error and go to recover function if defined.
@ -289,7 +295,7 @@ func (c *Controller) StopRun() {
// 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 ...string) string { func (c *Controller) UrlFor(endpoint string, values ...interface{}) string {
if len(endpoint) <= 0 { if len(endpoint) <= 0 {
return "" return ""
} }
@ -363,67 +369,144 @@ func (c *Controller) ParseForm(obj interface{}) error {
return ParseForm(c.Input(), obj) return ParseForm(c.Input(), obj)
} }
// GetString returns the input value by key string. // GetString returns the input value by key string or the default value while it's present and input is blank
func (c *Controller) GetString(key string) string { func (c *Controller) GetString(key string, def ...string) string {
return c.Ctx.Input.Query(key) var defv string
if len(def) > 0 {
defv = def[0]
}
if v := c.Ctx.Input.Query(key); v != "" {
return v
} else {
return defv
}
} }
// GetStrings returns the input string slice by key string. // GetStrings returns the input string slice by key string or the default value while it's present and input is blank
// it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection. // it's designed for multi-value input field such as checkbox(input[type=checkbox]), multi-selection.
func (c *Controller) GetStrings(key string) []string { func (c *Controller) GetStrings(key string, def ...[]string) []string {
var defv []string
if len(def) > 0 {
defv = def[0]
}
f := c.Input() f := c.Input()
if f == nil { if f == nil {
return []string{} return defv
} }
vs := f[key] vs := f[key]
if len(vs) > 0 { if len(vs) > 0 {
return vs return vs
} else {
return defv
} }
return []string{}
} }
// GetInt returns input as an int // GetInt returns input as an int or the default value while it's present and input is blank
func (c *Controller) GetInt(key string) (int, error) { func (c *Controller) GetInt(key string, def ...int) (int, error) {
return strconv.Atoi(c.Ctx.Input.Query(key)) var defv int
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.Atoi(strv)
} else {
return defv, nil
}
} }
// GetInt8 return input as an int8 // GetInt8 return input as an int8 or the default value while it's present and input is blank
func (c *Controller) GetInt8(key string) (int8, error) { func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 8) var defv int8
i8 := int8(i64) if len(def) > 0 {
defv = def[0]
}
return i8, err if strv := c.Ctx.Input.Query(key); strv != "" {
i64, err := strconv.ParseInt(strv, 10, 8)
i8 := int8(i64)
return i8, err
} else {
return defv, nil
}
} }
// GetInt16 returns input as an int16 // GetInt16 returns input as an int16 or the default value while it's present and input is blank
func (c *Controller) GetInt16(key string) (int16, error) { func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 16) var defv int16
i16 := int16(i64) if len(def) > 0 {
defv = def[0]
}
return i16, err if strv := c.Ctx.Input.Query(key); strv != "" {
i64, err := strconv.ParseInt(strv, 10, 16)
i16 := int16(i64)
return i16, err
} else {
return defv, nil
}
} }
// GetInt32 returns input as an int32 // GetInt32 returns input as an int32 or the default value while it's present and input is blank
func (c *Controller) GetInt32(key string) (int32, error) { func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) var defv int32
i32 := int32(i64) if len(def) > 0 {
defv = def[0]
}
return i32, err if strv := c.Ctx.Input.Query(key); strv != "" {
i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32)
i32 := int32(i64)
return i32, err
} else {
return defv, nil
}
} }
// GetInt64 returns input value as int64. // GetInt64 returns input value as int64 or the default value while it's present and input is blank.
func (c *Controller) GetInt64(key string) (int64, error) { func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
return strconv.ParseInt(c.Ctx.Input.Query(key), 10, 64) var defv int64
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseInt(strv, 10, 64)
} else {
return defv, nil
}
} }
// GetBool returns input value as bool. // GetBool returns input value as bool or the default value while it's present and input is blank.
func (c *Controller) GetBool(key string) (bool, error) { func (c *Controller) GetBool(key string, def ...bool) (bool, error) {
return strconv.ParseBool(c.Ctx.Input.Query(key)) var defv bool
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseBool(strv)
} else {
return defv, nil
}
} }
// GetFloat returns input value as float64. // GetFloat returns input value as float64 or the default value while it's present and input is blank.
func (c *Controller) GetFloat(key string) (float64, error) { func (c *Controller) GetFloat(key string, def ...float64) (float64, error) {
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64) var defv float64
if len(def) > 0 {
defv = def[0]
}
if strv := c.Ctx.Input.Query(key); strv != "" {
return strconv.ParseFloat(c.Ctx.Input.Query(key), 64)
} else {
return defv, nil
}
} }
// GetFile returns the file data in file upload field named as key. // GetFile returns the file data in file upload field named as key.

View File

@ -12,20 +12,26 @@
// 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 middleware package beego
import ( import (
"fmt" "fmt"
"html/template" "html/template"
"net/http" "net/http"
"reflect"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/utils"
) )
var ( const (
AppName string errorTypeHandler = iota
VERSION string errorTypeController
) )
var tpl = ` var tpl = `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -76,18 +82,18 @@ var tpl = `
` `
// render default application error page with error and stack string. // render default application error page with error and stack string.
func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack string) { func showErr(err interface{}, ctx *context.Context, Stack string) {
t, _ := template.New("beegoerrortemp").Parse(tpl) t, _ := template.New("beegoerrortemp").Parse(tpl)
data := make(map[string]string) data := make(map[string]string)
data["AppError"] = AppName + ":" + fmt.Sprint(err) data["AppError"] = AppName + ":" + fmt.Sprint(err)
data["RequestMethod"] = r.Method data["RequestMethod"] = ctx.Input.Method()
data["RequestURL"] = r.RequestURI data["RequestURL"] = ctx.Input.Uri()
data["RemoteAddr"] = r.RemoteAddr data["RemoteAddr"] = ctx.Input.IP()
data["Stack"] = Stack data["Stack"] = Stack
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
data["GoVersion"] = runtime.Version() data["GoVersion"] = runtime.Version()
rw.WriteHeader(500) ctx.Output.SetStatus(500)
t.Execute(rw, data) t.Execute(ctx.ResponseWriter, data)
} }
var errtpl = ` var errtpl = `
@ -190,15 +196,68 @@ var errtpl = `
</html> </html>
` `
type errorInfo struct {
controllerType reflect.Type
handler http.HandlerFunc
method string
errorType int
}
// map of http handlers for each error string. // map of http handlers for each error string.
var ErrorMaps map[string]http.HandlerFunc var ErrorMaps map[string]*errorInfo
func init() { func init() {
ErrorMaps = make(map[string]http.HandlerFunc) ErrorMaps = make(map[string]*errorInfo)
}
// show 401 unauthorized error.
func unauthorized(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Unauthorized"
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 402 Payment Required
func paymentRequired(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Payment Required"
data["Content"] = template.HTML("<br>The page you have requested Payment Required." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The credentials you supplied are incorrect" +
"<br>There are errors in the website address" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 403 forbidden error.
func forbidden(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Forbidden"
data["Content"] = template.HTML("<br>The page you have requested is forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Your address may be blocked" +
"<br>The site may be disabled" +
"<br>You need to log in" +
"</ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
} }
// show 404 notfound error. // show 404 notfound error.
func NotFound(rw http.ResponseWriter, r *http.Request) { func notFound(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Page Not Found" data["Title"] = "Page Not Found"
@ -211,45 +270,66 @@ func NotFound(rw http.ResponseWriter, r *http.Request) {
"<br>You like 404 pages" + "<br>You like 404 pages" +
"</ul>") "</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusNotFound)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 401 unauthorized error. // show 405 Method Not Allowed
func Unauthorized(rw http.ResponseWriter, r *http.Request) { func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Unauthorized" data["Title"] = "Method Not Allowed"
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." + data["Content"] = template.HTML("<br>The method you have requested Not Allowed." +
"<br>Perhaps you are here because:" + "<br>Perhaps you are here because:" +
"<br><br><ul>" + "<br><br><ul>" +
"<br>The credentials you supplied are incorrect" + "<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" +
"<br>There are errors in the website address" + "<br>The response MUST include an Allow header containing a list of valid methods for the requested resource." +
"</ul>") "</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusUnauthorized)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 403 forbidden error. // show 500 internal server error.
func Forbidden(rw http.ResponseWriter, r *http.Request) { func internalServerError(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Forbidden" data["Title"] = "Internal Server Error"
data["Content"] = template.HTML("<br>The page you have requested is forbidden." + data["Content"] = template.HTML("<br>The page you have requested is down right now." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" + "<br><br><ul>" +
"<br>Your address may be blocked" + "<br>Please try again later and report the error to the website administrator" +
"<br>The site may be disabled" + "<br></ul>")
"<br>You need to log in" + data["BeegoVersion"] = VERSION
"</ul>") t.Execute(rw, data)
}
// show 501 Not Implemented.
func notImplemented(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Not Implemented"
data["Content"] = template.HTML("<br>The page you have requested is Not Implemented." +
"<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION
t.Execute(rw, data)
}
// show 502 Bad Gateway.
func badGateway(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{})
data["Title"] = "Bad Gateway"
data["Content"] = template.HTML("<br>The page you have requested is down right now." +
"<br><br><ul>" +
"<br>The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request." +
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusForbidden)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 503 service unavailable error. // show 503 service unavailable error.
func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) { func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Service Unavailable" data["Title"] = "Service Unavailable"
@ -260,80 +340,151 @@ func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) {
"<br>Please try again later." + "<br>Please try again later." +
"</ul>") "</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusServiceUnavailable)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 500 internal server error. // show 504 Gateway Timeout.
func InternalServerError(rw http.ResponseWriter, r *http.Request) { func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := make(map[string]interface{}) data := make(map[string]interface{})
data["Title"] = "Internal Server Error" data["Title"] = "Gateway Timeout"
data["Content"] = template.HTML("<br>The page you have requested is down right now." + data["Content"] = template.HTML("<br>The page you have requested is unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" + "<br><br><ul>" +
"<br>Please try again later and report the error to the website administrator" + "<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI." +
"<br></ul>") "<br>Please try again later." +
"</ul>")
data["BeegoVersion"] = VERSION data["BeegoVersion"] = VERSION
//rw.WriteHeader(http.StatusInternalServerError)
t.Execute(rw, data) t.Execute(rw, data)
} }
// show 500 internal error with simple text string.
func SimpleServerError(rw http.ResponseWriter, r *http.Request) {
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
// add http handler for given error string.
func Errorhandler(err string, h http.HandlerFunc) {
ErrorMaps[err] = h
}
// register default error http handlers, 404,401,403,500 and 503. // register default error http handlers, 404,401,403,500 and 503.
func RegisterErrorHandler() { func registerDefaultErrorHandler() {
if _, ok := ErrorMaps["404"]; !ok { if _, ok := ErrorMaps["401"]; !ok {
ErrorMaps["404"] = NotFound Errorhandler("401", unauthorized)
} }
if _, ok := ErrorMaps["401"]; !ok { if _, ok := ErrorMaps["402"]; !ok {
ErrorMaps["401"] = Unauthorized Errorhandler("402", paymentRequired)
} }
if _, ok := ErrorMaps["403"]; !ok { if _, ok := ErrorMaps["403"]; !ok {
ErrorMaps["403"] = Forbidden Errorhandler("403", forbidden)
} }
if _, ok := ErrorMaps["503"]; !ok { if _, ok := ErrorMaps["404"]; !ok {
ErrorMaps["503"] = ServiceUnavailable Errorhandler("404", notFound)
}
if _, ok := ErrorMaps["405"]; !ok {
Errorhandler("405", methodNotAllowed)
} }
if _, ok := ErrorMaps["500"]; !ok { if _, ok := ErrorMaps["500"]; !ok {
ErrorMaps["500"] = InternalServerError Errorhandler("500", internalServerError)
} }
if _, ok := ErrorMaps["501"]; !ok {
Errorhandler("501", notImplemented)
}
if _, ok := ErrorMaps["502"]; !ok {
Errorhandler("502", badGateway)
}
if _, ok := ErrorMaps["503"]; !ok {
Errorhandler("503", serviceUnavailable)
}
if _, ok := ErrorMaps["504"]; !ok {
Errorhandler("504", gatewayTimeout)
}
}
// ErrorHandler registers http.HandlerFunc to each http err code string.
// usage:
// beego.ErrorHandler("404",NotFound)
// beego.ErrorHandler("500",InternalServerError)
func Errorhandler(code string, h http.HandlerFunc) *App {
errinfo := &errorInfo{}
errinfo.errorType = errorTypeHandler
errinfo.handler = h
errinfo.method = code
ErrorMaps[code] = errinfo
return BeeApp
}
// ErrorController registers ControllerInterface to each http err code string.
// usage:
// beego.ErrorHandler(&controllers.ErrorController{})
func ErrorController(c ControllerInterface) *App {
reflectVal := reflect.ValueOf(c)
rt := reflectVal.Type()
ct := reflect.Indirect(reflectVal).Type()
for i := 0; i < rt.NumMethod(); i++ {
if !utils.InSlice(rt.Method(i).Name, exceptMethod) && strings.HasPrefix(rt.Method(i).Name, "Error") {
errinfo := &errorInfo{}
errinfo.errorType = errorTypeController
errinfo.controllerType = ct
errinfo.method = rt.Method(i).Name
errname := strings.TrimPrefix(rt.Method(i).Name, "Error")
ErrorMaps[errname] = errinfo
}
}
return BeeApp
} }
// show error string as simple text message. // show error string as simple text message.
// if error string is empty, show 500 error as default. // if error string is empty, show 500 error as default.
func Exception(errcode string, w http.ResponseWriter, r *http.Request, msg string) { func exception(errcode string, ctx *context.Context) {
code, err := strconv.Atoi(errcode)
if err != nil {
code = 503
}
ctx.ResponseWriter.WriteHeader(code)
if h, ok := ErrorMaps[errcode]; ok { if h, ok := ErrorMaps[errcode]; ok {
isint, err := strconv.Atoi(errcode) executeError(h, ctx)
if err != nil { return
isint = 500 } else if h, ok := ErrorMaps["503"]; ok {
} executeError(h, ctx)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(isint)
h(w, r)
return return
} else { } else {
isint, err := strconv.Atoi(errcode) ctx.WriteString(errcode)
if err != nil { }
isint = 500 }
}
if isint == 400 { func executeError(err *errorInfo, ctx *context.Context) {
msg = "404 page not found" if err.errorType == errorTypeHandler {
} err.handler(ctx.ResponseWriter, ctx.Request)
w.Header().Set("Content-Type", "text/plain; charset=utf-8") return
w.WriteHeader(isint) }
fmt.Fprintln(w, msg) if err.errorType == errorTypeController {
return //Invoke the request handler
vc := reflect.New(err.controllerType)
execController, ok := vc.Interface().(ControllerInterface)
if !ok {
panic("controller is not ControllerInterface")
}
//call the controller init function
execController.Init(ctx, err.controllerType.Name(), err.method, vc.Interface())
//call prepare function
execController.Prepare()
execController.URLMapping()
in := make([]reflect.Value, 0)
method := vc.MethodByName(err.method)
method.Call(in)
//render template
if ctx.Output.Status == 0 {
if AutoRender {
if err := execController.Render(); err != nil {
panic(err)
}
}
}
// finish all runrouter. release resource
execController.Finish()
} }
} }

View File

@ -17,47 +17,47 @@ type ObjectController struct {
beego.Controller beego.Controller
} }
func (this *ObjectController) Post() { func (o *ObjectController) Post() {
var ob models.Object var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob) json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
objectid := models.AddOne(ob) objectid := models.AddOne(ob)
this.Data["json"] = map[string]string{"ObjectId": objectid} o.Data["json"] = map[string]string{"ObjectId": objectid}
this.ServeJson() o.ServeJson()
} }
func (this *ObjectController) Get() { func (o *ObjectController) Get() {
objectId := this.Ctx.Input.Params[":objectId"] objectId := o.Ctx.Input.Params[":objectId"]
if objectId != "" { if objectId != "" {
ob, err := models.GetOne(objectId) ob, err := models.GetOne(objectId)
if err != nil { if err != nil {
this.Data["json"] = err o.Data["json"] = err
} else { } else {
this.Data["json"] = ob o.Data["json"] = ob
} }
} else { } else {
obs := models.GetAll() obs := models.GetAll()
this.Data["json"] = obs o.Data["json"] = obs
} }
this.ServeJson() o.ServeJson()
} }
func (this *ObjectController) Put() { func (o *ObjectController) Put() {
objectId := this.Ctx.Input.Params[":objectId"] objectId := o.Ctx.Input.Params[":objectId"]
var ob models.Object var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob) json.Unmarshal(o.Ctx.Input.RequestBody, &ob)
err := models.Update(objectId, ob.Score) err := models.Update(objectId, ob.Score)
if err != nil { if err != nil {
this.Data["json"] = err o.Data["json"] = err
} else { } else {
this.Data["json"] = "update success!" o.Data["json"] = "update success!"
} }
this.ServeJson() o.ServeJson()
} }
func (this *ObjectController) Delete() { func (o *ObjectController) Delete() {
objectId := this.Ctx.Input.Params[":objectId"] objectId := o.Ctx.Input.Params[":objectId"]
models.Delete(objectId) models.Delete(objectId)
this.Data["json"] = "delete success!" o.Data["json"] = "delete success!"
this.ServeJson() o.ServeJson()
} }

View File

@ -14,7 +14,7 @@ type MainController struct {
beego.Controller beego.Controller
} }
func (this *MainController) Get() { func (m *MainController) Get() {
this.Data["host"] = this.Ctx.Request.Host m.Data["host"] = m.Ctx.Request.Host
this.TplNames = "index.tpl" m.TplNames = "index.tpl"
} }

View File

@ -150,14 +150,14 @@ type WSController struct {
} }
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
ReadBufferSize: 1024, ReadBufferSize: 1024,
WriteBufferSize: 1024, WriteBufferSize: 1024,
} }
func (this *WSController) Get() { func (w *WSController) Get() {
ws, err := upgrader.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request,nil) ws, err := upgrader.Upgrade(w.Ctx.ResponseWriter, w.Ctx.Request, nil)
if _, ok := err.(websocket.HandshakeError); ok { if _, ok := err.(websocket.HandshakeError); ok {
http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) http.Error(w.Ctx.ResponseWriter, "Not a websocket handshake", 400)
return return
} else if err != nil { } else if err != nil {
return return

View File

@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>Chat Example</title> <title>Chat Example</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {

View File

@ -14,6 +14,11 @@
package beego package beego
import "github.com/astaxie/beego/context"
// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)
// FilterRouter defines filter operation before controller handler execution. // FilterRouter defines filter operation before controller handler execution.
// it can match patterned url and do filter function when action arrives. // it can match patterned url and do filter function when action arrives.
type FilterRouter struct { type FilterRouter struct {

View File

@ -25,12 +25,12 @@ type TestFlashController struct {
Controller Controller
} }
func (this *TestFlashController) TestWriteFlash() { func (t *TestFlashController) TestWriteFlash() {
flash := NewFlash() flash := NewFlash()
flash.Notice("TestFlashString") flash.Notice("TestFlashString")
flash.Store(&this.Controller) flash.Store(&t.Controller)
// we choose to serve json because we don't want to load a template html file // we choose to serve json because we don't want to load a template html file
this.ServeJson(true) t.ServeJson(true)
} }
func TestFlashHeader(t *testing.T) { func TestFlashHeader(t *testing.T) {

View File

@ -37,6 +37,7 @@ import (
"encoding/xml" "encoding/xml"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"mime/multipart" "mime/multipart"
"net" "net"
"net/http" "net/http"
@ -252,6 +253,59 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest {
return b return b
} }
func (b *BeegoHttpRequest) buildUrl(paramBody string) {
// build GET url with query string
if b.req.Method == "GET" && len(paramBody) > 0 {
if strings.Index(b.url, "?") != -1 {
b.url += "&" + paramBody
} else {
b.url = b.url + "?" + paramBody
}
return
}
// build POST url and body
if b.req.Method == "POST" && b.req.Body == nil {
// with files
if len(b.files) > 0 {
pr, pw := io.Pipe()
bodyWriter := multipart.NewWriter(pw)
go func() {
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
log.Fatal(err)
}
fh, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
log.Fatal(err)
}
}
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
bodyWriter.Close()
pw.Close()
}()
b.Header("Content-Type", bodyWriter.FormDataContentType())
b.req.Body = ioutil.NopCloser(pr)
return
}
// with params
if len(paramBody) > 0 {
b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody)
}
}
}
func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
if b.resp.StatusCode != 0 { if b.resp.StatusCode != 0 {
return b.resp, nil return b.resp, nil
@ -269,46 +323,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
paramBody = paramBody[0 : len(paramBody)-1] paramBody = paramBody[0 : len(paramBody)-1]
} }
if b.req.Method == "GET" && len(paramBody) > 0 { b.buildUrl(paramBody)
if strings.Index(b.url, "?") != -1 {
b.url += "&" + paramBody
} else {
b.url = b.url + "?" + paramBody
}
} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
if len(b.files) > 0 {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for formname, filename := range b.files {
fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
if err != nil {
return nil, err
}
fh, err := os.Open(filename)
if err != nil {
return nil, err
}
//iocopy
_, err = io.Copy(fileWriter, fh)
fh.Close()
if err != nil {
return nil, err
}
}
for k, v := range b.params {
bodyWriter.WriteField(k, v)
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
b.Header("Content-Type", contentType)
b.req.Body = ioutil.NopCloser(bodyBuf)
b.req.ContentLength = int64(bodyBuf.Len())
} else {
b.Header("Content-Type", "application/x-www-form-urlencoded")
b.Body(paramBody)
}
}
url, err := url.Parse(b.url) url, err := url.Parse(b.url)
if err != nil { if err != nil {
return nil, err return nil, err
@ -340,14 +355,12 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) {
} }
} }
var jar http.CookieJar var jar http.CookieJar = nil
if b.setting.EnableCookie { if b.setting.EnableCookie {
if defaultCookieJar == nil { if defaultCookieJar == nil {
createDefaultCookie() createDefaultCookie()
} }
jar = defaultCookieJar jar = defaultCookieJar
} else {
jar = nil
} }
client := &http.Client{ client := &http.Client{
@ -400,12 +413,11 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) {
return nil, nil return nil, nil
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body) b.body, err = ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
b.body = data return b.body, nil
return data, nil
} }
// ToFile saves the body data in response to one file. // ToFile saves the body data in response to one file.
@ -436,8 +448,7 @@ func (b *BeegoHttpRequest) ToJson(v interface{}) error {
if err != nil { if err != nil {
return err return err
} }
err = json.Unmarshal(data, v) return json.Unmarshal(data, v)
return err
} }
// ToXml returns the map that marshals from the body bytes as xml in response . // ToXml returns the map that marshals from the body bytes as xml in response .
@ -447,8 +458,7 @@ func (b *BeegoHttpRequest) ToXml(v interface{}) error {
if err != nil { if err != nil {
return err return err
} }
err = xml.Unmarshal(data, v) return xml.Unmarshal(data, v)
return err
} }
// Response executes request client gets response mannually. // Response executes request client gets response mannually.

View File

@ -66,23 +66,24 @@ func TestSimplePost(t *testing.T) {
} }
} }
func TestPostFile(t *testing.T) { //func TestPostFile(t *testing.T) {
v := "smallfish" // v := "smallfish"
req := Post("http://httpbin.org/post") // req := Post("http://httpbin.org/post")
req.Param("username", v) // req.Debug(true)
req.PostFile("uploadfile", "httplib_test.go") // req.Param("username", v)
// req.PostFile("uploadfile", "httplib_test.go")
str, err := req.String() // str, err := req.String()
if err != nil { // if err != nil {
t.Fatal(err) // t.Fatal(err)
} // }
t.Log(str) // t.Log(str)
n := strings.Index(str, v) // n := strings.Index(str, v)
if n == -1 { // if n == -1 {
t.Fatal(v + " not found in post") // t.Fatal(v + " not found in post")
} // }
} //}
func TestSimplePut(t *testing.T) { func TestSimplePut(t *testing.T) {
str, err := Put("http://httpbin.org/put").String() str, err := Put("http://httpbin.org/put").String()

View File

@ -43,11 +43,7 @@ func NewConn() LoggerInterface {
// init connection writer with json config. // init connection writer with json config.
// json config only need key "level". // json config only need key "level".
func (c *ConnWriter) Init(jsonconfig string) error { func (c *ConnWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), c) return json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
} }
// write message in connection. // write message in connection.
@ -77,10 +73,9 @@ func (c *ConnWriter) Flush() {
// destroy connection writer and close tcp listener. // destroy connection writer and close tcp listener.
func (c *ConnWriter) Destroy() { func (c *ConnWriter) Destroy() {
if c.innerWriter == nil { if c.innerWriter != nil {
return c.innerWriter.Close()
} }
c.innerWriter.Close()
} }
func (c *ConnWriter) connect() error { func (c *ConnWriter) connect() error {

View File

@ -50,9 +50,10 @@ type ConsoleWriter struct {
// create ConsoleWriter returning as LoggerInterface. // create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface { func NewConsole() LoggerInterface {
cw := new(ConsoleWriter) cw := &ConsoleWriter{
cw.lg = log.New(os.Stdout, "", log.Ldate|log.Ltime) lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
cw.Level = LevelDebug Level: LevelDebug,
}
return cw return cw
} }
@ -62,11 +63,7 @@ func (c *ConsoleWriter) Init(jsonconfig string) error {
if len(jsonconfig) == 0 { if len(jsonconfig) == 0 {
return nil return nil
} }
err := json.Unmarshal([]byte(jsonconfig), c) return json.Unmarshal([]byte(jsonconfig), c)
if err != nil {
return err
}
return nil
} }
// write message in console. // write message in console.
@ -76,9 +73,10 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
} }
if goos := runtime.GOOS; goos == "windows" { if goos := runtime.GOOS; goos == "windows" {
c.lg.Println(msg) c.lg.Println(msg)
} else { return nil
c.lg.Println(colors[level](msg))
} }
c.lg.Println(colors[level](msg))
return nil return nil
} }

View File

@ -15,10 +15,11 @@
package logs package logs
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -122,11 +123,7 @@ func (w *FileLogWriter) startLogger() error {
return err return err
} }
w.mw.SetFd(fd) w.mw.SetFd(fd)
err = w.initFd() return w.initFd()
if err != nil {
return err
}
return nil
} }
func (w *FileLogWriter) docheck(size int) { func (w *FileLogWriter) docheck(size int) {
@ -169,18 +166,44 @@ func (w *FileLogWriter) initFd() error {
} }
w.maxsize_cursize = int(finfo.Size()) w.maxsize_cursize = int(finfo.Size())
w.daily_opendate = time.Now().Day() w.daily_opendate = time.Now().Day()
w.maxlines_curlines = 0
if finfo.Size() > 0 { if finfo.Size() > 0 {
content, err := ioutil.ReadFile(w.Filename) count, err := w.lines()
if err != nil { if err != nil {
return err return err
} }
w.maxlines_curlines = len(strings.Split(string(content), "\n")) w.maxlines_curlines = count
} else {
w.maxlines_curlines = 0
} }
return nil return nil
} }
func (w *FileLogWriter) lines() (int, error) {
fd, err := os.Open(w.Filename)
if err != nil {
return 0, err
}
defer fd.Close()
buf := make([]byte, 32768) // 32k
count := 0
lineSep := []byte{'\n'}
for {
c, err := fd.Read(buf)
if err != nil && err != io.EOF {
return count, err
}
count += bytes.Count(buf[:c], lineSep)
if err == io.EOF {
break
}
}
return count, nil
}
// DoRotate means it need to write file in new file. // 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.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error { func (w *FileLogWriter) DoRotate() error {

View File

@ -155,6 +155,9 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
lm.level = loglevel lm.level = loglevel
if bl.enableFuncCallDepth { if bl.enableFuncCallDepth {
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) {
_, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1)
}
if ok { if ok {
_, filename := path.Split(file) _, filename := path.Split(file)
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
@ -289,9 +292,9 @@ func (bl *BeeLogger) Close() {
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err) fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
} }
} }
} else { continue
break
} }
break
} }
for _, l := range bl.outputs { for _, l := range bl.outputs {
l.Flush() l.Flush()

View File

@ -25,7 +25,8 @@ import (
) )
const ( const (
subjectPhrase = "Diagnostic message from server" // no usage
// subjectPhrase = "Diagnostic message from server"
) )
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server. // smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
@ -146,9 +147,7 @@ func (s *SmtpWriter) WriteMsg(msg string, level int) error {
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress + mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg) ">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
err := s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg) return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
return err
} }
// implementing method. empty. // implementing method. empty.

View File

@ -1,49 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package middleware
import "fmt"
// http exceptions
type HTTPException struct {
StatusCode int // http status code 4xx, 5xx
Description string
}
// return http exception error string, e.g. "400 Bad Request".
func (e *HTTPException) Error() string {
return fmt.Sprintf("%d %s", e.StatusCode, e.Description)
}
// map of http exceptions for each http status code int.
// defined 400,401,403,404,405,500,502,503 and 504 default.
var HTTPExceptionMaps map[int]HTTPException
func init() {
HTTPExceptionMaps = make(map[int]HTTPException)
// Normal 4XX HTTP Status
HTTPExceptionMaps[400] = HTTPException{400, "Bad Request"}
HTTPExceptionMaps[401] = HTTPException{401, "Unauthorized"}
HTTPExceptionMaps[403] = HTTPException{403, "Forbidden"}
HTTPExceptionMaps[404] = HTTPException{404, "Not Found"}
HTTPExceptionMaps[405] = HTTPException{405, "Method Not Allowed"}
// Normal 5XX HTTP Status
HTTPExceptionMaps[500] = HTTPException{500, "Internal Server Error"}
HTTPExceptionMaps[502] = HTTPException{502, "Bad Gateway"}
HTTPExceptionMaps[503] = HTTPException{503, "Service Unavailable"}
HTTPExceptionMaps[504] = HTTPException{504, "Gateway Timeout"}
}

View File

@ -34,7 +34,6 @@ type Translation struct {
} }
func NewLocale(filepath string, defaultlocal string) *Translation { func NewLocale(filepath string, defaultlocal string) *Translation {
i18n := make(map[string]map[string]string)
file, err := os.Open(filepath) file, err := os.Open(filepath)
if err != nil { if err != nil {
panic("open " + filepath + " err :" + err.Error()) panic("open " + filepath + " err :" + err.Error())
@ -43,8 +42,9 @@ func NewLocale(filepath string, defaultlocal string) *Translation {
if err != nil { if err != nil {
panic("read " + filepath + " err :" + err.Error()) panic("read " + filepath + " err :" + err.Error())
} }
err = json.Unmarshal(data, &i18n)
if err != nil { i18n := make(map[string]map[string]string)
if err = json.Unmarshal(data, &i18n); err != nil {
panic("json.Unmarshal " + filepath + " err :" + err.Error()) panic("json.Unmarshal " + filepath + " err :" + err.Error())
} }
return &Translation{ return &Translation{

46
migration/ddl.go Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package migration
type Table struct {
TableName string
Columns []*Column
}
func (t *Table) Create() string {
return ""
}
func (t *Table) Drop() string {
return ""
}
type Column struct {
Name string
Type string
Default interface{}
}
func Create(tbname string, columns ...Column) string {
return ""
}
func Drop(tbname string, columns ...Column) string {
return ""
}
func TableDDL(tbname string, columns ...Column) string {
return ""
}

View File

@ -19,7 +19,6 @@ import (
"strings" "strings"
beecontext "github.com/astaxie/beego/context" beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
) )
type namespaceCond func(*beecontext.Context) bool type namespaceCond func(*beecontext.Context) bool
@ -57,7 +56,7 @@ func NewNamespace(prefix string, params ...innnerNamespace) *Namespace {
func (n *Namespace) Cond(cond namespaceCond) *Namespace { func (n *Namespace) Cond(cond namespaceCond) *Namespace {
fn := func(ctx *beecontext.Context) { fn := func(ctx *beecontext.Context) {
if !cond(ctx) { if !cond(ctx) {
middleware.Exception("405", ctx.ResponseWriter, ctx.Request, "Method not allowed") exception("405", ctx)
} }
} }
if v, ok := n.handlers.filters[BeforeRouter]; ok { if v, ok := n.handlers.filters[BeforeRouter]; ok {
@ -217,7 +216,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace {
n.handlers.routers[k] = t n.handlers.routers[k] = t
} }
} }
if n.handlers.enableFilter { if ni.handlers.enableFilter {
for pos, filterList := range ni.handlers.filters { for pos, filterList := range ni.handlers.filters {
for _, mr := range filterList { for _, mr := range filterList {
t := NewTree() t := NewTree()

View File

@ -117,7 +117,7 @@ o.Begin()
... ...
user := User{Name: "slene"} user := User{Name: "slene"}
id, err := o.Insert(&user) id, err := o.Insert(&user)
if err != nil { if err == nil {
o.Commit() o.Commit()
} else { } else {
o.Rollback() o.Rollback()

View File

@ -104,7 +104,11 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string {
typ += " " + "NOT NULL" typ += " " + "NOT NULL"
} }
return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s", Q, fi.mi.table, Q, Q, fi.column, Q, typ) return fmt.Sprintf("ALTER TABLE %s%s%s ADD COLUMN %s%s%s %s %s",
Q, fi.mi.table, Q,
Q, fi.column, Q,
typ, getColumnDefault(fi),
)
} }
// create database creation string. // create database creation string.
@ -155,6 +159,9 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
//if fi.initial.String() != "" { //if fi.initial.String() != "" {
// column += " DEFAULT " + fi.initial.String() // column += " DEFAULT " + fi.initial.String()
//} //}
// Append attribute DEFAULT
column += getColumnDefault(fi)
if fi.unique { if fi.unique {
column += " " + "UNIQUE" column += " " + "UNIQUE"
@ -239,3 +246,44 @@ func getDbCreateSql(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
return return
} }
// Get string value for the attribute "DEFAULT" for the CREATE, ALTER commands
func getColumnDefault(fi *fieldInfo) string {
var (
v, t, d string
)
// Skip default attribute if field is in relations
if fi.rel || fi.reverse {
return v
}
t = " DEFAULT '%s' "
// These defaults will be useful if there no config value orm:"default" and NOT NULL is on
switch fi.fieldType {
case TypeDateField, TypeDateTimeField:
return v;
case TypeBooleanField, TypeBitField, TypeSmallIntegerField, TypeIntegerField,
TypeBigIntegerField, TypePositiveBitField, TypePositiveSmallIntegerField,
TypePositiveIntegerField, TypePositiveBigIntegerField, TypeFloatField,
TypeDecimalField:
d = "0"
}
if fi.colDefault {
if !fi.initial.Exist() {
v = fmt.Sprintf(t, "")
} else {
v = fmt.Sprintf(t, fi.initial.String())
}
} else {
if !fi.null {
v = fmt.Sprintf(t, d)
}
}
return v
}

View File

@ -116,6 +116,7 @@ type fieldInfo struct {
null bool null bool
index bool index bool
unique bool unique bool
colDefault bool
initial StrTo initial StrTo
size int size int
auto_now bool auto_now bool
@ -280,6 +281,11 @@ checkType:
fi.pk = attrs["pk"] fi.pk = attrs["pk"]
fi.unique = attrs["unique"] fi.unique = attrs["unique"]
// Mark object property if there is attribute "default" in the orm configuration
if _, ok := tags["default"]; ok {
fi.colDefault = true
}
switch fieldType { switch fieldType {
case RelManyToMany, RelReverseMany, RelReverseOne: case RelManyToMany, RelReverseMany, RelReverseOne:
fi.null = false fi.null = false

View File

@ -489,10 +489,6 @@ func (o *orm) Driver() Driver {
return driver(o.alias.Name) return driver(o.alias.Name)
} }
func (o *orm) GetDB() dbQuerier {
panic(ErrNotImplement)
}
// create new orm // create new orm
func NewOrm() Ormer { func NewOrm() Ormer {
BootStrap() // execute only once BootStrap() // execute only once

View File

@ -51,7 +51,6 @@ type Ormer interface {
Rollback() error Rollback() error
Raw(string, ...interface{}) RawSeter Raw(string, ...interface{}) RawSeter
Driver() Driver Driver() Driver
GetDB() dbQuerier
} }
// insert prepared statement // insert prepared statement

View File

@ -42,20 +42,25 @@ func init() {
var ( var (
lastupdateFilename string = "lastupdate.tmp" lastupdateFilename string = "lastupdate.tmp"
commentFilename string
pkgLastupdate map[string]int64 pkgLastupdate map[string]int64
genInfoList map[string][]ControllerComments genInfoList map[string][]ControllerComments
) )
const COMMENTFL = "commentsRouter_"
func init() { func init() {
pkgLastupdate = make(map[string]int64) pkgLastupdate = make(map[string]int64)
genInfoList = make(map[string][]ControllerComments)
} }
func parserPkg(pkgRealpath, pkgpath string) error { func parserPkg(pkgRealpath, pkgpath string) error {
rep := strings.NewReplacer("/", "_", ".", "_")
commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go"
if !compareFile(pkgRealpath) { if !compareFile(pkgRealpath) {
Info(pkgRealpath + " don't has updated") Info(pkgRealpath + " has not changed, not reloading")
return nil return nil
} }
genInfoList = make(map[string][]ControllerComments)
fileSet := token.NewFileSet() fileSet := token.NewFileSet()
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool { astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
name := info.Name() name := info.Name()
@ -155,7 +160,7 @@ func genRouterCode() {
} }
} }
if globalinfo != "" { if globalinfo != "" {
f, err := os.Create(path.Join(workPath, "routers", "commentsRouter.go")) f, err := os.Create(path.Join(workPath, "routers", commentFilename))
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -165,7 +170,7 @@ func genRouterCode() {
} }
func compareFile(pkgRealpath string) bool { func compareFile(pkgRealpath string) bool {
if !utils.FileExists(path.Join(workPath, "routers", "commentsRouter.go")) { if !utils.FileExists(path.Join(workPath, "routers", commentFilename)) {
return true return true
} }
if utils.FileExists(path.Join(workPath, lastupdateFilename)) { if utils.FileExists(path.Join(workPath, lastupdateFilename)) {

View File

@ -217,6 +217,7 @@ func Allow(opts *Options) beego.FilterFunc {
ctx.Output.Header(key, value) ctx.Output.Header(key, value)
} }
ctx.Output.SetStatus(http.StatusOK) ctx.Output.SetStatus(http.StatusOK)
ctx.WriteString("")
return return
} }
headers = opts.Header(origin) headers = opts.Header(origin)

167
router.go
View File

@ -30,7 +30,6 @@ import (
"time" "time"
beecontext "github.com/astaxie/beego/context" beecontext "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/toolbox"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
@ -72,11 +71,31 @@ var (
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
"GetControllerAndAction"} "GetControllerAndAction"}
url_placeholder = "{{placeholder}}" url_placeholder = "{{placeholder}}"
DefaultLogFilter FilterHandler = &logFilter{}
FilterRouterLog func(*beecontext.Context) bool
) )
type FilterHandler interface {
Filter(*beecontext.Context) bool
}
// default log filter static file will not show
type logFilter struct {
}
func (l *logFilter) Filter(ctx *beecontext.Context) bool {
requestPath := path.Clean(ctx.Input.Request.URL.Path)
if requestPath == "/favicon.ico" || requestPath == "/robots.txt" {
return true
}
for prefix, _ := range StaticDir {
if strings.HasPrefix(requestPath, prefix) {
return true
}
}
return false
}
// To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter // 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)
@ -133,7 +152,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM
if val := reflectVal.MethodByName(colon[1]); val.IsValid() { if val := reflectVal.MethodByName(colon[1]); val.IsValid() {
methods[strings.ToUpper(m)] = colon[1] methods[strings.ToUpper(m)] = colon[1]
} else { } else {
panic(colon[1] + " method doesn't exist in the controller " + t.Name()) panic("'" + colon[1] + "' method doesn't exist in the controller " + t.Name())
} }
} else { } else {
panic(v + " is an invalid method mapping. Method doesn't exist " + m) panic(v + " is an invalid method mapping. Method doesn't exist " + m)
@ -409,7 +428,7 @@ func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error
// 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 ...string) string { func (p *ControllerRegistor) 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")
@ -424,16 +443,16 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
key := "" key := ""
for k, v := range values { for k, v := range values {
if k%2 == 0 { if k%2 == 0 {
key = v key = fmt.Sprint(v)
} else { } else {
params[key] = v params[key] = fmt.Sprint(v)
} }
} }
} }
controllName := strings.Join(paths[:len(paths)-1], "/") controllName := strings.Join(paths[:len(paths)-1], "/")
methodName := paths[len(paths)-1] methodName := paths[len(paths)-1]
for _, t := range p.routers { for m, t := range p.routers {
ok, url := p.geturl(t, "/", controllName, methodName, params) ok, url := p.geturl(t, "/", controllName, methodName, params, m)
if ok { if ok {
return url return url
} }
@ -441,17 +460,17 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...string) string {
return "" return ""
} }
func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string) (bool, string) { func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) {
for k, subtree := range t.fixrouters { 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) ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod)
if ok { if ok {
return ok, u return ok, u
} }
} }
if t.wildcard != nil { if t.wildcard != nil {
url = path.Join(url, url_placeholder) u := path.Join(url, url_placeholder)
ok, u := p.geturl(t.wildcard, url, controllName, methodName, params) ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod)
if ok { if ok {
return ok, u return ok, u
} }
@ -471,8 +490,8 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin
} }
} }
if !find { if !find {
for _, md := range c.methods { for m, md := range c.methods {
if md == methodName { if (m == "*" || m == httpMethod) && md == methodName {
find = true find = true
} }
} }
@ -557,7 +576,6 @@ 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 *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
defer p.recoverPanic(rw, r)
starttime := time.Now() starttime := time.Now()
var runrouter reflect.Type var runrouter reflect.Type
var findrouter bool var findrouter bool
@ -580,6 +598,8 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
context.Output.Context = context context.Output.Context = context
context.Output.EnableGzip = EnableGzip context.Output.EnableGzip = EnableGzip
defer p.recoverPanic(context)
var urlPath string var urlPath string
if !RouterCaseSensitive { if !RouterCaseSensitive {
urlPath = strings.ToLower(r.URL.Path) urlPath = strings.ToLower(r.URL.Path)
@ -624,7 +644,13 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
// session init // session init
if SessionOn { if SessionOn {
context.Input.CruSession = GlobalSessions.SessionStart(w, r) var err error
context.Input.CruSession, err = GlobalSessions.SessionStart(w, r)
if err != nil {
Error(err)
exception("503", context)
return
}
defer func() { defer func() {
context.Input.CruSession.SessionRelease(w) context.Input.CruSession.SessionRelease(w)
}() }()
@ -677,7 +703,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
//if no matches to url, throw a not found exception //if no matches to url, throw a not found exception
if !findrouter { if !findrouter {
middleware.Exception("404", rw, r, "") exception("404", context)
goto Admin goto Admin
} }
@ -693,7 +719,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
isRunable = true isRunable = true
routerInfo.runfunction(context) routerInfo.runfunction(context)
} else { } else {
middleware.Exception("405", rw, r, "Method Not Allowed") exception("405", context)
goto Admin goto Admin
} }
} else if routerInfo.routerType == routerTypeHandler { } else if routerInfo.routerType == routerTypeHandler {
@ -804,7 +830,7 @@ Admin:
} }
} }
if RunMode == "dev" { if RunMode == "dev" || AccessLogs {
var devinfo string var devinfo string
if findrouter { if findrouter {
if routerInfo != nil { if routerInfo != nil {
@ -815,7 +841,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 FilterRouterLog == nil || !FilterRouterLog(context) { if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) {
Debug(devinfo) Debug(devinfo)
} }
} }
@ -826,26 +852,51 @@ Admin:
} }
} }
func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Request) { func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) {
if err := recover(); err != nil { if err := recover(); err != nil {
if err == USERSTOPRUN { if err == USERSTOPRUN {
return return
} }
if _, ok := err.(middleware.HTTPException); ok { if RunMode == "dev" {
// catch intented errors, only for HTTP 4XX and 5XX if !RecoverPanic {
} else { panic(err)
if RunMode == "dev" { } else {
if !RecoverPanic { if ErrorsShow {
panic(err) if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok {
} else { executeError(handler, context)
if ErrorsShow { return
if handler, ok := middleware.ErrorMaps[fmt.Sprint(err)]; ok {
handler(rw, r)
return
}
} }
var stack string }
Critical("the request url is ", r.URL.Path) var stack string
Critical("the request url is ", context.Input.Url())
Critical("Handler crashed with error", err)
for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
}
showErr(err, context, stack)
}
} else {
if !RecoverPanic {
panic(err)
} else {
// in production model show all infomation
if ErrorsShow {
if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok {
executeError(handler, context)
return
} else if handler, ok := ErrorMaps["503"]; ok {
executeError(handler, context)
return
} else {
context.WriteString(fmt.Sprint(err))
}
} else {
Critical("the request url is ", context.Input.Url())
Critical("Handler crashed with error", err) Critical("Handler crashed with error", err)
for i := 1; ; i++ { for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i) _, file, line, ok := runtime.Caller(i)
@ -853,55 +904,13 @@ func (p *ControllerRegistor) recoverPanic(rw http.ResponseWriter, r *http.Reques
break break
} }
Critical(fmt.Sprintf("%s:%d", file, line)) Critical(fmt.Sprintf("%s:%d", file, line))
stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
}
middleware.ShowErr(err, rw, r, stack)
}
} else {
if !RecoverPanic {
panic(err)
} else {
// in production model show all infomation
if ErrorsShow {
handler := p.getErrorHandler(fmt.Sprint(err))
handler(rw, r)
return
} else {
Critical("the request url is ", r.URL.Path)
Critical("Handler crashed with error", err)
for i := 1; ; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
Critical(fmt.Sprintf("%s:%d", file, line))
}
} }
} }
} }
} }
} }
} }
// there always should be error handler that sets error code accordingly for all unhandled errors.
// in order to have custom UI for error page it's necessary to override "500" error.
func (p *ControllerRegistor) getErrorHandler(errorCode string) func(rw http.ResponseWriter, r *http.Request) {
handler := middleware.SimpleServerError
ok := true
if errorCode != "" {
handler, ok = middleware.ErrorMaps[errorCode]
if !ok {
handler, ok = middleware.ErrorMaps["500"]
}
if !ok || handler == nil {
handler = middleware.SimpleServerError
}
}
return handler
}
//responseWriter is a wrapper for the http.ResponseWriter //responseWriter is a wrapper for the http.ResponseWriter
//started set to true if response was written to then don't execute other handler //started set to true if response was written to then don't execute other handler
type responseWriter struct { type responseWriter struct {

View File

@ -27,33 +27,33 @@ type TestController struct {
Controller Controller
} }
func (this *TestController) Get() { func (tc *TestController) Get() {
this.Data["Username"] = "astaxie" tc.Data["Username"] = "astaxie"
this.Ctx.Output.Body([]byte("ok")) tc.Ctx.Output.Body([]byte("ok"))
} }
func (this *TestController) Post() { func (tc *TestController) Post() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name"))) tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
} }
func (this *TestController) Param() { func (tc *TestController) Param() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Query(":name"))) tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
} }
func (this *TestController) List() { func (tc *TestController) List() {
this.Ctx.Output.Body([]byte("i am list")) tc.Ctx.Output.Body([]byte("i am list"))
} }
func (this *TestController) Params() { func (tc *TestController) Params() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Params["0"] + this.Ctx.Input.Params["1"] + this.Ctx.Input.Params["2"])) tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Params["0"] + tc.Ctx.Input.Params["1"] + tc.Ctx.Input.Params["2"]))
} }
func (this *TestController) Myext() { func (tc *TestController) Myext() {
this.Ctx.Output.Body([]byte(this.Ctx.Input.Param(":ext"))) tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
} }
func (this *TestController) GetUrl() { func (tc *TestController) GetUrl() {
this.Ctx.Output.Body([]byte(this.UrlFor(".Myext"))) tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext")))
} }
func (t *TestController) GetParams() { func (t *TestController) GetParams() {

View File

@ -0,0 +1,180 @@
package session
import (
"net/http"
"strconv"
"strings"
"sync"
"github.com/astaxie/beego/session"
"github.com/siddontang/ledisdb/config"
"github.com/siddontang/ledisdb/ledis"
)
var ledispder = &LedisProvider{}
var c *ledis.DB
// ledis session store
type LedisSessionStore struct {
sid string
lock sync.RWMutex
values map[interface{}]interface{}
maxlifetime int64
}
// set value in ledis session
func (ls *LedisSessionStore) Set(key, value interface{}) error {
ls.lock.Lock()
defer ls.lock.Unlock()
ls.values[key] = value
return nil
}
// get value in ledis session
func (ls *LedisSessionStore) Get(key interface{}) interface{} {
ls.lock.RLock()
defer ls.lock.RUnlock()
if v, ok := ls.values[key]; ok {
return v
} else {
return nil
}
}
// delete value in ledis session
func (ls *LedisSessionStore) Delete(key interface{}) error {
ls.lock.Lock()
defer ls.lock.Unlock()
delete(ls.values, key)
return nil
}
// clear all values in ledis session
func (ls *LedisSessionStore) Flush() error {
ls.lock.Lock()
defer ls.lock.Unlock()
ls.values = make(map[interface{}]interface{})
return nil
}
// get ledis session id
func (ls *LedisSessionStore) SessionID() string {
return ls.sid
}
// save session values to ledis
func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) {
b, err := session.EncodeGob(ls.values)
if err != nil {
return
}
c.Set([]byte(ls.sid), b)
c.Expire([]byte(ls.sid), ls.maxlifetime)
}
// ledis session provider
type LedisProvider struct {
maxlifetime int64
savePath string
db int
}
// init ledis session
// savepath like ledis server saveDataPath,pool size
// e.g. 127.0.0.1:6379,100,astaxie
func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
var err error
lp.maxlifetime = maxlifetime
configs := strings.Split(savepath, ",")
if len(configs) == 1 {
lp.savePath = configs[0]
} else if len(configs) == 2 {
lp.savePath = configs[0]
lp.db, err = strconv.Atoi(configs[1])
if err != nil {
return err
}
}
cfg := new(config.Config)
cfg.DataDir = lp.savePath
nowLedis, err := ledis.Open(cfg)
c, err = nowLedis.Select(lp.db)
if err != nil {
println(err)
return nil
}
return nil
}
// read ledis session by sid
func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) {
kvs, err := c.Get([]byte(sid))
var kv map[interface{}]interface{}
if len(kvs) == 0 {
kv = make(map[interface{}]interface{})
} else {
kv, err = session.DecodeGob(kvs)
if err != nil {
return nil, err
}
}
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
return ls, nil
}
// check ledis session exist by sid
func (lp *LedisProvider) SessionExist(sid string) bool {
count, _ := c.Exists([]byte(sid))
if count == 0 {
return false
} else {
return true
}
}
// generate new sid for ledis session
func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) {
count, _ := c.Exists([]byte(sid))
if count == 0 {
// oldsid doesn't exists, set the new sid directly
// ignore error here, since if it return error
// the existed value will be 0
c.Set([]byte(sid), []byte(""))
c.Expire([]byte(sid), lp.maxlifetime)
} else {
data, _ := c.Get([]byte(oldsid))
c.Set([]byte(sid), data)
c.Expire([]byte(sid), lp.maxlifetime)
}
kvs, err := c.Get([]byte(sid))
var kv map[interface{}]interface{}
if len(kvs) == 0 {
kv = make(map[interface{}]interface{})
} else {
kv, err = session.DecodeGob([]byte(kvs))
if err != nil {
return nil, err
}
}
ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
return ls, nil
}
// delete ledis session by id
func (lp *LedisProvider) SessionDestroy(sid string) error {
c.Del([]byte(sid))
return nil
}
// Impelment method, no used.
func (lp *LedisProvider) SessionGC() {
return
}
// @todo
func (lp *LedisProvider) SessionAll() int {
return 0
}
func init() {
session.Register("ledis", ledispder)
}

View File

@ -118,12 +118,13 @@ type RedisProvider struct {
savePath string savePath string
poolsize int poolsize int
password string password string
dbNum int
poollist *redis.Pool poollist *redis.Pool
} }
// init redis session // init redis session
// savepath like redis server addr,pool size,password // savepath like redis server addr,pool size,password,dbnum
// e.g. 127.0.0.1:6379,100,astaxie // e.g. 127.0.0.1:6379,100,astaxie,0
func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
rp.maxlifetime = maxlifetime rp.maxlifetime = maxlifetime
configs := strings.Split(savePath, ",") configs := strings.Split(savePath, ",")
@ -143,6 +144,16 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
if len(configs) > 2 { if len(configs) > 2 {
rp.password = configs[2] rp.password = configs[2]
} }
if len(configs) > 3 {
dbnum, err := strconv.Atoi(configs[1])
if err != nil || dbnum < 0 {
rp.dbNum = 0
} else {
rp.dbNum = dbnum
}
} else {
rp.dbNum = 0
}
rp.poollist = redis.NewPool(func() (redis.Conn, error) { rp.poollist = redis.NewPool(func() (redis.Conn, error) {
c, err := redis.Dial("tcp", rp.savePath) c, err := redis.Dial("tcp", rp.savePath)
if err != nil { if err != nil {
@ -154,6 +165,11 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
return nil, err return nil, err
} }
} }
_, err = c.Do("SELECT", rp.dbNum)
if err != nil {
c.Close()
return nil, err
}
return c, err return c, err
}, rp.poolsize) }, rp.poolsize)

View File

@ -29,7 +29,10 @@ func TestCookie(t *testing.T) {
} }
r, _ := http.NewRequest("GET", "/", nil) r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
sess := globalSessions.SessionStart(w, r) sess, err := globalSessions.SessionStart(w, r)
if err != nil {
t.Fatal("set error,", err)
}
err = sess.Set("username", "astaxie") err = sess.Set("username", "astaxie")
if err != nil { if err != nil {
t.Fatal("set error,", err) t.Fatal("set error,", err)

View File

@ -26,9 +26,12 @@ func TestMem(t *testing.T) {
go globalSessions.GC() go globalSessions.GC()
r, _ := http.NewRequest("GET", "/", nil) r, _ := http.NewRequest("GET", "/", nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
sess := globalSessions.SessionStart(w, r) sess, err := globalSessions.SessionStart(w, r)
if err != nil {
t.Fatal("set error,", err)
}
defer sess.SessionRelease(w) defer sess.SessionRelease(w)
err := sess.Set("username", "astaxie") err = sess.Set("username", "astaxie")
if err != nil { if err != nil {
t.Fatal("set error,", err) t.Fatal("set error,", err)
} }

View File

@ -28,19 +28,13 @@
package session package session
import ( import (
"crypto/hmac"
"crypto/md5"
"crypto/rand" "crypto/rand"
"crypto/sha1"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
"github.com/astaxie/beego/utils"
) )
// SessionStore contains all data for one session process with specific id. // SessionStore contains all data for one session process with specific id.
@ -81,16 +75,15 @@ func Register(name string, provide Provider) {
} }
type managerConfig struct { type managerConfig struct {
CookieName string `json:"cookieName"` CookieName string `json:"cookieName"`
EnableSetCookie bool `json:"enableSetCookie,omitempty"` EnableSetCookie bool `json:"enableSetCookie,omitempty"`
Gclifetime int64 `json:"gclifetime"` Gclifetime int64 `json:"gclifetime"`
Maxlifetime int64 `json:"maxLifetime"` Maxlifetime int64 `json:"maxLifetime"`
Secure bool `json:"secure"` Secure bool `json:"secure"`
SessionIDHashFunc string `json:"sessionIDHashFunc"` CookieLifeTime int `json:"cookieLifeTime"`
SessionIDHashKey string `json:"sessionIDHashKey"` ProviderConfig string `json:"providerConfig"`
CookieLifeTime int `json:"cookieLifeTime"` Domain string `json:"domain"`
ProviderConfig string `json:"providerConfig"` SessionIdLength int64 `json:"sessionIdLength"`
Domain string `json:"domain"`
} }
// Manager contains Provider and its configuration. // Manager contains Provider and its configuration.
@ -129,11 +122,9 @@ func NewManager(provideName, config string) (*Manager, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if cf.SessionIDHashFunc == "" {
cf.SessionIDHashFunc = "sha1" if cf.SessionIdLength == 0 {
} cf.SessionIdLength = 16
if cf.SessionIDHashKey == "" {
cf.SessionIDHashKey = string(generateRandomKey(16))
} }
return &Manager{ return &Manager{
@ -144,11 +135,14 @@ func NewManager(provideName, config string) (*Manager, error) {
// Start session. generate or read the session id from http request. // Start session. generate or read the session id from http request.
// if session id exists, return SessionStore with this id. // if session id exists, return SessionStore with this id.
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) { func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore, err error) {
cookie, err := r.Cookie(manager.config.CookieName) cookie, errs := r.Cookie(manager.config.CookieName)
if err != nil || cookie.Value == "" { if errs != nil || cookie.Value == "" {
sid := manager.sessionId(r) sid, errs := manager.sessionId(r)
session, _ = manager.provider.SessionRead(sid) if errs != nil {
return nil, errs
}
session, err = manager.provider.SessionRead(sid)
cookie = &http.Cookie{Name: manager.config.CookieName, cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
@ -163,12 +157,18 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
} }
r.AddCookie(cookie) r.AddCookie(cookie)
} else { } else {
sid, _ := url.QueryUnescape(cookie.Value) sid, errs := url.QueryUnescape(cookie.Value)
if errs != nil {
return nil, errs
}
if manager.provider.SessionExist(sid) { if manager.provider.SessionExist(sid) {
session, _ = manager.provider.SessionRead(sid) session, err = manager.provider.SessionRead(sid)
} else { } else {
sid = manager.sessionId(r) sid, err = manager.sessionId(r)
session, _ = manager.provider.SessionRead(sid) if err != nil {
return nil, err
}
session, err = manager.provider.SessionRead(sid)
cookie = &http.Cookie{Name: manager.config.CookieName, cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
@ -219,7 +219,10 @@ func (manager *Manager) GC() {
// Regenerate a session id for this SessionStore who's id is saving in http request. // Regenerate a session id for this SessionStore who's id is saving in http request.
func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) { func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) {
sid := manager.sessionId(r) sid, err := manager.sessionId(r)
if err != nil {
return
}
cookie, err := r.Cookie(manager.config.CookieName) cookie, err := r.Cookie(manager.config.CookieName)
if err != nil && cookie.Value == "" { if err != nil && cookie.Value == "" {
//delete old cookie //delete old cookie
@ -251,36 +254,16 @@ func (manager *Manager) GetActiveSession() int {
return manager.provider.SessionAll() return manager.provider.SessionAll()
} }
// Set hash function for generating session id.
func (manager *Manager) SetHashFunc(hasfunc, hashkey string) {
manager.config.SessionIDHashFunc = hasfunc
manager.config.SessionIDHashKey = hashkey
}
// Set cookie with https. // Set cookie with https.
func (manager *Manager) SetSecure(secure bool) { func (manager *Manager) SetSecure(secure bool) {
manager.config.Secure = secure manager.config.Secure = secure
} }
// generate session id with rand string, unix nano time, remote addr by hash function. func (manager *Manager) sessionId(r *http.Request) (string, error) {
func (manager *Manager) sessionId(r *http.Request) (sid string) { b := make([]byte, manager.config.SessionIdLength)
bs := make([]byte, 32) n, err := rand.Read(b)
if n, err := io.ReadFull(rand.Reader, bs); n != 32 || err != nil { if n != len(b) || err != nil {
bs = utils.RandomCreateBytes(32) return "", fmt.Errorf("Could not successfully read from the system CSPRNG.")
} }
sig := fmt.Sprintf("%s%d%s", r.RemoteAddr, time.Now().UnixNano(), bs) return hex.EncodeToString(b), nil
if manager.config.SessionIDHashFunc == "md5" {
h := md5.New()
h.Write([]byte(sig))
sid = hex.EncodeToString(h.Sum(nil))
} else if manager.config.SessionIDHashFunc == "sha1" {
h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
fmt.Fprintf(h, "%s", sig)
sid = hex.EncodeToString(h.Sum(nil))
} else {
h := hmac.New(sha1.New, []byte(manager.config.SessionIDHashKey))
fmt.Fprintf(h, "%s", sig)
sid = hex.EncodeToString(h.Sum(nil))
}
return
} }

View File

@ -22,7 +22,6 @@ import (
"strings" "strings"
"github.com/astaxie/beego/context" "github.com/astaxie/beego/context"
"github.com/astaxie/beego/middleware"
"github.com/astaxie/beego/utils" "github.com/astaxie/beego/utils"
) )
@ -67,7 +66,7 @@ func serverStaticRouter(ctx *context.Context) {
//if the request is dir and DirectoryIndex is false then //if the request is dir and DirectoryIndex is false then
if finfo.IsDir() { if finfo.IsDir() {
if !DirectoryIndex { if !DirectoryIndex {
middleware.Exception("403", ctx.ResponseWriter, ctx.Request, "403 Forbidden") exception("403", ctx)
return return
} else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' { } else if ctx.Input.Request.URL.Path[len(ctx.Input.Request.URL.Path)-1] != '/' {
http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302) http.Redirect(ctx.ResponseWriter, ctx.Request, ctx.Input.Request.URL.Path+"/", 302)

View File

@ -42,6 +42,9 @@ func init() {
beegoTplFuncMap["dateformat"] = DateFormat beegoTplFuncMap["dateformat"] = DateFormat
beegoTplFuncMap["date"] = Date beegoTplFuncMap["date"] = Date
beegoTplFuncMap["compare"] = Compare beegoTplFuncMap["compare"] = Compare
beegoTplFuncMap["compare_not"] = CompareNot
beegoTplFuncMap["not_nil"] = NotNil
beegoTplFuncMap["not_null"] = NotNil
beegoTplFuncMap["substr"] = Substr beegoTplFuncMap["substr"] = Substr
beegoTplFuncMap["html2str"] = Html2str beegoTplFuncMap["html2str"] = Html2str
beegoTplFuncMap["str2html"] = Str2html beegoTplFuncMap["str2html"] = Str2html

View File

@ -139,6 +139,14 @@ func Compare(a, b interface{}) (equal bool) {
return return
} }
func CompareNot(a, b interface{}) (equal bool) {
return ! Compare(a, b)
}
func NotNil(a interface{}) (is_nil bool) {
return CompareNot(a, nil)
}
func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) { func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) {
switch returnType { switch returnType {
case "String": case "String":
@ -246,7 +254,7 @@ 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 ...string) string { func UrlFor(endpoint string, values ...interface{}) string {
return BeeApp.Handlers.UrlFor(endpoint, values...) return BeeApp.Handlers.UrlFor(endpoint, values...)
} }
@ -302,6 +310,14 @@ func ParseForm(form url.Values, obj interface{}) error {
switch fieldT.Type.Kind() { switch fieldT.Type.Kind() {
case reflect.Bool: case reflect.Bool:
if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
fieldV.SetBool(true)
continue
}
if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
fieldV.SetBool(false)
continue
}
b, err := strconv.ParseBool(value) b, err := strconv.ParseBool(value)
if err != nil { if err != nil {
return err return err
@ -329,11 +345,45 @@ func ParseForm(form url.Values, obj interface{}) error {
fieldV.Set(reflect.ValueOf(value)) fieldV.Set(reflect.ValueOf(value))
case reflect.String: case reflect.String:
fieldV.SetString(value) fieldV.SetString(value)
case reflect.Struct:
switch fieldT.Type.String() {
case "time.Time":
format := time.RFC3339
if len(tags) > 1 {
format = tags[1]
}
t, err := time.Parse(format, value)
if err != nil {
return err
}
fieldV.Set(reflect.ValueOf(t))
}
case reflect.Slice:
if fieldT.Type == sliceOfInts {
formVals := form[tag]
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals)))
for i := 0; i < len(formVals); i++ {
val, err := strconv.Atoi(formVals[i])
if err != nil {
return err
}
fieldV.Index(i).SetInt(int64(val))
}
} else if fieldT.Type == sliceOfStrings {
formVals := form[tag]
fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals)))
for i := 0; i < len(formVals); i++ {
fieldV.Index(i).SetString(formVals[i])
}
}
} }
} }
return nil return nil
} }
var sliceOfInts = reflect.TypeOf([]int(nil))
var sliceOfStrings = reflect.TypeOf([]string(nil))
var unKind = map[reflect.Kind]bool{ var unKind = map[reflect.Kind]bool{
reflect.Uintptr: true, reflect.Uintptr: true,
reflect.Complex64: true, reflect.Complex64: true,

View File

@ -72,7 +72,7 @@ func TestDate(t *testing.T) {
} }
} }
func TestCompare(t *testing.T) { func TestCompareRelated(t *testing.T) {
if !Compare("abc", "abc") { if !Compare("abc", "abc") {
t.Error("should be equal") t.Error("should be equal")
} }
@ -82,6 +82,15 @@ func TestCompare(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") {
t.Error("should be equal")
}
if !CompareNot("abc", "aBc") {
t.Error("should be not equal")
}
if !NotNil("a string") {
t.Error("should not be nil")
}
} }
func TestHtmlquote(t *testing.T) { func TestHtmlquote(t *testing.T) {
@ -102,12 +111,14 @@ 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"`
Email string Email string
Intro string `form:",textarea"` Intro string `form:",textarea"`
StrBool bool `form:"strbool"`
Date time.Time `form:"date,2006-01-02"`
} }
u := user{} u := user{}
@ -119,6 +130,8 @@ func TestParseForm(t *testing.T) {
"age": []string{"40"}, "age": []string{"40"},
"Email": []string{"test@gmail.com"}, "Email": []string{"test@gmail.com"},
"Intro": []string{"I am an engineer!"}, "Intro": []string{"I am an engineer!"},
"strbool": []string{"yes"},
"date": []string{"2014-11-12"},
} }
if err := ParseForm(form, u); err == nil { if err := ParseForm(form, u); err == nil {
t.Fatal("nothing will be changed") t.Fatal("nothing will be changed")
@ -144,6 +157,13 @@ func TestParseForm(t *testing.T) {
if u.Intro != "I am an engineer!" { if u.Intro != "I am an engineer!" {
t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro) t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro)
} }
if u.StrBool != true {
t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool)
}
y, m, d := u.Date.Date()
if y != 2014 || m.String() != "November" || d != 12 {
t.Errorf("Date should equal `2014-11-12`, but got `%v`", u.Date.String())
}
} }
func TestRenderForm(t *testing.T) { func TestRenderForm(t *testing.T) {

View File

@ -84,6 +84,7 @@ type TaskFunc func() error
// task interface // task interface
type Tasker interface { type Tasker interface {
GetSpec() string
GetStatus() string GetStatus() string
Run() error Run() error
SetNext(time.Time) SetNext(time.Time)
@ -102,6 +103,7 @@ type taskerr struct {
type Task struct { type Task struct {
Taskname string Taskname string
Spec *Schedule Spec *Schedule
SpecStr string
DoFunc TaskFunc DoFunc TaskFunc
Prev time.Time Prev time.Time
Next time.Time Next time.Time
@ -116,16 +118,22 @@ func NewTask(tname string, spec string, f TaskFunc) *Task {
Taskname: tname, Taskname: tname,
DoFunc: f, DoFunc: f,
ErrLimit: 100, ErrLimit: 100,
SpecStr: spec,
} }
task.SetCron(spec) task.SetCron(spec)
return task return task
} }
//get spec string
func (s *Task) GetSpec() string {
return s.SpecStr
}
// get current task status // get current task status
func (tk *Task) GetStatus() string { func (tk *Task) GetStatus() string {
var str string var str string
for _, v := range tk.Errlist { for _, v := range tk.Errlist {
str += v.t.String() + ":" + v.errinfo + "\n" str += v.t.String() + ":" + v.errinfo + "<br>"
} }
return str return str
} }

View File

@ -394,6 +394,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
} }
return true, params return true, params
} }
if len(wildcardValues) <= j {
return false, nil
}
params[v] = wildcardValues[j] params[v] = wildcardValues[j]
j += 1 j += 1
} }
@ -419,6 +422,9 @@ func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string
// "/admin/" -> ["admin"] // "/admin/" -> ["admin"]
// "/admin/users" -> ["admin", "users"] // "/admin/users" -> ["admin", "users"]
func splitPath(key string) []string { func splitPath(key string) []string {
if key == "" {
return []string{}
}
elements := strings.Split(key, "/") elements := strings.Split(key, "/")
if elements[0] == "" { if elements[0] == "" {
elements = elements[1:] elements = elements[1:]

View File

@ -149,7 +149,11 @@ func TestAddTree2(t *testing.T) {
} }
func TestSplitPath(t *testing.T) { func TestSplitPath(t *testing.T) {
a := splitPath("/") a := splitPath("")
if len(a) != 0 {
t.Fatal("/ should retrun []")
}
a = splitPath("/")
if len(a) != 0 { if len(a) != 0 {
t.Fatal("/ should retrun []") t.Fatal("/ should retrun []")
} }

View File

@ -21,6 +21,6 @@ import (
// Instantiates a Paginator and assigns it to context.Input.Data["paginator"]. // Instantiates a Paginator and assigns it to context.Input.Data["paginator"].
func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) { func SetPaginator(context *context.Context, per int, nums int64) (paginator *Paginator) {
paginator = NewPaginator(context.Request, per, nums) paginator = NewPaginator(context.Request, per, nums)
context.Input.Data["paginator"] = paginator context.Input.Data["paginator"] = &paginator
return return
} }

View File

@ -114,7 +114,7 @@ func (p *Paginator) Pages() []int {
// Returns URL for a given page index. // Returns URL for a given page index.
func (p *Paginator) PageLink(page int) string { func (p *Paginator) PageLink(page int) string {
link, _ := url.ParseRequestURI(p.Request.RequestURI) link, _ := url.ParseRequestURI(p.Request.URL.String())
values := link.Query() values := link.Query()
if page == 1 { if page == 1 {
values.Del("p") values.Del("p")