Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
B
beego
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
beego
Commits
af73a2d5
Unverified
Commit
af73a2d5
authored
Jul 20, 2018
by
astaxie
Committed by
GitHub
Jul 20, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into fix_response_http_code
parents
6df42d63
67a6b872
Hide whitespace changes
Inline
Side-by-side
Showing
42 changed files
with
711 additions
and
223 deletions
+711
-223
.travis.yml
.travis.yml
+4
-4
app.go
app.go
+3
-3
README.md
cache/README.md
+1
-1
redis.go
cache/redis/redis.go
+9
-4
redis_test.go
cache/redis/redis_test.go
+1
-1
config.go
config.go
+7
-7
config.go
config/config.go
+3
-3
fake.go
config/fake.go
+1
-1
ini.go
config/ini.go
+7
-7
json.go
config/json.go
+5
-5
json_test.go
config/json_test.go
+1
-1
xml.go
config/xml/xml.go
+5
-5
yaml.go
config/yaml/yaml.go
+21
-8
output.go
context/output.go
+11
-6
controller.go
controller.go
+13
-0
httplib.go
httplib/httplib.go
+3
-1
README.md
logs/README.md
+32
-23
accesslog.go
logs/accesslog.go
+4
-5
file.go
logs/file.go
+12
-8
file_test.go
logs/file_test.go
+3
-2
log.go
logs/log.go
+21
-2
logger.go
logs/logger.go
+1
-1
logger_test.go
logs/logger_test.go
+2
-2
multifile.go
logs/multifile.go
+4
-1
ddl.go
migration/ddl.go
+4
-4
cmd_utils.go
orm/cmd_utils.go
+4
-0
db.go
orm/db.go
+4
-0
models_info_f.go
orm/models_info_f.go
+2
-0
orm_queryset.go
orm/orm_queryset.go
+22
-11
types.go
orm/types.go
+4
-0
parser.go
parser.go
+41
-31
router.go
router.go
+3
-3
sess_redis.go
session/redis/sess_redis.go
+39
-26
redis_cluster.go
session/redis_cluster/redis_cluster.go
+220
-0
staticfile.go
staticfile.go
+15
-9
staticfile_test.go
staticfile_test.go
+4
-4
swagger.go
swagger/swagger.go
+2
-1
template.go
template.go
+1
-0
templatefunc.go
templatefunc.go
+15
-26
templatefunc_test.go
templatefunc_test.go
+3
-3
validation.go
validation/validation.go
+35
-4
validation_test.go
validation/validation_test.go
+119
-0
No files found.
.travis.yml
View file @
af73a2d5
language
:
go
go
:
-
1.7.5
-
1.8.5
-
1.9.2
-
"
1.9.2"
-
"
1.10.2"
services
:
-
redis-server
-
mysql
...
...
@@ -22,7 +21,7 @@ install:
-
go get github.com/go-sql-driver/mysql
-
go get github.com/mattn/go-sqlite3
-
go get github.com/bradfitz/gomemcache/memcache
-
go get github.com/g
aryburd
/redigo/redis
-
go get github.com/g
omodule
/redigo/redis
-
go get github.com/beego/x2j
-
go get github.com/couchbase/go-couchbase
-
go get github.com/beego/goyaml2
...
...
@@ -38,6 +37,7 @@ install:
-
go get -u github.com/mdempsky/unconvert
-
go get -u github.com/gordonklaus/ineffassign
-
go get -u github.com/golang/lint/golint
-
go get -u github.com/go-redis/redis
before_script
:
-
psql --version
-
sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
...
...
app.go
View file @
af73a2d5
...
...
@@ -24,8 +24,8 @@ import (
"net/http/fcgi"
"os"
"path"
"time"
"strings"
"time"
"github.com/astaxie/beego/grace"
"github.com/astaxie/beego/logs"
...
...
@@ -101,7 +101,7 @@ func (app *App) Run(mws ...MiddleWare) {
}
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
{
continue
}
...
...
@@ -167,7 +167,7 @@ func (app *App) Run(mws ...MiddleWare) {
if
BConfig
.
Listen
.
HTTPSPort
!=
0
{
app
.
Server
.
Addr
=
fmt
.
Sprintf
(
"%s:%d"
,
BConfig
.
Listen
.
HTTPSAddr
,
BConfig
.
Listen
.
HTTPSPort
)
}
else
if
BConfig
.
Listen
.
EnableHTTP
{
BeeLogger
.
Info
(
"Start https server error, confict with http.Please reset https port"
)
BeeLogger
.
Info
(
"Start https server error, conf
l
ict with http.Please reset https port"
)
return
}
logs
.
Info
(
"https server Running on https://%s"
,
app
.
Server
.
Addr
)
...
...
cache/README.md
View file @
af73a2d5
...
...
@@ -52,7 +52,7 @@ Configure like this:
## Redis adapter
Redis adapter use the
[
redigo
](
http://github.com/g
aryburd
/redigo
)
client.
Redis adapter use the
[
redigo
](
http://github.com/g
omodule
/redigo
)
client.
Configure like this:
...
...
cache/redis/redis.go
View file @
af73a2d5
...
...
@@ -14,9 +14,9 @@
// Package redis for cache provider
//
// depend on github.com/g
aryburd
/redigo/redis
// depend on github.com/g
omodule
/redigo/redis
//
// go install github.com/g
aryburd
/redigo/redis
// go install github.com/g
omodule
/redigo/redis
//
// Usage:
// import(
...
...
@@ -36,7 +36,7 @@ import (
"strconv"
"time"
"github.com/g
aryburd
/redigo/redis"
"github.com/g
omodule
/redigo/redis"
"github.com/astaxie/beego/cache"
)
...
...
@@ -53,6 +53,7 @@ type Cache struct {
dbNum
int
key
string
password
string
maxIdle
int
}
// 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
{
cf
[
"password"
]
=
""
}
if
_
,
ok
:=
cf
[
"maxIdle"
];
!
ok
{
cf
[
"maxIdle"
]
=
"3"
}
rc
.
key
=
cf
[
"key"
]
rc
.
conninfo
=
cf
[
"conn"
]
rc
.
dbNum
,
_
=
strconv
.
Atoi
(
cf
[
"dbNum"
])
rc
.
password
=
cf
[
"password"
]
rc
.
maxIdle
,
_
=
strconv
.
Atoi
(
cf
[
"maxIdle"
])
rc
.
connectInit
()
...
...
@@ -206,7 +211,7 @@ func (rc *Cache) connectInit() {
}
// initialize a new pool
rc
.
p
=
&
redis
.
Pool
{
MaxIdle
:
3
,
MaxIdle
:
rc
.
maxIdle
,
IdleTimeout
:
180
*
time
.
Second
,
Dial
:
dialFunc
,
}
...
...
cache/redis/redis_test.go
View file @
af73a2d5
...
...
@@ -19,7 +19,7 @@ import (
"time"
"github.com/astaxie/beego/cache"
"github.com/g
aryburd
/redigo/redis"
"github.com/g
omodule
/redigo/redis"
)
func
TestRedisCache
(
t
*
testing
.
T
)
{
...
...
config.go
View file @
af73a2d5
...
...
@@ -98,9 +98,9 @@ type SessionConfig struct {
SessionAutoSetCookie
bool
SessionDomain
string
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
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
...
...
@@ -138,8 +138,8 @@ func init() {
panic
(
err
)
}
var
filename
=
"app.conf"
if
os
.
Getenv
(
"BEEGO_MODE"
)
!=
""
{
filename
=
os
.
Getenv
(
"BEEGO_MODE"
)
+
".app.conf"
if
os
.
Getenv
(
"BEEGO_
RUN
MODE"
)
!=
""
{
filename
=
os
.
Getenv
(
"BEEGO_
RUN
MODE"
)
+
".app.conf"
}
appConfigPath
=
filepath
.
Join
(
workPath
,
"conf"
,
filename
)
if
!
utils
.
FileExists
(
appConfigPath
)
{
...
...
@@ -193,7 +193,7 @@ func recoverPanic(ctx *context.Context) {
func
newBConfig
()
*
Config
{
return
&
Config
{
AppName
:
"beego"
,
RunMode
:
DEV
,
RunMode
:
PROD
,
RouterCaseSensitive
:
true
,
ServerName
:
"beegoServer:"
+
VERSION
,
RecoverPanic
:
true
,
...
...
@@ -245,9 +245,9 @@ func newBConfig() *Config {
SessionCookieLifeTime
:
0
,
//set cookie default is the browser life
SessionAutoSetCookie
:
true
,
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"
,
SessionEnableSidInURLQuery
:
false
,
//
enable get the sessionId from Url Query params
SessionEnableSidInURLQuery
:
false
,
//
enable get the sessionId from Url Query params
},
},
Log
:
LogConfig
{
...
...
config/config.go
View file @
af73a2d5
...
...
@@ -150,12 +150,12 @@ func ExpandValueEnv(value string) (realValue string) {
}
key
:=
""
defa
lu
tV
:=
""
defa
ul
tV
:=
""
// value start with "${"
for
i
:=
2
;
i
<
vLen
;
i
++
{
if
value
[
i
]
==
'|'
&&
(
i
+
1
<
vLen
&&
value
[
i
+
1
]
==
'|'
)
{
key
=
value
[
2
:
i
]
defa
lu
tV
=
value
[
i
+
2
:
vLen
-
1
]
// other string is default value.
defa
ul
tV
=
value
[
i
+
2
:
vLen
-
1
]
// other string is default value.
break
}
else
if
value
[
i
]
==
'}'
{
key
=
value
[
2
:
i
]
...
...
@@ -165,7 +165,7 @@ func ExpandValueEnv(value string) (realValue string) {
realValue
=
os
.
Getenv
(
key
)
if
realValue
==
""
{
realValue
=
defa
lu
tV
realValue
=
defa
ul
tV
}
return
...
...
config/fake.go
View file @
af73a2d5
...
...
@@ -126,7 +126,7 @@ func (c *fakeConfigContainer) SaveConfigFile(filename string) error {
var
_
Configer
=
new
(
fakeConfigContainer
)
// NewFakeConfig return a fake Con
g
iger
// NewFakeConfig return a fake Con
f
iger
func
NewFakeConfig
()
Configer
{
return
&
fakeConfigContainer
{
data
:
make
(
map
[
string
]
string
),
...
...
config/ini.go
View file @
af73a2d5
...
...
@@ -215,7 +215,7 @@ func (c *IniConfigContainer) Bool(key string) (bool, error) {
}
// DefaultBool returns the boolean value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
IniConfigContainer
)
DefaultBool
(
key
string
,
defaultval
bool
)
bool
{
v
,
err
:=
c
.
Bool
(
key
)
if
err
!=
nil
{
...
...
@@ -230,7 +230,7 @@ func (c *IniConfigContainer) Int(key string) (int, error) {
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
IniConfigContainer
)
DefaultInt
(
key
string
,
defaultval
int
)
int
{
v
,
err
:=
c
.
Int
(
key
)
if
err
!=
nil
{
...
...
@@ -245,7 +245,7 @@ func (c *IniConfigContainer) Int64(key string) (int64, error) {
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
IniConfigContainer
)
DefaultInt64
(
key
string
,
defaultval
int64
)
int64
{
v
,
err
:=
c
.
Int64
(
key
)
if
err
!=
nil
{
...
...
@@ -260,7 +260,7 @@ func (c *IniConfigContainer) Float(key string) (float64, error) {
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
IniConfigContainer
)
DefaultFloat
(
key
string
,
defaultval
float64
)
float64
{
v
,
err
:=
c
.
Float
(
key
)
if
err
!=
nil
{
...
...
@@ -275,7 +275,7 @@ func (c *IniConfigContainer) String(key string) string {
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
IniConfigContainer
)
DefaultString
(
key
string
,
defaultval
string
)
string
{
v
:=
c
.
String
(
key
)
if
v
==
""
{
...
...
@@ -295,7 +295,7 @@ func (c *IniConfigContainer) Strings(key string) []string {
}
// DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
IniConfigContainer
)
DefaultStrings
(
key
string
,
defaultval
[]
string
)
[]
string
{
v
:=
c
.
Strings
(
key
)
if
v
==
nil
{
...
...
@@ -314,7 +314,7 @@ func (c *IniConfigContainer) GetSection(section string) (map[string]string, erro
// SaveConfigFile save the config into file.
//
// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Func
a
tion.
// BUG(env): The environment variable config item will be saved with real value in SaveConfigFile Function.
func
(
c
*
IniConfigContainer
)
SaveConfigFile
(
filename
string
)
(
err
error
)
{
// Write configuration file by filename.
f
,
err
:=
os
.
Create
(
filename
)
...
...
config/json.go
View file @
af73a2d5
...
...
@@ -101,7 +101,7 @@ func (c *JSONConfigContainer) Int(key string) (int, error) {
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
JSONConfigContainer
)
DefaultInt
(
key
string
,
defaultval
int
)
int
{
if
v
,
err
:=
c
.
Int
(
key
);
err
==
nil
{
return
v
...
...
@@ -122,7 +122,7 @@ func (c *JSONConfigContainer) Int64(key string) (int64, error) {
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
JSONConfigContainer
)
DefaultInt64
(
key
string
,
defaultval
int64
)
int64
{
if
v
,
err
:=
c
.
Int64
(
key
);
err
==
nil
{
return
v
...
...
@@ -143,7 +143,7 @@ func (c *JSONConfigContainer) Float(key string) (float64, error) {
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
JSONConfigContainer
)
DefaultFloat
(
key
string
,
defaultval
float64
)
float64
{
if
v
,
err
:=
c
.
Float
(
key
);
err
==
nil
{
return
v
...
...
@@ -163,7 +163,7 @@ func (c *JSONConfigContainer) String(key string) string {
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
JSONConfigContainer
)
DefaultString
(
key
string
,
defaultval
string
)
string
{
// TODO FIXME should not use "" to replace non existence
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.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
JSONConfigContainer
)
DefaultStrings
(
key
string
,
defaultval
[]
string
)
[]
string
{
if
v
:=
c
.
Strings
(
key
);
v
!=
nil
{
return
v
...
...
config/json_test.go
View file @
af73a2d5
...
...
@@ -216,7 +216,7 @@ func TestJson(t *testing.T) {
t
.
Error
(
"unknown keys should return an error when expecting a Bool"
)
}
if
!
jsonconf
.
DefaultBool
(
"unknow"
,
true
)
{
if
!
jsonconf
.
DefaultBool
(
"unknow
n
"
,
true
)
{
t
.
Error
(
"unknown keys with default value wrong"
)
}
}
config/xml/xml.go
View file @
af73a2d5
...
...
@@ -102,7 +102,7 @@ func (c *ConfigContainer) Int(key string) (int, error) {
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultInt
(
key
string
,
defaultval
int
)
int
{
v
,
err
:=
c
.
Int
(
key
)
if
err
!=
nil
{
...
...
@@ -117,7 +117,7 @@ func (c *ConfigContainer) Int64(key string) (int64, error) {
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultInt64
(
key
string
,
defaultval
int64
)
int64
{
v
,
err
:=
c
.
Int64
(
key
)
if
err
!=
nil
{
...
...
@@ -133,7 +133,7 @@ func (c *ConfigContainer) Float(key string) (float64, error) {
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultFloat
(
key
string
,
defaultval
float64
)
float64
{
v
,
err
:=
c
.
Float
(
key
)
if
err
!=
nil
{
...
...
@@ -151,7 +151,7 @@ func (c *ConfigContainer) String(key string) string {
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultString
(
key
string
,
defaultval
string
)
string
{
v
:=
c
.
String
(
key
)
if
v
==
""
{
...
...
@@ -170,7 +170,7 @@ func (c *ConfigContainer) Strings(key string) []string {
}
// DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultStrings
(
key
string
,
defaultval
[]
string
)
[]
string
{
v
:=
c
.
Strings
(
key
)
if
v
==
nil
{
...
...
config/yaml/yaml.go
View file @
af73a2d5
...
...
@@ -154,7 +154,7 @@ func (c *ConfigContainer) Int(key string) (int, error) {
}
// DefaultInt returns the integer value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultInt
(
key
string
,
defaultval
int
)
int
{
v
,
err
:=
c
.
Int
(
key
)
if
err
!=
nil
{
...
...
@@ -174,7 +174,7 @@ func (c *ConfigContainer) Int64(key string) (int64, error) {
}
// DefaultInt64 returns the int64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultInt64
(
key
string
,
defaultval
int64
)
int64
{
v
,
err
:=
c
.
Int64
(
key
)
if
err
!=
nil
{
...
...
@@ -198,7 +198,7 @@ func (c *ConfigContainer) Float(key string) (float64, error) {
}
// DefaultFloat returns the float64 value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultFloat
(
key
string
,
defaultval
float64
)
float64
{
v
,
err
:=
c
.
Float
(
key
)
if
err
!=
nil
{
...
...
@@ -218,7 +218,7 @@ func (c *ConfigContainer) String(key string) string {
}
// DefaultString returns the string value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultString
(
key
string
,
defaultval
string
)
string
{
v
:=
c
.
String
(
key
)
if
v
==
""
{
...
...
@@ -237,7 +237,7 @@ func (c *ConfigContainer) Strings(key string) []string {
}
// DefaultStrings returns the []string value for a given key.
// if err != nil return defaltval
// if err != nil return defa
u
ltval
func
(
c
*
ConfigContainer
)
DefaultStrings
(
key
string
,
defaultval
[]
string
)
[]
string
{
v
:=
c
.
Strings
(
key
)
if
v
==
nil
{
...
...
@@ -285,9 +285,22 @@ func (c *ConfigContainer) getData(key string) (interface{}, error) {
if
len
(
key
)
==
0
{
return
nil
,
errors
.
New
(
"key is empty"
)
}
if
v
,
ok
:=
c
.
data
[
key
];
ok
{
return
v
,
nil
keys
:=
strings
.
Split
(
key
,
"."
)
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
)
}
...
...
context/output.go
View file @
af73a2d5
...
...
@@ -182,8 +182,8 @@ func errorRenderer(err error) Renderer {
}
// JSON writes json to response body.
// if coding is true, it converts utf-8 to \u0000 type.
func
(
output
*
BeegoOutput
)
JSON
(
data
interface
{},
hasIndent
bool
,
coding
bool
)
error
{
// if
en
coding is true, it converts utf-8 to \u0000 type.
func
(
output
*
BeegoOutput
)
JSON
(
data
interface
{},
hasIndent
bool
,
en
coding
bool
)
error
{
output
.
Header
(
"Content-Type"
,
"application/json; charset=utf-8"
)
var
content
[]
byte
var
err
error
...
...
@@ -196,7 +196,7 @@ func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, coding bool) e
http
.
Error
(
output
.
Context
.
ResponseWriter
,
err
.
Error
(),
http
.
StatusInternalServerError
)
return
err
}
if
coding
{
if
en
coding
{
content
=
[]
byte
(
stringsToJSON
(
string
(
content
)))
}
return
output
.
Body
(
content
)
...
...
@@ -260,7 +260,7 @@ func (output *BeegoOutput) Download(file string, filename ...string) {
}
else
{
fName
=
filepath
.
Base
(
file
)
}
output
.
Header
(
"Content-Disposition"
,
"attachment; filename="
+
url
.
Query
Escape
(
fName
))
output
.
Header
(
"Content-Disposition"
,
"attachment; filename="
+
url
.
Path
Escape
(
fName
))
output
.
Header
(
"Content-Description"
,
"File Transfer"
)
output
.
Header
(
"Content-Type"
,
"application/octet-stream"
)
output
.
Header
(
"Content-Transfer-Encoding"
,
"binary"
)
...
...
@@ -325,13 +325,13 @@ func (output *BeegoOutput) IsForbidden() bool {
}
// IsNotFound returns boolean of this request is not found.
// HTTP 404 means
forbidden
.
// HTTP 404 means
not found
.
func
(
output
*
BeegoOutput
)
IsNotFound
()
bool
{
return
output
.
Status
==
404
}
// IsClientError returns boolean of this request client sends error data.
// HTTP 4xx means
forbidden
.
// HTTP 4xx means
client error
.
func
(
output
*
BeegoOutput
)
IsClientError
()
bool
{
return
output
.
Status
>=
400
&&
output
.
Status
<
500
}
...
...
@@ -350,6 +350,11 @@ func stringsToJSON(str string) string {
jsons
.
WriteRune
(
r
)
}
else
{
jsons
.
WriteString
(
"
\\
u"
)
if
rint
<
0x100
{
jsons
.
WriteString
(
"00"
)
}
else
if
rint
<
0x1000
{
jsons
.
WriteString
(
"0"
)
}
jsons
.
WriteString
(
strconv
.
FormatInt
(
int64
(
rint
),
16
))
}
}
...
...
controller.go
View file @
af73a2d5
...
...
@@ -275,6 +275,19 @@ func (c *Controller) Redirect(url string, code int) {
c
.
Ctx
.
Redirect
(
code
,
url
)
}
// Set the data depending on the accepted
func
(
c
*
Controller
)
SetData
(
data
interface
{})
{
accept
:=
c
.
Ctx
.
Input
.
Header
(
"Accept"
)
switch
accept
{
case
applicationJSON
:
c
.
Data
[
"json"
]
=
data
case
applicationXML
,
textXML
:
c
.
Data
[
"xml"
]
=
data
default
:
c
.
Data
[
"json"
]
=
data
}
}
// Abort stops controller handler and show the error data if code is defined in ErrorMap or code string.
func
(
c
*
Controller
)
Abort
(
code
string
)
{
status
,
err
:=
strconv
.
Atoi
(
code
)
...
...
httplib/httplib.go
View file @
af73a2d5
...
...
@@ -317,6 +317,7 @@ func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest {
}
return
b
}
// XMLBody adds request raw body encoding by XML.
func
(
b
*
BeegoHTTPRequest
)
XMLBody
(
obj
interface
{})
(
*
BeegoHTTPRequest
,
error
)
{
if
b
.
req
.
Body
==
nil
&&
obj
!=
nil
{
...
...
@@ -330,6 +331,7 @@ func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) {
}
return
b
,
nil
}
// JSONBody adds request raw body encoding by JSON.
func
(
b
*
BeegoHTTPRequest
)
JSONBody
(
obj
interface
{})
(
*
BeegoHTTPRequest
,
error
)
{
if
b
.
req
.
Body
==
nil
&&
obj
!=
nil
{
...
...
@@ -444,7 +446,7 @@ func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) {
TLSClientConfig
:
b
.
setting
.
TLSClientConfig
,
Proxy
:
b
.
setting
.
Proxy
,
Dial
:
TimeoutDialer
(
b
.
setting
.
ConnectTimeout
,
b
.
setting
.
ReadWriteTimeout
),
MaxIdleConnsPerHost
:
-
1
,
MaxIdleConnsPerHost
:
100
,
}
}
else
{
// if b.transport is *http.Transport then set the settings.
...
...
logs/README.md
View file @
af73a2d5
...
...
@@ -16,48 +16,57 @@ As of now this logs support console, file,smtp and conn.
First you must import it
import (
"github.com/astaxie/beego/logs"
)
```
golang
import
(
"github.com/astaxie/beego/logs"
)
```
Then init a Log (example with console adapter)
log := NewLogger(10000)
log.SetLogger("console", "")
```
golang
log
:=
logs
.
NewLogger
(
10000
)
log
.
SetLogger
(
"console"
,
""
)
```
> the first params stand for how many channel
Use it like this:
log.Trace("trace")
log.Info("info")
log.Warn("warning")
log.Debug("debug")
log.Critical("critical")
Use it like this:
```
golang
log
.
Trace
(
"trace"
)
log
.
Info
(
"info"
)
log
.
Warn
(
"warning"
)
log
.
Debug
(
"debug"
)
log
.
Critical
(
"critical"
)
```
## File adapter
Configure file adapter like this:
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test.log"}`)
```
golang
log
:=
NewLogger
(
10000
)
log
.
SetLogger
(
"file"
,
`{"filename":"test.log"}`
)
```
## Conn adapter
Configure like this:
log := NewLogger(1000)
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
log.Info("info")
```
golang
log
:=
NewLogger
(
1000
)
log
.
SetLogger
(
"conn"
,
`{"net":"tcp","addr":":7020"}`
)
log
.
Info
(
"info"
)
```
## Smtp adapter
Configure like this:
log := NewLogger(10000)
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
log.Critical("sendmail critical")
time.Sleep(time.Second * 30)
```
golang
log
:=
NewLogger
(
10000
)
log
.
SetLogger
(
"smtp"
,
`{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`
)
log
.
Critical
(
"sendmail critical"
)
time
.
Sleep
(
time
.
Second
*
30
)
```
logs/accesslog.go
View file @
af73a2d5
...
...
@@ -17,12 +17,12 @@ package logs
import
(
"bytes"
"encoding/json"
"time"
"fmt"
"time"
)
const
(
apacheFormatPattern
=
"%s - - [%s]
\"
%s %d %d
\"
%f %s %s
\n
"
apacheFormatPattern
=
"%s - - [%s]
\"
%s %d %d
\"
%f %s %s"
apacheFormat
=
"APACHE_FORMAT"
jsonFormat
=
"JSON_FORMAT"
)
...
...
@@ -53,10 +53,9 @@ func (r *AccessLogRecord) json() ([]byte, error) {
}
func
disableEscapeHTML
(
i
interface
{})
{
e
,
ok
:=
i
.
(
interface
{
if
e
,
ok
:=
i
.
(
interface
{
SetEscapeHTML
(
bool
)
});
if
ok
{
});
ok
{
e
.
SetEscapeHTML
(
false
)
}
}
...
...
logs/file.go
View file @
af73a2d5
...
...
@@ -40,6 +40,9 @@ type fileLogWriter struct {
MaxLines
int
`json:"maxlines"`
maxLinesCurLines
int
MaxFiles
int
`json:"maxfiles"`
MaxFilesCurFiles
int
// Rotate at size
MaxSize
int
`json:"maxsize"`
maxSizeCurSize
int
...
...
@@ -70,6 +73,9 @@ func newFileWriter() Logger {
RotatePerm
:
"0440"
,
Level
:
LevelTrace
,
Perm
:
"0660"
,
MaxLines
:
10000000
,
MaxFiles
:
999
,
MaxSize
:
1
<<
28
,
}
return
w
}
...
...
@@ -238,7 +244,7 @@ func (w *fileLogWriter) lines() (int, error) {
func
(
w
*
fileLogWriter
)
doRotate
(
logTime
time
.
Time
)
error
{
// file exists
// Find the next available number
num
:=
1
num
:=
w
.
MaxFilesCurFiles
+
1
fName
:=
""
rotatePerm
,
err
:=
strconv
.
ParseInt
(
w
.
RotatePerm
,
8
,
64
)
if
err
!=
nil
{
...
...
@@ -251,18 +257,16 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
goto
RESTART_LOGGER
}
// only when one of them be setted, then the file would be splited
if
w
.
MaxLines
>
0
||
w
.
MaxSize
>
0
{
for
;
err
==
nil
&&
num
<=
999
;
num
++
{
for
;
err
==
nil
&&
num
<=
w
.
MaxFiles
;
num
++
{
fName
=
w
.
fileNameOnly
+
fmt
.
Sprintf
(
".%s.%03d%s"
,
logTime
.
Format
(
"2006-01-02"
),
num
,
w
.
suffix
)
_
,
err
=
os
.
Lstat
(
fName
)
}
}
else
{
fName
=
fmt
.
Sprintf
(
"%s.%s%s"
,
w
.
fileNameOnly
,
w
.
dailyOpenTime
.
Format
(
"2006-01-02"
)
,
w
.
suffix
)
fName
=
w
.
fileNameOnly
+
fmt
.
Sprintf
(
".%s.%03d%s"
,
w
.
dailyOpenTime
.
Format
(
"2006-01-02"
),
num
,
w
.
suffix
)
_
,
err
=
os
.
Lstat
(
fName
)
for
;
err
==
nil
&&
num
<=
999
;
num
++
{
fName
=
w
.
fileNameOnly
+
fmt
.
Sprintf
(
".%s.%03d%s"
,
w
.
dailyOpenTime
.
Format
(
"2006-01-02"
),
num
,
w
.
suffix
)
_
,
err
=
os
.
Lstat
(
fName
)
}
w
.
MaxFilesCurFiles
=
num
}
// return error if the last file checked still existed
if
err
==
nil
{
...
...
@@ -308,7 +312,7 @@ func (w *fileLogWriter) deleteOldLog() {
return
}
if
!
info
.
IsDir
()
&&
info
.
ModTime
()
.
Add
(
24
*
time
.
Hour
*
time
.
Duration
(
w
.
MaxDays
))
.
Before
(
time
.
Now
())
{
if
!
info
.
IsDir
()
&&
info
.
ModTime
()
.
Add
(
24
*
time
.
Hour
*
time
.
Duration
(
w
.
MaxDays
))
.
Before
(
time
.
Now
())
{
if
strings
.
HasPrefix
(
filepath
.
Base
(
path
),
filepath
.
Base
(
w
.
fileNameOnly
))
&&
strings
.
HasSuffix
(
filepath
.
Base
(
path
),
w
.
suffix
)
{
os
.
Remove
(
path
)
...
...
logs/file_test.go
View file @
af73a2d5
...
...
@@ -135,7 +135,7 @@ func TestFileRotate_01(t *testing.T) {
func
TestFileRotate_02
(
t
*
testing
.
T
)
{
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
)
}
...
...
@@ -150,7 +150,7 @@ func TestFileRotate_03(t *testing.T) {
func
TestFileRotate_04
(
t
*
testing
.
T
)
{
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
)
}
...
...
@@ -200,6 +200,7 @@ func testFileRotate(t *testing.T, fn1, fn2 string) {
for
_
,
file
:=
range
[]
string
{
fn1
,
fn2
}
{
_
,
err
:=
os
.
Stat
(
file
)
if
err
!=
nil
{
t
.
Log
(
err
)
t
.
FailNow
()
}
os
.
Remove
(
file
)
...
...
logs/log.go
View file @
af73a2d5
...
...
@@ -47,7 +47,7 @@ import (
// RFC5424 log message levels.
const
(
LevelEmergency
=
iota
LevelEmergency
=
iota
LevelAlert
LevelCritical
LevelError
...
...
@@ -116,6 +116,7 @@ type BeeLogger struct {
enableFuncCallDepth
bool
loggerFuncCallDepth
int
asynchronous
bool
prefix
string
msgChanLen
int64
msgChan
chan
*
logMsg
signalChan
chan
string
...
...
@@ -247,7 +248,7 @@ func (bl *BeeLogger) Write(p []byte) (n int, err error) {
}
// writeMsg will always add a '\n' character
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
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
{
msg
=
fmt
.
Sprintf
(
msg
,
v
...
)
}
msg
=
bl
.
prefix
+
" "
+
msg
when
:=
time
.
Now
()
if
bl
.
enableFuncCallDepth
{
_
,
file
,
line
,
ok
:=
runtime
.
Caller
(
bl
.
loggerFuncCallDepth
)
...
...
@@ -305,6 +309,11 @@ func (bl *BeeLogger) SetLevel(l int) {
bl
.
level
=
l
}
// GetLevel Get Current log message level.
func
(
bl
*
BeeLogger
)
GetLevel
()
int
{
return
bl
.
level
}
// SetLogFuncCallDepth set log funcCallDepth
func
(
bl
*
BeeLogger
)
SetLogFuncCallDepth
(
d
int
)
{
bl
.
loggerFuncCallDepth
=
d
...
...
@@ -320,6 +329,11 @@ func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
bl
.
enableFuncCallDepth
=
b
}
// set prefix
func
(
bl
*
BeeLogger
)
SetPrefix
(
s
string
)
{
bl
.
prefix
=
s
}
// start logger chan reading.
// when chan is not empty, write logs.
func
(
bl
*
BeeLogger
)
startLogger
()
{
...
...
@@ -544,6 +558,11 @@ func SetLevel(l int) {
beeLogger
.
SetLevel
(
l
)
}
// SetPrefix sets the prefix
func
SetPrefix
(
s
string
)
{
beeLogger
.
SetPrefix
(
s
)
}
// EnableFuncCallDepth enable log funcCallDepth
func
EnableFuncCallDepth
(
b
bool
)
{
beeLogger
.
enableFuncCallDepth
=
b
...
...
logs/logger.go
View file @
af73a2d5
...
...
@@ -93,7 +93,7 @@ const (
func
formatTimeHeader
(
when
time
.
Time
)
([]
byte
,
int
)
{
y
,
mo
,
d
:=
when
.
Date
()
h
,
mi
,
s
:=
when
.
Clock
()
ns
:=
when
.
Nanosecond
()
/
1000000
ns
:=
when
.
Nanosecond
()
/
1000000
//len("2006/01/02 15:04:05.123 ")==24
var
buf
[
24
]
byte
...
...
logs/logger_test.go
View file @
af73a2d5
...
...
@@ -31,7 +31,7 @@ func TestFormatHeader_0(t *testing.T) {
break
}
h
,
_
:=
formatTimeHeader
(
tm
)
if
tm
.
Format
(
"2006/01/02 15:04:05.
999
"
)
!=
string
(
h
)
{
if
tm
.
Format
(
"2006/01/02 15:04:05.
000
"
)
!=
string
(
h
)
{
t
.
Log
(
tm
)
t
.
FailNow
()
}
...
...
@@ -49,7 +49,7 @@ func TestFormatHeader_1(t *testing.T) {
break
}
h
,
_
:=
formatTimeHeader
(
tm
)
if
tm
.
Format
(
"2006/01/02 15:04:05.
999
"
)
!=
string
(
h
)
{
if
tm
.
Format
(
"2006/01/02 15:04:05.
000
"
)
!=
string
(
h
)
{
t
.
Log
(
tm
)
t
.
FailNow
()
}
...
...
logs/multifile.go
View file @
af73a2d5
...
...
@@ -67,7 +67,10 @@ func (f *multiFileLogWriter) Init(config string) error {
jsonMap
[
"level"
]
=
i
bs
,
_
:=
json
.
Marshal
(
jsonMap
)
writer
=
newFileWriter
()
.
(
*
fileLogWriter
)
writer
.
Init
(
string
(
bs
))
err
:=
writer
.
Init
(
string
(
bs
))
if
err
!=
nil
{
return
err
}
f
.
writers
[
i
]
=
writer
}
}
...
...
migration/ddl.go
View file @
af73a2d5
...
...
@@ -322,7 +322,7 @@ func (m *Migration) GetSQL() (sql string) {
sql
+=
fmt
.
Sprintf
(
"
\n
DROP COLUMN `%s`"
,
column
.
Name
)
}
if
len
(
m
.
Columns
)
>
index
{
if
len
(
m
.
Columns
)
>
index
+
1
{
sql
+=
","
}
}
...
...
@@ -355,7 +355,7 @@ func (m *Migration) GetSQL() (sql string) {
}
else
{
sql
+=
fmt
.
Sprintf
(
"
\n
DROP COLUMN `%s`"
,
column
.
Name
)
}
if
len
(
m
.
Columns
)
>
index
{
if
len
(
m
.
Columns
)
>
index
+
1
{
sql
+=
","
}
}
...
...
@@ -366,14 +366,14 @@ func (m *Migration) GetSQL() (sql string) {
for
index
,
unique
:=
range
m
.
Uniques
{
sql
+=
fmt
.
Sprintf
(
"
\n
DROP KEY `%s`"
,
unique
.
Definition
)
if
len
(
m
.
Uniques
)
>
index
{
if
len
(
m
.
Uniques
)
>
index
+
1
{
sql
+=
","
}
}
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
)
if
len
(
m
.
Renames
)
>
index
{
if
len
(
m
.
Renames
)
>
index
+
1
{
sql
+=
","
}
}
...
...
orm/cmd_utils.go
View file @
af73a2d5
...
...
@@ -197,6 +197,10 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
if
strings
.
Contains
(
column
,
"%COL%"
)
{
column
=
strings
.
Replace
(
column
,
"%COL%"
,
fi
.
column
,
-
1
)
}
if
fi
.
description
!=
""
{
column
+=
" "
+
fmt
.
Sprintf
(
"COMMENT '%s'"
,
fi
.
description
)
}
columns
=
append
(
columns
,
column
)
}
...
...
orm/db.go
View file @
af73a2d5
...
...
@@ -969,6 +969,10 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
}
query
:=
fmt
.
Sprintf
(
"%s %s FROM %s%s%s T0 %s%s%s%s%s"
,
sqlSelect
,
sels
,
Q
,
mi
.
table
,
Q
,
join
,
where
,
groupBy
,
orderBy
,
limit
)
if
qs
.
forupdate
{
query
+=
" FOR UPDATE"
}
d
.
ins
.
ReplaceMarks
(
&
query
)
var
rs
*
sql
.
Rows
...
...
orm/models_info_f.go
View file @
af73a2d5
...
...
@@ -136,6 +136,7 @@ type fieldInfo struct {
decimals
int
isFielder
bool
// implement Fielder interface
onDelete
string
description
string
}
// new field info
...
...
@@ -300,6 +301,7 @@ checkType:
fi
.
sf
=
sf
fi
.
fullName
=
mi
.
fullName
+
mName
+
"."
+
sf
.
Name
fi
.
description
=
sf
.
Tag
.
Get
(
"description"
)
fi
.
null
=
attrs
[
"null"
]
fi
.
index
=
attrs
[
"index"
]
fi
.
auto
=
attrs
[
"auto"
]
...
...
orm/orm_queryset.go
View file @
af73a2d5
...
...
@@ -55,16 +55,17 @@ func ColValue(opt operator, value interface{}) interface{} {
// real query struct
type
querySet
struct
{
mi
*
modelInfo
cond
*
Condition
related
[]
string
relDepth
int
limit
int64
offset
int64
groups
[]
string
orders
[]
string
distinct
bool
orm
*
orm
mi
*
modelInfo
cond
*
Condition
related
[]
string
relDepth
int
limit
int64
offset
int64
groups
[]
string
orders
[]
string
distinct
bool
forupdate
bool
orm
*
orm
}
var
_
QuerySeter
=
new
(
querySet
)
...
...
@@ -127,6 +128,12 @@ func (o querySet) Distinct() QuerySeter {
return
&
o
}
// add FOR UPDATE to SELECT
func
(
o
querySet
)
ForUpdate
()
QuerySeter
{
o
.
forupdate
=
true
return
&
o
}
// set relation model to query together.
// it will query relation models and assign to parent model.
func
(
o
querySet
)
RelatedSel
(
params
...
interface
{})
QuerySeter
{
...
...
@@ -191,7 +198,11 @@ func (o *querySet) PrepareInsert() (Inserter, error) {
// query all data and map to containers.
// cols means the columns when querying.
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.
...
...
orm/types.go
View file @
af73a2d5
...
...
@@ -190,6 +190,10 @@ type QuerySeter interface {
// Distinct().
// All(&permissions)
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
// for example:
// num, err = qs.Filter("profile__age__gt", 28).Count()
...
...
parser.go
View file @
af73a2d5
...
...
@@ -114,20 +114,21 @@ type parsedParam struct {
func
parserComments
(
f
*
ast
.
FuncDecl
,
controllerName
,
pkgpath
string
)
error
{
if
f
.
Doc
!=
nil
{
parsedComment
,
err
:=
parseComment
(
f
.
Doc
.
List
)
parsedComment
s
,
err
:=
parseComment
(
f
.
Doc
.
List
)
if
err
!=
nil
{
return
err
}
if
parsedComment
.
routerPath
!=
""
{
key
:=
pkgpath
+
":"
+
controllerName
cc
:=
ControllerComments
{}
cc
.
Method
=
f
.
Name
.
String
()
cc
.
Router
=
parsedComment
.
routerPath
cc
.
AllowHTTPMethods
=
parsedComment
.
methods
cc
.
MethodParams
=
buildMethodParams
(
f
.
Type
.
Params
.
List
,
parsedComment
)
genInfoList
[
key
]
=
append
(
genInfoList
[
key
],
cc
)
for
_
,
parsedComment
:=
range
parsedComments
{
if
parsedComment
.
routerPath
!=
""
{
key
:=
pkgpath
+
":"
+
controllerName
cc
:=
ControllerComments
{}
cc
.
Method
=
f
.
Name
.
String
()
cc
.
Router
=
parsedComment
.
routerPath
cc
.
AllowHTTPMethods
=
parsedComment
.
methods
cc
.
MethodParams
=
buildMethodParams
(
f
.
Type
.
Params
.
List
,
parsedComment
)
genInfoList
[
key
]
=
append
(
genInfoList
[
key
],
cc
)
}
}
}
return
nil
}
...
...
@@ -177,26 +178,13 @@ func paramInPath(name, route string) bool {
var
routeRegex
=
regexp
.
MustCompile
(
`@router\s+(\S+)(?:\s+\[(\S+)\])?`
)
func
parseComment
(
lines
[]
*
ast
.
Comment
)
(
pc
*
parsedComment
,
err
error
)
{
pc
=
&
parsedComment
{}
func
parseComment
(
lines
[]
*
ast
.
Comment
)
(
pcs
[]
*
parsedComment
,
err
error
)
{
pcs
=
[]
*
parsedComment
{}
params
:=
map
[
string
]
parsedParam
{}
for
_
,
c
:=
range
lines
{
t
:=
strings
.
TrimSpace
(
strings
.
TrimLeft
(
c
.
Text
,
"//"
))
if
strings
.
HasPrefix
(
t
,
"@router"
)
{
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"
)
{
if
strings
.
HasPrefix
(
t
,
"@Param"
)
{
pv
:=
getparams
(
strings
.
TrimSpace
(
strings
.
TrimLeft
(
t
,
"@Param"
)))
if
len
(
pv
)
<
4
{
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
.
required
,
_
=
strconv
.
ParseBool
(
pv
[
4
])
}
if
pc
.
params
==
nil
{
pc
.
params
=
map
[
string
]
parsedParam
{}
params
[
funcParamName
]
=
p
}
}
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
...
...
router.go
View file @
af73a2d5
...
...
@@ -202,10 +202,10 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt
numOfFields
:=
elemVal
.
NumField
()
for
i
:=
0
;
i
<
numOfFields
;
i
++
{
fieldType
:=
elemType
.
Field
(
i
)
if
e
xecElem
.
FieldByName
(
fieldType
.
Name
)
.
CanSet
()
{
elemField
:=
execElem
.
FieldByName
(
fieldType
.
Name
)
if
e
lemField
.
CanSet
()
{
fieldVal
:=
elemVal
.
Field
(
i
)
e
xecElem
.
FieldByName
(
fieldType
.
Name
)
.
Set
(
fieldVal
)
e
lemField
.
Set
(
fieldVal
)
}
}
...
...
session/redis/sess_redis.go
View file @
af73a2d5
...
...
@@ -14,9 +14,9 @@
// Package redis for session provider
//
// depend on github.com/g
aryburd
/redigo/redis
// depend on github.com/g
omodule
/redigo/redis
//
// go install github.com/g
aryburd
/redigo/redis
// go install github.com/g
omodule
/redigo/redis
//
// Usage:
// import(
...
...
@@ -24,10 +24,10 @@
// "github.com/astaxie/beego/session"
// )
//
// func init() {
// globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``)
// go globalSessions.GC()
// }
//
func init() {
//
globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``)
//
go globalSessions.GC()
//
}
//
// more docs: http://beego.me/docs/module/session.md
package
redis
...
...
@@ -37,10 +37,11 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/astaxie/beego/session"
"github.com/g
aryburd
/redigo/redis"
"github.com/g
omodule
/redigo/redis"
)
var
redispder
=
&
Provider
{}
...
...
@@ -118,8 +119,8 @@ type Provider struct {
}
// SessionInit init redis session
// savepath like redis server addr,pool size,password,dbnum
// e.g. 127.0.0.1:6379,100,astaxie,0
// savepath like redis server addr,pool size,password,dbnum
,IdleTimeout second
// e.g. 127.0.0.1:6379,100,astaxie,0
,30
func
(
rp
*
Provider
)
SessionInit
(
maxlifetime
int64
,
savePath
string
)
error
{
rp
.
maxlifetime
=
maxlifetime
configs
:=
strings
.
Split
(
savePath
,
","
)
...
...
@@ -149,27 +150,39 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error {
}
else
{
rp
.
dbNum
=
0
}
rp
.
poollist
=
redis
.
NewPool
(
func
()
(
redis
.
Conn
,
error
)
{
c
,
err
:=
redis
.
Dial
(
"tcp"
,
rp
.
savePath
)
if
err
!=
nil
{
return
nil
,
err
var
idleTimeout
time
.
Duration
=
0
if
len
(
configs
)
>
4
{
timeout
,
err
:=
strconv
.
Atoi
(
configs
[
4
])
if
err
==
nil
&&
timeout
>
0
{
idleTimeout
=
time
.
Duration
(
timeout
)
*
time
.
Second
}
if
rp
.
password
!=
""
{
if
_
,
err
=
c
.
Do
(
"AUTH"
,
rp
.
password
);
err
!=
nil
{
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
)
}
rp
.
poollist
=
&
redis
.
Pool
{
Dial
:
func
()
(
redis
.
Conn
,
error
)
{
c
,
err
:=
redis
.
Dial
(
"tcp"
,
rp
.
savePath
)
if
err
!=
nil
{
c
.
Close
()
return
nil
,
err
}
}
return
c
,
err
},
rp
.
poolsize
)
if
rp
.
password
!=
""
{
if
_
,
err
=
c
.
Do
(
"AUTH"
,
rp
.
password
);
err
!=
nil
{
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
()
}
...
...
session/redis_cluster/redis_cluster.go
0 → 100644
View file @
af73a2d5
// 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
)
}
staticfile.go
View file @
af73a2d5
...
...
@@ -74,7 +74,7 @@ func serverStaticRouter(ctx *context.Context) {
if
enableCompress
{
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
BConfig
.
RunMode
==
DEV
{
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
))
}
http
.
ServeContent
(
ctx
.
ResponseWriter
,
ctx
.
Request
,
filePath
,
sch
.
modTime
,
sch
)
http
.
ServeContent
(
ctx
.
ResponseWriter
,
ctx
.
Request
,
filePath
,
sch
.
modTime
,
reader
)
}
type
serveContentHolder
struct
{
*
bytes
.
Reader
data
[]
byte
modTime
time
.
Time
size
int64
encoding
string
}
type
serveContentReader
struct
{
*
bytes
.
Reader
}
var
(
staticFileMap
=
make
(
map
[
string
]
*
serveContentHolder
)
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
mapLock
.
RLock
()
mapFile
:=
staticFileMap
[
mapKey
]
mapLock
.
RUnlock
()
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
()
defer
mapLock
.
Unlock
()
if
mapFile
=
staticFileMap
[
mapKey
];
!
isOk
(
mapFile
,
fi
)
{
file
,
err
:=
os
.
Open
(
filePath
)
if
err
!=
nil
{
return
false
,
""
,
nil
,
err
return
false
,
""
,
nil
,
nil
,
err
}
defer
file
.
Close
()
var
bufferWriter
bytes
.
Buffer
_
,
n
,
err
:=
context
.
WriteFile
(
acceptEncoding
,
&
bufferWriter
,
file
)
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
}
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
{
...
...
staticfile_test.go
View file @
af73a2d5
...
...
@@ -16,7 +16,7 @@ var licenseFile = filepath.Join(currentWorkDir, "LICENSE")
func
testOpenFile
(
encoding
string
,
content
[]
byte
,
t
*
testing
.
T
)
{
fi
,
_
:=
os
.
Stat
(
licenseFile
)
b
,
n
,
sch
,
err
:=
openFile
(
licenseFile
,
fi
,
encoding
)
b
,
n
,
sch
,
reader
,
err
:=
openFile
(
licenseFile
,
fi
,
encoding
)
if
err
!=
nil
{
t
.
Log
(
err
)
t
.
Fail
()
...
...
@@ -24,7 +24,7 @@ func testOpenFile(encoding string, content []byte, t *testing.T) {
t
.
Log
(
"open static file encoding "
+
n
,
b
)
assetOpenFileAndContent
(
sch
,
content
,
t
)
assetOpenFileAndContent
(
sch
,
reader
,
content
,
t
)
}
func
TestOpenStaticFile_1
(
t
*
testing
.
T
)
{
file
,
_
:=
os
.
Open
(
licenseFile
)
...
...
@@ -53,13 +53,13 @@ func TestOpenStaticFileDeflate_1(t *testing.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
))
if
sch
.
size
!=
int64
(
len
(
content
))
{
t
.
Log
(
"static content file size not same"
)
t
.
Fail
()
}
bs
,
_
:=
ioutil
.
ReadAll
(
sch
)
bs
,
_
:=
ioutil
.
ReadAll
(
reader
)
for
i
,
v
:=
range
content
{
if
v
!=
bs
[
i
]
{
t
.
Log
(
"content not same"
)
...
...
swagger/swagger.go
View file @
af73a2d5
...
...
@@ -122,6 +122,7 @@ type Schema struct {
Items
*
Schema
`json:"items,omitempty" yaml:"items,omitempty"`
Properties
map
[
string
]
Propertie
`json:"properties,omitempty" yaml:"properties,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
...
...
@@ -131,7 +132,7 @@ type Propertie struct {
Description
string
`json:"description,omitempty" yaml:"description,omitempty"`
Default
interface
{}
`json:"default,omitempty" yaml:"default,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"`
Format
string
`json:"format,omitempty" yaml:"format,omitempty"`
ReadOnly
bool
`json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
...
...
template.go
View file @
af73a2d5
...
...
@@ -218,6 +218,7 @@ func BuildTemplate(dir string, files ...string) error {
}
if
err
!=
nil
{
logs
.
Error
(
"parse template err:"
,
file
,
err
)
templatesLock
.
Unlock
()
return
err
}
beeTemplates
[
file
]
=
t
...
...
templatefunc.go
View file @
af73a2d5
...
...
@@ -17,6 +17,7 @@ package beego
import
(
"errors"
"fmt"
"html"
"html/template"
"net/url"
"reflect"
...
...
@@ -84,24 +85,24 @@ func DateFormat(t time.Time, layout string) (datestring string) {
var
datePatterns
=
[]
string
{
// year
"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
"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
"M"
,
"Jan"
,
// A short textual representation of a month, three letters Jan through Dec
"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
"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
// day
"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
"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
// 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
"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
...
...
@@ -207,14 +208,12 @@ func Htmlquote(text string) string {
'<'&">'
*/
text
=
strings
.
Replace
(
text
,
"&"
,
"&"
,
-
1
)
// Must be done first!
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
=
html
.
EscapeString
(
text
)
text
=
strings
.
NewReplacer
(
`“`
,
"“"
,
`”`
,
"”"
,
` `
,
" "
,
)
.
Replace
(
text
)
return
strings
.
TrimSpace
(
text
)
}
...
...
@@ -228,17 +227,7 @@ func Htmlunquote(text string) string {
'<\\'&">'
*/
// strings.Replace(s, old, new, n)
// 在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!
text
=
html
.
UnescapeString
(
text
)
return
strings
.
TrimSpace
(
text
)
}
...
...
templatefunc_test.go
View file @
af73a2d5
...
...
@@ -94,7 +94,7 @@ func TestCompareRelated(t *testing.T) {
}
func
TestHtmlquote
(
t
*
testing
.
T
)
{
h
:=
`<' ”“&&
quot
;>`
h
:=
`<' ”“&&
#34
;>`
s
:=
`<' ”“&">`
if
Htmlquote
(
s
)
!=
h
{
t
.
Error
(
"should be equal"
)
...
...
@@ -102,8 +102,8 @@ func TestHtmlquote(t *testing.T) {
}
func
TestHtmlunquote
(
t
*
testing
.
T
)
{
h
:=
`<' ”“&&
quot
;>`
s
:=
`<'
”“&">`
h
:=
`<' ”“&&
#34
;>`
s
:=
`<'
”“&">`
if
Htmlunquote
(
h
)
!=
s
{
t
.
Error
(
"should be equal"
)
}
...
...
validation/validation.go
View file @
af73a2d5
...
...
@@ -245,7 +245,21 @@ func (v *Validation) ZipCode(obj interface{}, key string) *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
}
}
...
...
@@ -351,13 +365,24 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) {
return
}
var
hasReuired
bool
var
hasRe
q
uired
bool
for
_
,
vf
:=
range
vfs
{
if
vf
.
Name
==
"Required"
{
hasReuired
=
true
hasRequired
=
true
}
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
()
}
}
if
!
hasReuired
&&
v
.
RequiredFirst
&&
len
(
objV
.
Field
(
i
)
.
String
())
==
0
{
chk
:=
Required
{
""
}
.
IsSatisfied
(
currentField
)
if
!
hasRequired
&&
v
.
RequiredFirst
&&
!
chk
{
if
_
,
ok
:=
CanSkipFuncs
[
vf
.
Name
];
ok
{
continue
}
...
...
@@ -414,3 +439,9 @@ func (v *Validation) RecursiveValid(objc interface{}) (bool, error) {
}
return
pass
,
err
}
func
(
v
*
Validation
)
CanSkipAlso
(
skipFunc
string
)
{
if
_
,
ok
:=
CanSkipFuncs
[
skipFunc
];
!
ok
{
CanSkipFuncs
[
skipFunc
]
=
struct
{}{}
}
}
validation/validation_test.go
View file @
af73a2d5
...
...
@@ -442,3 +442,122 @@ func TestSkipValid(t *testing.T) {
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"
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment