mirror of
https://github.com/astaxie/beego.git
synced 2024-11-22 19:50:56 +00:00
Merge branch 'develop' into fix_response_http_code
This commit is contained in:
commit
af73a2d515
@ -1,9 +1,8 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.7.5
|
- "1.9.2"
|
||||||
- 1.8.5
|
- "1.10.2"
|
||||||
- 1.9.2
|
|
||||||
services:
|
services:
|
||||||
- redis-server
|
- redis-server
|
||||||
- mysql
|
- mysql
|
||||||
@ -22,7 +21,7 @@ install:
|
|||||||
- go get github.com/go-sql-driver/mysql
|
- go get github.com/go-sql-driver/mysql
|
||||||
- go get github.com/mattn/go-sqlite3
|
- go get github.com/mattn/go-sqlite3
|
||||||
- go get github.com/bradfitz/gomemcache/memcache
|
- go get github.com/bradfitz/gomemcache/memcache
|
||||||
- go get github.com/garyburd/redigo/redis
|
- go get github.com/gomodule/redigo/redis
|
||||||
- go get github.com/beego/x2j
|
- go get github.com/beego/x2j
|
||||||
- go get github.com/couchbase/go-couchbase
|
- go get github.com/couchbase/go-couchbase
|
||||||
- go get github.com/beego/goyaml2
|
- go get github.com/beego/goyaml2
|
||||||
@ -38,6 +37,7 @@ install:
|
|||||||
- go get -u github.com/mdempsky/unconvert
|
- go get -u github.com/mdempsky/unconvert
|
||||||
- go get -u github.com/gordonklaus/ineffassign
|
- go get -u github.com/gordonklaus/ineffassign
|
||||||
- go get -u github.com/golang/lint/golint
|
- go get -u github.com/golang/lint/golint
|
||||||
|
- go get -u github.com/go-redis/redis
|
||||||
before_script:
|
before_script:
|
||||||
- psql --version
|
- psql --version
|
||||||
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
|
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
|
||||||
|
6
app.go
6
app.go
@ -24,8 +24,8 @@ import (
|
|||||||
"net/http/fcgi"
|
"net/http/fcgi"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"time"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego/grace"
|
"github.com/astaxie/beego/grace"
|
||||||
"github.com/astaxie/beego/logs"
|
"github.com/astaxie/beego/logs"
|
||||||
@ -101,7 +101,7 @@ func (app *App) Run(mws ...MiddleWare) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.Server.Handler = app.Handlers
|
app.Server.Handler = app.Handlers
|
||||||
for i:=len(mws)-1;i>=0;i-- {
|
for i := len(mws) - 1; i >= 0; i-- {
|
||||||
if mws[i] == nil {
|
if mws[i] == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ func (app *App) Run(mws ...MiddleWare) {
|
|||||||
if BConfig.Listen.HTTPSPort != 0 {
|
if BConfig.Listen.HTTPSPort != 0 {
|
||||||
app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
|
app.Server.Addr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
|
||||||
} else if BConfig.Listen.EnableHTTP {
|
} else if BConfig.Listen.EnableHTTP {
|
||||||
BeeLogger.Info("Start https server error, confict with http.Please reset https port")
|
BeeLogger.Info("Start https server error, conflict with http.Please reset https port")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logs.Info("https server Running on https://%s", app.Server.Addr)
|
logs.Info("https server Running on https://%s", app.Server.Addr)
|
||||||
|
2
cache/README.md
vendored
2
cache/README.md
vendored
@ -52,7 +52,7 @@ Configure like this:
|
|||||||
|
|
||||||
## Redis adapter
|
## Redis adapter
|
||||||
|
|
||||||
Redis adapter use the [redigo](http://github.com/garyburd/redigo) client.
|
Redis adapter use the [redigo](http://github.com/gomodule/redigo) client.
|
||||||
|
|
||||||
Configure like this:
|
Configure like this:
|
||||||
|
|
||||||
|
13
cache/redis/redis.go
vendored
13
cache/redis/redis.go
vendored
@ -14,9 +14,9 @@
|
|||||||
|
|
||||||
// Package redis for cache provider
|
// Package redis for cache provider
|
||||||
//
|
//
|
||||||
// depend on github.com/garyburd/redigo/redis
|
// depend on github.com/gomodule/redigo/redis
|
||||||
//
|
//
|
||||||
// go install github.com/garyburd/redigo/redis
|
// go install github.com/gomodule/redigo/redis
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// import(
|
// import(
|
||||||
@ -36,7 +36,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/gomodule/redigo/redis"
|
||||||
|
|
||||||
"github.com/astaxie/beego/cache"
|
"github.com/astaxie/beego/cache"
|
||||||
)
|
)
|
||||||
@ -53,6 +53,7 @@ type Cache struct {
|
|||||||
dbNum int
|
dbNum int
|
||||||
key string
|
key string
|
||||||
password string
|
password string
|
||||||
|
maxIdle int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRedisCache create new redis cache with default collection name.
|
// NewRedisCache create new redis cache with default collection name.
|
||||||
@ -169,10 +170,14 @@ func (rc *Cache) StartAndGC(config string) error {
|
|||||||
if _, ok := cf["password"]; !ok {
|
if _, ok := cf["password"]; !ok {
|
||||||
cf["password"] = ""
|
cf["password"] = ""
|
||||||
}
|
}
|
||||||
|
if _, ok := cf["maxIdle"]; !ok {
|
||||||
|
cf["maxIdle"] = "3"
|
||||||
|
}
|
||||||
rc.key = cf["key"]
|
rc.key = cf["key"]
|
||||||
rc.conninfo = cf["conn"]
|
rc.conninfo = cf["conn"]
|
||||||
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
|
rc.dbNum, _ = strconv.Atoi(cf["dbNum"])
|
||||||
rc.password = cf["password"]
|
rc.password = cf["password"]
|
||||||
|
rc.maxIdle, _ = strconv.Atoi(cf["maxIdle"])
|
||||||
|
|
||||||
rc.connectInit()
|
rc.connectInit()
|
||||||
|
|
||||||
@ -206,7 +211,7 @@ func (rc *Cache) connectInit() {
|
|||||||
}
|
}
|
||||||
// initialize a new pool
|
// initialize a new pool
|
||||||
rc.p = &redis.Pool{
|
rc.p = &redis.Pool{
|
||||||
MaxIdle: 3,
|
MaxIdle: rc.maxIdle,
|
||||||
IdleTimeout: 180 * time.Second,
|
IdleTimeout: 180 * time.Second,
|
||||||
Dial: dialFunc,
|
Dial: dialFunc,
|
||||||
}
|
}
|
||||||
|
2
cache/redis/redis_test.go
vendored
2
cache/redis/redis_test.go
vendored
@ -19,7 +19,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego/cache"
|
"github.com/astaxie/beego/cache"
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/gomodule/redigo/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRedisCache(t *testing.T) {
|
func TestRedisCache(t *testing.T) {
|
||||||
|
@ -138,8 +138,8 @@ func init() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var filename = "app.conf"
|
var filename = "app.conf"
|
||||||
if os.Getenv("BEEGO_MODE") != "" {
|
if os.Getenv("BEEGO_RUNMODE") != "" {
|
||||||
filename = os.Getenv("BEEGO_MODE") + ".app.conf"
|
filename = os.Getenv("BEEGO_RUNMODE") + ".app.conf"
|
||||||
}
|
}
|
||||||
appConfigPath = filepath.Join(workPath, "conf", filename)
|
appConfigPath = filepath.Join(workPath, "conf", filename)
|
||||||
if !utils.FileExists(appConfigPath) {
|
if !utils.FileExists(appConfigPath) {
|
||||||
@ -193,7 +193,7 @@ func recoverPanic(ctx *context.Context) {
|
|||||||
func newBConfig() *Config {
|
func newBConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
AppName: "beego",
|
AppName: "beego",
|
||||||
RunMode: DEV,
|
RunMode: PROD,
|
||||||
RouterCaseSensitive: true,
|
RouterCaseSensitive: true,
|
||||||
ServerName: "beegoServer:" + VERSION,
|
ServerName: "beegoServer:" + VERSION,
|
||||||
RecoverPanic: true,
|
RecoverPanic: true,
|
||||||
|
@ -150,12 +150,12 @@ func ExpandValueEnv(value string) (realValue string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
key := ""
|
key := ""
|
||||||
defalutV := ""
|
defaultV := ""
|
||||||
// value start with "${"
|
// value start with "${"
|
||||||
for i := 2; i < vLen; i++ {
|
for i := 2; i < vLen; i++ {
|
||||||
if value[i] == '|' && (i+1 < vLen && value[i+1] == '|') {
|
if value[i] == '|' && (i+1 < vLen && value[i+1] == '|') {
|
||||||
key = value[2:i]
|
key = value[2:i]
|
||||||
defalutV = value[i+2 : vLen-1] // other string is default value.
|
defaultV = value[i+2 : vLen-1] // other string is default value.
|
||||||
break
|
break
|
||||||
} else if value[i] == '}' {
|
} else if value[i] == '}' {
|
||||||
key = value[2:i]
|
key = value[2:i]
|
||||||
@ -165,7 +165,7 @@ func ExpandValueEnv(value string) (realValue string) {
|
|||||||
|
|
||||||
realValue = os.Getenv(key)
|
realValue = os.Getenv(key)
|
||||||
if realValue == "" {
|
if realValue == "" {
|
||||||
realValue = defalutV
|
realValue = defaultV
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -126,7 +126,7 @@ func (c *fakeConfigContainer) SaveConfigFile(filename string) error {
|
|||||||
|
|
||||||
var _ Configer = new(fakeConfigContainer)
|
var _ Configer = new(fakeConfigContainer)
|
||||||
|
|
||||||
// NewFakeConfig return a fake Congiger
|
// NewFakeConfig return a fake Configer
|
||||||
func NewFakeConfig() Configer {
|
func NewFakeConfig() Configer {
|
||||||
return &fakeConfigContainer{
|
return &fakeConfigContainer{
|
||||||
data: make(map[string]string),
|
data: make(map[string]string),
|
||||||
|
@ -215,7 +215,7 @@ func (c *IniConfigContainer) Bool(key string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBool returns the boolean value for a given key.
|
// DefaultBool returns the boolean value for a given key.
|
||||||
// if err != nil return defaltval
|
// if err != nil return defaultval
|
||||||
func (c *IniConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
func (c *IniConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
||||||
v, err := c.Bool(key)
|
v, err := c.Bool(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -230,7 +230,7 @@ func (c *IniConfigContainer) 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 defaultval
|
||||||
func (c *IniConfigContainer) DefaultInt(key string, defaultval int) int {
|
func (c *IniConfigContainer) DefaultInt(key string, defaultval int) int {
|
||||||
v, err := c.Int(key)
|
v, err := c.Int(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -245,7 +245,7 @@ func (c *IniConfigContainer) 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 defaultval
|
||||||
func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
func (c *IniConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||||
v, err := c.Int64(key)
|
v, err := c.Int64(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -260,7 +260,7 @@ func (c *IniConfigContainer) 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 defaultval
|
||||||
func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
func (c *IniConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||||
v, err := c.Float(key)
|
v, err := c.Float(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -275,7 +275,7 @@ func (c *IniConfigContainer) 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 defaultval
|
||||||
func (c *IniConfigContainer) DefaultString(key string, defaultval string) string {
|
func (c *IniConfigContainer) DefaultString(key string, defaultval string) string {
|
||||||
v := c.String(key)
|
v := c.String(key)
|
||||||
if v == "" {
|
if v == "" {
|
||||||
@ -295,7 +295,7 @@ func (c *IniConfigContainer) Strings(key string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 defaultval
|
||||||
func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
func (c *IniConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||||
v := c.Strings(key)
|
v := c.Strings(key)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
@ -314,7 +314,7 @@ func (c *IniConfigContainer) GetSection(section string) (map[string]string, erro
|
|||||||
|
|
||||||
// SaveConfigFile save the config into file.
|
// SaveConfigFile save the config into file.
|
||||||
//
|
//
|
||||||
// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Funcation.
|
// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Function.
|
||||||
func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
||||||
// Write configuration file by filename.
|
// Write configuration file by filename.
|
||||||
f, err := os.Create(filename)
|
f, err := os.Create(filename)
|
||||||
|
@ -101,7 +101,7 @@ 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 defaultval
|
||||||
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 v
|
return v
|
||||||
@ -122,7 +122,7 @@ 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 defaultval
|
||||||
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 v
|
return v
|
||||||
@ -143,7 +143,7 @@ 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 defaultval
|
||||||
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 v
|
return v
|
||||||
@ -163,7 +163,7 @@ 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 defaultval
|
||||||
func (c *JSONConfigContainer) DefaultString(key string, defaultval string) string {
|
func (c *JSONConfigContainer) DefaultString(key string, defaultval string) string {
|
||||||
// TODO FIXME should not use "" to replace non existence
|
// TODO FIXME should not use "" to replace non existence
|
||||||
if v := c.String(key); v != "" {
|
if v := c.String(key); v != "" {
|
||||||
@ -182,7 +182,7 @@ func (c *JSONConfigContainer) Strings(key string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 defaultval
|
||||||
func (c *JSONConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
func (c *JSONConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||||
if v := c.Strings(key); v != nil {
|
if v := c.Strings(key); v != nil {
|
||||||
return v
|
return v
|
||||||
|
@ -216,7 +216,7 @@ func TestJson(t *testing.T) {
|
|||||||
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) {
|
if !jsonconf.DefaultBool("unknown", true) {
|
||||||
t.Error("unknown keys with default value wrong")
|
t.Error("unknown keys with default value wrong")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
|
func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
|
||||||
v, err := c.Int(key)
|
v, err := c.Int(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -117,7 +117,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||||
v, err := c.Int64(key)
|
v, err := c.Int64(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -133,7 +133,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||||
v, err := c.Float(key)
|
v, err := c.Float(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -151,7 +151,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
|
func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
|
||||||
v := c.String(key)
|
v := c.String(key)
|
||||||
if v == "" {
|
if v == "" {
|
||||||
@ -170,7 +170,7 @@ func (c *ConfigContainer) Strings(key string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||||
v := c.Strings(key)
|
v := c.Strings(key)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
|
@ -154,7 +154,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
|
func (c *ConfigContainer) DefaultInt(key string, defaultval int) int {
|
||||||
v, err := c.Int(key)
|
v, err := c.Int(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -174,7 +174,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
func (c *ConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
||||||
v, err := c.Int64(key)
|
v, err := c.Int64(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -198,7 +198,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
func (c *ConfigContainer) DefaultFloat(key string, defaultval float64) float64 {
|
||||||
v, err := c.Float(key)
|
v, err := c.Float(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -218,7 +218,7 @@ func (c *ConfigContainer) 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
|
func (c *ConfigContainer) DefaultString(key string, defaultval string) string {
|
||||||
v := c.String(key)
|
v := c.String(key)
|
||||||
if v == "" {
|
if v == "" {
|
||||||
@ -237,7 +237,7 @@ func (c *ConfigContainer) Strings(key string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 defaultval
|
||||||
func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []string {
|
||||||
v := c.Strings(key)
|
v := c.Strings(key)
|
||||||
if v == nil {
|
if v == nil {
|
||||||
@ -285,10 +285,23 @@ func (c *ConfigContainer) getData(key string) (interface{}, error) {
|
|||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
return nil, errors.New("key is empty")
|
return nil, errors.New("key is empty")
|
||||||
}
|
}
|
||||||
|
keys := strings.Split(key, ".")
|
||||||
if v, ok := c.data[key]; ok {
|
tmpData := c.data
|
||||||
|
for _, k := range keys {
|
||||||
|
if v, ok := tmpData[k]; ok {
|
||||||
|
switch v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
{
|
||||||
|
tmpData = v.(map[string]interface{})
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("not exist key %q", key)
|
return nil, fmt.Errorf("not exist key %q", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,8 +182,8 @@ func errorRenderer(err error) Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JSON writes json to response body.
|
// JSON writes json to response body.
|
||||||
// if coding is true, it converts utf-8 to \u0000 type.
|
// if encoding 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, encoding 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
|
||||||
@ -196,7 +196,7 @@ func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, coding bool) e
|
|||||||
http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
|
http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if coding {
|
if encoding {
|
||||||
content = []byte(stringsToJSON(string(content)))
|
content = []byte(stringsToJSON(string(content)))
|
||||||
}
|
}
|
||||||
return output.Body(content)
|
return output.Body(content)
|
||||||
@ -260,7 +260,7 @@ func (output *BeegoOutput) Download(file string, filename ...string) {
|
|||||||
} else {
|
} else {
|
||||||
fName = filepath.Base(file)
|
fName = filepath.Base(file)
|
||||||
}
|
}
|
||||||
output.Header("Content-Disposition", "attachment; filename="+url.QueryEscape(fName))
|
output.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fName))
|
||||||
output.Header("Content-Description", "File Transfer")
|
output.Header("Content-Description", "File Transfer")
|
||||||
output.Header("Content-Type", "application/octet-stream")
|
output.Header("Content-Type", "application/octet-stream")
|
||||||
output.Header("Content-Transfer-Encoding", "binary")
|
output.Header("Content-Transfer-Encoding", "binary")
|
||||||
@ -325,13 +325,13 @@ func (output *BeegoOutput) IsForbidden() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsNotFound returns boolean of this request is not found.
|
// IsNotFound returns boolean of this request is not found.
|
||||||
// HTTP 404 means forbidden.
|
// HTTP 404 means not found.
|
||||||
func (output *BeegoOutput) IsNotFound() bool {
|
func (output *BeegoOutput) IsNotFound() bool {
|
||||||
return output.Status == 404
|
return output.Status == 404
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClientError returns boolean of this request client sends error data.
|
// IsClientError returns boolean of this request client sends error data.
|
||||||
// HTTP 4xx means forbidden.
|
// HTTP 4xx means client error.
|
||||||
func (output *BeegoOutput) IsClientError() bool {
|
func (output *BeegoOutput) IsClientError() bool {
|
||||||
return output.Status >= 400 && output.Status < 500
|
return output.Status >= 400 && output.Status < 500
|
||||||
}
|
}
|
||||||
@ -350,6 +350,11 @@ func stringsToJSON(str string) string {
|
|||||||
jsons.WriteRune(r)
|
jsons.WriteRune(r)
|
||||||
} else {
|
} else {
|
||||||
jsons.WriteString("\\u")
|
jsons.WriteString("\\u")
|
||||||
|
if rint < 0x100 {
|
||||||
|
jsons.WriteString("00")
|
||||||
|
} else if rint < 0x1000 {
|
||||||
|
jsons.WriteString("0")
|
||||||
|
}
|
||||||
jsons.WriteString(strconv.FormatInt(int64(rint), 16))
|
jsons.WriteString(strconv.FormatInt(int64(rint), 16))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,6 +275,19 @@ func (c *Controller) Redirect(url string, code int) {
|
|||||||
c.Ctx.Redirect(code, url)
|
c.Ctx.Redirect(code, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the data depending on the accepted
|
||||||
|
func (c *Controller) SetData(data interface{}) {
|
||||||
|
accept := c.Ctx.Input.Header("Accept")
|
||||||
|
switch accept {
|
||||||
|
case applicationJSON:
|
||||||
|
c.Data["json"] = data
|
||||||
|
case applicationXML, textXML:
|
||||||
|
c.Data["xml"] = data
|
||||||
|
default:
|
||||||
|
c.Data["json"] = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Abort stops controller handler and show the error data if code is defined in ErrorMap or code string.
|
// Abort stops controller handler and show the error data if code is defined in ErrorMap or code string.
|
||||||
func (c *Controller) Abort(code string) {
|
func (c *Controller) Abort(code string) {
|
||||||
status, err := strconv.Atoi(code)
|
status, err := strconv.Atoi(code)
|
||||||
|
@ -317,6 +317,7 @@ func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
|
|||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// XMLBody adds request raw body encoding by XML.
|
// XMLBody adds request raw body encoding by XML.
|
||||||
func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
||||||
if b.req.Body == nil && obj != nil {
|
if b.req.Body == nil && obj != nil {
|
||||||
@ -330,6 +331,7 @@ func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
|||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONBody adds request raw body encoding by JSON.
|
// JSONBody adds request raw body encoding by JSON.
|
||||||
func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
||||||
if b.req.Body == nil && obj != nil {
|
if b.req.Body == nil && obj != nil {
|
||||||
@ -444,7 +446,7 @@ func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
|
|||||||
TLSClientConfig: b.setting.TLSClientConfig,
|
TLSClientConfig: b.setting.TLSClientConfig,
|
||||||
Proxy: b.setting.Proxy,
|
Proxy: b.setting.Proxy,
|
||||||
Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
|
Dial: TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
|
||||||
MaxIdleConnsPerHost: -1,
|
MaxIdleConnsPerHost: 100,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if b.transport is *http.Transport then set the settings.
|
// if b.transport is *http.Transport then set the settings.
|
||||||
|
@ -16,48 +16,57 @@ As of now this logs support console, file,smtp and conn.
|
|||||||
|
|
||||||
First you must import it
|
First you must import it
|
||||||
|
|
||||||
import (
|
```golang
|
||||||
|
import (
|
||||||
"github.com/astaxie/beego/logs"
|
"github.com/astaxie/beego/logs"
|
||||||
)
|
)
|
||||||
|
```
|
||||||
|
|
||||||
Then init a Log (example with console adapter)
|
Then init a Log (example with console adapter)
|
||||||
|
|
||||||
log := NewLogger(10000)
|
```golang
|
||||||
log.SetLogger("console", "")
|
log := logs.NewLogger(10000)
|
||||||
|
log.SetLogger("console", "")
|
||||||
|
```
|
||||||
|
|
||||||
> the first params stand for how many channel
|
> the first params stand for how many channel
|
||||||
|
|
||||||
Use it like this:
|
Use it like this:
|
||||||
|
|
||||||
log.Trace("trace")
|
```golang
|
||||||
log.Info("info")
|
log.Trace("trace")
|
||||||
log.Warn("warning")
|
log.Info("info")
|
||||||
log.Debug("debug")
|
log.Warn("warning")
|
||||||
log.Critical("critical")
|
log.Debug("debug")
|
||||||
|
log.Critical("critical")
|
||||||
|
```
|
||||||
|
|
||||||
## File adapter
|
## File adapter
|
||||||
|
|
||||||
Configure file adapter like this:
|
Configure file adapter like this:
|
||||||
|
|
||||||
log := NewLogger(10000)
|
```golang
|
||||||
log.SetLogger("file", `{"filename":"test.log"}`)
|
log := NewLogger(10000)
|
||||||
|
log.SetLogger("file", `{"filename":"test.log"}`)
|
||||||
|
```
|
||||||
|
|
||||||
## Conn adapter
|
## Conn adapter
|
||||||
|
|
||||||
Configure like this:
|
Configure like this:
|
||||||
|
|
||||||
log := NewLogger(1000)
|
```golang
|
||||||
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
log := NewLogger(1000)
|
||||||
log.Info("info")
|
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
|
||||||
|
log.Info("info")
|
||||||
|
```
|
||||||
|
|
||||||
## Smtp adapter
|
## Smtp adapter
|
||||||
|
|
||||||
Configure like this:
|
Configure like this:
|
||||||
|
|
||||||
log := NewLogger(10000)
|
```golang
|
||||||
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
|
log := NewLogger(10000)
|
||||||
log.Critical("sendmail critical")
|
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
|
||||||
time.Sleep(time.Second * 30)
|
log.Critical("sendmail critical")
|
||||||
|
time.Sleep(time.Second * 30)
|
||||||
|
```
|
||||||
|
@ -17,12 +17,12 @@ package logs
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
apacheFormatPattern = "%s - - [%s] \"%s %d %d\" %f %s %s\n"
|
apacheFormatPattern = "%s - - [%s] \"%s %d %d\" %f %s %s"
|
||||||
apacheFormat = "APACHE_FORMAT"
|
apacheFormat = "APACHE_FORMAT"
|
||||||
jsonFormat = "JSON_FORMAT"
|
jsonFormat = "JSON_FORMAT"
|
||||||
)
|
)
|
||||||
@ -53,10 +53,9 @@ func (r *AccessLogRecord) json() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func disableEscapeHTML(i interface{}) {
|
func disableEscapeHTML(i interface{}) {
|
||||||
e, ok := i.(interface {
|
if e, ok := i.(interface {
|
||||||
SetEscapeHTML(bool)
|
SetEscapeHTML(bool)
|
||||||
});
|
}); ok {
|
||||||
if ok {
|
|
||||||
e.SetEscapeHTML(false)
|
e.SetEscapeHTML(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
logs/file.go
18
logs/file.go
@ -40,6 +40,9 @@ type fileLogWriter struct {
|
|||||||
MaxLines int `json:"maxlines"`
|
MaxLines int `json:"maxlines"`
|
||||||
maxLinesCurLines int
|
maxLinesCurLines int
|
||||||
|
|
||||||
|
MaxFiles int `json:"maxfiles"`
|
||||||
|
MaxFilesCurFiles int
|
||||||
|
|
||||||
// Rotate at size
|
// Rotate at size
|
||||||
MaxSize int `json:"maxsize"`
|
MaxSize int `json:"maxsize"`
|
||||||
maxSizeCurSize int
|
maxSizeCurSize int
|
||||||
@ -70,6 +73,9 @@ func newFileWriter() Logger {
|
|||||||
RotatePerm: "0440",
|
RotatePerm: "0440",
|
||||||
Level: LevelTrace,
|
Level: LevelTrace,
|
||||||
Perm: "0660",
|
Perm: "0660",
|
||||||
|
MaxLines: 10000000,
|
||||||
|
MaxFiles: 999,
|
||||||
|
MaxSize: 1 << 28,
|
||||||
}
|
}
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
@ -238,7 +244,7 @@ func (w *fileLogWriter) lines() (int, error) {
|
|||||||
func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
||||||
// file exists
|
// file exists
|
||||||
// Find the next available number
|
// Find the next available number
|
||||||
num := 1
|
num := w.MaxFilesCurFiles + 1
|
||||||
fName := ""
|
fName := ""
|
||||||
rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64)
|
rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -251,18 +257,16 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
|||||||
goto RESTART_LOGGER
|
goto RESTART_LOGGER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only when one of them be setted, then the file would be splited
|
||||||
if w.MaxLines > 0 || w.MaxSize > 0 {
|
if w.MaxLines > 0 || w.MaxSize > 0 {
|
||||||
for ; err == nil && num <= 999; num++ {
|
for ; err == nil && num <= w.MaxFiles; num++ {
|
||||||
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, w.suffix)
|
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, w.suffix)
|
||||||
_, err = os.Lstat(fName)
|
_, err = os.Lstat(fName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fName = fmt.Sprintf("%s.%s%s", w.fileNameOnly, w.dailyOpenTime.Format("2006-01-02"), w.suffix)
|
|
||||||
_, err = os.Lstat(fName)
|
|
||||||
for ; err == nil && num <= 999; num++ {
|
|
||||||
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", w.dailyOpenTime.Format("2006-01-02"), num, w.suffix)
|
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", w.dailyOpenTime.Format("2006-01-02"), num, w.suffix)
|
||||||
_, err = os.Lstat(fName)
|
_, err = os.Lstat(fName)
|
||||||
}
|
w.MaxFilesCurFiles = num
|
||||||
}
|
}
|
||||||
// return error if the last file checked still existed
|
// return error if the last file checked still existed
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -308,7 +312,7 @@ func (w *fileLogWriter) deleteOldLog() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !info.IsDir() && info.ModTime().Add(24*time.Hour*time.Duration(w.MaxDays)).Before(time.Now()) {
|
if !info.IsDir() && info.ModTime().Add(24 * time.Hour * time.Duration(w.MaxDays)).Before(time.Now()) {
|
||||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||||
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||||
os.Remove(path)
|
os.Remove(path)
|
||||||
|
@ -135,7 +135,7 @@ func TestFileRotate_01(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileRotate_02(t *testing.T) {
|
func TestFileRotate_02(t *testing.T) {
|
||||||
fn1 := "rotate_day.log"
|
fn1 := "rotate_day.log"
|
||||||
fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
|
fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log"
|
||||||
testFileRotate(t, fn1, fn2)
|
testFileRotate(t, fn1, fn2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ func TestFileRotate_03(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileRotate_04(t *testing.T) {
|
func TestFileRotate_04(t *testing.T) {
|
||||||
fn1 := "rotate_day.log"
|
fn1 := "rotate_day.log"
|
||||||
fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".log"
|
fn2 := "rotate_day." + time.Now().Add(-24*time.Hour).Format("2006-01-02") + ".001.log"
|
||||||
testFileDailyRotate(t, fn1, fn2)
|
testFileDailyRotate(t, fn1, fn2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +200,7 @@ func testFileRotate(t *testing.T, fn1, fn2 string) {
|
|||||||
for _, file := range []string{fn1, fn2} {
|
for _, file := range []string{fn1, fn2} {
|
||||||
_, err := os.Stat(file)
|
_, err := os.Stat(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
os.Remove(file)
|
os.Remove(file)
|
||||||
|
21
logs/log.go
21
logs/log.go
@ -116,6 +116,7 @@ type BeeLogger struct {
|
|||||||
enableFuncCallDepth bool
|
enableFuncCallDepth bool
|
||||||
loggerFuncCallDepth int
|
loggerFuncCallDepth int
|
||||||
asynchronous bool
|
asynchronous bool
|
||||||
|
prefix string
|
||||||
msgChanLen int64
|
msgChanLen int64
|
||||||
msgChan chan *logMsg
|
msgChan chan *logMsg
|
||||||
signalChan chan string
|
signalChan chan string
|
||||||
@ -247,7 +248,7 @@ func (bl *BeeLogger) Write(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
// writeMsg will always add a '\n' character
|
// writeMsg will always add a '\n' character
|
||||||
if p[len(p)-1] == '\n' {
|
if p[len(p)-1] == '\n' {
|
||||||
p = p[0 : len(p)-1]
|
p = p[0: len(p)-1]
|
||||||
}
|
}
|
||||||
// set levelLoggerImpl to ensure all log message will be write out
|
// set levelLoggerImpl to ensure all log message will be write out
|
||||||
err = bl.writeMsg(levelLoggerImpl, string(p))
|
err = bl.writeMsg(levelLoggerImpl, string(p))
|
||||||
@ -267,6 +268,9 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error
|
|||||||
if len(v) > 0 {
|
if len(v) > 0 {
|
||||||
msg = fmt.Sprintf(msg, v...)
|
msg = fmt.Sprintf(msg, v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg = bl.prefix + " " + msg
|
||||||
|
|
||||||
when := time.Now()
|
when := time.Now()
|
||||||
if bl.enableFuncCallDepth {
|
if bl.enableFuncCallDepth {
|
||||||
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
|
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
|
||||||
@ -305,6 +309,11 @@ func (bl *BeeLogger) SetLevel(l int) {
|
|||||||
bl.level = l
|
bl.level = l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLevel Get Current log message level.
|
||||||
|
func (bl *BeeLogger) GetLevel() int {
|
||||||
|
return bl.level
|
||||||
|
}
|
||||||
|
|
||||||
// SetLogFuncCallDepth set log funcCallDepth
|
// SetLogFuncCallDepth set log funcCallDepth
|
||||||
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
|
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
|
||||||
bl.loggerFuncCallDepth = d
|
bl.loggerFuncCallDepth = d
|
||||||
@ -320,6 +329,11 @@ func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
|
|||||||
bl.enableFuncCallDepth = b
|
bl.enableFuncCallDepth = b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set prefix
|
||||||
|
func (bl *BeeLogger) SetPrefix(s string) {
|
||||||
|
bl.prefix = s
|
||||||
|
}
|
||||||
|
|
||||||
// start logger chan reading.
|
// start logger chan reading.
|
||||||
// when chan is not empty, write logs.
|
// when chan is not empty, write logs.
|
||||||
func (bl *BeeLogger) startLogger() {
|
func (bl *BeeLogger) startLogger() {
|
||||||
@ -544,6 +558,11 @@ func SetLevel(l int) {
|
|||||||
beeLogger.SetLevel(l)
|
beeLogger.SetLevel(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPrefix sets the prefix
|
||||||
|
func SetPrefix(s string) {
|
||||||
|
beeLogger.SetPrefix(s)
|
||||||
|
}
|
||||||
|
|
||||||
// EnableFuncCallDepth enable log funcCallDepth
|
// EnableFuncCallDepth enable log funcCallDepth
|
||||||
func EnableFuncCallDepth(b bool) {
|
func EnableFuncCallDepth(b bool) {
|
||||||
beeLogger.enableFuncCallDepth = b
|
beeLogger.enableFuncCallDepth = b
|
||||||
|
@ -93,7 +93,7 @@ const (
|
|||||||
func formatTimeHeader(when time.Time) ([]byte, int) {
|
func formatTimeHeader(when time.Time) ([]byte, int) {
|
||||||
y, mo, d := when.Date()
|
y, mo, d := when.Date()
|
||||||
h, mi, s := when.Clock()
|
h, mi, s := when.Clock()
|
||||||
ns := when.Nanosecond()/1000000
|
ns := when.Nanosecond() / 1000000
|
||||||
//len("2006/01/02 15:04:05.123 ")==24
|
//len("2006/01/02 15:04:05.123 ")==24
|
||||||
var buf [24]byte
|
var buf [24]byte
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ func TestFormatHeader_0(t *testing.T) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
h, _ := formatTimeHeader(tm)
|
h, _ := formatTimeHeader(tm)
|
||||||
if tm.Format("2006/01/02 15:04:05.999 ") != string(h) {
|
if tm.Format("2006/01/02 15:04:05.000 ") != string(h) {
|
||||||
t.Log(tm)
|
t.Log(tm)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ func TestFormatHeader_1(t *testing.T) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
h, _ := formatTimeHeader(tm)
|
h, _ := formatTimeHeader(tm)
|
||||||
if tm.Format("2006/01/02 15:04:05.999 ") != string(h) {
|
if tm.Format("2006/01/02 15:04:05.000 ") != string(h) {
|
||||||
t.Log(tm)
|
t.Log(tm)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,10 @@ func (f *multiFileLogWriter) Init(config string) error {
|
|||||||
jsonMap["level"] = i
|
jsonMap["level"] = i
|
||||||
bs, _ := json.Marshal(jsonMap)
|
bs, _ := json.Marshal(jsonMap)
|
||||||
writer = newFileWriter().(*fileLogWriter)
|
writer = newFileWriter().(*fileLogWriter)
|
||||||
writer.Init(string(bs))
|
err := writer.Init(string(bs))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
f.writers[i] = writer
|
f.writers[i] = writer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -322,7 +322,7 @@ func (m *Migration) GetSQL() (sql string) {
|
|||||||
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(m.Columns) > index {
|
if len(m.Columns) > index+1 {
|
||||||
sql += ","
|
sql += ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,7 +355,7 @@ func (m *Migration) GetSQL() (sql string) {
|
|||||||
} else {
|
} else {
|
||||||
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name)
|
||||||
}
|
}
|
||||||
if len(m.Columns) > index {
|
if len(m.Columns) > index+1 {
|
||||||
sql += ","
|
sql += ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -366,14 +366,14 @@ func (m *Migration) GetSQL() (sql string) {
|
|||||||
|
|
||||||
for index, unique := range m.Uniques {
|
for index, unique := range m.Uniques {
|
||||||
sql += fmt.Sprintf("\n DROP KEY `%s`", unique.Definition)
|
sql += fmt.Sprintf("\n DROP KEY `%s`", unique.Definition)
|
||||||
if len(m.Uniques) > index {
|
if len(m.Uniques) > index+1 {
|
||||||
sql += ","
|
sql += ","
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
for index, column := range m.Renames {
|
for index, column := range m.Renames {
|
||||||
sql += fmt.Sprintf("\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault)
|
sql += fmt.Sprintf("\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault)
|
||||||
if len(m.Renames) > index {
|
if len(m.Renames) > index+1 {
|
||||||
sql += ","
|
sql += ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,10 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
|||||||
column = strings.Replace(column, "%COL%", fi.column, -1)
|
column = strings.Replace(column, "%COL%", fi.column, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fi.description != "" {
|
||||||
|
column += " " + fmt.Sprintf("COMMENT '%s'",fi.description)
|
||||||
|
}
|
||||||
|
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -969,6 +969,10 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
|
|||||||
}
|
}
|
||||||
query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit)
|
query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit)
|
||||||
|
|
||||||
|
if qs.forupdate {
|
||||||
|
query += " FOR UPDATE"
|
||||||
|
}
|
||||||
|
|
||||||
d.ins.ReplaceMarks(&query)
|
d.ins.ReplaceMarks(&query)
|
||||||
|
|
||||||
var rs *sql.Rows
|
var rs *sql.Rows
|
||||||
|
@ -136,6 +136,7 @@ type fieldInfo struct {
|
|||||||
decimals int
|
decimals int
|
||||||
isFielder bool // implement Fielder interface
|
isFielder bool // implement Fielder interface
|
||||||
onDelete string
|
onDelete string
|
||||||
|
description string
|
||||||
}
|
}
|
||||||
|
|
||||||
// new field info
|
// new field info
|
||||||
@ -300,6 +301,7 @@ checkType:
|
|||||||
fi.sf = sf
|
fi.sf = sf
|
||||||
fi.fullName = mi.fullName + mName + "." + sf.Name
|
fi.fullName = mi.fullName + mName + "." + sf.Name
|
||||||
|
|
||||||
|
fi.description = sf.Tag.Get("description")
|
||||||
fi.null = attrs["null"]
|
fi.null = attrs["null"]
|
||||||
fi.index = attrs["index"]
|
fi.index = attrs["index"]
|
||||||
fi.auto = attrs["auto"]
|
fi.auto = attrs["auto"]
|
||||||
|
@ -64,6 +64,7 @@ type querySet struct {
|
|||||||
groups []string
|
groups []string
|
||||||
orders []string
|
orders []string
|
||||||
distinct bool
|
distinct bool
|
||||||
|
forupdate bool
|
||||||
orm *orm
|
orm *orm
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,6 +128,12 @@ func (o querySet) Distinct() QuerySeter {
|
|||||||
return &o
|
return &o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add FOR UPDATE to SELECT
|
||||||
|
func (o querySet) ForUpdate() QuerySeter {
|
||||||
|
o.forupdate = true
|
||||||
|
return &o
|
||||||
|
}
|
||||||
|
|
||||||
// set relation model to query together.
|
// set relation model to query together.
|
||||||
// it will query relation models and assign to parent model.
|
// it will query relation models and assign to parent model.
|
||||||
func (o querySet) RelatedSel(params ...interface{}) QuerySeter {
|
func (o querySet) RelatedSel(params ...interface{}) QuerySeter {
|
||||||
@ -191,7 +198,11 @@ func (o *querySet) PrepareInsert() (Inserter, error) {
|
|||||||
// query all data and map to containers.
|
// query all data and map to containers.
|
||||||
// cols means the columns when querying.
|
// cols means the columns when querying.
|
||||||
func (o *querySet) All(container interface{}, cols ...string) (int64, error) {
|
func (o *querySet) All(container interface{}, cols ...string) (int64, error) {
|
||||||
return o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
|
num, err := o.orm.alias.DbBaser.ReadBatch(o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
|
||||||
|
if num == 0 {
|
||||||
|
return 0, ErrNoRows
|
||||||
|
}
|
||||||
|
return num, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// query one row data and map to containers.
|
// query one row data and map to containers.
|
||||||
|
@ -190,6 +190,10 @@ type QuerySeter interface {
|
|||||||
// Distinct().
|
// Distinct().
|
||||||
// All(&permissions)
|
// All(&permissions)
|
||||||
Distinct() QuerySeter
|
Distinct() QuerySeter
|
||||||
|
// set FOR UPDATE to query.
|
||||||
|
// for example:
|
||||||
|
// o.QueryTable("user").Filter("uid", uid).ForUpdate().All(&users)
|
||||||
|
ForUpdate() QuerySeter
|
||||||
// return QuerySeter execution result number
|
// return QuerySeter execution result number
|
||||||
// for example:
|
// for example:
|
||||||
// num, err = qs.Filter("profile__age__gt", 28).Count()
|
// num, err = qs.Filter("profile__age__gt", 28).Count()
|
||||||
|
56
parser.go
56
parser.go
@ -114,10 +114,11 @@ type parsedParam struct {
|
|||||||
|
|
||||||
func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
||||||
if f.Doc != nil {
|
if f.Doc != nil {
|
||||||
parsedComment, err := parseComment(f.Doc.List)
|
parsedComments, err := parseComment(f.Doc.List)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, parsedComment := range parsedComments {
|
||||||
if parsedComment.routerPath != "" {
|
if parsedComment.routerPath != "" {
|
||||||
key := pkgpath + ":" + controllerName
|
key := pkgpath + ":" + controllerName
|
||||||
cc := ControllerComments{}
|
cc := ControllerComments{}
|
||||||
@ -127,7 +128,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
|||||||
cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
|
cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
|
||||||
genInfoList[key] = append(genInfoList[key], cc)
|
genInfoList[key] = append(genInfoList[key], cc)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -177,26 +178,13 @@ func paramInPath(name, route string) bool {
|
|||||||
|
|
||||||
var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`)
|
var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`)
|
||||||
|
|
||||||
func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) {
|
func parseComment(lines []*ast.Comment) (pcs []*parsedComment, err error) {
|
||||||
pc = &parsedComment{}
|
pcs = []*parsedComment{}
|
||||||
|
params := map[string]parsedParam{}
|
||||||
|
|
||||||
for _, c := range lines {
|
for _, c := range lines {
|
||||||
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||||
if strings.HasPrefix(t, "@router") {
|
if strings.HasPrefix(t, "@Param") {
|
||||||
matches := routeRegex.FindStringSubmatch(t)
|
|
||||||
if len(matches) == 3 {
|
|
||||||
pc.routerPath = matches[1]
|
|
||||||
methods := matches[2]
|
|
||||||
if methods == "" {
|
|
||||||
pc.methods = []string{"get"}
|
|
||||||
//pc.hasGet = true
|
|
||||||
} else {
|
|
||||||
pc.methods = strings.Split(methods, ",")
|
|
||||||
//pc.hasGet = strings.Contains(methods, "get")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("Router information is missing")
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(t, "@Param") {
|
|
||||||
pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param")))
|
pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param")))
|
||||||
if len(pv) < 4 {
|
if len(pv) < 4 {
|
||||||
logs.Error("Invalid @Param format. Needs at least 4 parameters")
|
logs.Error("Invalid @Param format. Needs at least 4 parameters")
|
||||||
@ -217,10 +205,32 @@ func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) {
|
|||||||
p.defValue = pv[3]
|
p.defValue = pv[3]
|
||||||
p.required, _ = strconv.ParseBool(pv[4])
|
p.required, _ = strconv.ParseBool(pv[4])
|
||||||
}
|
}
|
||||||
if pc.params == nil {
|
params[funcParamName] = p
|
||||||
pc.params = map[string]parsedParam{}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range lines {
|
||||||
|
var pc = &parsedComment{}
|
||||||
|
pc.params = params
|
||||||
|
|
||||||
|
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||||
|
if strings.HasPrefix(t, "@router") {
|
||||||
|
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
|
||||||
|
matches := routeRegex.FindStringSubmatch(t)
|
||||||
|
if len(matches) == 3 {
|
||||||
|
pc.routerPath = matches[1]
|
||||||
|
methods := matches[2]
|
||||||
|
if methods == "" {
|
||||||
|
pc.methods = []string{"get"}
|
||||||
|
//pc.hasGet = true
|
||||||
|
} else {
|
||||||
|
pc.methods = strings.Split(methods, ",")
|
||||||
|
//pc.hasGet = strings.Contains(methods, "get")
|
||||||
|
}
|
||||||
|
pcs = append(pcs, pc)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("Router information is missing")
|
||||||
}
|
}
|
||||||
pc.params[funcParamName] = p
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -202,10 +202,10 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt
|
|||||||
numOfFields := elemVal.NumField()
|
numOfFields := elemVal.NumField()
|
||||||
for i := 0; i < numOfFields; i++ {
|
for i := 0; i < numOfFields; i++ {
|
||||||
fieldType := elemType.Field(i)
|
fieldType := elemType.Field(i)
|
||||||
|
elemField := execElem.FieldByName(fieldType.Name)
|
||||||
if execElem.FieldByName(fieldType.Name).CanSet() {
|
if elemField.CanSet() {
|
||||||
fieldVal := elemVal.Field(i)
|
fieldVal := elemVal.Field(i)
|
||||||
execElem.FieldByName(fieldType.Name).Set(fieldVal)
|
elemField.Set(fieldVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
|
|
||||||
// Package redis for session provider
|
// Package redis for session provider
|
||||||
//
|
//
|
||||||
// depend on github.com/garyburd/redigo/redis
|
// depend on github.com/gomodule/redigo/redis
|
||||||
//
|
//
|
||||||
// go install github.com/garyburd/redigo/redis
|
// go install github.com/gomodule/redigo/redis
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// import(
|
// import(
|
||||||
@ -37,10 +37,11 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego/session"
|
"github.com/astaxie/beego/session"
|
||||||
|
|
||||||
"github.com/garyburd/redigo/redis"
|
"github.com/gomodule/redigo/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
var redispder = &Provider{}
|
var redispder = &Provider{}
|
||||||
@ -118,8 +119,8 @@ type Provider struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SessionInit init redis session
|
// SessionInit init redis session
|
||||||
// savepath like redis server addr,pool size,password,dbnum
|
// savepath like redis server addr,pool size,password,dbnum,IdleTimeout second
|
||||||
// e.g. 127.0.0.1:6379,100,astaxie,0
|
// e.g. 127.0.0.1:6379,100,astaxie,0,30
|
||||||
func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||||
rp.maxlifetime = maxlifetime
|
rp.maxlifetime = maxlifetime
|
||||||
configs := strings.Split(savePath, ",")
|
configs := strings.Split(savePath, ",")
|
||||||
@ -149,7 +150,15 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
|||||||
} else {
|
} else {
|
||||||
rp.dbNum = 0
|
rp.dbNum = 0
|
||||||
}
|
}
|
||||||
rp.poollist = redis.NewPool(func() (redis.Conn, error) {
|
var idleTimeout time.Duration = 0
|
||||||
|
if len(configs) > 4 {
|
||||||
|
timeout, err := strconv.Atoi(configs[4])
|
||||||
|
if err == nil && timeout > 0 {
|
||||||
|
idleTimeout = time.Duration(timeout) * time.Second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rp.poollist = &redis.Pool{
|
||||||
|
Dial: func() (redis.Conn, error) {
|
||||||
c, err := redis.Dial("tcp", rp.savePath)
|
c, err := redis.Dial("tcp", rp.savePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -160,7 +169,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//some redis proxy such as twemproxy is not support select command
|
// some redis proxy such as twemproxy is not support select command
|
||||||
if rp.dbNum > 0 {
|
if rp.dbNum > 0 {
|
||||||
_, err = c.Do("SELECT", rp.dbNum)
|
_, err = c.Do("SELECT", rp.dbNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -169,7 +178,11 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c, err
|
return c, err
|
||||||
}, rp.poolsize)
|
},
|
||||||
|
MaxIdle: rp.poolsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
rp.poollist.IdleTimeout = idleTimeout
|
||||||
|
|
||||||
return rp.poollist.Get().Err()
|
return rp.poollist.Get().Err()
|
||||||
}
|
}
|
||||||
|
220
session/redis_cluster/redis_cluster.go
Normal file
220
session/redis_cluster/redis_cluster.go
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
// 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 redis for session provider
|
||||||
|
//
|
||||||
|
// depend on github.com/go-redis/redis
|
||||||
|
//
|
||||||
|
// go install github.com/go-redis/redis
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// import(
|
||||||
|
// _ "github.com/astaxie/beego/session/redis_cluster"
|
||||||
|
// "github.com/astaxie/beego/session"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func init() {
|
||||||
|
// globalSessions, _ = session.NewManager("redis_cluster", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070;127.0.0.1:7071"}``)
|
||||||
|
// go globalSessions.GC()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// more docs: http://beego.me/docs/module/session.md
|
||||||
|
package redis_cluster
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"github.com/astaxie/beego/session"
|
||||||
|
rediss "github.com/go-redis/redis"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var redispder = &Provider{}
|
||||||
|
|
||||||
|
// MaxPoolSize redis_cluster max pool size
|
||||||
|
var MaxPoolSize = 1000
|
||||||
|
|
||||||
|
// SessionStore redis_cluster session store
|
||||||
|
type SessionStore struct {
|
||||||
|
p *rediss.ClusterClient
|
||||||
|
sid string
|
||||||
|
lock sync.RWMutex
|
||||||
|
values map[interface{}]interface{}
|
||||||
|
maxlifetime int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set value in redis_cluster session
|
||||||
|
func (rs *SessionStore) Set(key, value interface{}) error {
|
||||||
|
rs.lock.Lock()
|
||||||
|
defer rs.lock.Unlock()
|
||||||
|
rs.values[key] = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get value in redis_cluster session
|
||||||
|
func (rs *SessionStore) Get(key interface{}) interface{} {
|
||||||
|
rs.lock.RLock()
|
||||||
|
defer rs.lock.RUnlock()
|
||||||
|
if v, ok := rs.values[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete value in redis_cluster session
|
||||||
|
func (rs *SessionStore) Delete(key interface{}) error {
|
||||||
|
rs.lock.Lock()
|
||||||
|
defer rs.lock.Unlock()
|
||||||
|
delete(rs.values, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush clear all values in redis_cluster session
|
||||||
|
func (rs *SessionStore) Flush() error {
|
||||||
|
rs.lock.Lock()
|
||||||
|
defer rs.lock.Unlock()
|
||||||
|
rs.values = make(map[interface{}]interface{})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionID get redis_cluster session id
|
||||||
|
func (rs *SessionStore) SessionID() string {
|
||||||
|
return rs.sid
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionRelease save session values to redis_cluster
|
||||||
|
func (rs *SessionStore) SessionRelease(w http.ResponseWriter) {
|
||||||
|
b, err := session.EncodeGob(rs.values)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c := rs.p
|
||||||
|
c.Set(rs.sid, string(b), time.Duration(rs.maxlifetime) * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider redis_cluster session provider
|
||||||
|
type Provider struct {
|
||||||
|
maxlifetime int64
|
||||||
|
savePath string
|
||||||
|
poolsize int
|
||||||
|
password string
|
||||||
|
dbNum int
|
||||||
|
poollist *rediss.ClusterClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionInit init redis_cluster session
|
||||||
|
// savepath like redis server addr,pool size,password,dbnum
|
||||||
|
// e.g. 127.0.0.1:6379;127.0.0.1:6380,100,test,0
|
||||||
|
func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
|
||||||
|
rp.maxlifetime = maxlifetime
|
||||||
|
configs := strings.Split(savePath, ",")
|
||||||
|
if len(configs) > 0 {
|
||||||
|
rp.savePath = configs[0]
|
||||||
|
}
|
||||||
|
if len(configs) > 1 {
|
||||||
|
poolsize, err := strconv.Atoi(configs[1])
|
||||||
|
if err != nil || poolsize < 0 {
|
||||||
|
rp.poolsize = MaxPoolSize
|
||||||
|
} else {
|
||||||
|
rp.poolsize = poolsize
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rp.poolsize = MaxPoolSize
|
||||||
|
}
|
||||||
|
if len(configs) > 2 {
|
||||||
|
rp.password = configs[2]
|
||||||
|
}
|
||||||
|
if len(configs) > 3 {
|
||||||
|
dbnum, err := strconv.Atoi(configs[3])
|
||||||
|
if err != nil || dbnum < 0 {
|
||||||
|
rp.dbNum = 0
|
||||||
|
} else {
|
||||||
|
rp.dbNum = dbnum
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rp.dbNum = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
rp.poollist = rediss.NewClusterClient(&rediss.ClusterOptions{
|
||||||
|
Addrs: strings.Split(rp.savePath, ";"),
|
||||||
|
Password: rp.password,
|
||||||
|
PoolSize: rp.poolsize,
|
||||||
|
})
|
||||||
|
return rp.poollist.Ping().Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionRead read redis_cluster session by sid
|
||||||
|
func (rp *Provider) SessionRead(sid string) (session.Store, error) {
|
||||||
|
var kv map[interface{}]interface{}
|
||||||
|
kvs, err := rp.poollist.Get(sid).Result()
|
||||||
|
if err != nil && err != rediss.Nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(kvs) == 0 {
|
||||||
|
kv = make(map[interface{}]interface{})
|
||||||
|
} else {
|
||||||
|
if kv, err = session.DecodeGob([]byte(kvs)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
|
||||||
|
return rs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionExist check redis_cluster session exist by sid
|
||||||
|
func (rp *Provider) SessionExist(sid string) bool {
|
||||||
|
c := rp.poollist
|
||||||
|
if existed, err := c.Exists(sid).Result(); err != nil || existed == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionRegenerate generate new sid for redis_cluster session
|
||||||
|
func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
|
||||||
|
c := rp.poollist
|
||||||
|
|
||||||
|
if existed, err := c.Exists(oldsid).Result(); err != nil || existed == 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(sid, "", time.Duration(rp.maxlifetime) * time.Second)
|
||||||
|
} else {
|
||||||
|
c.Rename(oldsid, sid)
|
||||||
|
c.Expire(sid, time.Duration(rp.maxlifetime) * time.Second)
|
||||||
|
}
|
||||||
|
return rp.SessionRead(sid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionDestroy delete redis session by id
|
||||||
|
func (rp *Provider) SessionDestroy(sid string) error {
|
||||||
|
c := rp.poollist
|
||||||
|
c.Del(sid)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionGC Impelment method, no used.
|
||||||
|
func (rp *Provider) SessionGC() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionAll return all activeSession
|
||||||
|
func (rp *Provider) SessionAll() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
session.Register("redis_cluster", redispder)
|
||||||
|
}
|
@ -74,7 +74,7 @@ func serverStaticRouter(ctx *context.Context) {
|
|||||||
if enableCompress {
|
if enableCompress {
|
||||||
acceptEncoding = context.ParseEncoding(ctx.Request)
|
acceptEncoding = context.ParseEncoding(ctx.Request)
|
||||||
}
|
}
|
||||||
b, n, sch, err := openFile(filePath, fileInfo, acceptEncoding)
|
b, n, sch, reader, err := openFile(filePath, fileInfo, acceptEncoding)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if BConfig.RunMode == DEV {
|
if BConfig.RunMode == DEV {
|
||||||
logs.Warn("Can't compress the file:", filePath, err)
|
logs.Warn("Can't compress the file:", filePath, err)
|
||||||
@ -89,47 +89,53 @@ func serverStaticRouter(ctx *context.Context) {
|
|||||||
ctx.Output.Header("Content-Length", strconv.FormatInt(sch.size, 10))
|
ctx.Output.Header("Content-Length", strconv.FormatInt(sch.size, 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch)
|
http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
type serveContentHolder struct {
|
type serveContentHolder struct {
|
||||||
*bytes.Reader
|
data []byte
|
||||||
modTime time.Time
|
modTime time.Time
|
||||||
size int64
|
size int64
|
||||||
encoding string
|
encoding string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serveContentReader struct {
|
||||||
|
*bytes.Reader
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
staticFileMap = make(map[string]*serveContentHolder)
|
staticFileMap = make(map[string]*serveContentHolder)
|
||||||
mapLock sync.RWMutex
|
mapLock sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, error) {
|
func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, *serveContentReader, error) {
|
||||||
mapKey := acceptEncoding + ":" + filePath
|
mapKey := acceptEncoding + ":" + filePath
|
||||||
mapLock.RLock()
|
mapLock.RLock()
|
||||||
mapFile := staticFileMap[mapKey]
|
mapFile := staticFileMap[mapKey]
|
||||||
mapLock.RUnlock()
|
mapLock.RUnlock()
|
||||||
if isOk(mapFile, fi) {
|
if isOk(mapFile, fi) {
|
||||||
return mapFile.encoding != "", mapFile.encoding, mapFile, nil
|
reader := &serveContentReader{Reader: bytes.NewReader(mapFile.data)}
|
||||||
|
return mapFile.encoding != "", mapFile.encoding, mapFile, reader, nil
|
||||||
}
|
}
|
||||||
mapLock.Lock()
|
mapLock.Lock()
|
||||||
defer mapLock.Unlock()
|
defer mapLock.Unlock()
|
||||||
if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
|
if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, nil, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
var bufferWriter bytes.Buffer
|
var bufferWriter bytes.Buffer
|
||||||
_, n, err := context.WriteFile(acceptEncoding, &bufferWriter, file)
|
_, n, err := context.WriteFile(acceptEncoding, &bufferWriter, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, "", nil, err
|
return false, "", nil, nil, err
|
||||||
}
|
}
|
||||||
mapFile = &serveContentHolder{Reader: bytes.NewReader(bufferWriter.Bytes()), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), encoding: n}
|
mapFile = &serveContentHolder{data: bufferWriter.Bytes(), modTime: fi.ModTime(), size: int64(bufferWriter.Len()), encoding: n}
|
||||||
staticFileMap[mapKey] = mapFile
|
staticFileMap[mapKey] = mapFile
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapFile.encoding != "", mapFile.encoding, mapFile, nil
|
reader := &serveContentReader{Reader: bytes.NewReader(mapFile.data)}
|
||||||
|
return mapFile.encoding != "", mapFile.encoding, mapFile, reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOk(s *serveContentHolder, fi os.FileInfo) bool {
|
func isOk(s *serveContentHolder, fi os.FileInfo) bool {
|
||||||
|
@ -16,7 +16,7 @@ var licenseFile = filepath.Join(currentWorkDir, "LICENSE")
|
|||||||
|
|
||||||
func testOpenFile(encoding string, content []byte, t *testing.T) {
|
func testOpenFile(encoding string, content []byte, t *testing.T) {
|
||||||
fi, _ := os.Stat(licenseFile)
|
fi, _ := os.Stat(licenseFile)
|
||||||
b, n, sch, err := openFile(licenseFile, fi, encoding)
|
b, n, sch, reader, err := openFile(licenseFile, fi, encoding)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@ -24,7 +24,7 @@ func testOpenFile(encoding string, content []byte, t *testing.T) {
|
|||||||
|
|
||||||
t.Log("open static file encoding "+n, b)
|
t.Log("open static file encoding "+n, b)
|
||||||
|
|
||||||
assetOpenFileAndContent(sch, content, t)
|
assetOpenFileAndContent(sch, reader, content, t)
|
||||||
}
|
}
|
||||||
func TestOpenStaticFile_1(t *testing.T) {
|
func TestOpenStaticFile_1(t *testing.T) {
|
||||||
file, _ := os.Open(licenseFile)
|
file, _ := os.Open(licenseFile)
|
||||||
@ -53,13 +53,13 @@ func TestOpenStaticFileDeflate_1(t *testing.T) {
|
|||||||
testOpenFile("deflate", content, t)
|
testOpenFile("deflate", content, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assetOpenFileAndContent(sch *serveContentHolder, content []byte, t *testing.T) {
|
func assetOpenFileAndContent(sch *serveContentHolder, reader *serveContentReader, content []byte, t *testing.T) {
|
||||||
t.Log(sch.size, len(content))
|
t.Log(sch.size, len(content))
|
||||||
if sch.size != int64(len(content)) {
|
if sch.size != int64(len(content)) {
|
||||||
t.Log("static content file size not same")
|
t.Log("static content file size not same")
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
bs, _ := ioutil.ReadAll(sch)
|
bs, _ := ioutil.ReadAll(reader)
|
||||||
for i, v := range content {
|
for i, v := range content {
|
||||||
if v != bs[i] {
|
if v != bs[i] {
|
||||||
t.Log("content not same")
|
t.Log("content not same")
|
||||||
|
@ -122,6 +122,7 @@ type Schema struct {
|
|||||||
Items *Schema `json:"items,omitempty" yaml:"items,omitempty"`
|
Items *Schema `json:"items,omitempty" yaml:"items,omitempty"`
|
||||||
Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
|
Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||||
Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
|
Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
|
||||||
|
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification
|
// Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification
|
||||||
@ -131,7 +132,7 @@ type Propertie struct {
|
|||||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
|
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||||
Example string `json:"example,omitempty" yaml:"example,omitempty"`
|
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
|
||||||
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||||
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||||
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
|
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
|
||||||
|
@ -218,6 +218,7 @@ func BuildTemplate(dir string, files ...string) error {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Error("parse template err:", file, err)
|
logs.Error("parse template err:", file, err)
|
||||||
|
templatesLock.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
beeTemplates[file] = t
|
beeTemplates[file] = t
|
||||||
|
@ -17,6 +17,7 @@ package beego
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -207,14 +208,12 @@ func Htmlquote(text string) string {
|
|||||||
'<'&">'
|
'<'&">'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
text = strings.Replace(text, "&", "&", -1) // Must be done first!
|
text = html.EscapeString(text)
|
||||||
text = strings.Replace(text, "<", "<", -1)
|
text = strings.NewReplacer(
|
||||||
text = strings.Replace(text, ">", ">", -1)
|
`“`, "“",
|
||||||
text = strings.Replace(text, "'", "'", -1)
|
`”`, "”",
|
||||||
text = strings.Replace(text, "\"", """, -1)
|
` `, " ",
|
||||||
text = strings.Replace(text, "“", "“", -1)
|
).Replace(text)
|
||||||
text = strings.Replace(text, "”", "”", -1)
|
|
||||||
text = strings.Replace(text, " ", " ", -1)
|
|
||||||
|
|
||||||
return strings.TrimSpace(text)
|
return strings.TrimSpace(text)
|
||||||
}
|
}
|
||||||
@ -228,17 +227,7 @@ func Htmlunquote(text string) string {
|
|||||||
'<\\'&">'
|
'<\\'&">'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// strings.Replace(s, old, new, n)
|
text = html.UnescapeString(text)
|
||||||
// 在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换
|
|
||||||
|
|
||||||
text = strings.Replace(text, " ", " ", -1)
|
|
||||||
text = strings.Replace(text, "”", "”", -1)
|
|
||||||
text = strings.Replace(text, "“", "“", -1)
|
|
||||||
text = strings.Replace(text, """, "\"", -1)
|
|
||||||
text = strings.Replace(text, "'", "'", -1)
|
|
||||||
text = strings.Replace(text, ">", ">", -1)
|
|
||||||
text = strings.Replace(text, "<", "<", -1)
|
|
||||||
text = strings.Replace(text, "&", "&", -1) // Must be done last!
|
|
||||||
|
|
||||||
return strings.TrimSpace(text)
|
return strings.TrimSpace(text)
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func TestCompareRelated(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHtmlquote(t *testing.T) {
|
func TestHtmlquote(t *testing.T) {
|
||||||
h := `<' ”“&">`
|
h := `<' ”“&">`
|
||||||
s := `<' ”“&">`
|
s := `<' ”“&">`
|
||||||
if Htmlquote(s) != h {
|
if Htmlquote(s) != h {
|
||||||
t.Error("should be equal")
|
t.Error("should be equal")
|
||||||
@ -102,8 +102,8 @@ func TestHtmlquote(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHtmlunquote(t *testing.T) {
|
func TestHtmlunquote(t *testing.T) {
|
||||||
h := `<' ”“&">`
|
h := `<' ”“&">`
|
||||||
s := `<' ”“&">`
|
s := `<' ”“&">`
|
||||||
if Htmlunquote(h) != s {
|
if Htmlunquote(h) != s {
|
||||||
t.Error("should be equal")
|
t.Error("should be equal")
|
||||||
}
|
}
|
||||||
|
@ -245,9 +245,23 @@ func (v *Validation) ZipCode(obj interface{}, key string) *Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Validation) apply(chk Validator, obj interface{}) *Result {
|
func (v *Validation) apply(chk Validator, obj interface{}) *Result {
|
||||||
|
if nil == obj {
|
||||||
if chk.IsSatisfied(obj) {
|
if chk.IsSatisfied(obj) {
|
||||||
return &Result{Ok: true}
|
return &Result{Ok: true}
|
||||||
}
|
}
|
||||||
|
} else if reflect.TypeOf(obj).Kind() == reflect.Ptr {
|
||||||
|
if reflect.ValueOf(obj).IsNil() {
|
||||||
|
if chk.IsSatisfied(nil) {
|
||||||
|
return &Result{Ok: true}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if chk.IsSatisfied(reflect.ValueOf(obj).Elem().Interface()) {
|
||||||
|
return &Result{Ok: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if chk.IsSatisfied(obj) {
|
||||||
|
return &Result{Ok: true}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the error to the validation context.
|
// Add the error to the validation context.
|
||||||
key := chk.GetKey()
|
key := chk.GetKey()
|
||||||
@ -351,13 +365,24 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasReuired bool
|
var hasRequired bool
|
||||||
for _, vf := range vfs {
|
for _, vf := range vfs {
|
||||||
if vf.Name == "Required" {
|
if vf.Name == "Required" {
|
||||||
hasReuired = true
|
hasRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasReuired && v.RequiredFirst && len(objV.Field(i).String()) == 0 {
|
currentField := objV.Field(i).Interface()
|
||||||
|
if objV.Field(i).Kind() == reflect.Ptr {
|
||||||
|
if objV.Field(i).IsNil() {
|
||||||
|
currentField = ""
|
||||||
|
} else {
|
||||||
|
currentField = objV.Field(i).Elem().Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
chk := Required{""}.IsSatisfied(currentField)
|
||||||
|
if !hasRequired && v.RequiredFirst && !chk {
|
||||||
if _, ok := CanSkipFuncs[vf.Name]; ok {
|
if _, ok := CanSkipFuncs[vf.Name]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -414,3 +439,9 @@ func (v *Validation) RecursiveValid(objc interface{}) (bool, error) {
|
|||||||
}
|
}
|
||||||
return pass, err
|
return pass, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Validation) CanSkipAlso(skipFunc string) {
|
||||||
|
if _, ok := CanSkipFuncs[skipFunc]; !ok {
|
||||||
|
CanSkipFuncs[skipFunc] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -442,3 +442,122 @@ func TestSkipValid(t *testing.T) {
|
|||||||
t.Fatal("validation should be passed")
|
t.Fatal("validation should be passed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPointer(t *testing.T) {
|
||||||
|
type User struct {
|
||||||
|
ID int
|
||||||
|
|
||||||
|
Email *string `valid:"Email"`
|
||||||
|
ReqEmail *string `valid:"Required;Email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
u := User{
|
||||||
|
ReqEmail: nil,
|
||||||
|
Email: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
valid := Validation{}
|
||||||
|
b, err := valid.Valid(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if b {
|
||||||
|
t.Fatal("validation should not be passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
validEmail := "a@a.com"
|
||||||
|
u = User{
|
||||||
|
ReqEmail: &validEmail,
|
||||||
|
Email: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = Validation{RequiredFirst: true}
|
||||||
|
b, err = valid.Valid(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !b {
|
||||||
|
t.Fatal("validation should be passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
u = User{
|
||||||
|
ReqEmail: &validEmail,
|
||||||
|
Email: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = Validation{}
|
||||||
|
b, err = valid.Valid(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if b {
|
||||||
|
t.Fatal("validation should not be passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidEmail := "a@a"
|
||||||
|
u = User{
|
||||||
|
ReqEmail: &validEmail,
|
||||||
|
Email: &invalidEmail,
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = Validation{RequiredFirst: true}
|
||||||
|
b, err = valid.Valid(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if b {
|
||||||
|
t.Fatal("validation should not be passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
u = User{
|
||||||
|
ReqEmail: &validEmail,
|
||||||
|
Email: &invalidEmail,
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = Validation{}
|
||||||
|
b, err = valid.Valid(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if b {
|
||||||
|
t.Fatal("validation should not be passed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestCanSkipAlso(t *testing.T) {
|
||||||
|
type User struct {
|
||||||
|
ID int
|
||||||
|
|
||||||
|
Email string `valid:"Email"`
|
||||||
|
ReqEmail string `valid:"Required;Email"`
|
||||||
|
MatchRange int `valid:"Range(10, 20)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
u := User{
|
||||||
|
ReqEmail: "a@a.com",
|
||||||
|
Email: "",
|
||||||
|
MatchRange: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
valid := Validation{RequiredFirst: true}
|
||||||
|
b, err := valid.Valid(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if b {
|
||||||
|
t.Fatal("validation should not be passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = Validation{RequiredFirst: true}
|
||||||
|
valid.CanSkipAlso("Range")
|
||||||
|
b, err = valid.Valid(u)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !b {
|
||||||
|
t.Fatal("validation should be passed")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user