mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
Merge branch 'master' into develop
This commit is contained in:
@ -8,6 +8,7 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetSessionCookieMaxAge(0)
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Timestamp())
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -83,9 +83,13 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error
|
||||
func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args)
|
||||
ctx := c.DB.GetCtx()
|
||||
if c.GetConfig().QueryTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
|
||||
}
|
||||
if c.DB.GetDebug() {
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...)
|
||||
rows, err = link.QueryContext(ctx, sql, args...)
|
||||
mTime2 := gtime.TimestampMilli()
|
||||
s := &Sql{
|
||||
Sql: sql,
|
||||
@ -98,7 +102,7 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro
|
||||
}
|
||||
c.writeSqlToLogger(s)
|
||||
} else {
|
||||
rows, err = link.QueryContext(c.DB.GetCtx(), sql, args...)
|
||||
rows, err = link.QueryContext(ctx, sql, args...)
|
||||
}
|
||||
if err == nil {
|
||||
return rows, nil
|
||||
@ -123,10 +127,14 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err
|
||||
func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args)
|
||||
ctx := c.DB.GetCtx()
|
||||
if c.GetConfig().ExecTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().ExecTimeout)
|
||||
}
|
||||
if c.DB.GetDebug() {
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
if !c.DB.GetDryRun() {
|
||||
result, err = link.ExecContext(c.DB.GetCtx(), sql, args...)
|
||||
result, err = link.ExecContext(ctx, sql, args...)
|
||||
} else {
|
||||
result = new(SqlResult)
|
||||
}
|
||||
@ -143,7 +151,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
|
||||
c.writeSqlToLogger(s)
|
||||
} else {
|
||||
if !c.DB.GetDryRun() {
|
||||
result, err = link.ExecContext(c.DB.GetCtx(), sql, args...)
|
||||
result, err = link.ExecContext(ctx, sql, args...)
|
||||
} else {
|
||||
result = new(SqlResult)
|
||||
}
|
||||
@ -178,7 +186,11 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error) {
|
||||
|
||||
// doPrepare calls prepare function on given link object and returns the statement object.
|
||||
func (c *Core) DoPrepare(link Link, sql string) (*sql.Stmt, error) {
|
||||
return link.PrepareContext(c.DB.GetCtx(), sql)
|
||||
ctx := c.DB.GetCtx()
|
||||
if c.GetConfig().QueryTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
|
||||
}
|
||||
return link.PrepareContext(ctx, sql)
|
||||
}
|
||||
|
||||
// GetAll queries and returns data records from database.
|
||||
@ -320,7 +332,11 @@ func (c *Core) Begin() (*TX, error) {
|
||||
if master, err := c.DB.Master(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if tx, err := master.Begin(); err == nil {
|
||||
ctx := c.DB.GetCtx()
|
||||
if c.GetConfig().TranTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().TranTimeout)
|
||||
}
|
||||
if tx, err := master.BeginTx(ctx, nil); err == nil {
|
||||
return &TX{
|
||||
db: c.DB,
|
||||
tx: tx,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -16,8 +16,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_GROUP_NAME = "default" // Deprecated, use DefaultGroupName instead.
|
||||
DefaultGroupName = "default" // Default group name.
|
||||
DefaultGroupName = "default" // Default group name.
|
||||
)
|
||||
|
||||
// Config is the configuration management object.
|
||||
@ -28,26 +27,30 @@ type ConfigGroup []ConfigNode
|
||||
|
||||
// ConfigNode is configuration for one node.
|
||||
type ConfigNode struct {
|
||||
Host string // Host of server, ip or domain like: 127.0.0.1, localhost
|
||||
Port string // Port, it's commonly 3306.
|
||||
User string // Authentication username.
|
||||
Pass string // Authentication password.
|
||||
Name string // Default used database name.
|
||||
Type string // Database type: mysql, sqlite, mssql, pgsql, oracle.
|
||||
Role string // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
|
||||
Debug bool // (Optional) Debug mode enables debug information logging and output.
|
||||
Prefix string // (Optional) Table prefix.
|
||||
DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
|
||||
Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node.
|
||||
Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database.
|
||||
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
MaxIdleConnCount int `json:"maxidle"` // (Optional) Max idle connection configuration for underlying connection pool.
|
||||
MaxOpenConnCount int `json:"maxopen"` // (Optional) Max open connection configuration for underlying connection pool.
|
||||
MaxConnLifetime time.Duration `json:"maxlifetime"` // (Optional) Max connection TTL configuration for underlying connection pool.
|
||||
CreatedAt string // (Optional) The filed name of table for automatic-filled created datetime.
|
||||
UpdatedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
DeletedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
TimeMaintainDisabled bool // (Optional) Disable the automatic time maintaining feature.
|
||||
Host string `json:"host"` // Host of server, ip or domain like: 127.0.0.1, localhost
|
||||
Port string `json:"port"` // Port, it's commonly 3306.
|
||||
User string `json:"user"` // Authentication username.
|
||||
Pass string `json:"pass"` // Authentication password.
|
||||
Name string `json:"name"` // Default used database name.
|
||||
Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle.
|
||||
Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
|
||||
Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output.
|
||||
Prefix string `json:"prefix"` // (Optional) Table prefix.
|
||||
DryRun bool `json:"dryRun"` // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
|
||||
Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node.
|
||||
Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database.
|
||||
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool.
|
||||
MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool.
|
||||
MaxConnLifetime time.Duration `json:"maxLifetime"` // (Optional) Max connection TTL configuration for underlying connection pool.
|
||||
QueryTimeout time.Duration `json:"queryTimeout"` // (Optional) Max query time for per dql.
|
||||
ExecTimeout time.Duration `json:"execTimeout"` // (Optional) Max exec time for dml.
|
||||
TranTimeout time.Duration `json:"tranTimeout"` // (Optional) Max exec time time for a transaction.
|
||||
PrepareTimeout time.Duration `json:"prepareTimeout"` // (Optional) Max exec time time for prepare operation.
|
||||
CreatedAt string `json:"createdAt"` // (Optional) The filed name of table for automatic-filled created datetime.
|
||||
UpdatedAt string `json:"updatedAt"` // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
DeletedAt string `json:"deletedAt"` // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature.
|
||||
}
|
||||
|
||||
// configs is internal used configuration object.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -48,9 +48,9 @@ type apiMapStrAny interface {
|
||||
}
|
||||
|
||||
const (
|
||||
ORM_TAG_FOR_STRUCT = "orm"
|
||||
ORM_TAG_FOR_UNIQUE = "unique"
|
||||
ORM_TAG_FOR_PRIMARY = "primary"
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -58,7 +58,7 @@ var (
|
||||
quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
|
||||
|
||||
// Priority tags for struct converting for orm field mapping.
|
||||
structTagPriority = append([]string{ORM_TAG_FOR_STRUCT}, gconv.StructTagPriority...)
|
||||
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
|
||||
)
|
||||
|
||||
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
|
||||
@ -315,14 +315,14 @@ func doQuoteString(s, charLeft, charRight string) string {
|
||||
// GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer.
|
||||
// This function automatically retrieves primary or unique field and its attribute value as condition.
|
||||
func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}, err error) {
|
||||
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
|
||||
tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
for _, field := range tagField {
|
||||
array = strings.Split(field.TagValue, ",")
|
||||
if len(array) > 1 && gstr.InArray([]string{ORM_TAG_FOR_UNIQUE, ORM_TAG_FOR_PRIMARY}, array[1]) {
|
||||
if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) {
|
||||
return array[0], []interface{}{field.Value()}, nil
|
||||
}
|
||||
if len(where) > 0 {
|
||||
@ -336,14 +336,14 @@ func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interf
|
||||
|
||||
// GetPrimaryKey retrieves and returns primary key field name from given struct.
|
||||
func GetPrimaryKey(pointer interface{}) (string, error) {
|
||||
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
|
||||
tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
for _, field := range tagField {
|
||||
array = strings.Split(field.TagValue, ",")
|
||||
if len(array) > 1 && array[1] == ORM_TAG_FOR_PRIMARY {
|
||||
if len(array) > 1 && array[1] == OrmTagForPrimary {
|
||||
return array[0], nil
|
||||
}
|
||||
}
|
||||
@ -741,7 +741,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
// convertMapToStruct maps the <data> to given struct.
|
||||
// Note that the given parameter <pointer> should be a pointer to s struct.
|
||||
func convertMapToStruct(data map[string]interface{}, pointer interface{}) error {
|
||||
tagNameMap, err := structs.TagMapName(pointer, []string{ORM_TAG_FOR_STRUCT})
|
||||
tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
)
|
||||
@ -48,7 +49,10 @@ func (r Record) GMap() *gmap.StrAnyMap {
|
||||
func (r Record) Struct(pointer interface{}) error {
|
||||
// If the record is empty, it returns error.
|
||||
if r.IsEmpty() {
|
||||
return sql.ErrNoRows
|
||||
if !empty.IsNil(pointer, true) {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Special handling for parameter type: reflect.Value
|
||||
if _, ok := pointer.(reflect.Value); ok {
|
||||
|
||||
@ -981,6 +981,18 @@ func Test_Model_Struct(t *testing.T) {
|
||||
err := db.Table(table).Where("id=-1").Struct(user)
|
||||
t.Assert(err, sql.ErrNoRows)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var user *User
|
||||
err := db.Table(table).Where("id=-1").Struct(&user)
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Struct_CustomType(t *testing.T) {
|
||||
@ -2135,6 +2147,27 @@ func Test_Model_Option_List(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_OmitEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr())
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table)
|
||||
_, err := db.Table(table).OmitEmpty().Data(g.Map{
|
||||
"id": 1,
|
||||
"name": "",
|
||||
}).Save()
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Option_Where(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createInitTable()
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -7,6 +7,7 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
@ -252,24 +253,25 @@ func Test_Struct_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
user := new(User)
|
||||
t.AssertNE(one.Struct(user), nil)
|
||||
err := db.Table(table).Where("id=100").Struct(user)
|
||||
t.Assert(err, sql.ErrNoRows)
|
||||
t.AssertNE(user, nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
var user *User
|
||||
t.AssertNE(one.Struct(&user), nil)
|
||||
t.Assert(one.Struct(&user), nil)
|
||||
t.Assert(user, nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
var user *User
|
||||
t.AssertNE(one.Struct(user), nil)
|
||||
err := db.Table(table).Where("id=100").Struct(&user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user, nil)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -36,17 +36,17 @@ type Conn struct {
|
||||
|
||||
// Redis configuration.
|
||||
type Config struct {
|
||||
Host string
|
||||
Port int
|
||||
Db int
|
||||
Pass string // Password for AUTH.
|
||||
MaxIdle int // Maximum number of connections allowed to be idle (default is 10)
|
||||
MaxActive int // Maximum number of connections limit (default is 0 means no limit).
|
||||
IdleTimeout time.Duration // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0)
|
||||
MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0)
|
||||
ConnectTimeout time.Duration // Dial connection timeout.
|
||||
TLS bool // Specifies the config to use when a TLS connection is dialed.
|
||||
TLSSkipVerify bool // Disables server name verification when connecting over TLS
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Db int `json:"db"`
|
||||
Pass string `json:"pass"` // Password for AUTH.
|
||||
MaxIdle int `json:"maxIdle"` // Maximum number of connections allowed to be idle (default is 10)
|
||||
MaxActive int `json:"maxActive"` // Maximum number of connections limit (default is 0 means no limit).
|
||||
IdleTimeout time.Duration `json:"idleTimeout"` // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0)
|
||||
MaxConnLifetime time.Duration `json:"maxConnLifetime"` // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0)
|
||||
ConnectTimeout time.Duration `json:"connectTimeout"` // Dial connection timeout.
|
||||
TLS bool `json:"tls"` // Specifies the config to use when a TLS connection is dialed.
|
||||
TLSSkipVerify bool `json:"tlsSkipVerify"` // Disables server name verification when connecting over TLS
|
||||
}
|
||||
|
||||
// Pool statistics.
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
func Example_autoMarshalUnmarshalMap() {
|
||||
@ -101,7 +102,7 @@ func Example_autoMarshalUnmarshalStructSlice() {
|
||||
fmt.Println(users2)
|
||||
}
|
||||
|
||||
func Example_hashSet() {
|
||||
func Example_hSet() {
|
||||
var (
|
||||
err error
|
||||
result *gvar.Var
|
||||
@ -124,3 +125,54 @@ func Example_hashSet() {
|
||||
// May Output:
|
||||
// map[id:10000 name:john]
|
||||
}
|
||||
|
||||
func Example_hMSet_Map() {
|
||||
var (
|
||||
key = "user_100"
|
||||
data = g.Map{
|
||||
"name": "gf",
|
||||
"sex": 0,
|
||||
"score": 100,
|
||||
}
|
||||
)
|
||||
_, err := g.Redis().Do("HMSET", append(g.Slice{key}, gutil.MapToSlice(data)...)...)
|
||||
if err != nil {
|
||||
g.Log().Fatal(err)
|
||||
}
|
||||
v, err := g.Redis().DoVar("HMGET", key, "name")
|
||||
if err != nil {
|
||||
g.Log().Fatal(err)
|
||||
}
|
||||
fmt.Println(v.Slice())
|
||||
|
||||
// May Output:
|
||||
// [gf]
|
||||
}
|
||||
|
||||
func Example_hMSet_Struct() {
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Sex int `json:"sex"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
var (
|
||||
key = "user_100"
|
||||
data = &User{
|
||||
Name: "gf",
|
||||
Sex: 0,
|
||||
Score: 100,
|
||||
}
|
||||
)
|
||||
_, err := g.Redis().Do("HMSET", append(g.Slice{key}, gutil.StructToSlice(data)...)...)
|
||||
if err != nil {
|
||||
g.Log().Fatal(err)
|
||||
}
|
||||
v, err := g.Redis().DoVar("HMGET", key, "name")
|
||||
if err != nil {
|
||||
g.Log().Fatal(err)
|
||||
}
|
||||
fmt.Println(v.Slice())
|
||||
|
||||
// May Output:
|
||||
// ["gf"]
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -255,9 +256,11 @@ func Test_HSet(t *testing.T) {
|
||||
|
||||
func Test_HGetAll1(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var err error
|
||||
redis := gredis.New(config)
|
||||
key := guid.S()
|
||||
var (
|
||||
err error
|
||||
key = guid.S()
|
||||
redis = gredis.New(config)
|
||||
)
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
_, err = redis.Do("HSET", key, "id", 100)
|
||||
@ -296,6 +299,54 @@ func Test_HGetAll2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_HMSet(t *testing.T) {
|
||||
// map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
err error
|
||||
key = guid.S()
|
||||
redis = gredis.New(config)
|
||||
data = g.Map{
|
||||
"name": "gf",
|
||||
"sex": 0,
|
||||
"score": 100,
|
||||
}
|
||||
)
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
_, err = redis.Do("HMSET", append(g.Slice{key}, gutil.MapToSlice(data)...)...)
|
||||
t.Assert(err, nil)
|
||||
v, err := redis.DoVar("HMGET", key, "name")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Slice(), g.Slice{data["name"]})
|
||||
})
|
||||
// struct
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Sex int `json:"sex"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
var (
|
||||
err error
|
||||
key = guid.S()
|
||||
redis = gredis.New(config)
|
||||
data = &User{
|
||||
Name: "gf",
|
||||
Sex: 0,
|
||||
Score: 100,
|
||||
}
|
||||
)
|
||||
defer redis.Do("DEL", key)
|
||||
|
||||
_, err = redis.Do("HMSET", append(g.Slice{key}, gutil.StructToSlice(data)...)...)
|
||||
t.Assert(err, nil)
|
||||
v, err := redis.DoVar("HMGET", key, "name")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Slice(), g.Slice{data.Name})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Auto_Marshal(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
|
||||
@ -169,6 +169,12 @@ func (err *Error) Next() error {
|
||||
return err.error
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (err *Error) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + err.Error() + `"`), nil
|
||||
}
|
||||
|
||||
// formatSubStack formats the stack for error.
|
||||
func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
index := 1
|
||||
|
||||
@ -9,6 +9,7 @@ package gerror_test
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
@ -308,3 +309,12 @@ func Test_Code(t *testing.T) {
|
||||
t.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Json(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := gerror.Wrap(gerror.New("1"), "2")
|
||||
b, e := json.Marshal(err)
|
||||
t.Assert(e, nil)
|
||||
t.Assert(string(b), `"2: 1"`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -52,9 +52,12 @@ func TryCatch(try func(), catch ...func(exception error)) {
|
||||
}
|
||||
|
||||
// IsNil checks whether given <value> is nil.
|
||||
// Parameter <traceSource> is used for tracing to the source variable if given <value> is type
|
||||
// of a pinter that also points to a pointer. It returns nil if the source is nil when <traceSource>
|
||||
// is true.
|
||||
// Note that it might use reflect feature which affects performance a little bit.
|
||||
func IsNil(value interface{}) bool {
|
||||
return empty.IsNil(value)
|
||||
func IsNil(value interface{}, traceSource ...bool) bool {
|
||||
return empty.IsNil(value, traceSource...)
|
||||
}
|
||||
|
||||
// IsEmpty checks whether given <value> empty.
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package empty provides functions for checking empty variables.
|
||||
// Package empty provides functions for checking empty/nil variables.
|
||||
package empty
|
||||
|
||||
import (
|
||||
@ -152,8 +152,11 @@ func IsEmpty(value interface{}) bool {
|
||||
}
|
||||
|
||||
// IsNil checks whether given <value> is nil.
|
||||
// Parameter <traceSource> is used for tracing to the source variable if given <value> is type
|
||||
// of a pinter that also points to a pointer. It returns nil if the source is nil when <traceSource>
|
||||
// is true.
|
||||
// Note that it might use reflect feature which affects performance a little bit.
|
||||
func IsNil(value interface{}) bool {
|
||||
func IsNil(value interface{}, traceSource ...bool) bool {
|
||||
if value == nil {
|
||||
return true
|
||||
}
|
||||
@ -168,10 +171,24 @@ func IsNil(value interface{}) bool {
|
||||
reflect.Map,
|
||||
reflect.Slice,
|
||||
reflect.Func,
|
||||
reflect.Ptr,
|
||||
reflect.Interface,
|
||||
reflect.UnsafePointer:
|
||||
return rv.IsNil()
|
||||
return !rv.IsValid() || rv.IsNil()
|
||||
|
||||
case reflect.Ptr:
|
||||
if len(traceSource) > 0 && traceSource[0] {
|
||||
for rv.Kind() == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
if !rv.IsValid() {
|
||||
return true
|
||||
}
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
return rv.IsNil()
|
||||
}
|
||||
} else {
|
||||
return !rv.IsValid() || rv.IsNil()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -108,3 +108,22 @@ func TestIsEmpty(t *testing.T) {
|
||||
t.Assert(empty.IsEmpty(tmpF6), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(empty.IsNil(nil), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var i int
|
||||
t.Assert(empty.IsNil(i), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var i *int
|
||||
t.Assert(empty.IsNil(i), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var i *int
|
||||
t.Assert(empty.IsNil(&i), false)
|
||||
t.Assert(empty.IsNil(&i, true), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -350,7 +350,7 @@ func (s *Server) startServer(fdMap listenerFdMap) {
|
||||
s.config.HTTPSAddr = s.config.Address
|
||||
s.config.Address = ""
|
||||
} else {
|
||||
s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR
|
||||
s.config.HTTPSAddr = defaultHttpsAddr
|
||||
}
|
||||
}
|
||||
httpsEnabled = len(s.config.HTTPSAddr) > 0
|
||||
@ -385,7 +385,7 @@ func (s *Server) startServer(fdMap listenerFdMap) {
|
||||
}
|
||||
// HTTP
|
||||
if !httpsEnabled && len(s.config.Address) == 0 {
|
||||
s.config.Address = gDEFAULT_HTTP_ADDR
|
||||
s.config.Address = defaultHttpAddr
|
||||
}
|
||||
var array []string
|
||||
if v, ok := fdMap["http"]; ok && len(v) > 0 {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -233,8 +233,13 @@ func shutdownWebServers(signal ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
// gracefulShutdownWebServers gracefully shuts down all servers.
|
||||
func gracefulShutdownWebServers() {
|
||||
// shutdownWebServersGracefully gracefully shuts down all servers.
|
||||
func shutdownWebServersGracefully(signal ...string) {
|
||||
if len(signal) > 0 {
|
||||
glog.Printf("%d: server gracefully shutting down by signal: %s", gproc.Pid(), signal[0])
|
||||
} else {
|
||||
glog.Printf("%d: server gracefully shutting down by api", gproc.Pid())
|
||||
}
|
||||
serverMapping.RLockFunc(func(m map[string]interface{}) {
|
||||
for _, v := range m {
|
||||
for _, s := range v.(*Server).servers {
|
||||
@ -262,7 +267,7 @@ func handleProcessMessage() {
|
||||
if msg := gproc.Receive(adminGProcCommGroup); msg != nil {
|
||||
if bytes.EqualFold(msg.Data, []byte("exit")) {
|
||||
intlog.Printf("%d: process message: exit", gproc.Pid())
|
||||
gracefulShutdownWebServers()
|
||||
shutdownWebServersGracefully()
|
||||
allDoneChan <- struct{}{}
|
||||
intlog.Printf("%d: process message: exit done", gproc.Pid())
|
||||
return
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright 2017 gf Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -35,14 +35,22 @@ func handleProcessSignal() {
|
||||
sig = <-procSignalChan
|
||||
intlog.Printf(`signal received: %s`, sig.String())
|
||||
switch sig {
|
||||
// Stop the servers.
|
||||
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGABRT:
|
||||
// Shutdown the servers.
|
||||
case syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL, syscall.SIGABRT:
|
||||
shutdownWebServers(sig.String())
|
||||
return
|
||||
|
||||
// Shutdown the servers gracefully.
|
||||
// Especially from K8S when running server in POD.
|
||||
case syscall.SIGTERM:
|
||||
shutdownWebServersGracefully(sig.String())
|
||||
return
|
||||
|
||||
// Restart the servers.
|
||||
case syscall.SIGUSR1:
|
||||
restartWebServers(sig.String())
|
||||
if err := restartWebServers(sig.String()); err != nil {
|
||||
intlog.Error(err)
|
||||
}
|
||||
return
|
||||
|
||||
default:
|
||||
|
||||
@ -27,12 +27,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_HTTP_ADDR = ":80" // Default listening port for HTTP.
|
||||
gDEFAULT_HTTPS_ADDR = ":443" // Default listening port for HTTPS.
|
||||
URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'.
|
||||
URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name.
|
||||
URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case.
|
||||
URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case.
|
||||
defaultHttpAddr = ":80" // Default listening port for HTTP.
|
||||
defaultHttpsAddr = ":443" // Default listening port for HTTPS.
|
||||
URI_TYPE_DEFAULT = 0 // Method name to URI converting type, which converts name to its lower case and joins the words using char '-'.
|
||||
URI_TYPE_FULLNAME = 1 // Method name to URI converting type, which does no converting to the method name.
|
||||
URI_TYPE_ALLLOWER = 2 // Method name to URI converting type, which converts name to its lower case.
|
||||
URI_TYPE_CAMEL = 3 // Method name to URI converting type, which converts name to its camel case.
|
||||
)
|
||||
|
||||
// ServerConfig is the HTTP Server configuration manager.
|
||||
@ -43,16 +43,16 @@ type ServerConfig struct {
|
||||
|
||||
// Address specifies the server listening address like "port" or ":port",
|
||||
// multiple addresses joined using ','.
|
||||
Address string
|
||||
Address string `json:"address"`
|
||||
|
||||
// HTTPSAddr specifies the HTTPS addresses, multiple addresses joined using char ','.
|
||||
HTTPSAddr string
|
||||
HTTPSAddr string `json:"httpsAddr"`
|
||||
|
||||
// HTTPSCertPath specifies certification file path for HTTPS service.
|
||||
HTTPSCertPath string
|
||||
HTTPSCertPath string `json:"httpsCertPath"`
|
||||
|
||||
// HTTPSKeyPath specifies the key file path for HTTPS service.
|
||||
HTTPSKeyPath string
|
||||
HTTPSKeyPath string `json:"httpsKeyPath"`
|
||||
|
||||
// TLSConfig optionally provides a TLS configuration for use
|
||||
// by ServeTLS and ListenAndServeTLS. Note that this value is
|
||||
@ -61,10 +61,10 @@ type ServerConfig struct {
|
||||
// tls.Config.SetSessionTicketKeys. To use
|
||||
// SetSessionTicketKeys, use Server.Serve with a TLS Listener
|
||||
// instead.
|
||||
TLSConfig *tls.Config
|
||||
TLSConfig *tls.Config `json:"tlsConfig"`
|
||||
|
||||
// Handler the handler for HTTP request.
|
||||
Handler http.Handler
|
||||
Handler http.Handler `json:"-"`
|
||||
|
||||
// ReadTimeout is the maximum duration for reading the entire
|
||||
// request, including the body.
|
||||
@ -73,19 +73,19 @@ type ServerConfig struct {
|
||||
// decisions on each request body's acceptable deadline or
|
||||
// upload rate, most users will prefer to use
|
||||
// ReadHeaderTimeout. It is valid to use them both.
|
||||
ReadTimeout time.Duration
|
||||
ReadTimeout time.Duration `json:"readTimeout"`
|
||||
|
||||
// WriteTimeout is the maximum duration before timing out
|
||||
// writes of the response. It is reset whenever a new
|
||||
// request's header is read. Like ReadTimeout, it does not
|
||||
// let Handlers make decisions on a per-request basis.
|
||||
WriteTimeout time.Duration
|
||||
WriteTimeout time.Duration `json:"writeTimeout"`
|
||||
|
||||
// IdleTimeout is the maximum amount of time to wait for the
|
||||
// next request when keep-alives are enabled. If IdleTimeout
|
||||
// is zero, the value of ReadTimeout is used. If both are
|
||||
// zero, there is no timeout.
|
||||
IdleTimeout time.Duration
|
||||
IdleTimeout time.Duration `json:"idleTimeout"`
|
||||
|
||||
// MaxHeaderBytes controls the maximum number of bytes the
|
||||
// server will read parsing the request header's keys and
|
||||
@ -94,98 +94,102 @@ type ServerConfig struct {
|
||||
//
|
||||
// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
|
||||
// It's 10240 bytes in default.
|
||||
MaxHeaderBytes int
|
||||
MaxHeaderBytes int `json:"maxHeaderBytes"`
|
||||
|
||||
// KeepAlive enables HTTP keep-alive.
|
||||
KeepAlive bool
|
||||
KeepAlive bool `json:"keepAlive"`
|
||||
|
||||
// ServerAgent specifies the server agent information, which is wrote to
|
||||
// HTTP response header as "Server".
|
||||
ServerAgent string
|
||||
ServerAgent string `json:"serverAgent"`
|
||||
|
||||
// View specifies the default template view object for the server.
|
||||
View *gview.View
|
||||
View *gview.View `json:"view"`
|
||||
|
||||
// ==================================
|
||||
// Static.
|
||||
// ==================================
|
||||
|
||||
// Rewrites specifies the URI rewrite rules map.
|
||||
Rewrites map[string]string
|
||||
Rewrites map[string]string `json:"rewrites"`
|
||||
|
||||
// IndexFiles specifies the index files for static folder.
|
||||
IndexFiles []string
|
||||
IndexFiles []string `json:"indexFiles"`
|
||||
|
||||
// IndexFolder specifies if listing sub-files when requesting folder.
|
||||
// The server responses HTTP status code 403 if it is false.
|
||||
IndexFolder bool
|
||||
IndexFolder bool `json:"indexFolder"`
|
||||
|
||||
// ServerRoot specifies the root directory for static service.
|
||||
ServerRoot string
|
||||
ServerRoot string `json:"serverRoot"`
|
||||
|
||||
// SearchPaths specifies additional searching directories for static service.
|
||||
SearchPaths []string
|
||||
SearchPaths []string `json:"searchPaths"`
|
||||
|
||||
// StaticPaths specifies URI to directory mapping array.
|
||||
StaticPaths []staticPathItem
|
||||
StaticPaths []staticPathItem `json:"staticPaths"`
|
||||
|
||||
// FileServerEnabled is the global switch for static service.
|
||||
// It is automatically set enabled if any static path is set.
|
||||
FileServerEnabled bool
|
||||
FileServerEnabled bool `json:"fileServerEnabled"`
|
||||
|
||||
// ==================================
|
||||
// Cookie.
|
||||
// ==================================
|
||||
|
||||
// CookieMaxAge specifies the max TTL for cookie items.
|
||||
CookieMaxAge time.Duration
|
||||
CookieMaxAge time.Duration `json:"cookieMaxAge"`
|
||||
|
||||
// CookiePath specifies cookie path.
|
||||
// It also affects the default storage for session id.
|
||||
CookiePath string
|
||||
CookiePath string `json:"cookiePath"`
|
||||
|
||||
// CookieDomain specifies cookie domain.
|
||||
// It also affects the default storage for session id.
|
||||
CookieDomain string
|
||||
CookieDomain string `json:"cookieDomain"`
|
||||
|
||||
// ==================================
|
||||
// Session.
|
||||
// ==================================
|
||||
|
||||
// SessionMaxAge specifies max TTL for session items.
|
||||
SessionMaxAge time.Duration
|
||||
|
||||
// SessionIdName specifies the session id name.
|
||||
SessionIdName string
|
||||
SessionIdName string `json:"sessionIdName"`
|
||||
|
||||
// SessionCookieOutput specifies whether automatic outputting session id to cookie.
|
||||
SessionCookieOutput bool
|
||||
// SessionMaxAge specifies max TTL for session items.
|
||||
SessionMaxAge time.Duration `json:"sessionMaxAge"`
|
||||
|
||||
// SessionPath specifies the session storage directory path for storing session files.
|
||||
// It only makes sense if the session storage is type of file storage.
|
||||
SessionPath string
|
||||
SessionPath string `json:"sessionPath"`
|
||||
|
||||
// SessionStorage specifies the session storage.
|
||||
SessionStorage gsession.Storage
|
||||
SessionStorage gsession.Storage `json:"sessionStorage"`
|
||||
|
||||
// SessionCookieMaxAge specifies the cookie ttl for session id.
|
||||
// It it is set 0, it means it expires along with browser session.
|
||||
SessionCookieMaxAge time.Duration `json:"sessionCookieMaxAge"`
|
||||
|
||||
// SessionCookieOutput specifies whether automatic outputting session id to cookie.
|
||||
SessionCookieOutput bool `json:"sessionCookieOutput"`
|
||||
|
||||
// ==================================
|
||||
// Logging.
|
||||
// ==================================
|
||||
Logger *glog.Logger // Logger specifies the logger for server.
|
||||
LogPath string // LogPath specifies the directory for storing logging files.
|
||||
LogLevel string // LogLevel specifies the logging level for logger.
|
||||
LogStdout bool // LogStdout specifies whether printing logging content to stdout.
|
||||
ErrorStack bool // ErrorStack specifies whether logging stack information when error.
|
||||
ErrorLogEnabled bool // ErrorLogEnabled enables error logging content to files.
|
||||
ErrorLogPattern string // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log
|
||||
AccessLogEnabled bool // AccessLogEnabled enables access logging content to files.
|
||||
AccessLogPattern string // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log
|
||||
Logger *glog.Logger `json:"logger"` // Logger specifies the logger for server.
|
||||
LogPath string `json:"logPath"` // LogPath specifies the directory for storing logging files.
|
||||
LogLevel string `json:"logLevel"` // LogLevel specifies the logging level for logger.
|
||||
LogStdout bool `json:"logStdout"` // LogStdout specifies whether printing logging content to stdout.
|
||||
ErrorStack bool `json:"errorStack"` // ErrorStack specifies whether logging stack information when error.
|
||||
ErrorLogEnabled bool `json:"errorLogEnabled"` // ErrorLogEnabled enables error logging content to files.
|
||||
ErrorLogPattern string `json:"errorLogPattern"` // ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log
|
||||
AccessLogEnabled bool `json:"accessLogEnabled"` // AccessLogEnabled enables access logging content to files.
|
||||
AccessLogPattern string `json:"accessLogPattern"` // AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log
|
||||
|
||||
// ==================================
|
||||
// PProf.
|
||||
// ==================================
|
||||
PProfEnabled bool // PProfEnabled enables PProf feature.
|
||||
PProfPattern string // PProfPattern specifies the PProf service pattern for router.
|
||||
PProfEnabled bool `json:"pprofEnabled"` // PProfEnabled enables PProf feature.
|
||||
PProfPattern string `json:"pprofPattern"` // PProfPattern specifies the PProf service pattern for router.
|
||||
|
||||
// ==================================
|
||||
// Other.
|
||||
@ -194,26 +198,26 @@ type ServerConfig struct {
|
||||
// ClientMaxBodySize specifies the max body size limit in bytes for client request.
|
||||
// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
|
||||
// It's 8MB in default.
|
||||
ClientMaxBodySize int64
|
||||
ClientMaxBodySize int64 `json:"clientMaxBodySize"`
|
||||
|
||||
// FormParsingMemory specifies max memory buffer size in bytes which can be used for
|
||||
// parsing multimedia form.
|
||||
// It can be configured in configuration file using string like: 1m, 10m, 500kb etc.
|
||||
// It's 1MB in default.
|
||||
FormParsingMemory int64
|
||||
FormParsingMemory int64 `json:"formParsingMemory"`
|
||||
|
||||
// NameToUriType specifies the type for converting struct method name to URI when
|
||||
// registering routes.
|
||||
NameToUriType int
|
||||
NameToUriType int `json:"nameToUriType"`
|
||||
|
||||
// RouteOverWrite allows overwrite the route if duplicated.
|
||||
RouteOverWrite bool
|
||||
RouteOverWrite bool `json:"routeOverWrite"`
|
||||
|
||||
// DumpRouterMap specifies whether automatically dumps router map when server starts.
|
||||
DumpRouterMap bool
|
||||
DumpRouterMap bool `json:"dumpRouterMap"`
|
||||
|
||||
// Graceful enables graceful reload feature for all servers of the process.
|
||||
Graceful bool
|
||||
Graceful bool `json:"graceful"`
|
||||
}
|
||||
|
||||
// Deprecated. Use NewConfig instead.
|
||||
@ -243,10 +247,11 @@ func NewConfig() ServerConfig {
|
||||
CookieMaxAge: time.Hour * 24 * 365,
|
||||
CookiePath: "/",
|
||||
CookieDomain: "",
|
||||
SessionMaxAge: time.Hour * 24,
|
||||
SessionIdName: "gfsessionid",
|
||||
SessionPath: gsession.DefaultStorageFilePath,
|
||||
SessionMaxAge: time.Hour * 24,
|
||||
SessionCookieOutput: true,
|
||||
SessionCookieMaxAge: time.Hour * 24,
|
||||
Logger: glog.New(),
|
||||
LogLevel: "all",
|
||||
LogStdout: true,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -32,6 +32,11 @@ func (s *Server) SetSessionCookieOutput(enabled bool) {
|
||||
s.config.SessionCookieOutput = enabled
|
||||
}
|
||||
|
||||
// SetSessionCookieMaxAge sets the SessionCookieMaxAge for server.
|
||||
func (s *Server) SetSessionCookieMaxAge(maxAge time.Duration) {
|
||||
s.config.SessionCookieMaxAge = maxAge
|
||||
}
|
||||
|
||||
// GetSessionMaxAge returns the SessionMaxAge of server.
|
||||
func (s *Server) GetSessionMaxAge() time.Duration {
|
||||
return s.config.SessionMaxAge
|
||||
@ -41,3 +46,8 @@ func (s *Server) GetSessionMaxAge() time.Duration {
|
||||
func (s *Server) GetSessionIdName() string {
|
||||
return s.config.SessionIdName
|
||||
}
|
||||
|
||||
// GetSessionCookieMaxAge returns the SessionCookieMaxAge of server.
|
||||
func (s *Server) GetSessionCookieMaxAge() time.Duration {
|
||||
return s.config.SessionCookieMaxAge
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// 静态文件搜索优先级: Resource > ServerPaths > ServerRoot > SearchPath
|
||||
// Static Searching Priority: Resource > ServerPaths > ServerRoot > SearchPath
|
||||
|
||||
package ghttp
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -14,9 +14,6 @@ import (
|
||||
// Cookie for HTTP COOKIE management.
|
||||
type Cookie struct {
|
||||
data map[string]*cookieItem // Underlying cookie items.
|
||||
path string // The default cookie path.
|
||||
domain string // The default cookie domain
|
||||
maxAge time.Duration // The default cookie max age.
|
||||
server *Server // Belonged HTTP server
|
||||
request *Request // Belonged HTTP request.
|
||||
response *Response // Belonged HTTP response.
|
||||
@ -47,13 +44,10 @@ func (c *Cookie) init() {
|
||||
return
|
||||
}
|
||||
c.data = make(map[string]*cookieItem)
|
||||
c.path = c.request.Server.GetCookiePath()
|
||||
c.domain = c.request.Server.GetCookieDomain()
|
||||
c.maxAge = c.request.Server.GetCookieMaxAge()
|
||||
c.response = c.request.Response
|
||||
// DO NOT ADD ANY DEFAULT COOKIE DOMAIN!
|
||||
//if c.domain == "" {
|
||||
// c.domain = c.request.GetHost()
|
||||
//if c.request.Server.GetCookieDomain() == "" {
|
||||
// c.request.Server.GetCookieDomain() = c.request.GetHost()
|
||||
//}
|
||||
for _, v := range c.request.Cookies() {
|
||||
c.data[v.Name] = &cookieItem{
|
||||
@ -86,7 +80,13 @@ func (c *Cookie) Contains(key string) bool {
|
||||
|
||||
// Set sets cookie item with default domain, path and expiration age.
|
||||
func (c *Cookie) Set(key, value string) {
|
||||
c.SetCookie(key, value, c.domain, c.path, c.maxAge)
|
||||
c.SetCookie(
|
||||
key,
|
||||
value,
|
||||
c.request.Server.GetCookieDomain(),
|
||||
c.request.Server.GetCookiePath(),
|
||||
c.request.Server.GetCookieMaxAge(),
|
||||
)
|
||||
}
|
||||
|
||||
// SetCookie sets cookie item given given domain, path and expiration age.
|
||||
@ -128,7 +128,13 @@ func (c *Cookie) GetSessionId() string {
|
||||
|
||||
// SetSessionId sets session id in the cookie.
|
||||
func (c *Cookie) SetSessionId(id string) {
|
||||
c.Set(c.server.GetSessionIdName(), id)
|
||||
c.SetCookie(
|
||||
c.server.GetSessionIdName(),
|
||||
id,
|
||||
c.request.Server.GetCookieDomain(),
|
||||
c.request.Server.GetCookiePath(),
|
||||
c.server.GetSessionCookieMaxAge(),
|
||||
)
|
||||
}
|
||||
|
||||
// Get retrieves and returns the value with specified key.
|
||||
@ -149,7 +155,13 @@ func (c *Cookie) Get(key string, def ...string) string {
|
||||
// Remove deletes specified key and its value from cookie using default domain and path.
|
||||
// It actually tells the http client that the cookie is expired, do not send it to server next time.
|
||||
func (c *Cookie) Remove(key string) {
|
||||
c.SetCookie(key, "", c.domain, c.path, -86400)
|
||||
c.SetCookie(
|
||||
key,
|
||||
"",
|
||||
c.request.Server.GetCookieDomain(),
|
||||
c.request.Server.GetCookiePath(),
|
||||
-86400,
|
||||
)
|
||||
}
|
||||
|
||||
// RemoveCookie deletes specified key and its value from cookie using given domain and path.
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
type utilPProf struct{}
|
||||
|
||||
const (
|
||||
gDEFAULT_PPROF_PATTERN = "/debug/pprof"
|
||||
defaultPProfPattern = "/debug/pprof"
|
||||
)
|
||||
|
||||
// EnablePProf enables PProf feature for server.
|
||||
@ -28,7 +28,7 @@ func (s *Server) EnablePProf(pattern ...string) {
|
||||
|
||||
// EnablePProf enables PProf feature for server of specified domain.
|
||||
func (d *Domain) EnablePProf(pattern ...string) {
|
||||
p := gDEFAULT_PPROF_PATTERN
|
||||
p := defaultPProfPattern
|
||||
if len(pattern) > 0 && pattern[0] != "" {
|
||||
p = pattern[0]
|
||||
}
|
||||
|
||||
@ -283,6 +283,10 @@ func (c *Config) FilePath(file ...string) (path string) {
|
||||
}
|
||||
})
|
||||
}
|
||||
// Already found?
|
||||
if path != "" {
|
||||
return
|
||||
}
|
||||
// Searching the file system.
|
||||
c.paths.RLockFunc(func(array []string) {
|
||||
for _, prefix := range array {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -21,25 +21,25 @@ import (
|
||||
|
||||
// Config is the configuration object for logger.
|
||||
type Config struct {
|
||||
Writer io.Writer // Customized io.Writer.
|
||||
Flags int // Extra flags for logging output features.
|
||||
Path string // Logging directory path.
|
||||
File string // Format for logging file.
|
||||
Level int // Output level.
|
||||
Prefix string // Prefix string for every logging content.
|
||||
StSkip int // Skip count for stack.
|
||||
StStatus int // Stack status(1: enabled - default; 0: disabled)
|
||||
StFilter string // Stack string filter.
|
||||
CtxKeys []interface{} // Context keys for logging, which is used for value retrieving from context.
|
||||
HeaderPrint bool `c:"header"` // Print header or not(true in default).
|
||||
StdoutPrint bool `c:"stdout"` // Output to stdout or not(true in default).
|
||||
LevelPrefixes map[int]string // Logging level to its prefix string mapping.
|
||||
RotateSize int64 // Rotate the logging file if its size > 0 in bytes.
|
||||
RotateExpire time.Duration // Rotate the logging file if its mtime exceeds this duration.
|
||||
RotateBackupLimit int // Max backup for rotated files, default is 0, means no backups.
|
||||
RotateBackupExpire time.Duration // Max expire for rotated files, which is 0 in default, means no expiration.
|
||||
RotateBackupCompress int // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression.
|
||||
RotateCheckInterval time.Duration // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default.
|
||||
Writer io.Writer `json:"-"` // Customized io.Writer.
|
||||
Flags int `json:"flags"` // Extra flags for logging output features.
|
||||
Path string `json:"path"` // Logging directory path.
|
||||
File string `json:"file"` // Format for logging file.
|
||||
Level int `json:"level"` // Output level.
|
||||
Prefix string `json:"prefix"` // Prefix string for every logging content.
|
||||
StSkip int `json:"stSkip"` // Skip count for stack.
|
||||
StStatus int `json:"stStatus"` // Stack status(1: enabled - default; 0: disabled)
|
||||
StFilter string `json:"stFilter"` // Stack string filter.
|
||||
CtxKeys []interface{} `json:"ctxKeys"` // Context keys for logging, which is used for value retrieving from context.
|
||||
HeaderPrint bool `json:"header"` // Print header or not(true in default).
|
||||
StdoutPrint bool `json:"stdout"` // Output to stdout or not(true in default).
|
||||
LevelPrefixes map[int]string `json:"levelPrefixes"` // Logging level to its prefix string mapping.
|
||||
RotateSize int64 `json:"rotateSize"` // Rotate the logging file if its size > 0 in bytes.
|
||||
RotateExpire time.Duration `json:"rotateExpire"` // Rotate the logging file if its mtime exceeds this duration.
|
||||
RotateBackupLimit int `json:"rotateBackupLimit"` // Max backup for rotated files, default is 0, means no backups.
|
||||
RotateBackupExpire time.Duration `json:"rotateBackupExpire"` // Max expire for rotated files, which is 0 in default, means no expiration.
|
||||
RotateBackupCompress int `json:"rotateBackupCompress"` // Compress level for rotated files using gzip algorithm. It's 0 in default, means no compression.
|
||||
RotateCheckInterval time.Duration `json:"rotateCheckInterval"` // Asynchronizely checks the backups and expiration at intervals. It's 1 hour in default.
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default configuration for logger.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -20,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
gPACKAGE_TEMPLATE = `
|
||||
packedGoSouceTemplate = `
|
||||
package %s
|
||||
|
||||
import "github.com/gogf/gf/os/gres"
|
||||
@ -39,8 +39,10 @@ func init() {
|
||||
//
|
||||
// Note that parameter <srcPaths> supports multiple paths join with ','.
|
||||
func Pack(srcPaths string, keyPrefix ...string) ([]byte, error) {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
headerPrefix := ""
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
headerPrefix = ""
|
||||
)
|
||||
if len(keyPrefix) > 0 && keyPrefix[0] != "" {
|
||||
headerPrefix = keyPrefix[0]
|
||||
}
|
||||
@ -79,7 +81,7 @@ func PackToGoFile(srcPath, goFilePath, pkgName string, keyPrefix ...string) erro
|
||||
}
|
||||
return gfile.PutContents(
|
||||
goFilePath,
|
||||
fmt.Sprintf(gstr.TrimLeft(gPACKAGE_TEMPLATE), pkgName, gbase64.EncodeToString(data)),
|
||||
fmt.Sprintf(gstr.TrimLeft(packedGoSouceTemplate), pkgName, gbase64.EncodeToString(data)),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"github.com/gogf/gf/internal/fileinfo"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
@ -72,7 +73,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix
|
||||
intlog.Printf(`exclude file path: %s`, file)
|
||||
continue
|
||||
}
|
||||
err := zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter)
|
||||
err = zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -83,7 +84,7 @@ func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix
|
||||
path = headerPrefix
|
||||
for {
|
||||
name = gfile.Basename(path)
|
||||
err := zipFileVirtual(
|
||||
err = zipFileVirtual(
|
||||
fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), path, zipWriter,
|
||||
)
|
||||
if err != nil {
|
||||
@ -136,7 +137,7 @@ func zipFileVirtual(info os.FileInfo, path string, zw *zip.Writer) error {
|
||||
return err
|
||||
}
|
||||
header.Name = path
|
||||
if _, err := zw.CreateHeader(header); err != nil {
|
||||
if _, err = zw.CreateHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -148,9 +149,9 @@ func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error)
|
||||
return nil, err
|
||||
}
|
||||
if len(prefix) > 0 {
|
||||
prefix = strings.Replace(prefix, `\`, `/`, -1)
|
||||
prefix = strings.TrimRight(prefix, `/`)
|
||||
header.Name = prefix + `/` + header.Name
|
||||
header.Name = strings.Replace(header.Name, `\`, `/`, -1)
|
||||
header.Name, _ = gregex.ReplaceString(`/{2,}`, `/`, header.Name)
|
||||
}
|
||||
return header, nil
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -74,6 +74,7 @@ func (r *Resource) Get(path string) *File {
|
||||
return nil
|
||||
}
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
path = strings.Replace(path, "//", "/", -1)
|
||||
if path != "/" {
|
||||
for path[len(path)-1] == '/' {
|
||||
path = path[:len(path)-1]
|
||||
@ -93,6 +94,7 @@ func (r *Resource) Get(path string) *File {
|
||||
func (r *Resource) GetWithIndex(path string, indexFiles []string) *File {
|
||||
// Necessary for double char '/' replacement in prefix.
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
path = strings.Replace(path, "//", "/", -1)
|
||||
if path != "/" {
|
||||
for path[len(path)-1] == '/' {
|
||||
path = path[:len(path)-1]
|
||||
@ -114,7 +116,6 @@ func (r *Resource) GetWithIndex(path string, indexFiles []string) *File {
|
||||
|
||||
// GetContent directly returns the content of <path>.
|
||||
func (r *Resource) GetContent(path string) []byte {
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
file := r.Get(path)
|
||||
if file != nil {
|
||||
return file.Content()
|
||||
@ -169,6 +170,7 @@ func (r *Resource) ScanDirFile(path string, pattern string, recursive ...bool) [
|
||||
// It scans directory recursively if given parameter <recursive> is true.
|
||||
func (r *Resource) doScanDir(path string, pattern string, recursive bool, onlyFile bool) []*File {
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
path = strings.Replace(path, "//", "/", -1)
|
||||
if path != "/" {
|
||||
for path[len(path)-1] == '/' {
|
||||
path = path[:len(path)-1]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
|
||||
2
os/gres/testdata/data/data.go
vendored
2
os/gres/testdata/data/data.go
vendored
File diff suppressed because one or more lines are too long
2
os/gres/testdata/testdata.go
vendored
2
os/gres/testdata/testdata.go
vendored
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -68,8 +68,10 @@ func NewStorageFile(path ...string) *StorageFile {
|
||||
// Batch updates the TTL for session ids timely.
|
||||
gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() {
|
||||
//intlog.Print("StorageFile.timer start")
|
||||
var id string
|
||||
var err error
|
||||
var (
|
||||
id string
|
||||
err error
|
||||
)
|
||||
for {
|
||||
if id = s.updatingIdSet.Pop(); id == "" {
|
||||
break
|
||||
@ -221,7 +223,7 @@ func (s *StorageFile) SetSession(id string, data *gmap.StrAnyMap, ttl time.Durat
|
||||
// It just adds the session id to the async handling queue.
|
||||
func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
|
||||
intlog.Printf("StorageFile.UpdateTTL: %s, %v", id, ttl)
|
||||
if ttl >= DefaultStorageRedisLoopInterval {
|
||||
if ttl >= DefaultStorageFileLoopInterval {
|
||||
s.updatingIdSet.Add(id)
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -26,7 +26,7 @@ type StorageRedis struct {
|
||||
var (
|
||||
// DefaultStorageRedisLoopInterval is the interval updating TTL for session ids
|
||||
// in last duration.
|
||||
DefaultStorageRedisLoopInterval = time.Minute
|
||||
DefaultStorageRedisLoopInterval = 10 * time.Second
|
||||
)
|
||||
|
||||
// NewStorageRedis creates and returns a redis storage object for session.
|
||||
@ -45,9 +45,11 @@ func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis {
|
||||
// Batch updates the TTL for session ids timely.
|
||||
gtimer.AddSingleton(DefaultStorageRedisLoopInterval, func() {
|
||||
intlog.Print("StorageRedis.timer start")
|
||||
var id string
|
||||
var err error
|
||||
var ttlSeconds int
|
||||
var (
|
||||
id string
|
||||
err error
|
||||
ttlSeconds int
|
||||
)
|
||||
for {
|
||||
if id, ttlSeconds = s.updatingIdMap.Pop(); id == "" {
|
||||
break
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
|
||||
@ -318,6 +318,113 @@ func (t *Time) Sub(u *Time) time.Duration {
|
||||
return t.Time.Sub(u.Time)
|
||||
}
|
||||
|
||||
// StartOfMinute clones and returns a new time of which the seconds is set to 0.
|
||||
func (t *Time) StartOfMinute() *Time {
|
||||
newTime := t.Clone()
|
||||
newTime.Time = newTime.Time.Truncate(time.Minute)
|
||||
return newTime
|
||||
}
|
||||
|
||||
// StartOfHour clones and returns a new time of which the hour, minutes and seconds are set to 0.
|
||||
func (t *Time) StartOfHour() *Time {
|
||||
y, m, d := t.Date()
|
||||
newTime := t.Clone()
|
||||
newTime.Time = time.Date(y, m, d, newTime.Time.Hour(), 0, 0, 0, newTime.Time.Location())
|
||||
return newTime
|
||||
}
|
||||
|
||||
// StartOfDay clones and returns a new time which is the start of day, its time is set to 00:00:00.
|
||||
func (t *Time) StartOfDay() *Time {
|
||||
y, m, d := t.Date()
|
||||
newTime := t.Clone()
|
||||
newTime.Time = time.Date(y, m, d, 0, 0, 0, 0, newTime.Time.Location())
|
||||
return newTime
|
||||
}
|
||||
|
||||
// StartOfWeek clones and returns a new time which is the first day of week and its time is set to
|
||||
// 00:00:00.
|
||||
func (t *Time) StartOfWeek() *Time {
|
||||
weekday := int(t.Weekday())
|
||||
return t.StartOfDay().AddDate(0, 0, -weekday)
|
||||
}
|
||||
|
||||
// StartOfMonth clones and returns a new time which is the first day of the month and its is set to
|
||||
// 00:00:00
|
||||
func (t *Time) StartOfMonth() *Time {
|
||||
y, m, _ := t.Date()
|
||||
newTime := t.Clone()
|
||||
newTime.Time = time.Date(y, m, 1, 0, 0, 0, 0, newTime.Time.Location())
|
||||
return newTime
|
||||
}
|
||||
|
||||
// StartOfQuarter clones and returns a new time which is the first day of the quarter and its time is set
|
||||
// to 00:00:00.
|
||||
func (t *Time) StartOfQuarter() *Time {
|
||||
month := t.StartOfMonth()
|
||||
offset := (int(month.Month()) - 1) % 3
|
||||
return month.AddDate(0, -offset, 0)
|
||||
}
|
||||
|
||||
// StartOfHalf clones and returns a new time which is the first day of the half year and its time is set
|
||||
// to 00:00:00.
|
||||
func (t *Time) StartOfHalf() *Time {
|
||||
month := t.StartOfMonth()
|
||||
offset := (int(month.Month()) - 1) % 6
|
||||
return month.AddDate(0, -offset, 0)
|
||||
}
|
||||
|
||||
// StartOfYear clones and returns a new time which is the first day of the year and its time is set to
|
||||
// 00:00:00.
|
||||
func (t *Time) StartOfYear() *Time {
|
||||
y, _, _ := t.Date()
|
||||
newTime := t.Clone()
|
||||
newTime.Time = time.Date(y, time.January, 1, 0, 0, 0, 0, newTime.Time.Location())
|
||||
return newTime
|
||||
}
|
||||
|
||||
// EndOfMinute clones and returns a new time of which the seconds is set to 59.
|
||||
func (t *Time) EndOfMinute() *Time {
|
||||
return t.StartOfMinute().Add(time.Minute - time.Nanosecond)
|
||||
}
|
||||
|
||||
// EndOfHour clones and returns a new time of which the minutes and seconds are both set to 59.
|
||||
func (t *Time) EndOfHour() *Time {
|
||||
return t.StartOfHour().Add(time.Hour - time.Nanosecond)
|
||||
}
|
||||
|
||||
// EndOfDay clones and returns a new time which is the end of day the and its time is set to 23:59:59.
|
||||
func (t *Time) EndOfDay() *Time {
|
||||
y, m, d := t.Date()
|
||||
newTime := t.Clone()
|
||||
newTime.Time = time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), newTime.Time.Location())
|
||||
return newTime
|
||||
}
|
||||
|
||||
// EndOfWeek clones and returns a new time which is the end of week and its time is set to 23:59:59.
|
||||
func (t *Time) EndOfWeek() *Time {
|
||||
return t.StartOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// EndOfMonth clones and returns a new time which is the end of the month and its time is set to 23:59:59.
|
||||
func (t *Time) EndOfMonth() *Time {
|
||||
return t.StartOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// EndOfQuarter clones and returns a new time which is end of the quarter and its time is set to 23:59:59.
|
||||
func (t *Time) EndOfQuarter() *Time {
|
||||
return t.StartOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// EndOfHalf clones and returns a new time which is the end of the half year and its time is set to 23:59:59.
|
||||
func (t *Time) EndOfHalf() *Time {
|
||||
return t.StartOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// EndOfYear clones and returns a new time which is the end of the year and its time is set to 23:59:59.
|
||||
func (t *Time) EndOfYear() *Time {
|
||||
return t.StartOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (t *Time) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + t.String() + `"`), nil
|
||||
|
||||
@ -256,3 +256,131 @@ func Test_Truncate(t *testing.T) {
|
||||
t.Assert(timeTemp.UnixNano(), timeTemp1.Truncate(time.Hour).UnixNano())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfMinute(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfMinute()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:24:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfMinute(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfMinute()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:24:59")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfHour(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfHour()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfHour(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfHour()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 18:59:59")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfDay(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfDay()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 00:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfDay(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfDay()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 23:59:59")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfWeek(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfWeek()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-06 00:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfWeek(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfWeek()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-12 23:59:59")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfMonth(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfMonth()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-01 00:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfMonth(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-12 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfMonth()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfQuarter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfQuarter()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-10-01 00:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfQuarter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfQuarter()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfHalf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfHalf()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-07-01 00:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfHalf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfHalf()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StartOfYear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
|
||||
timeTemp1 := timeTemp.StartOfYear()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-01-01 00:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_EndOfYear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.NewFromStr("2020-12-06 18:24:06")
|
||||
timeTemp1 := timeTemp.EndOfYear()
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "2020-12-31 23:59:59")
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -21,12 +21,12 @@ import (
|
||||
|
||||
// Config is the configuration object for template engine.
|
||||
type Config struct {
|
||||
Paths []string // Searching array for path, NOT concurrent-safe for performance purpose.
|
||||
Data map[string]interface{} // Global template variables including configuration.
|
||||
DefaultFile string // Default template file for parsing.
|
||||
Delimiters []string // Custom template delimiters.
|
||||
AutoEncode bool // Automatically encodes and provides safe html output, which is good for avoiding XSS.
|
||||
I18nManager *gi18n.Manager // I18n manager for the view.
|
||||
Paths []string `json:"paths"` // Searching array for path, NOT concurrent-safe for performance purpose.
|
||||
Data map[string]interface{} `json:"data"` // Global template variables including configuration.
|
||||
DefaultFile string `json:"defaultFile"` // Default template file for parsing.
|
||||
Delimiters []string `json:"delimiters"` // Custom template delimiters.
|
||||
AutoEncode bool `json:"autoEncode"` // Automatically encodes and provides safe html output, which is good for avoiding XSS.
|
||||
I18nManager *gi18n.Manager `json:"-"` // I18n manager for the view.
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -6,15 +6,15 @@
|
||||
//
|
||||
// | Function | Result |
|
||||
// |-----------------------------------|--------------------|
|
||||
// | SnakeCase(s) | any_kind_of_string |
|
||||
// | SnakeScreamingCase(s) | ANY_KIND_OF_STRING |
|
||||
// | KebabCase(s) | any-kind-of-string |
|
||||
// | KebabScreamingCase(s) | ANY-KIND-OF-STRING |
|
||||
// | DelimitedCase(s, '.') | any.kind.of.string |
|
||||
// | DelimitedScreamingCase(s, '.') | ANY.KIND.OF.STRING |
|
||||
// | CamelCase(s) | AnyKindOfString |
|
||||
// | CamelLowerCase(s) | anyKindOfString |
|
||||
// | SnakeFirstUpperCase(RGBCodeMd5) | rgb_code_md5 |
|
||||
// | CaseSnake(s) | any_kind_of_string |
|
||||
// | CaseSnakeScreaming(s) | ANY_KIND_OF_STRING |
|
||||
// | CaseSnakeFirstUpper("RGBCodeMd5") | rgb_code_md5 |
|
||||
// | CaseKebab(s) | any-kind-of-string |
|
||||
// | CaseKebabScreaming(s) | ANY-KIND-OF-STRING |
|
||||
// | CaseDelimited(s, '.') | any.kind.of.string |
|
||||
// | CaseDelimitedScreaming(s, '.') | ANY.KIND.OF.STRING |
|
||||
// | CaseCamel(s) | AnyKindOfString |
|
||||
// | CaseCamelLower(s) | anyKindOfString |
|
||||
|
||||
package gstr
|
||||
|
||||
@ -30,12 +30,24 @@ var (
|
||||
)
|
||||
|
||||
// CamelCase converts a string to CamelCase.
|
||||
// Deprecated, use CaseCamel instead.
|
||||
func CamelCase(s string) string {
|
||||
return CaseCamel(s)
|
||||
}
|
||||
|
||||
// CaseCamel converts a string to CamelCase.
|
||||
func CaseCamel(s string) string {
|
||||
return toCamelInitCase(s, true)
|
||||
}
|
||||
|
||||
// CamelLowerCase converts a string to lowerCamelCase.
|
||||
// Deprecated, use CaseCamelLower instead.
|
||||
func CamelLowerCase(s string) string {
|
||||
return CaseCamelLower(s)
|
||||
}
|
||||
|
||||
// CaseCamelLower converts a string to lowerCamelCase.
|
||||
func CaseCamelLower(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
@ -46,19 +58,37 @@ func CamelLowerCase(s string) string {
|
||||
}
|
||||
|
||||
// SnakeCase converts a string to snake_case.
|
||||
// Deprecated, use CaseSnake instead.
|
||||
func SnakeCase(s string) string {
|
||||
return CaseSnake(s)
|
||||
}
|
||||
|
||||
// CaseSnake converts a string to snake_case.
|
||||
func CaseSnake(s string) string {
|
||||
return DelimitedCase(s, '_')
|
||||
}
|
||||
|
||||
// SnakeScreamingCase converts a string to SNAKE_CASE_SCREAMING.
|
||||
// Deprecated, use CaseSnakeScreaming instead.
|
||||
func SnakeScreamingCase(s string) string {
|
||||
return CaseSnakeScreaming(s)
|
||||
}
|
||||
|
||||
// CaseSnakeScreaming converts a string to SNAKE_CASE_SCREAMING.
|
||||
func CaseSnakeScreaming(s string) string {
|
||||
return DelimitedScreamingCase(s, '_', true)
|
||||
}
|
||||
|
||||
// SnakeFirstUpperCase converts a string from RGBCodeMd5 to rgb_code_md5.
|
||||
// The length of word should not be too long
|
||||
// TODO for efficiency should change regexp to traversing string in future
|
||||
// Deprecated, use CaseSnakeFirstUpper instead.
|
||||
func SnakeFirstUpperCase(word string, underscore ...string) string {
|
||||
return CaseSnakeFirstUpper(word, underscore...)
|
||||
}
|
||||
|
||||
// CaseSnakeFirstUpper converts a string like "RGBCodeMd5" to "rgb_code_md5".
|
||||
// TODO for efficiency should change regexp to traversing string in future.
|
||||
func CaseSnakeFirstUpper(word string, underscore ...string) string {
|
||||
replace := "_"
|
||||
if len(underscore) > 0 {
|
||||
replace = underscore[0]
|
||||
@ -73,7 +103,7 @@ func SnakeFirstUpperCase(word string, underscore ...string) string {
|
||||
m := firstCamelCaseStart.FindAllStringSubmatch(word, 1)
|
||||
if len(m) > 0 && m[0][1] != "" {
|
||||
w := strings.ToLower(m[0][1])
|
||||
w = string(w[:len(w)-1]) + replace + string(w[len(w)-1])
|
||||
w = w[:len(w)-1] + replace + string(w[len(w)-1])
|
||||
|
||||
word = strings.Replace(word, m[0][1], w, 1)
|
||||
} else {
|
||||
@ -84,23 +114,47 @@ func SnakeFirstUpperCase(word string, underscore ...string) string {
|
||||
return TrimLeft(word, replace)
|
||||
}
|
||||
|
||||
// KebabCase converts a string to kebab-case
|
||||
// KebabCase converts a string to kebab-case.
|
||||
// Deprecated, use CaseKebab instead.
|
||||
func KebabCase(s string) string {
|
||||
return DelimitedCase(s, '-')
|
||||
return CaseKebab(s)
|
||||
}
|
||||
|
||||
// CaseKebab converts a string to kebab-case
|
||||
func CaseKebab(s string) string {
|
||||
return CaseDelimited(s, '-')
|
||||
}
|
||||
|
||||
// KebabScreamingCase converts a string to KEBAB-CASE-SCREAMING.
|
||||
// Deprecated, use CaseKebabScreaming instead.
|
||||
func KebabScreamingCase(s string) string {
|
||||
return DelimitedScreamingCase(s, '-', true)
|
||||
return CaseKebabScreaming(s)
|
||||
}
|
||||
|
||||
// CaseKebabScreaming converts a string to KEBAB-CASE-SCREAMING.
|
||||
func CaseKebabScreaming(s string) string {
|
||||
return CaseDelimitedScreaming(s, '-', true)
|
||||
}
|
||||
|
||||
// DelimitedCase converts a string to snake.case.delimited.
|
||||
// Deprecated, use CaseDelimited instead.
|
||||
func DelimitedCase(s string, del uint8) string {
|
||||
return DelimitedScreamingCase(s, del, false)
|
||||
return CaseDelimited(s, del)
|
||||
}
|
||||
|
||||
// CaseDelimited converts a string to snake.case.delimited.
|
||||
func CaseDelimited(s string, del uint8) string {
|
||||
return CaseDelimitedScreaming(s, del, false)
|
||||
}
|
||||
|
||||
// DelimitedScreamingCase converts a string to DELIMITED.SCREAMING.CASE or delimited.screaming.case.
|
||||
// Deprecated, use CaseDelimitedScreaming instead.
|
||||
func DelimitedScreamingCase(s string, del uint8, screaming bool) string {
|
||||
return CaseDelimitedScreaming(s, del, screaming)
|
||||
}
|
||||
|
||||
// CaseDelimitedScreaming converts a string to DELIMITED.SCREAMING.CASE or delimited.screaming.case.
|
||||
func CaseDelimitedScreaming(s string, del uint8, screaming bool) string {
|
||||
s = addWordBoundariesToNumbers(s)
|
||||
s = strings.Trim(s, " ")
|
||||
n := ""
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -7,12 +7,13 @@
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
func Test_CamelCase(t *testing.T) {
|
||||
func Test_CaseCamel(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"test_case", "TestCase"},
|
||||
{"test", "Test"},
|
||||
@ -28,14 +29,14 @@ func Test_CamelCase(t *testing.T) {
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.CamelCase(in)
|
||||
result := gstr.CaseCamel(in)
|
||||
if result != out {
|
||||
t.Error("'" + result + "' != '" + out + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CamelLowerCase(t *testing.T) {
|
||||
func Test_CaseCamelLower(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"foo-bar", "fooBar"},
|
||||
{"TestCase", "testCase"},
|
||||
@ -45,14 +46,14 @@ func Test_CamelLowerCase(t *testing.T) {
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.CamelLowerCase(in)
|
||||
result := gstr.CaseCamelLower(in)
|
||||
if result != out {
|
||||
t.Error("'" + result + "' != '" + out + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SnakeCase(t *testing.T) {
|
||||
func Test_CaseSnake(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"testCase", "test_case"},
|
||||
{"TestCase", "test_case"},
|
||||
@ -75,14 +76,14 @@ func Test_SnakeCase(t *testing.T) {
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.SnakeCase(in)
|
||||
result := gstr.CaseSnake(in)
|
||||
if result != out {
|
||||
t.Error("'" + in + "'('" + result + "' != '" + out + "')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_DelimitedCase(t *testing.T) {
|
||||
func Test_CaseDelimited(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"testCase", "test@case"},
|
||||
{"TestCase", "test@case"},
|
||||
@ -106,28 +107,28 @@ func Test_DelimitedCase(t *testing.T) {
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.DelimitedCase(in, '@')
|
||||
result := gstr.CaseDelimited(in, '@')
|
||||
if result != out {
|
||||
t.Error("'" + in + "' ('" + result + "' != '" + out + "')")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SnakeScreamingCase(t *testing.T) {
|
||||
func Test_CaseSnakeScreaming(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"testCase", "TEST_CASE"},
|
||||
}
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.SnakeScreamingCase(in)
|
||||
result := gstr.CaseSnakeScreaming(in)
|
||||
if result != out {
|
||||
t.Error("'" + result + "' != '" + out + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KebabCase(t *testing.T) {
|
||||
func Test_CaseKebab(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"testCase", "test-case"},
|
||||
{"optimization1.0.0", "optimization-1-0-0"},
|
||||
@ -135,42 +136,42 @@ func Test_KebabCase(t *testing.T) {
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.KebabCase(in)
|
||||
result := gstr.CaseKebab(in)
|
||||
if result != out {
|
||||
t.Error("'" + result + "' != '" + out + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KebabScreamingCase(t *testing.T) {
|
||||
func Test_CaseKebabScreaming(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"testCase", "TEST-CASE"},
|
||||
}
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.KebabScreamingCase(in)
|
||||
result := gstr.CaseKebabScreaming(in)
|
||||
if result != out {
|
||||
t.Error("'" + result + "' != '" + out + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_DelimitedScreamingCase(t *testing.T) {
|
||||
func Test_CaseDelimitedScreaming(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"testCase", "TEST.CASE"},
|
||||
}
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.DelimitedScreamingCase(in, '.', true)
|
||||
result := gstr.CaseDelimitedScreaming(in, '.', true)
|
||||
if result != out {
|
||||
t.Error("'" + result + "' != '" + out + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnakeFirstUpperCase(t *testing.T) {
|
||||
func Test_CaseSnakeFirstUpper(t *testing.T) {
|
||||
cases := [][]string{
|
||||
{"RGBCodeMd5", "rgb_code_md5"},
|
||||
{"testCase", "test_case"},
|
||||
@ -182,13 +183,12 @@ func TestSnakeFirstUpperCase(t *testing.T) {
|
||||
{"User_ID", "user_id"},
|
||||
{"user_id", "user_id"},
|
||||
{"md5", "md5"},
|
||||
{"Numbers2And55With000", "numbers2_and55_with000"},
|
||||
}
|
||||
for _, i := range cases {
|
||||
in := i[0]
|
||||
out := i[1]
|
||||
result := gstr.SnakeFirstUpperCase(in)
|
||||
if result != out {
|
||||
t.Error("'" + result + "' != '" + out + "'")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for _, item := range cases {
|
||||
t.Assert(gstr.CaseSnakeFirstUpper(item[0]), item[1])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@ -9,11 +9,12 @@
|
||||
package grand_test
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
cryptoRand "crypto/rand"
|
||||
mathRand "math/rand"
|
||||
|
||||
"encoding/binary"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -23,15 +24,21 @@ var (
|
||||
strForStr = "我爱GoFrame"
|
||||
)
|
||||
|
||||
func Benchmark_Rand_Buffer4(b *testing.B) {
|
||||
func Benchmark_Math_Rand_Int(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
rand.Read(randBuffer4)
|
||||
mathRand.Int()
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Rand_Buffer1024(b *testing.B) {
|
||||
func Benchmark_CryptoRand_Buffer4(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
rand.Read(randBuffer1024)
|
||||
cryptoRand.Read(randBuffer4)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_CryptoRand_Buffer1024(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
cryptoRand.Read(randBuffer1024)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,9 +108,9 @@ func Benchmark_Uint32Converting(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Buffer(b *testing.B) {
|
||||
func Benchmark_CryptoRand_Buffer(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := rand.Read(buffer); err == nil {
|
||||
if _, err := cryptoRand.Read(buffer); err == nil {
|
||||
binary.LittleEndian.Uint64(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,6 +8,7 @@
|
||||
package gvalid
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
@ -62,6 +63,136 @@ import (
|
||||
// like: map[field] => string|map[rule]string
|
||||
type CustomMsg = map[string]interface{}
|
||||
|
||||
const (
|
||||
// regular expression pattern for single validation rule.
|
||||
singleRulePattern = `^([\w-]+):{0,1}(.*)`
|
||||
invalidRulesErrKey = "invalid_rules"
|
||||
invalidParamsErrKey = "invalid_params"
|
||||
invalidObjectErrKey = "invalid_object"
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultValidator is the default validator for package functions.
|
||||
defaultValidator = New()
|
||||
|
||||
// all internal error keys.
|
||||
internalErrKeyMap = map[string]string{
|
||||
invalidRulesErrKey: invalidRulesErrKey,
|
||||
invalidParamsErrKey: invalidParamsErrKey,
|
||||
invalidObjectErrKey: invalidObjectErrKey,
|
||||
}
|
||||
// regular expression object for single rule
|
||||
// which is compiled just once and of repeatable usage.
|
||||
ruleRegex, _ = regexp.Compile(singleRulePattern)
|
||||
|
||||
// mustCheckRulesEvenValueEmpty specifies some rules that must be validated
|
||||
// even the value is empty (nil or empty).
|
||||
mustCheckRulesEvenValueEmpty = map[string]struct{}{
|
||||
"required": {},
|
||||
"required-if": {},
|
||||
"required-unless": {},
|
||||
"required-with": {},
|
||||
"required-with-all": {},
|
||||
"required-without": {},
|
||||
"required-without-all": {},
|
||||
//"same": {},
|
||||
//"different": {},
|
||||
//"in": {},
|
||||
//"not-in": {},
|
||||
//"regex": {},
|
||||
}
|
||||
// allSupportedRules defines all supported rules that is used for quick checks.
|
||||
allSupportedRules = map[string]struct{}{
|
||||
"required": {},
|
||||
"required-if": {},
|
||||
"required-unless": {},
|
||||
"required-with": {},
|
||||
"required-with-all": {},
|
||||
"required-without": {},
|
||||
"required-without-all": {},
|
||||
"date": {},
|
||||
"date-format": {},
|
||||
"email": {},
|
||||
"phone": {},
|
||||
"phone-loose": {},
|
||||
"telephone": {},
|
||||
"passport": {},
|
||||
"password": {},
|
||||
"password2": {},
|
||||
"password3": {},
|
||||
"postcode": {},
|
||||
"resident-id": {},
|
||||
"bank-card": {},
|
||||
"qq": {},
|
||||
"ip": {},
|
||||
"ipv4": {},
|
||||
"ipv6": {},
|
||||
"mac": {},
|
||||
"url": {},
|
||||
"domain": {},
|
||||
"length": {},
|
||||
"min-length": {},
|
||||
"max-length": {},
|
||||
"between": {},
|
||||
"min": {},
|
||||
"max": {},
|
||||
"json": {},
|
||||
"integer": {},
|
||||
"float": {},
|
||||
"boolean": {},
|
||||
"same": {},
|
||||
"different": {},
|
||||
"in": {},
|
||||
"not-in": {},
|
||||
"regex": {},
|
||||
}
|
||||
// boolMap defines the boolean values.
|
||||
boolMap = map[string]struct{}{
|
||||
"1": {},
|
||||
"true": {},
|
||||
"on": {},
|
||||
"yes": {},
|
||||
"": {},
|
||||
"0": {},
|
||||
"false": {},
|
||||
"off": {},
|
||||
"no": {},
|
||||
}
|
||||
)
|
||||
|
||||
// Check checks single value with specified rules.
|
||||
// It returns nil if successful validation.
|
||||
//
|
||||
// The parameter <value> can be any type of variable, which will be converted to string
|
||||
// for validation.
|
||||
// The parameter <rules> can be one or more rules, multiple rules joined using char '|'.
|
||||
// The parameter <messages> specifies the custom error messages, which can be type of:
|
||||
// string/map/struct/*struct.
|
||||
// The optional parameter <params> specifies the extra validation parameters for some rules
|
||||
// like: required-*、same、different, etc.
|
||||
func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
|
||||
return defaultValidator.Check(value, rules, messages, params...)
|
||||
}
|
||||
|
||||
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
|
||||
//
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
return defaultValidator.CheckMap(params, rules, messages...)
|
||||
}
|
||||
|
||||
// CheckStruct validates strcut and returns the error result.
|
||||
//
|
||||
// The parameter <object> should be type of struct/*struct.
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
return defaultValidator.CheckStruct(object, rules, messages...)
|
||||
}
|
||||
|
||||
// parseSequenceTag parses one sequence tag to field, rule and error message.
|
||||
// The sequence tag is like: [alias@]rule[...#msg...]
|
||||
func parseSequenceTag(tag string) (field, rule, msg string) {
|
||||
|
||||
31
util/gvalid/gvalid_validator.go
Normal file
31
util/gvalid/gvalid_validator.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gvalid
|
||||
|
||||
// Validator is the validation manager.
|
||||
type Validator struct {
|
||||
i18nLang string // I18n language.
|
||||
}
|
||||
|
||||
// New creates and returns a new Validator.
|
||||
func New() *Validator {
|
||||
return &Validator{}
|
||||
}
|
||||
|
||||
// Clone creates and returns a new Validator which is a shallow copy of current one.
|
||||
func (v *Validator) Clone() *Validator {
|
||||
newValidator := New()
|
||||
*newValidator = *v
|
||||
return newValidator
|
||||
}
|
||||
|
||||
// I18n is a chaining operation function which sets the I18n language for next validation.
|
||||
func (v *Validator) I18n(language string) *Validator {
|
||||
newValidator := v.Clone()
|
||||
newValidator.i18nLang = language
|
||||
return newValidator
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -14,105 +14,10 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// regular expression pattern for single validation rule.
|
||||
singleRulePattern = `^([\w-]+):{0,1}(.*)`
|
||||
invalidRulesErrKey = "invalid_rules"
|
||||
invalidParamsErrKey = "invalid_params"
|
||||
invalidObjectErrKey = "invalid_object"
|
||||
)
|
||||
|
||||
var (
|
||||
// all internal error keys.
|
||||
internalErrKeyMap = map[string]string{
|
||||
invalidRulesErrKey: invalidRulesErrKey,
|
||||
invalidParamsErrKey: invalidParamsErrKey,
|
||||
invalidObjectErrKey: invalidObjectErrKey,
|
||||
}
|
||||
// regular expression object for single rule
|
||||
// which is compiled just once and of repeatable usage.
|
||||
ruleRegex, _ = regexp.Compile(singleRulePattern)
|
||||
|
||||
// mustCheckRulesEvenValueEmpty specifies some rules that must be validated
|
||||
// even the value is empty (nil or empty).
|
||||
mustCheckRulesEvenValueEmpty = map[string]struct{}{
|
||||
"required": {},
|
||||
"required-if": {},
|
||||
"required-unless": {},
|
||||
"required-with": {},
|
||||
"required-with-all": {},
|
||||
"required-without": {},
|
||||
"required-without-all": {},
|
||||
//"same": {},
|
||||
//"different": {},
|
||||
//"in": {},
|
||||
//"not-in": {},
|
||||
//"regex": {},
|
||||
}
|
||||
// allSupportedRules defines all supported rules that is used for quick checks.
|
||||
allSupportedRules = map[string]struct{}{
|
||||
"required": {},
|
||||
"required-if": {},
|
||||
"required-unless": {},
|
||||
"required-with": {},
|
||||
"required-with-all": {},
|
||||
"required-without": {},
|
||||
"required-without-all": {},
|
||||
"date": {},
|
||||
"date-format": {},
|
||||
"email": {},
|
||||
"phone": {},
|
||||
"phone-loose": {},
|
||||
"telephone": {},
|
||||
"passport": {},
|
||||
"password": {},
|
||||
"password2": {},
|
||||
"password3": {},
|
||||
"postcode": {},
|
||||
"resident-id": {},
|
||||
"bank-card": {},
|
||||
"qq": {},
|
||||
"ip": {},
|
||||
"ipv4": {},
|
||||
"ipv6": {},
|
||||
"mac": {},
|
||||
"url": {},
|
||||
"domain": {},
|
||||
"length": {},
|
||||
"min-length": {},
|
||||
"max-length": {},
|
||||
"between": {},
|
||||
"min": {},
|
||||
"max": {},
|
||||
"json": {},
|
||||
"integer": {},
|
||||
"float": {},
|
||||
"boolean": {},
|
||||
"same": {},
|
||||
"different": {},
|
||||
"in": {},
|
||||
"not-in": {},
|
||||
"regex": {},
|
||||
}
|
||||
// boolMap defines the boolean values.
|
||||
boolMap = map[string]struct{}{
|
||||
"1": {},
|
||||
"true": {},
|
||||
"on": {},
|
||||
"yes": {},
|
||||
"": {},
|
||||
"0": {},
|
||||
"false": {},
|
||||
"off": {},
|
||||
"no": {},
|
||||
}
|
||||
)
|
||||
|
||||
// Check checks single value with specified rules.
|
||||
// It returns nil if successful validation.
|
||||
//
|
||||
@ -123,12 +28,12 @@ var (
|
||||
// string/map/struct/*struct.
|
||||
// The optional parameter <params> specifies the extra validation parameters for some rules
|
||||
// like: required-*、same、different, etc.
|
||||
func Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
|
||||
return doCheck("", value, rules, messages, params...)
|
||||
func (v *Validator) Check(value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
|
||||
return v.doCheck("", value, rules, messages, params...)
|
||||
}
|
||||
|
||||
// doCheck does the really rules validation for single key-value.
|
||||
func doCheck(key string, value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
|
||||
func (v *Validator) doCheck(key string, value interface{}, rules string, messages interface{}, params ...interface{}) *Error {
|
||||
// If there's no validation rules, it does nothing and returns quickly.
|
||||
if rules == "" {
|
||||
return nil
|
||||
@ -196,7 +101,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
// It checks custom validation rules with most priority.
|
||||
var (
|
||||
dataMap map[string]interface{}
|
||||
message = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
message = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
)
|
||||
if len(params) > 0 {
|
||||
dataMap = gconv.Map(params[0])
|
||||
@ -209,7 +114,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
}
|
||||
} else {
|
||||
// It checks build-in validation rules if there's no custom rule.
|
||||
match, err = doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap)
|
||||
match, err = v.doCheckBuildInRules(index, value, ruleKey, rulePattern, ruleItems, data, customMsgMap)
|
||||
if !match && err != nil {
|
||||
errorMsgArray[ruleKey] = err.Error()
|
||||
}
|
||||
@ -220,7 +125,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
// It does nothing if the error message for this rule
|
||||
// is already set in previous validation.
|
||||
if _, ok := errorMsgArray[ruleKey]; !ok {
|
||||
errorMsgArray[ruleKey] = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
errorMsgArray[ruleKey] = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
}
|
||||
}
|
||||
index++
|
||||
@ -233,7 +138,7 @@ func doCheck(key string, value interface{}, rules string, messages interface{},
|
||||
return nil
|
||||
}
|
||||
|
||||
func doCheckBuildInRules(
|
||||
func (v *Validator) doCheckBuildInRules(
|
||||
index int,
|
||||
value interface{},
|
||||
ruleKey string,
|
||||
@ -253,7 +158,7 @@ func doCheckBuildInRules(
|
||||
"required-with-all",
|
||||
"required-without",
|
||||
"required-without-all":
|
||||
match = checkRequired(valueStr, ruleKey, rulePattern, dataMap)
|
||||
match = v.checkRequired(valueStr, ruleKey, rulePattern, dataMap)
|
||||
|
||||
// Length rules.
|
||||
// It also supports length of unicode string.
|
||||
@ -261,7 +166,7 @@ func doCheckBuildInRules(
|
||||
"length",
|
||||
"min-length",
|
||||
"max-length":
|
||||
if msg := checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
|
||||
if msg := v.checkLength(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
|
||||
return match, errors.New(msg)
|
||||
} else {
|
||||
match = true
|
||||
@ -272,7 +177,7 @@ func doCheckBuildInRules(
|
||||
"min",
|
||||
"max",
|
||||
"between":
|
||||
if msg := checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
|
||||
if msg := v.checkRange(valueStr, ruleKey, rulePattern, customMsgMap); msg != "" {
|
||||
return match, errors.New(msg)
|
||||
} else {
|
||||
match = true
|
||||
@ -308,7 +213,7 @@ func doCheckBuildInRules(
|
||||
match = true
|
||||
} else {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":format", rulePattern, -1)
|
||||
return match, errors.New(msg)
|
||||
}
|
||||
@ -322,7 +227,7 @@ func doCheckBuildInRules(
|
||||
}
|
||||
if !match {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":field", rulePattern, -1)
|
||||
return match, errors.New(msg)
|
||||
}
|
||||
@ -337,7 +242,7 @@ func doCheckBuildInRules(
|
||||
}
|
||||
if !match {
|
||||
var msg string
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":field", rulePattern, -1)
|
||||
return match, errors.New(msg)
|
||||
}
|
||||
@ -430,11 +335,11 @@ func doCheckBuildInRules(
|
||||
// 总:
|
||||
// (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
|
||||
case "resident-id":
|
||||
match = checkResidentId(valueStr)
|
||||
match = v.checkResidentId(valueStr)
|
||||
|
||||
// Bank card number using LUHN algorithm.
|
||||
case "bank-card":
|
||||
match = checkLuHn(valueStr)
|
||||
match = v.checkLuHn(valueStr)
|
||||
|
||||
// Universal passport format rule:
|
||||
// Starting with letter, containing only numbers or underscores, length between 6 and 18.
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -7,9 +7,8 @@
|
||||
package gvalid
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CheckMap validates map and returns the error result. It returns nil if with successful validation.
|
||||
@ -17,7 +16,7 @@ import (
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
func (v *Validator) CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
// If there's no validation rules, it does nothing and returns quickly.
|
||||
if params == nil || rules == nil {
|
||||
return nil
|
||||
@ -96,7 +95,7 @@ func CheckMap(params interface{}, rules interface{}, messages ...CustomMsg) *Err
|
||||
value = v
|
||||
}
|
||||
// It checks each rule and its value in loop.
|
||||
if e := doCheck(key, value, rule, customMsgs[key], data); e != nil {
|
||||
if e := v.doCheck(key, value, rule, customMsgs[key], data); e != nil {
|
||||
_, item := e.FirstItem()
|
||||
// ===========================================================
|
||||
// Only in map and struct validations, if value is nil or empty
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -7,10 +7,9 @@
|
||||
package gvalid
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -24,7 +23,7 @@ var (
|
||||
// The parameter <rules> can be type of []string/map[string]string. It supports sequence in error result
|
||||
// if <rules> is type of []string.
|
||||
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
|
||||
func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
|
||||
// It here must use structs.TagFields not structs.MapField to ensure error sequence.
|
||||
tagField, err := structs.TagFields(object, structTagPriority)
|
||||
if err != nil {
|
||||
@ -166,7 +165,7 @@ func CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *
|
||||
value = v
|
||||
}
|
||||
// It checks each rule and its value in loop.
|
||||
if e := doCheck(key, value, rule, customMessage[key], params); e != nil {
|
||||
if e := v.doCheck(key, value, rule, customMessage[key], params); e != nil {
|
||||
_, item := e.FirstItem()
|
||||
// ===========================================================
|
||||
// Only in map and struct validations, if value is nil or empty
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -63,18 +63,18 @@ var defaultMessages = map[string]string{
|
||||
// getErrorMessageByRule retrieves and returns the error message for specified rule.
|
||||
// It firstly retrieves the message from custom message map, and then checks i18n manager,
|
||||
// it returns the default error message if it's not found in custom message map or i18n manager.
|
||||
func getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string {
|
||||
func (v *Validator) getErrorMessageByRule(ruleKey string, customMsgMap map[string]string) string {
|
||||
content := customMsgMap[ruleKey]
|
||||
if content != "" {
|
||||
return content
|
||||
}
|
||||
content = gi18n.GetContent(fmt.Sprintf(`gf.gvalid.rule.%s`, ruleKey))
|
||||
content = gi18n.GetContent(fmt.Sprintf(`gf.gvalid.rule.%s`, ruleKey), v.i18nLang)
|
||||
if content == "" {
|
||||
content = defaultMessages[ruleKey]
|
||||
}
|
||||
// If there's no configured rule message, it uses default one.
|
||||
if content == "" {
|
||||
content = gi18n.GetContent(`gf.gvalid.rule.__default__`)
|
||||
content = gi18n.GetContent(`gf.gvalid.rule.__default__`, v.i18nLang)
|
||||
if content == "" {
|
||||
content = defaultMessages["__default__"]
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -15,7 +15,7 @@ import (
|
||||
// checkLength checks <value> using length rules.
|
||||
// The length is calculated using unicode string, which means one chinese character or letter
|
||||
// both has the length of 1.
|
||||
func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
|
||||
func (v *Validator) checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
|
||||
var (
|
||||
msg = ""
|
||||
runeArray = gconv.Runes(value)
|
||||
@ -39,7 +39,7 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string)
|
||||
}
|
||||
}
|
||||
if valueLen < min || valueLen > max {
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
|
||||
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
|
||||
return msg
|
||||
@ -48,14 +48,14 @@ func checkLength(value, ruleKey, ruleVal string, customMsgMap map[string]string)
|
||||
case "min-length":
|
||||
min, err := strconv.Atoi(ruleVal)
|
||||
if valueLen < min || err != nil {
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":min", strconv.Itoa(min), -1)
|
||||
}
|
||||
|
||||
case "max-length":
|
||||
max, err := strconv.Atoi(ruleVal)
|
||||
if valueLen > max || err != nil {
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":max", strconv.Itoa(max), -1)
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -8,7 +8,7 @@ package gvalid
|
||||
|
||||
// checkLuHn checks <value> with LUHN algorithm.
|
||||
// It's usually used for bank card number validation.
|
||||
func checkLuHn(value string) bool {
|
||||
func (v *Validator) checkLuHn(value string) bool {
|
||||
var (
|
||||
sum = 0
|
||||
nDigits = len(value)
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// checkRange checks <value> using range rules.
|
||||
func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
|
||||
func (v *Validator) checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string) string {
|
||||
msg := ""
|
||||
switch ruleKey {
|
||||
// Value range.
|
||||
@ -30,9 +30,9 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string)
|
||||
max = v
|
||||
}
|
||||
}
|
||||
v, err := strconv.ParseFloat(value, 10)
|
||||
if v < min || v > max || err != nil {
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
valueF, err := strconv.ParseFloat(value, 10)
|
||||
if valueF < min || valueF > max || err != nil {
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
|
||||
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
|
||||
}
|
||||
@ -44,7 +44,7 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string)
|
||||
valueN, err2 = strconv.ParseFloat(value, 10)
|
||||
)
|
||||
if valueN < min || err1 != nil || err2 != nil {
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":min", strconv.FormatFloat(min, 'f', -1, 64), -1)
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ func checkRange(value, ruleKey, ruleVal string, customMsgMap map[string]string)
|
||||
valueN, err2 = strconv.ParseFloat(value, 10)
|
||||
)
|
||||
if valueN > max || err1 != nil || err2 != nil {
|
||||
msg = getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = v.getErrorMessageByRule(ruleKey, customMsgMap)
|
||||
msg = strings.Replace(msg, ":max", strconv.FormatFloat(max, 'f', -1, 64), -1)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// checkRequired checks <value> using required rules.
|
||||
func checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool {
|
||||
func (v *Validator) checkRequired(value, ruleKey, ruleVal string, params map[string]string) bool {
|
||||
required := false
|
||||
switch ruleKey {
|
||||
// Required.
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -32,7 +32,7 @@ import (
|
||||
//
|
||||
// 总:
|
||||
// (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)
|
||||
func checkResidentId(id string) bool {
|
||||
func (v *Validator) checkResidentId(id string) bool {
|
||||
id = strings.ToUpper(strings.TrimSpace(id))
|
||||
if len(id) != 18 {
|
||||
return false
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.15.0"
|
||||
const VERSION = "v1.15.1"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user