mirror of
https://github.com/astaxie/beego.git
synced 2024-11-22 08:20:55 +00:00
fix one confict
This commit is contained in:
commit
38a2f32252
15
.travis.yml
15
.travis.yml
@ -1,9 +1,8 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.7.5
|
- "1.9.7"
|
||||||
- 1.8.5
|
- "1.10.3"
|
||||||
- 1.9.2
|
|
||||||
services:
|
services:
|
||||||
- redis-server
|
- redis-server
|
||||||
- mysql
|
- mysql
|
||||||
@ -22,10 +21,11 @@ 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
|
||||||
|
- go get gopkg.in/yaml.v2
|
||||||
- go get github.com/belogik/goes
|
- go get github.com/belogik/goes
|
||||||
- go get github.com/siddontang/ledisdb/config
|
- go get github.com/siddontang/ledisdb/config
|
||||||
- go get github.com/siddontang/ledisdb/ledis
|
- go get github.com/siddontang/ledisdb/ledis
|
||||||
@ -38,13 +38,14 @@ 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"
|
||||||
- sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
|
- sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
|
||||||
- sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
|
- sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
|
||||||
- sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi"
|
- sh -c "go get github.com/golang/lint/golint; golint ./...;"
|
||||||
- sh -c "if [ $(go version) == *1.[5-9]* ]; then go tool vet .; fi"
|
- sh -c "go tool vet ."
|
||||||
- mkdir -p res/var
|
- mkdir -p res/var
|
||||||
- ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
|
- ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
|
||||||
after_script:
|
after_script:
|
||||||
@ -58,4 +59,4 @@ script:
|
|||||||
- find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
|
- find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
|
||||||
- golint ./...
|
- golint ./...
|
||||||
addons:
|
addons:
|
||||||
postgresql: "9.4"
|
postgresql: "9.6"
|
||||||
|
12
admin.go
12
admin.go
@ -76,6 +76,18 @@ func adminIndex(rw http.ResponseWriter, r *http.Request) {
|
|||||||
func qpsIndex(rw http.ResponseWriter, r *http.Request) {
|
func qpsIndex(rw http.ResponseWriter, r *http.Request) {
|
||||||
data := make(map[interface{}]interface{})
|
data := make(map[interface{}]interface{})
|
||||||
data["Content"] = toolbox.StatisticsMap.GetMap()
|
data["Content"] = toolbox.StatisticsMap.GetMap()
|
||||||
|
|
||||||
|
// do html escape before display path, avoid xss
|
||||||
|
if content, ok := (data["Content"]).(map[string]interface{}); ok {
|
||||||
|
if resultLists, ok := (content["Data"]).([][]string); ok {
|
||||||
|
for i := range resultLists {
|
||||||
|
if len(resultLists[i]) > 0 {
|
||||||
|
resultLists[i][0] = template.HTMLEscapeString(resultLists[i][0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
execTpl(rw, data, qpsTpl, defaultScriptsTpl)
|
execTpl(rw, data, qpsTpl, defaultScriptsTpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ func oldMap() map[string]interface{} {
|
|||||||
m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
|
m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
|
||||||
m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
|
m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
|
||||||
m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
|
m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
|
||||||
|
m["BConfig.Log.EnableStaticLogs"] = BConfig.Log.EnableStaticLogs
|
||||||
m["BConfig.Log.AccessLogsFormat"] = BConfig.Log.AccessLogsFormat
|
m["BConfig.Log.AccessLogsFormat"] = BConfig.Log.AccessLogsFormat
|
||||||
m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
|
m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
|
||||||
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs
|
||||||
|
32
app.go
32
app.go
@ -24,12 +24,13 @@ 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"
|
||||||
"github.com/astaxie/beego/utils"
|
"github.com/astaxie/beego/utils"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -101,7 +102,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
|
||||||
}
|
}
|
||||||
@ -117,7 +118,7 @@ func (app *App) Run(mws ...MiddleWare) {
|
|||||||
app.Server.Addr = httpsAddr
|
app.Server.Addr = httpsAddr
|
||||||
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(20 * time.Microsecond)
|
time.Sleep(1000 * time.Microsecond)
|
||||||
if BConfig.Listen.HTTPSPort != 0 {
|
if BConfig.Listen.HTTPSPort != 0 {
|
||||||
httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
|
httpsAddr = fmt.Sprintf("%s:%d", BConfig.Listen.HTTPSAddr, BConfig.Listen.HTTPSPort)
|
||||||
app.Server.Addr = httpsAddr
|
app.Server.Addr = httpsAddr
|
||||||
@ -126,13 +127,21 @@ func (app *App) Run(mws ...MiddleWare) {
|
|||||||
server.Server.ReadTimeout = app.Server.ReadTimeout
|
server.Server.ReadTimeout = app.Server.ReadTimeout
|
||||||
server.Server.WriteTimeout = app.Server.WriteTimeout
|
server.Server.WriteTimeout = app.Server.WriteTimeout
|
||||||
if BConfig.Listen.EnableMutualHTTPS {
|
if BConfig.Listen.EnableMutualHTTPS {
|
||||||
|
|
||||||
if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil {
|
if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil {
|
||||||
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||||
time.Sleep(100 * time.Microsecond)
|
time.Sleep(100 * time.Microsecond)
|
||||||
endRunning <- true
|
endRunning <- true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if BConfig.Listen.AutoTLS {
|
||||||
|
m := autocert.Manager{
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...),
|
||||||
|
Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir),
|
||||||
|
}
|
||||||
|
app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
|
||||||
|
BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", ""
|
||||||
|
}
|
||||||
if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
|
||||||
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
|
||||||
time.Sleep(100 * time.Microsecond)
|
time.Sleep(100 * time.Microsecond)
|
||||||
@ -163,15 +172,23 @@ func (app *App) Run(mws ...MiddleWare) {
|
|||||||
// run normal mode
|
// run normal mode
|
||||||
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(20 * time.Microsecond)
|
time.Sleep(1000 * time.Microsecond)
|
||||||
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)
|
||||||
if BConfig.Listen.EnableMutualHTTPS {
|
if BConfig.Listen.AutoTLS {
|
||||||
|
m := autocert.Manager{
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
HostPolicy: autocert.HostWhitelist(BConfig.Listen.Domains...),
|
||||||
|
Cache: autocert.DirCache(BConfig.Listen.TLSCacheDir),
|
||||||
|
}
|
||||||
|
app.Server.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
|
||||||
|
BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile = "", ""
|
||||||
|
} else if BConfig.Listen.EnableMutualHTTPS {
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile)
|
data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -190,6 +207,7 @@ func (app *App) Run(mws ...MiddleWare) {
|
|||||||
endRunning <- true
|
endRunning <- true
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
if BConfig.Listen.EnableHTTP {
|
if BConfig.Listen.EnableHTTP {
|
||||||
go func() {
|
go func() {
|
||||||
|
5
beego.go
5
beego.go
@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// VERSION represent beego web framework version.
|
// VERSION represent beego web framework version.
|
||||||
VERSION = "1.9.2"
|
VERSION = "1.10.0"
|
||||||
|
|
||||||
// DEV is for develop
|
// DEV is for develop
|
||||||
DEV = "dev"
|
DEV = "dev"
|
||||||
@ -62,6 +62,8 @@ func Run(params ...string) {
|
|||||||
if len(strs) > 1 && strs[1] != "" {
|
if len(strs) > 1 && strs[1] != "" {
|
||||||
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
|
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BConfig.Listen.Domains = params
|
||||||
}
|
}
|
||||||
|
|
||||||
BeeApp.Run()
|
BeeApp.Run()
|
||||||
@ -74,6 +76,7 @@ func RunWithMiddleWares(addr string, mws ...MiddleWare) {
|
|||||||
strs := strings.Split(addr, ":")
|
strs := strings.Split(addr, ":")
|
||||||
if len(strs) > 0 && strs[0] != "" {
|
if len(strs) > 0 && strs[0] != "" {
|
||||||
BConfig.Listen.HTTPAddr = strs[0]
|
BConfig.Listen.HTTPAddr = strs[0]
|
||||||
|
BConfig.Listen.Domains = []string{strs[0]}
|
||||||
}
|
}
|
||||||
if len(strs) > 1 && strs[1] != "" {
|
if len(strs) > 1 && strs[1] != "" {
|
||||||
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
|
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
|
||||||
|
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) {
|
||||||
|
27
config.go
27
config.go
@ -55,6 +55,9 @@ type Listen struct {
|
|||||||
EnableHTTP bool
|
EnableHTTP bool
|
||||||
HTTPAddr string
|
HTTPAddr string
|
||||||
HTTPPort int
|
HTTPPort int
|
||||||
|
AutoTLS bool
|
||||||
|
Domains []string
|
||||||
|
TLSCacheDir string
|
||||||
EnableHTTPS bool
|
EnableHTTPS bool
|
||||||
EnableMutualHTTPS bool
|
EnableMutualHTTPS bool
|
||||||
HTTPSAddr string
|
HTTPSAddr string
|
||||||
@ -98,14 +101,15 @@ type SessionConfig struct {
|
|||||||
SessionAutoSetCookie bool
|
SessionAutoSetCookie bool
|
||||||
SessionDomain string
|
SessionDomain string
|
||||||
SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies.
|
SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies.
|
||||||
SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
|
SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
|
||||||
SessionNameInHTTPHeader string
|
SessionNameInHTTPHeader string
|
||||||
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
|
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogConfig holds Log related config
|
// LogConfig holds Log related config
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
AccessLogs bool
|
AccessLogs bool
|
||||||
|
EnableStaticLogs bool //log static files requests default: false
|
||||||
AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
|
AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string
|
||||||
FileLineNum bool
|
FileLineNum bool
|
||||||
Outputs map[string]string // Store Adaptor : config
|
Outputs map[string]string // Store Adaptor : config
|
||||||
@ -138,8 +142,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) {
|
||||||
@ -182,13 +186,18 @@ func recoverPanic(ctx *context.Context) {
|
|||||||
if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
|
if BConfig.RunMode == DEV && BConfig.EnableErrorsRender {
|
||||||
showErr(err, ctx, stack)
|
showErr(err, ctx, stack)
|
||||||
}
|
}
|
||||||
|
if ctx.Output.Status != 0 {
|
||||||
|
ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
|
||||||
|
} else {
|
||||||
|
ctx.ResponseWriter.WriteHeader(500)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
@ -203,6 +212,9 @@ func newBConfig() *Config {
|
|||||||
ServerTimeOut: 0,
|
ServerTimeOut: 0,
|
||||||
ListenTCP4: false,
|
ListenTCP4: false,
|
||||||
EnableHTTP: true,
|
EnableHTTP: true,
|
||||||
|
AutoTLS: false,
|
||||||
|
Domains: []string{},
|
||||||
|
TLSCacheDir: ".",
|
||||||
HTTPAddr: "",
|
HTTPAddr: "",
|
||||||
HTTPPort: 8080,
|
HTTPPort: 8080,
|
||||||
EnableHTTPS: false,
|
EnableHTTPS: false,
|
||||||
@ -240,13 +252,14 @@ func newBConfig() *Config {
|
|||||||
SessionCookieLifeTime: 0, //set cookie default is the browser life
|
SessionCookieLifeTime: 0, //set cookie default is the browser life
|
||||||
SessionAutoSetCookie: true,
|
SessionAutoSetCookie: true,
|
||||||
SessionDomain: "",
|
SessionDomain: "",
|
||||||
SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
|
SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
|
||||||
SessionNameInHTTPHeader: "Beegosessionid",
|
SessionNameInHTTPHeader: "Beegosessionid",
|
||||||
SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
|
SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Log: LogConfig{
|
Log: LogConfig{
|
||||||
AccessLogs: false,
|
AccessLogs: false,
|
||||||
|
EnableStaticLogs: false,
|
||||||
AccessLogsFormat: "APACHE_FORMAT",
|
AccessLogsFormat: "APACHE_FORMAT",
|
||||||
FileLineNum: true,
|
FileLineNum: true,
|
||||||
Outputs: map[string]string{"console": ""},
|
Outputs: map[string]string{"console": ""},
|
||||||
|
@ -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 {
|
||||||
|
@ -119,7 +119,7 @@ func parseYML(buf []byte) (cnf map[string]interface{}, err error) {
|
|||||||
// ConfigContainer A Config represents the yaml configuration.
|
// ConfigContainer A Config represents the yaml configuration.
|
||||||
type ConfigContainer struct {
|
type ConfigContainer struct {
|
||||||
data map[string]interface{}
|
data map[string]interface{}
|
||||||
sync.Mutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bool returns the boolean value for a given key.
|
// Bool returns the boolean value for a given key.
|
||||||
@ -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,9 +285,25 @@ 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")
|
||||||
}
|
}
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
|
||||||
if v, ok := c.data[key]; ok {
|
keys := strings.Split(key, ".")
|
||||||
return v, nil
|
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 nil, fmt.Errorf("not exist key %q", key)
|
return nil, fmt.Errorf("not exist key %q", key)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func TestAssignConfig_02(t *testing.T) {
|
|||||||
|
|
||||||
jcf := &config.JSONConfig{}
|
jcf := &config.JSONConfig{}
|
||||||
bs, _ = json.Marshal(configMap)
|
bs, _ = json.Marshal(configMap)
|
||||||
ac, _ := jcf.ParseData([]byte(bs))
|
ac, _ := jcf.ParseData(bs)
|
||||||
|
|
||||||
for _, i := range []interface{}{_BConfig, &_BConfig.Listen, &_BConfig.WebConfig, &_BConfig.Log, &_BConfig.WebConfig.Session} {
|
for _, i := range []interface{}{_BConfig, &_BConfig.Listen, &_BConfig.WebConfig, &_BConfig.Log, &_BConfig.WebConfig.Session} {
|
||||||
assignSingleConfig(i, ac)
|
assignSingleConfig(i, ac)
|
||||||
|
@ -37,6 +37,7 @@ var (
|
|||||||
acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
|
acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
|
||||||
acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
|
acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
|
||||||
acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
|
acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
|
||||||
|
acceptsYAMLRegex = regexp.MustCompile(`(application/x-yaml)(?:,|$)`)
|
||||||
maxParam = 50
|
maxParam = 50
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -203,6 +204,10 @@ func (input *BeegoInput) AcceptsXML() bool {
|
|||||||
func (input *BeegoInput) AcceptsJSON() bool {
|
func (input *BeegoInput) AcceptsJSON() bool {
|
||||||
return acceptsJSONRegex.MatchString(input.Header("Accept"))
|
return acceptsJSONRegex.MatchString(input.Header("Accept"))
|
||||||
}
|
}
|
||||||
|
// AcceptsYAML Checks if request accepts json response
|
||||||
|
func (input *BeegoInput) AcceptsYAML() bool {
|
||||||
|
return acceptsYAMLRegex.MatchString(input.Header("Accept"))
|
||||||
|
}
|
||||||
|
|
||||||
// IP returns request client ip.
|
// IP returns request client ip.
|
||||||
// if in proxy, return first proxy id.
|
// if in proxy, return first proxy id.
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BeegoOutput does work for sending response header.
|
// BeegoOutput does work for sending response header.
|
||||||
@ -182,8 +183,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,12 +197,26 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// YAML writes yaml to response body.
|
||||||
|
func (output *BeegoOutput) YAML(data interface{}) error {
|
||||||
|
output.Header("Content-Type", "application/application/x-yaml; charset=utf-8")
|
||||||
|
var content []byte
|
||||||
|
var err error
|
||||||
|
content, err = yaml.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return output.Body(content)
|
||||||
|
}
|
||||||
|
|
||||||
// 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")
|
||||||
@ -260,7 +275,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 +340,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 +365,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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
applicationJSON = "application/json"
|
applicationJSON = "application/json"
|
||||||
applicationXML = "application/xml"
|
applicationXML = "application/xml"
|
||||||
|
applicationYAML = "application/x-yaml"
|
||||||
textXML = "text/xml"
|
textXML = "text/xml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -272,7 +273,22 @@ func (c *Controller) viewPath() string {
|
|||||||
|
|
||||||
// Redirect sends the redirection response to url with status code.
|
// Redirect sends the redirection response to url with status code.
|
||||||
func (c *Controller) Redirect(url string, code int) {
|
func (c *Controller) Redirect(url string, code int) {
|
||||||
|
logAccess(c.Ctx, nil, code)
|
||||||
c.Ctx.Redirect(code, url)
|
c.Ctx.Redirect(code, url)
|
||||||
|
panic(ErrAbort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
@ -347,6 +363,11 @@ func (c *Controller) ServeXML() {
|
|||||||
c.Ctx.Output.XML(c.Data["xml"], hasIndent)
|
c.Ctx.Output.XML(c.Data["xml"], hasIndent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeXML sends xml response.
|
||||||
|
func (c *Controller) ServeYAML() {
|
||||||
|
c.Ctx.Output.YAML(c.Data["yaml"])
|
||||||
|
}
|
||||||
|
|
||||||
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header
|
// ServeFormatted serve Xml OR Json, depending on the value of the Accept header
|
||||||
func (c *Controller) ServeFormatted() {
|
func (c *Controller) ServeFormatted() {
|
||||||
accept := c.Ctx.Input.Header("Accept")
|
accept := c.Ctx.Input.Header("Accept")
|
||||||
@ -355,6 +376,8 @@ func (c *Controller) ServeFormatted() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
case applicationXML, textXML:
|
case applicationXML, textXML:
|
||||||
c.ServeXML()
|
c.ServeXML()
|
||||||
|
case applicationYAML:
|
||||||
|
c.ServeYAML()
|
||||||
default:
|
default:
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
10
error.go
10
error.go
@ -28,7 +28,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errorTypeHandler = iota
|
errorTypeHandler = iota
|
||||||
errorTypeController
|
errorTypeController
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -93,11 +93,6 @@ func showErr(err interface{}, ctx *context.Context, stack string) {
|
|||||||
"BeegoVersion": VERSION,
|
"BeegoVersion": VERSION,
|
||||||
"GoVersion": runtime.Version(),
|
"GoVersion": runtime.Version(),
|
||||||
}
|
}
|
||||||
if ctx.Output.Status != 0 {
|
|
||||||
ctx.ResponseWriter.WriteHeader(ctx.Output.Status)
|
|
||||||
} else {
|
|
||||||
ctx.ResponseWriter.WriteHeader(500)
|
|
||||||
}
|
|
||||||
t.Execute(ctx.ResponseWriter, data)
|
t.Execute(ctx.ResponseWriter, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,6 +434,9 @@ func exception(errCode string, ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func executeError(err *errorInfo, ctx *context.Context, code int) {
|
func executeError(err *errorInfo, ctx *context.Context, code int) {
|
||||||
|
//make sure to log the error in the access log
|
||||||
|
logAccess(ctx, nil, code)
|
||||||
|
|
||||||
if err.errorType == errorTypeHandler {
|
if err.errorType == errorTypeHandler {
|
||||||
ctx.ResponseWriter.WriteHeader(code)
|
ctx.ResponseWriter.WriteHeader(code)
|
||||||
err.handler(ctx.ResponseWriter, ctx.Request)
|
err.handler(ctx.ResponseWriter, ctx.Request)
|
||||||
|
@ -50,6 +50,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultSetting = BeegoHTTPSettings{
|
var defaultSetting = BeegoHTTPSettings{
|
||||||
@ -317,6 +318,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 +332,21 @@ func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
|||||||
}
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YAMLBody adds request raw body encoding by YAML.
|
||||||
|
func (b *BeegoHTTPRequest) YAMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
|
||||||
|
if b.req.Body == nil && obj != nil {
|
||||||
|
byts, err := yaml.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
b.req.Body = ioutil.NopCloser(bytes.NewReader(byts))
|
||||||
|
b.req.ContentLength = int64(len(byts))
|
||||||
|
b.req.Header.Set("Content-Type", "application/x+yaml")
|
||||||
|
}
|
||||||
|
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 {
|
||||||
@ -429,12 +446,12 @@ func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.buildURL(paramBody)
|
b.buildURL(paramBody)
|
||||||
url, err := url.Parse(b.url)
|
urlParsed, err := url.Parse(b.url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.req.URL = url
|
b.req.URL = urlParsed
|
||||||
|
|
||||||
trans := b.setting.Transport
|
trans := b.setting.Transport
|
||||||
|
|
||||||
@ -444,7 +461,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.
|
||||||
@ -579,6 +596,16 @@ func (b *BeegoHTTPRequest) ToXML(v interface{}) error {
|
|||||||
return xml.Unmarshal(data, v)
|
return xml.Unmarshal(data, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToYAML returns the map that marshals from the body bytes as yaml in response .
|
||||||
|
// it calls Response inner.
|
||||||
|
func (b *BeegoHTTPRequest) ToYAML(v interface{}) error {
|
||||||
|
data, err := b.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return yaml.Unmarshal(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
// Response executes request client gets response mannually.
|
// Response executes request client gets response mannually.
|
||||||
func (b *BeegoHTTPRequest) Response() (*http.Response, error) {
|
func (b *BeegoHTTPRequest) Response() (*http.Response, error) {
|
||||||
return b.getResponse()
|
return b.getResponse()
|
||||||
|
@ -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
|
||||||
"github.com/astaxie/beego/logs"
|
import (
|
||||||
)
|
"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")
|
|
||||||
log.Info("info")
|
|
||||||
log.Warn("warning")
|
|
||||||
log.Debug("debug")
|
|
||||||
log.Critical("critical")
|
|
||||||
|
|
||||||
|
```golang
|
||||||
|
log.Trace("trace")
|
||||||
|
log.Info("info")
|
||||||
|
log.Warn("warning")
|
||||||
|
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)
|
||||||
|
```
|
||||||
|
@ -16,13 +16,14 @@ package logs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"strings"
|
||||||
"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 +54,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,9 +64,7 @@ func disableEscapeHTML(i interface{}) {
|
|||||||
// AccessLog - Format and print access log.
|
// AccessLog - Format and print access log.
|
||||||
func AccessLog(r *AccessLogRecord, format string) {
|
func AccessLog(r *AccessLogRecord, format string) {
|
||||||
var msg string
|
var msg string
|
||||||
|
|
||||||
switch format {
|
switch format {
|
||||||
|
|
||||||
case apacheFormat:
|
case apacheFormat:
|
||||||
timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05")
|
timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05")
|
||||||
msg = fmt.Sprintf(apacheFormatPattern, r.RemoteAddr, timeFormatted, r.Request, r.Status, r.BodyBytesSent,
|
msg = fmt.Sprintf(apacheFormatPattern, r.RemoteAddr, timeFormatted, r.Request, r.Status, r.BodyBytesSent,
|
||||||
@ -81,6 +79,5 @@ func AccessLog(r *AccessLogRecord, format string) {
|
|||||||
msg = string(jsonData)
|
msg = string(jsonData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
beeLogger.writeMsg(levelLoggerImpl, strings.TrimSpace(msg))
|
||||||
beeLogger.Debug(msg)
|
|
||||||
}
|
}
|
||||||
|
36
logs/file.go
36
logs/file.go
@ -21,6 +21,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -40,6 +41,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
|
||||||
@ -78,6 +82,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
|
||||||
}
|
}
|
||||||
@ -184,6 +191,10 @@ func (w *fileLogWriter) createLogFile() (*os.File, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filepath := path.Dir(w.Filename)
|
||||||
|
os.MkdirAll(filepath, os.FileMode(perm))
|
||||||
|
|
||||||
fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
|
fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Make sure file perm is user set perm cause of `os.OpenFile` will obey umask
|
// Make sure file perm is user set perm cause of `os.OpenFile` will obey umask
|
||||||
@ -280,7 +291,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 := ""
|
||||||
format := ""
|
format := ""
|
||||||
var openTime time.Time
|
var openTime time.Time
|
||||||
@ -295,6 +306,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
|||||||
goto RESTART_LOGGER
|
goto RESTART_LOGGER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
if w.Hourly {
|
if w.Hourly {
|
||||||
format = "2006010215"
|
format = "2006010215"
|
||||||
openTime = w.hourlyOpenTime
|
openTime = w.hourlyOpenTime
|
||||||
@ -315,6 +327,18 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
|
|||||||
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", openTime.Format(format), num, w.suffix)
|
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", openTime.Format(format), num, w.suffix)
|
||||||
_, err = os.Lstat(fName)
|
_, err = os.Lstat(fName)
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
// only when one of them be setted, then the file would be splited
|
||||||
|
if w.MaxLines > 0 || w.MaxSize > 0 {
|
||||||
|
for ; err == nil && num <= w.MaxFiles; num++ {
|
||||||
|
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, w.suffix)
|
||||||
|
_, err = os.Lstat(fName)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fName = w.fileNameOnly + fmt.Sprintf(".%s.%03d%s", w.dailyOpenTime.Format("2006-01-02"), num, w.suffix)
|
||||||
|
_, err = os.Lstat(fName)
|
||||||
|
w.MaxFilesCurFiles = num
|
||||||
|
>>>>>>> old/develop
|
||||||
}
|
}
|
||||||
// return error if the last file checked still existed
|
// return error if the last file checked still existed
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -359,6 +383,7 @@ func (w *fileLogWriter) deleteOldLog() {
|
|||||||
if info == nil {
|
if info == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
if w.Hourly {
|
if w.Hourly {
|
||||||
if !info.IsDir() && info.ModTime().Add(1*time.Hour*time.Duration(w.MaxHours)).Before(time.Now()) {
|
if !info.IsDir() && info.ModTime().Add(1*time.Hour*time.Duration(w.MaxHours)).Before(time.Now()) {
|
||||||
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.fileNameOnly)) &&
|
||||||
@ -374,6 +399,15 @@ func (w *fileLogWriter) deleteOldLog() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
|
||||||
|
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)) &&
|
||||||
|
strings.HasSuffix(filepath.Base(path), w.suffix) {
|
||||||
|
os.Remove(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>>>>>>> old/develop
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ func TestFileDailyRotate_01(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileDailyRotate_02(t *testing.T) {
|
func TestFileDailyRotate_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, true, false)
|
testFileRotate(t, fn1, fn2, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ func TestFileDailyRotate_03(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileDailyRotate_04(t *testing.T) {
|
func TestFileDailyRotate_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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +286,7 @@ func testFileRotate(t *testing.T, fn1, fn2 string, daily, hourly bool) {
|
|||||||
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)
|
||||||
|
23
logs/log.go
23
logs/log.go
@ -47,7 +47,7 @@ import (
|
|||||||
|
|
||||||
// RFC5424 log message levels.
|
// RFC5424 log message levels.
|
||||||
const (
|
const (
|
||||||
LevelEmergency = iota
|
LevelEmergency = iota
|
||||||
LevelAlert
|
LevelAlert
|
||||||
LevelCritical
|
LevelCritical
|
||||||
LevelError
|
LevelError
|
||||||
@ -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, int) {
|
func formatTimeHeader(when time.Time) ([]byte, int, 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
|
||||||
|
|
||||||
|
@ -30,8 +30,13 @@ func TestFormatHeader_0(t *testing.T) {
|
|||||||
if tm.Year() >= 2100 {
|
if tm.Year() >= 2100 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
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.999 ") != string(h) {
|
||||||
|
=======
|
||||||
|
h, _ := formatTimeHeader(tm)
|
||||||
|
if tm.Format("2006/01/02 15:04:05.000 ") != string(h) {
|
||||||
|
>>>>>>> old/develop
|
||||||
t.Log(tm)
|
t.Log(tm)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@ -48,8 +53,13 @@ func TestFormatHeader_1(t *testing.T) {
|
|||||||
if tm.Year() >= year+1 {
|
if tm.Year() >= year+1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
<<<<<<< HEAD
|
||||||
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.999 ") != string(h) {
|
||||||
|
=======
|
||||||
|
h, _ := formatTimeHeader(tm)
|
||||||
|
if tm.Format("2006/01/02 15:04:05.000 ") != string(h) {
|
||||||
|
>>>>>>> old/develop
|
||||||
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 += ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,10 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
|
|||||||
if strings.Contains(column, "%COL%") {
|
if strings.Contains(column, "%COL%") {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -536,6 +536,8 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a
|
|||||||
updates := make([]string, len(names))
|
updates := make([]string, len(names))
|
||||||
var conflitValue interface{}
|
var conflitValue interface{}
|
||||||
for i, v := range names {
|
for i, v := range names {
|
||||||
|
// identifier in database may not be case-sensitive, so quote it
|
||||||
|
v = fmt.Sprintf("%s%s%s", Q, v, Q)
|
||||||
marks[i] = "?"
|
marks[i] = "?"
|
||||||
valueStr := argsMap[strings.ToLower(v)]
|
valueStr := argsMap[strings.ToLower(v)]
|
||||||
if v == args0 {
|
if v == args0 {
|
||||||
@ -969,6 +971,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
|
||||||
|
@ -86,7 +86,7 @@ func (e *BooleanField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Bool()
|
v, err := StrTo(d).Bool()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -191,7 +191,7 @@ func (e *TimeField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := timeParse(d, formatTime)
|
v, err := timeParse(d, formatTime)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -250,7 +250,7 @@ func (e *DateField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := timeParse(d, formatDate)
|
v, err := timeParse(d, formatDate)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -300,7 +300,7 @@ func (e *DateTimeField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := timeParse(d, formatDateTime)
|
v, err := timeParse(d, formatDateTime)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -350,9 +350,10 @@ func (e *FloatField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Float64()
|
v, err := StrTo(d).Float64()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<FloatField.SetRaw> unknown value `%s`", value)
|
return fmt.Errorf("<FloatField.SetRaw> unknown value `%s`", value)
|
||||||
}
|
}
|
||||||
@ -397,9 +398,10 @@ func (e *SmallIntegerField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Int16()
|
v, err := StrTo(d).Int16()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<SmallIntegerField.SetRaw> unknown value `%s`", value)
|
return fmt.Errorf("<SmallIntegerField.SetRaw> unknown value `%s`", value)
|
||||||
}
|
}
|
||||||
@ -444,9 +446,10 @@ func (e *IntegerField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Int32()
|
v, err := StrTo(d).Int32()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<IntegerField.SetRaw> unknown value `%s`", value)
|
return fmt.Errorf("<IntegerField.SetRaw> unknown value `%s`", value)
|
||||||
}
|
}
|
||||||
@ -491,9 +494,10 @@ func (e *BigIntegerField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Int64()
|
v, err := StrTo(d).Int64()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<BigIntegerField.SetRaw> unknown value `%s`", value)
|
return fmt.Errorf("<BigIntegerField.SetRaw> unknown value `%s`", value)
|
||||||
}
|
}
|
||||||
@ -538,9 +542,10 @@ func (e *PositiveSmallIntegerField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Uint16()
|
v, err := StrTo(d).Uint16()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<PositiveSmallIntegerField.SetRaw> unknown value `%s`", value)
|
return fmt.Errorf("<PositiveSmallIntegerField.SetRaw> unknown value `%s`", value)
|
||||||
}
|
}
|
||||||
@ -585,9 +590,10 @@ func (e *PositiveIntegerField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Uint32()
|
v, err := StrTo(d).Uint32()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<PositiveIntegerField.SetRaw> unknown value `%s`", value)
|
return fmt.Errorf("<PositiveIntegerField.SetRaw> unknown value `%s`", value)
|
||||||
}
|
}
|
||||||
@ -632,9 +638,10 @@ func (e *PositiveBigIntegerField) SetRaw(value interface{}) error {
|
|||||||
e.Set(d)
|
e.Set(d)
|
||||||
case string:
|
case string:
|
||||||
v, err := StrTo(d).Uint64()
|
v, err := StrTo(d).Uint64()
|
||||||
if err != nil {
|
if err == nil {
|
||||||
e.Set(v)
|
e.Set(v)
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("<PositiveBigIntegerField.SetRaw> unknown value `%s`", value)
|
return fmt.Errorf("<PositiveBigIntegerField.SetRaw> unknown value `%s`", value)
|
||||||
}
|
}
|
||||||
|
@ -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"]
|
||||||
|
@ -75,7 +75,8 @@ func addModelFields(mi *modelInfo, ind reflect.Value, mName string, index []int)
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
//record current field index
|
//record current field index
|
||||||
fi.fieldIndex = append(index, i)
|
fi.fieldIndex = append(fi.fieldIndex, index...)
|
||||||
|
fi.fieldIndex = append(fi.fieldIndex, i)
|
||||||
fi.mi = mi
|
fi.mi = mi
|
||||||
fi.inModel = true
|
fi.inModel = true
|
||||||
if !mi.fields.Add(fi) {
|
if !mi.fields.Add(fi) {
|
||||||
|
@ -433,53 +433,57 @@ var (
|
|||||||
dDbBaser dbBaser
|
dDbBaser dbBaser
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
helpinfo = `need driver and source!
|
||||||
|
|
||||||
|
Default DB Drivers.
|
||||||
|
|
||||||
|
driver: url
|
||||||
|
mysql: https://github.com/go-sql-driver/mysql
|
||||||
|
sqlite3: https://github.com/mattn/go-sqlite3
|
||||||
|
postgres: https://github.com/lib/pq
|
||||||
|
tidb: https://github.com/pingcap/tidb
|
||||||
|
|
||||||
|
usage:
|
||||||
|
|
||||||
|
go get -u github.com/astaxie/beego/orm
|
||||||
|
go get -u github.com/go-sql-driver/mysql
|
||||||
|
go get -u github.com/mattn/go-sqlite3
|
||||||
|
go get -u github.com/lib/pq
|
||||||
|
go get -u github.com/pingcap/tidb
|
||||||
|
|
||||||
|
#### MySQL
|
||||||
|
mysql -u root -e 'create database orm_test;'
|
||||||
|
export ORM_DRIVER=mysql
|
||||||
|
export ORM_SOURCE="root:@/orm_test?charset=utf8"
|
||||||
|
go test -v github.com/astaxie/beego/orm
|
||||||
|
|
||||||
|
|
||||||
|
#### Sqlite3
|
||||||
|
export ORM_DRIVER=sqlite3
|
||||||
|
export ORM_SOURCE='file:memory_test?mode=memory'
|
||||||
|
go test -v github.com/astaxie/beego/orm
|
||||||
|
|
||||||
|
|
||||||
|
#### PostgreSQL
|
||||||
|
psql -c 'create database orm_test;' -U postgres
|
||||||
|
export ORM_DRIVER=postgres
|
||||||
|
export ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
|
||||||
|
go test -v github.com/astaxie/beego/orm
|
||||||
|
|
||||||
|
#### TiDB
|
||||||
|
export ORM_DRIVER=tidb
|
||||||
|
export ORM_SOURCE='memory://test/test'
|
||||||
|
go test -v github.com/astaxie/beego/orm
|
||||||
|
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Debug, _ = StrTo(DBARGS.Debug).Bool()
|
Debug, _ = StrTo(DBARGS.Debug).Bool()
|
||||||
|
|
||||||
if DBARGS.Driver == "" || DBARGS.Source == "" {
|
if DBARGS.Driver == "" || DBARGS.Source == "" {
|
||||||
fmt.Println(`need driver and source!
|
fmt.Println(helpinfo)
|
||||||
|
|
||||||
Default DB Drivers.
|
|
||||||
|
|
||||||
driver: url
|
|
||||||
mysql: https://github.com/go-sql-driver/mysql
|
|
||||||
sqlite3: https://github.com/mattn/go-sqlite3
|
|
||||||
postgres: https://github.com/lib/pq
|
|
||||||
tidb: https://github.com/pingcap/tidb
|
|
||||||
|
|
||||||
usage:
|
|
||||||
|
|
||||||
go get -u github.com/astaxie/beego/orm
|
|
||||||
go get -u github.com/go-sql-driver/mysql
|
|
||||||
go get -u github.com/mattn/go-sqlite3
|
|
||||||
go get -u github.com/lib/pq
|
|
||||||
go get -u github.com/pingcap/tidb
|
|
||||||
|
|
||||||
#### MySQL
|
|
||||||
mysql -u root -e 'create database orm_test;'
|
|
||||||
export ORM_DRIVER=mysql
|
|
||||||
export ORM_SOURCE="root:@/orm_test?charset=utf8"
|
|
||||||
go test -v github.com/astaxie/beego/orm
|
|
||||||
|
|
||||||
|
|
||||||
#### Sqlite3
|
|
||||||
export ORM_DRIVER=sqlite3
|
|
||||||
export ORM_SOURCE='file:memory_test?mode=memory'
|
|
||||||
go test -v github.com/astaxie/beego/orm
|
|
||||||
|
|
||||||
|
|
||||||
#### PostgreSQL
|
|
||||||
psql -c 'create database orm_test;' -U postgres
|
|
||||||
export ORM_DRIVER=postgres
|
|
||||||
export ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable"
|
|
||||||
go test -v github.com/astaxie/beego/orm
|
|
||||||
|
|
||||||
#### TiDB
|
|
||||||
export ORM_DRIVER=tidb
|
|
||||||
export ORM_SOURCE='memory://test/test'
|
|
||||||
go test -v github.com/astaxie/beego/orm
|
|
||||||
|
|
||||||
`)
|
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
// Package orm provide ORM for MySQL/PostgreSQL/sqlite
|
// Package orm provide ORM for MySQL/PostgreSQL/sqlite
|
||||||
// Simple Usage
|
// Simple Usage
|
||||||
//
|
//
|
||||||
@ -52,6 +54,7 @@
|
|||||||
package orm
|
package orm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -458,11 +461,15 @@ func (o *orm) Using(name string) error {
|
|||||||
|
|
||||||
// begin transaction
|
// begin transaction
|
||||||
func (o *orm) Begin() error {
|
func (o *orm) Begin() error {
|
||||||
|
return o.BeginTx(context.Background(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *orm) BeginTx(ctx context.Context, opts *sql.TxOptions) error {
|
||||||
if o.isTx {
|
if o.isTx {
|
||||||
return ErrTxHasBegan
|
return ErrTxHasBegan
|
||||||
}
|
}
|
||||||
var tx *sql.Tx
|
var tx *sql.Tx
|
||||||
tx, err := o.db.(txer).Begin()
|
tx, err := o.db.(txer).BeginTx(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package orm
|
package orm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -150,6 +151,13 @@ func (d *dbQueryLog) Begin() (*sql.Tx, error) {
|
|||||||
return tx, err
|
return tx, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *dbQueryLog) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) {
|
||||||
|
a := time.Now()
|
||||||
|
tx, err := d.db.(txer).BeginTx(ctx, opts)
|
||||||
|
debugLogQueies(d.alias, "db.BeginTx", "START TRANSACTION", a, err)
|
||||||
|
return tx, err
|
||||||
|
}
|
||||||
|
|
||||||
func (d *dbQueryLog) Commit() error {
|
func (d *dbQueryLog) Commit() error {
|
||||||
a := time.Now()
|
a := time.Now()
|
||||||
err := d.db.(txEnder).Commit()
|
err := d.db.(txEnder).Commit()
|
||||||
|
@ -55,16 +55,17 @@ func ColValue(opt operator, value interface{}) interface{} {
|
|||||||
|
|
||||||
// real query struct
|
// real query struct
|
||||||
type querySet struct {
|
type querySet struct {
|
||||||
mi *modelInfo
|
mi *modelInfo
|
||||||
cond *Condition
|
cond *Condition
|
||||||
related []string
|
related []string
|
||||||
relDepth int
|
relDepth int
|
||||||
limit int64
|
limit int64
|
||||||
offset int64
|
offset int64
|
||||||
groups []string
|
groups []string
|
||||||
orders []string
|
orders []string
|
||||||
distinct bool
|
distinct bool
|
||||||
orm *orm
|
forupdate bool
|
||||||
|
orm *orm
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ QuerySeter = new(querySet)
|
var _ QuerySeter = new(querySet)
|
||||||
@ -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.
|
||||||
|
@ -12,10 +12,13 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
package orm
|
package orm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -452,9 +455,9 @@ func TestNullDataTypes(t *testing.T) {
|
|||||||
throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
|
throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr))
|
||||||
throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
|
throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr))
|
||||||
throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))
|
throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr))
|
||||||
throwFail(t, AssertIs((*d.TimePtr).Format(testTime), timePtr.Format(testTime)))
|
throwFail(t, AssertIs((*d.TimePtr).UTC().Format(testTime), timePtr.UTC().Format(testTime)))
|
||||||
throwFail(t, AssertIs((*d.DatePtr).Format(testDate), datePtr.Format(testDate)))
|
throwFail(t, AssertIs((*d.DatePtr).UTC().Format(testDate), datePtr.UTC().Format(testDate)))
|
||||||
throwFail(t, AssertIs((*d.DateTimePtr).Format(testDateTime), dateTimePtr.Format(testDateTime)))
|
throwFail(t, AssertIs((*d.DateTimePtr).UTC().Format(testDateTime), dateTimePtr.UTC().Format(testDateTime)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDataCustomTypes(t *testing.T) {
|
func TestDataCustomTypes(t *testing.T) {
|
||||||
@ -1008,13 +1011,13 @@ func TestAll(t *testing.T) {
|
|||||||
|
|
||||||
qs = dORM.QueryTable("user")
|
qs = dORM.QueryTable("user")
|
||||||
num, err = qs.Filter("user_name", "nothing").All(&users)
|
num, err = qs.Filter("user_name", "nothing").All(&users)
|
||||||
throwFailNow(t, err)
|
throwFailNow(t, AssertIs(err, ErrNoRows))
|
||||||
throwFailNow(t, AssertIs(num, 0))
|
throwFailNow(t, AssertIs(num, 0))
|
||||||
|
|
||||||
var users3 []*User
|
var users3 []*User
|
||||||
qs = dORM.QueryTable("user")
|
qs = dORM.QueryTable("user")
|
||||||
num, err = qs.Filter("user_name", "nothing").All(&users3)
|
num, err = qs.Filter("user_name", "nothing").All(&users3)
|
||||||
throwFailNow(t, err)
|
throwFailNow(t, AssertIs(err, ErrNoRows))
|
||||||
throwFailNow(t, AssertIs(num, 0))
|
throwFailNow(t, AssertIs(num, 0))
|
||||||
throwFailNow(t, AssertIs(users3 == nil, false))
|
throwFailNow(t, AssertIs(users3 == nil, false))
|
||||||
}
|
}
|
||||||
@ -1990,6 +1993,66 @@ func TestTransaction(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransactionIsolationLevel(t *testing.T) {
|
||||||
|
// this test worked when database support transaction isolation level
|
||||||
|
if IsSqlite {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
o1 := NewOrm()
|
||||||
|
o2 := NewOrm()
|
||||||
|
|
||||||
|
// start two transaction with isolation level repeatable read
|
||||||
|
err := o1.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
|
||||||
|
throwFail(t, err)
|
||||||
|
err = o2.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
|
||||||
|
throwFail(t, err)
|
||||||
|
|
||||||
|
// o1 insert tag
|
||||||
|
var tag Tag
|
||||||
|
tag.Name = "test-transaction"
|
||||||
|
id, err := o1.Insert(&tag)
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(id > 0, true))
|
||||||
|
|
||||||
|
// o2 query tag table, no result
|
||||||
|
num, err := o2.QueryTable("tag").Filter("name", "test-transaction").Count()
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(num, 0))
|
||||||
|
|
||||||
|
// o1 commit
|
||||||
|
o1.Commit()
|
||||||
|
|
||||||
|
// o2 query tag table, still no result
|
||||||
|
num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count()
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(num, 0))
|
||||||
|
|
||||||
|
// o2 commit and query tag table, get the result
|
||||||
|
o2.Commit()
|
||||||
|
num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count()
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(num, 1))
|
||||||
|
|
||||||
|
num, err = o1.QueryTable("tag").Filter("name", "test-transaction").Delete()
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(num, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBeginTxWithContextCanceled(t *testing.T) {
|
||||||
|
o := NewOrm()
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
o.BeginTx(ctx, nil)
|
||||||
|
id, err := o.Insert(&Tag{Name: "test-context"})
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(id > 0, true))
|
||||||
|
|
||||||
|
// cancel the context before commit to make it error
|
||||||
|
cancel()
|
||||||
|
err = o.Commit()
|
||||||
|
throwFail(t, AssertIs(err, context.Canceled))
|
||||||
|
}
|
||||||
|
|
||||||
func TestReadOrCreate(t *testing.T) {
|
func TestReadOrCreate(t *testing.T) {
|
||||||
u := &User{
|
u := &User{
|
||||||
UserName: "Kyle",
|
UserName: "Kyle",
|
||||||
@ -2260,6 +2323,7 @@ func TestIgnoreCaseTag(t *testing.T) {
|
|||||||
throwFail(t, AssertIs(info.fields.GetByName("Name02").column, "Name"))
|
throwFail(t, AssertIs(info.fields.GetByName("Name02").column, "Name"))
|
||||||
throwFail(t, AssertIs(info.fields.GetByName("Name03").column, "name"))
|
throwFail(t, AssertIs(info.fields.GetByName("Name03").column, "name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInsertOrUpdate(t *testing.T) {
|
func TestInsertOrUpdate(t *testing.T) {
|
||||||
RegisterModel(new(User))
|
RegisterModel(new(User))
|
||||||
user := User{UserName: "unique_username133", Status: 1, Password: "o"}
|
user := User{UserName: "unique_username133", Status: 1, Password: "o"}
|
||||||
@ -2297,6 +2361,11 @@ func TestInsertOrUpdate(t *testing.T) {
|
|||||||
throwFailNow(t, AssertIs(user2.Status, test.Status))
|
throwFailNow(t, AssertIs(user2.Status, test.Status))
|
||||||
throwFailNow(t, AssertIs(user2.Password, strings.TrimSpace(test.Password)))
|
throwFailNow(t, AssertIs(user2.Password, strings.TrimSpace(test.Password)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values
|
||||||
|
if IsPostgres {
|
||||||
|
return
|
||||||
|
}
|
||||||
//test3 +
|
//test3 +
|
||||||
_, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status+1")
|
_, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status+1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
17
orm/types.go
17
orm/types.go
@ -15,6 +15,7 @@
|
|||||||
package orm
|
package orm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
@ -106,6 +107,17 @@ type Ormer interface {
|
|||||||
// ...
|
// ...
|
||||||
// err = o.Rollback()
|
// err = o.Rollback()
|
||||||
Begin() error
|
Begin() error
|
||||||
|
// begin transaction with provided context and option
|
||||||
|
// the provided context is used until the transaction is committed or rolled back.
|
||||||
|
// if the context is canceled, the transaction will be rolled back.
|
||||||
|
// the provided TxOptions is optional and may be nil if defaults should be used.
|
||||||
|
// if a non-default isolation level is used that the driver doesn't support, an error will be returned.
|
||||||
|
// for example:
|
||||||
|
// o := NewOrm()
|
||||||
|
// err := o.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
|
||||||
|
// ...
|
||||||
|
// err = o.Rollback()
|
||||||
|
BeginTx(ctx context.Context, opts *sql.TxOptions) error
|
||||||
// commit transaction
|
// commit transaction
|
||||||
Commit() error
|
Commit() error
|
||||||
// rollback transaction
|
// rollback transaction
|
||||||
@ -190,6 +202,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()
|
||||||
@ -397,6 +413,7 @@ type dbQuerier interface {
|
|||||||
// transaction beginner
|
// transaction beginner
|
||||||
type txer interface {
|
type txer interface {
|
||||||
Begin() (*sql.Tx, error)
|
Begin() (*sql.Tx, error)
|
||||||
|
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction ending
|
// transaction ending
|
||||||
|
72
parser.go
72
parser.go
@ -114,20 +114,21 @@ 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
|
||||||
}
|
}
|
||||||
if parsedComment.routerPath != "" {
|
for _, parsedComment := range parsedComments {
|
||||||
key := pkgpath + ":" + controllerName
|
if parsedComment.routerPath != "" {
|
||||||
cc := ControllerComments{}
|
key := pkgpath + ":" + controllerName
|
||||||
cc.Method = f.Name.String()
|
cc := ControllerComments{}
|
||||||
cc.Router = parsedComment.routerPath
|
cc.Method = f.Name.String()
|
||||||
cc.AllowHTTPMethods = parsedComment.methods
|
cc.Router = parsedComment.routerPath
|
||||||
cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
|
cc.AllowHTTPMethods = parsedComment.methods
|
||||||
genInfoList[key] = append(genInfoList[key], cc)
|
cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment)
|
||||||
|
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
|
||||||
|
103
router.go
103
router.go
@ -43,7 +43,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
routerTypeBeego = iota
|
routerTypeBeego = iota
|
||||||
routerTypeRESTFul
|
routerTypeRESTFul
|
||||||
routerTypeHandler
|
routerTypeHandler
|
||||||
)
|
)
|
||||||
@ -71,7 +71,7 @@ var (
|
|||||||
// these beego.Controller's methods shouldn't reflect to AutoRouter
|
// these beego.Controller's methods shouldn't reflect to AutoRouter
|
||||||
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
|
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
|
||||||
"RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJSON", "ServeJSONP",
|
"RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJSON", "ServeJSONP",
|
||||||
"ServeXML", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool",
|
"ServeYAML", "ServeXML", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool",
|
||||||
"GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession",
|
"GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession",
|
||||||
"DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie",
|
"DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie",
|
||||||
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
|
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
|
||||||
@ -201,9 +201,12 @@ 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++ {
|
||||||
fieldVal := elemVal.Field(i)
|
|
||||||
fieldType := elemType.Field(i)
|
fieldType := elemType.Field(i)
|
||||||
execElem.FieldByName(fieldType.Name).Set(fieldVal)
|
elemField := execElem.FieldByName(fieldType.Name)
|
||||||
|
if elemField.CanSet() {
|
||||||
|
fieldVal := elemVal.Field(i)
|
||||||
|
elemField.Set(fieldVal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return execController
|
return execController
|
||||||
@ -874,13 +877,15 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Admin:
|
Admin:
|
||||||
//admin module record QPS
|
//admin module record QPS
|
||||||
|
|
||||||
statusCode := context.ResponseWriter.Status
|
statusCode := context.ResponseWriter.Status
|
||||||
if statusCode == 0 {
|
if statusCode == 0 {
|
||||||
statusCode = 200
|
statusCode = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logAccess(context, &startTime, statusCode)
|
||||||
|
|
||||||
if BConfig.Listen.EnableAdmin {
|
if BConfig.Listen.EnableAdmin {
|
||||||
timeDur := time.Since(startTime)
|
timeDur := time.Since(startTime)
|
||||||
pattern := ""
|
pattern := ""
|
||||||
@ -897,49 +902,30 @@ Admin:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if BConfig.RunMode == DEV || BConfig.Log.AccessLogs {
|
if BConfig.RunMode == DEV && !BConfig.Log.AccessLogs {
|
||||||
timeDur := time.Since(startTime)
|
|
||||||
var devInfo string
|
var devInfo string
|
||||||
|
timeDur := time.Since(startTime)
|
||||||
iswin := (runtime.GOOS == "windows")
|
iswin := (runtime.GOOS == "windows")
|
||||||
statusColor := logs.ColorByStatus(iswin, statusCode)
|
statusColor := logs.ColorByStatus(iswin, statusCode)
|
||||||
methodColor := logs.ColorByMethod(iswin, r.Method)
|
methodColor := logs.ColorByMethod(iswin, r.Method)
|
||||||
resetColor := logs.ColorByMethod(iswin, "")
|
resetColor := logs.ColorByMethod(iswin, "")
|
||||||
if BConfig.Log.AccessLogsFormat != "" {
|
if findRouter {
|
||||||
record := &logs.AccessLogRecord{
|
if routerInfo != nil {
|
||||||
RemoteAddr: context.Input.IP(),
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
|
||||||
RequestTime: startTime,
|
resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
|
||||||
RequestMethod: r.Method,
|
routerInfo.pattern)
|
||||||
Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
|
|
||||||
ServerProtocol: r.Proto,
|
|
||||||
Host: r.Host,
|
|
||||||
Status: statusCode,
|
|
||||||
ElapsedTime: timeDur,
|
|
||||||
HTTPReferrer: r.Header.Get("Referer"),
|
|
||||||
HTTPUserAgent: r.Header.Get("User-Agent"),
|
|
||||||
RemoteUser: r.Header.Get("Remote-User"),
|
|
||||||
BodyBytesSent: 0, //@todo this one is missing!
|
|
||||||
}
|
|
||||||
logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
|
|
||||||
} else {
|
|
||||||
if findRouter {
|
|
||||||
if routerInfo != nil {
|
|
||||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s r:%s", context.Input.IP(), statusColor, statusCode,
|
|
||||||
resetColor, timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path,
|
|
||||||
routerInfo.pattern)
|
|
||||||
} else {
|
|
||||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
|
||||||
timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||||
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
|
timeDur.String(), "match", methodColor, r.Method, resetColor, r.URL.Path)
|
||||||
}
|
|
||||||
if iswin {
|
|
||||||
logs.W32Debug(devInfo)
|
|
||||||
} else {
|
|
||||||
logs.Debug(devInfo)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s", context.Input.IP(), statusColor, statusCode, resetColor,
|
||||||
|
timeDur.String(), "nomatch", methodColor, r.Method, resetColor, r.URL.Path)
|
||||||
|
}
|
||||||
|
if iswin {
|
||||||
|
logs.W32Debug(devInfo)
|
||||||
|
} else {
|
||||||
|
logs.Debug(devInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Call WriteHeader if status code has been set changed
|
// Call WriteHeader if status code has been set changed
|
||||||
@ -957,7 +943,7 @@ func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, ex
|
|||||||
context.RenderMethodResult(resultValue)
|
context.RenderMethodResult(resultValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !context.ResponseWriter.Started && context.Output.Status == 0 {
|
if !context.ResponseWriter.Started && len(results) > 0 && context.Output.Status == 0 {
|
||||||
context.Output.SetStatus(200)
|
context.Output.SetStatus(200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -988,3 +974,38 @@ func toURL(params map[string]string) string {
|
|||||||
}
|
}
|
||||||
return strings.TrimRight(u, "&")
|
return strings.TrimRight(u, "&")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func logAccess(ctx *beecontext.Context, startTime *time.Time, statusCode int) {
|
||||||
|
//Skip logging if AccessLogs config is false
|
||||||
|
if !BConfig.Log.AccessLogs {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//Skip logging static requests unless EnableStaticLogs config is true
|
||||||
|
if !BConfig.Log.EnableStaticLogs && DefaultAccessLogFilter.Filter(ctx) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
requestTime time.Time
|
||||||
|
elapsedTime time.Duration
|
||||||
|
r = ctx.Request
|
||||||
|
)
|
||||||
|
if startTime != nil {
|
||||||
|
requestTime = *startTime
|
||||||
|
elapsedTime = time.Since(*startTime)
|
||||||
|
}
|
||||||
|
record := &logs.AccessLogRecord{
|
||||||
|
RemoteAddr: ctx.Input.IP(),
|
||||||
|
RequestTime: requestTime,
|
||||||
|
RequestMethod: r.Method,
|
||||||
|
Request: fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto),
|
||||||
|
ServerProtocol: r.Proto,
|
||||||
|
Host: r.Host,
|
||||||
|
Status: statusCode,
|
||||||
|
ElapsedTime: elapsedTime,
|
||||||
|
HTTPReferrer: r.Header.Get("Referer"),
|
||||||
|
HTTPUserAgent: r.Header.Get("User-Agent"),
|
||||||
|
RemoteUser: r.Header.Get("Remote-User"),
|
||||||
|
BodyBytesSent: 0, //@todo this one is missing!
|
||||||
|
}
|
||||||
|
logs.AccessLog(record, BConfig.Log.AccessLogsFormat)
|
||||||
|
}
|
||||||
|
@ -695,3 +695,30 @@ func beegoResetParams(ctx *context.Context) {
|
|||||||
func beegoHandleResetParams(ctx *context.Context) {
|
func beegoHandleResetParams(ctx *context.Context) {
|
||||||
ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat"))
|
ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YAML
|
||||||
|
type YAMLController struct {
|
||||||
|
Controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jc *YAMLController) Prepare() {
|
||||||
|
jc.Data["yaml"] = "prepare"
|
||||||
|
jc.ServeYAML()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jc *YAMLController) Get() {
|
||||||
|
jc.Data["Username"] = "astaxie"
|
||||||
|
jc.Ctx.Output.Body([]byte("ok"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestYAMLPrepare(t *testing.T) {
|
||||||
|
r, _ := http.NewRequest("GET", "/yaml/list", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
handler := NewControllerRegister()
|
||||||
|
handler.Add("/yaml/list", &YAMLController{})
|
||||||
|
handler.ServeHTTP(w, r)
|
||||||
|
if strings.TrimSpace(w.Body.String()) != "prepare" {
|
||||||
|
t.Errorf(w.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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(
|
||||||
@ -24,10 +24,10 @@
|
|||||||
// "github.com/astaxie/beego/session"
|
// "github.com/astaxie/beego/session"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// func init() {
|
// func init() {
|
||||||
// globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``)
|
// globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``)
|
||||||
// go globalSessions.GC()
|
// go globalSessions.GC()
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// more docs: http://beego.me/docs/module/session.md
|
// more docs: http://beego.me/docs/module/session.md
|
||||||
package redis
|
package redis
|
||||||
@ -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,27 +150,39 @@ 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
|
||||||
c, err := redis.Dial("tcp", rp.savePath)
|
if len(configs) > 4 {
|
||||||
if err != nil {
|
timeout, err := strconv.Atoi(configs[4])
|
||||||
return nil, err
|
if err == nil && timeout > 0 {
|
||||||
|
idleTimeout = time.Duration(timeout) * time.Second
|
||||||
}
|
}
|
||||||
if rp.password != "" {
|
}
|
||||||
if _, err = c.Do("AUTH", rp.password); err != nil {
|
rp.poollist = &redis.Pool{
|
||||||
c.Close()
|
Dial: func() (redis.Conn, error) {
|
||||||
return nil, err
|
c, err := redis.Dial("tcp", rp.savePath)
|
||||||
}
|
|
||||||
}
|
|
||||||
//some redis proxy such as twemproxy is not support select command
|
|
||||||
if rp.dbNum > 0 {
|
|
||||||
_, err = c.Do("SELECT", rp.dbNum)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Close()
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
if rp.password != "" {
|
||||||
return c, err
|
if _, err = c.Do("AUTH", rp.password); err != nil {
|
||||||
}, rp.poolsize)
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// some redis proxy such as twemproxy is not support select command
|
||||||
|
if rp.dbNum > 0 {
|
||||||
|
_, err = c.Do("SELECT", rp.dbNum)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c, err
|
||||||
|
},
|
||||||
|
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"
|
||||||
@ -84,24 +85,24 @@ func DateFormat(t time.Time, layout string) (datestring string) {
|
|||||||
var datePatterns = []string{
|
var datePatterns = []string{
|
||||||
// year
|
// year
|
||||||
"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
|
"Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
|
||||||
"y", "06", //A two digit representation of a year Examples: 99 or 03
|
"y", "06", //A two digit representation of a year Examples: 99 or 03
|
||||||
|
|
||||||
// month
|
// month
|
||||||
"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
|
"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
|
||||||
"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
|
"n", "1", // Numeric representation of a month, without leading zeros 1 through 12
|
||||||
"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
|
"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
|
||||||
"F", "January", // A full textual representation of a month, such as January or March January through December
|
"F", "January", // A full textual representation of a month, such as January or March January through December
|
||||||
|
|
||||||
// day
|
// day
|
||||||
"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
|
"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
|
||||||
"j", "2", // Day of the month without leading zeros 1 to 31
|
"j", "2", // Day of the month without leading zeros 1 to 31
|
||||||
|
|
||||||
// week
|
// week
|
||||||
"D", "Mon", // A textual representation of a day, three letters Mon through Sun
|
"D", "Mon", // A textual representation of a day, three letters Mon through Sun
|
||||||
"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
|
"l", "Monday", // A full textual representation of the day of the week Sunday through Saturday
|
||||||
|
|
||||||
// time
|
// time
|
||||||
"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
|
"g", "3", // 12-hour format of an hour without leading zeros 1 through 12
|
||||||
"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
|
"G", "15", // 24-hour format of an hour without leading zeros 0 through 23
|
||||||
"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
|
"h", "03", // 12-hour format of an hour with leading zeros 01 through 12
|
||||||
"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
|
"H", "15", // 24-hour format of an hour with leading zeros 00 through 23
|
||||||
@ -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,7 +245,21 @@ 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 chk.IsSatisfied(obj) {
|
if nil == obj {
|
||||||
|
if chk.IsSatisfied(obj) {
|
||||||
|
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}
|
return &Result{Ok: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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