Compare commits

..

20 Commits

Author SHA1 Message Date
a36e00efeb improve ghttp for session storage with redis server 2019-07-28 13:10:34 +08:00
bd4d273e1c improve ghttp for session 2019-07-28 11:50:12 +08:00
45465c1bd1 version updates 2019-07-24 21:49:25 +08:00
8acecc88f9 improve internal/debug, gdb packages 2019-07-24 21:21:08 +08:00
7ff4a00063 Merge pull request #250 from hailaz/master 2019-07-23 21:51:17 +08:00
9f9cb097d9 修复gvalid.CheckStruct()的msgs覆盖tag的msg存在的bug。 2019-07-17 20:07:43 +08:00
f7c5d7fc7f gvalid补充修代码注释,移除错误的代码修改。 2019-07-15 21:14:46 +08:00
73235f1967 修复gvalid多条数据校验时,规则key没有对应校验数据导致校验异常的问题。 2019-07-13 15:20:41 +08:00
1ebc8092a6 Merge pull request #6 from gogf/master
sync master
2019-07-13 14:30:34 +08:00
88c43d1772 Merge pull request #5 from gogf/master
sync master
2019-07-13 12:35:33 +08:00
a0a8eb4700 TagMapField改回调用MapField 2019-07-13 11:47:20 +08:00
f24847576d 先移除新增的两个gvalid的test文件。 2019-07-13 10:52:02 +08:00
dfaf27e9e5 先还原对gvalid.Check()的修改。 2019-07-13 10:50:01 +08:00
32d5b28423 先还原对gvalid.Check()的修改。 2019-07-13 10:48:46 +08:00
0b8ca3313e 矫正gvalid.CheckStruct()字段别名记录的注释。
优化gvalid.CheckStruct()中属性的tag解析循环。
2019-07-13 10:47:52 +08:00
cdf92b64d6 Merge pull request #4 from gogf/master
sync master
2019-07-13 09:22:12 +08:00
cf745422f3 Fixed use cname in gvalid tag.
Added gvaild data type support for array, slice, map.
Added array, slice, map data test in gvaild unit test.
2019-07-11 15:41:10 +08:00
df8623c4e2 Merge pull request #3 from gogf/master
sync master
2019-07-10 10:30:21 +08:00
32fc6868aa Merge pull request #2 from gogf/master
sync master
2019-07-05 08:58:19 +08:00
d62c1dedf3 Merge pull request #1 from gogf/master
sync master
2019-07-03 11:03:54 +08:00
17 changed files with 317 additions and 210 deletions

View File

@ -12,11 +12,12 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/text/gstr"
"reflect"
"regexp"
"strings"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/gtime"
@ -30,8 +31,8 @@ const (
)
var (
// 用于可转义的单词的识别正则对象
wordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
wordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`)
)
// 获取最近一条执行的sql
@ -698,12 +699,12 @@ func (bs *dbBase) formatWhere(where interface{}, args []interface{}) (newWhere s
newWhere = buffer.String()
// 查询条件参数处理主要处理slice参数类型
if len(newArgs) > 0 {
// 支持例如 Where/And/Or("uid", 1) 这种格式
// 支持例如 Where/And/Or("uid", 1) , Where/And/Or("uid>=", 1) 这种格式
if gstr.Pos(newWhere, "?") == -1 {
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
newWhere += "=?"
} else {
if lastOperatorReg.MatchString(newWhere) {
newWhere += "?"
} else if wordReg.MatchString(newWhere) {
newWhere += "=?"
}
}
}

View File

@ -43,6 +43,9 @@ func formatQuery(query string, args []interface{}) (newQuery string, newArgs []i
switch kind {
// '?'占位符支持slice类型, 这里会将slice参数拆散并更新原有占位符'?'为多个'?',使用','符号连接。
case reflect.Slice, reflect.Array:
if rv.Len() == 0 {
continue
}
// 不拆分[]byte类型
if _, ok := arg.([]byte); ok {
newArgs = append(newArgs, arg)

View File

@ -641,6 +641,12 @@ func Test_Model_Where(t *testing.T) {
gtest.AssertGT(len(result), 0)
gtest.Assert(result["id"].Int(), 3)
})
gtest.Case(t, func() {
result, err := db.Table(table).Where("id=3", g.Slice{}).One()
gtest.Assert(err, nil)
gtest.AssertGT(len(result), 0)
gtest.Assert(result["id"].Int(), 3)
})
gtest.Case(t, func() {
result, err := db.Table(table).Where("id=?", g.Slice{3}).One()
gtest.Assert(err, nil)

View File

@ -60,6 +60,12 @@ func StackWithFilter(filter string, skip ...int) string {
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, gFILTER_KEY) {
continue
}
if fn := runtime.FuncForPC(pc); fn == nil {
name = "unknown"
} else {
@ -92,6 +98,12 @@ func CallerWithFilter(filter string, skip ...int) string {
}
for i := callerFromIndex(filter) + number; i < gMAX_DEPTH; i++ {
if _, file, line, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, gFILTER_KEY) {
continue
}
return fmt.Sprintf(`%s:%d`, file, line)
} else {
break

View File

@ -9,11 +9,12 @@ package ghttp
import (
"crypto/tls"
"fmt"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/glog"
"net/http"
"strconv"
"time"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/glog"
)
const (
@ -25,7 +26,7 @@ const (
NAME_TO_URI_TYPE_CAMEL = 3 // 采用驼峰命名方式
gDEFAULT_COOKIE_PATH = "/" // 默认path
gDEFAULT_COOKIE_MAX_AGE = 86400 * 365 // 默认cookie有效期(一年)
gDEFAULT_SESSION_MAX_AGE = 600000 // 默认session有效期(600秒)
gDEFAULT_SESSION_MAX_AGE = 86400 // 默认session有效期(一天)
gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称
gCHANGE_CONFIG_WHILE_RUNNING_ERROR = "cannot be changed while running"
)

View File

@ -1,34 +1,36 @@
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). 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.
// 并发安全的Session管理器
package ghttp
import (
"encoding/json"
"strconv"
"strings"
"time"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"strconv"
"strings"
"time"
)
// SESSION对象
// SESSION对象,并发安全
type Session struct {
id string // SessionId
data *gmap.StrAnyMap // Session数据
dirty bool // 数据是否被修改
server *Server // 所属Server
request *Request // 关联的请求
}
// 生成一个唯一的SessionId字符串长度18位。
func makeSessionId() string {
return strings.ToUpper(strconv.FormatInt(gtime.Nanosecond(), 36) + grand.RandStr(6))
return strings.ToUpper(strconv.FormatInt(gtime.Nanosecond(), 36) + grand.Str(6))
}
// 获取或者生成一个session对象(延迟初始化)
@ -41,15 +43,12 @@ func GetSession(r *Request) *Session {
}
}
// 执行初始化(用于延迟初始化).
// 延迟初始化
func (s *Session) init() {
if len(s.id) == 0 {
s.server = s.request.Server
// 根据提交的SESSION ID获取已存在SESSION
id := s.request.Cookie.GetSessionId()
if id != "" {
data := s.server.sessions.Get(id)
if data != nil {
if id := s.request.Cookie.GetSessionId(); id != "" {
if data := s.server.sessions.Get(id); data != nil {
s.id = id
s.data = data.(*gmap.StrAnyMap)
return
@ -59,6 +58,7 @@ func (s *Session) init() {
s.id = s.request.Cookie.MakeSessionId()
s.data = gmap.NewStrAnyMap()
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
s.dirty = true
}
}
@ -68,7 +68,7 @@ func (s *Session) Id() string {
return s.id
}
// 获取当前session所有数据
// 获取当前session所有数据,注意是值拷贝
func (s *Session) Map() map[string]interface{} {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
@ -77,16 +77,27 @@ func (s *Session) Map() map[string]interface{} {
return nil
}
// 获得session map大小
func (s *Session) Size() int {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
return s.data.Size()
}
return 0
}
// 设置session
func (s *Session) Set(key string, value interface{}) {
s.init()
s.data.Set(key, value)
s.dirty = true
}
// 批量设置
func (s *Session) Sets(m map[string]interface{}) {
s.init()
s.data.Sets(m)
s.dirty = true
}
// 判断键名是否存在
@ -98,6 +109,58 @@ func (s *Session) Contains(key string) bool {
return false
}
// 判断session是否有修改(包括新创建)
func (s *Session) IsDirty() bool {
return s.dirty
}
// 删除指定session键值对
func (s *Session) Remove(key string) {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
s.data.Remove(key)
s.dirty = true
}
}
// 将session数据导出为[]byte数据(目前使用json进行序列化)
func (s *Session) Export() (data []byte, err error) {
if s.Size() > 0 {
data, err = json.Marshal(s.data)
}
return
}
// 从[]byte中恢复session数据(目前使用json进行序列化)
func (s *Session) Restore(data []byte) (err error) {
if len(data) == 0 {
return nil
}
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
s.data.LockFunc(func(m map[string]interface{}) {
err = json.Unmarshal(data, &m)
})
}
return
}
// 清空session
func (s *Session) Clear() {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
s.data.Clear()
s.dirty = true
}
}
// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除)
func (s *Session) UpdateExpire() {
if len(s.id) > 0 && s.data.Size() > 0 {
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
}
}
// 获取SESSION变量
func (s *Session) Get(key string, def ...interface{}) interface{} {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
@ -117,29 +180,6 @@ func (s *Session) GetVar(key string, def ...interface{}) *gvar.Var {
return gvar.New(s.Get(key, def...), true)
}
// 删除session
func (s *Session) Remove(key string) {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
s.data.Remove(key)
}
}
// 清空session
func (s *Session) Clear() {
if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" {
s.init()
s.data.Clear()
}
}
// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除)
func (s *Session) UpdateExpire() {
if len(s.id) > 0 && s.data.Size() > 0 {
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
}
}
func (s *Session) GetString(key string, def ...interface{}) string {
return gconv.String(s.Get(key, def...))
}
@ -228,7 +268,34 @@ func (s *Session) GetDuration(key string, def ...interface{}) time.Duration {
return gconv.Duration(s.Get(key, def...))
}
// 将变量转换为对象,注意 pointer 参数必须为struct指针
func (s *Session) GetMap(value interface{}, tags ...string) map[string]interface{} {
return gconv.Map(value, tags...)
}
func (s *Session) GetMapDeep(value interface{}, tags ...string) map[string]interface{} {
return gconv.MapDeep(value, tags...)
}
func (s *Session) GetMaps(value interface{}, tags ...string) []map[string]interface{} {
return gconv.Maps(value, tags...)
}
func (s *Session) GetMapsDeep(value interface{}, tags ...string) []map[string]interface{} {
return gconv.MapsDeep(value, tags...)
}
func (s *Session) GetStruct(key string, pointer interface{}, mapping ...map[string]string) error {
return gconv.Struct(s.Get(key), pointer, mapping...)
}
func (s *Session) GetStructDeep(key string, pointer interface{}, mapping ...map[string]string) error {
return gconv.StructDeep(s.Get(key), pointer, mapping...)
}
func (s *Session) GetStructs(key string, pointer interface{}, mapping ...map[string]string) error {
return gconv.Structs(s.Get(key), pointer, mapping...)
}
func (s *Session) GetStructsDeep(key string, pointer interface{}, mapping ...map[string]string) error {
return gconv.StructsDeep(s.Get(key), pointer, mapping...)
}

View File

@ -10,104 +10,70 @@ package gcache_test
import (
"github.com/gogf/gf/g/os/gcache"
"sync"
"testing"
)
var (
c = gcache.New()
clru = gcache.New(10000)
mInt = make(map[int]int)
mMap = make(map[interface{}]interface{})
muInt = sync.RWMutex{}
muMap = sync.RWMutex{}
cache = gcache.New()
cacheLru = gcache.New(10000)
)
func Benchmark_CacheSet(b *testing.B) {
for i := 0; i < b.N; i++ {
c.Set(i, i, 0)
}
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
cache.Set(i, i, 0)
i++
}
})
}
func Benchmark_CacheGet(b *testing.B) {
for i := 0; i < b.N; i++ {
c.Get(i)
}
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
cache.Get(i)
i++
}
})
}
func Benchmark_CacheRemove(b *testing.B) {
for i := 0; i < b.N; i++ {
c.Remove(i)
}
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
cache.Remove(i)
i++
}
})
}
func Benchmark_CacheLruSet(b *testing.B) {
for i := 0; i < b.N; i++ {
clru.Set(i, i, 0)
}
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
cacheLru.Set(i, i, 0)
i++
}
})
}
func Benchmark_CacheLruGet(b *testing.B) {
for i := 0; i < b.N; i++ {
clru.Get(i)
}
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
cacheLru.Get(i)
i++
}
})
}
func Benchmark_CacheLruRemove(b *testing.B) {
for i := 0; i < b.N; i++ {
clru.Remove(i)
}
}
func Benchmark_InterfaceMapWithLockSet(b *testing.B) {
for i := 0; i < b.N; i++ {
muMap.Lock()
mMap[i] = i
muMap.Unlock()
}
}
func Benchmark_InterfaceMapWithLockGet(b *testing.B) {
for i := 0; i < b.N; i++ {
muMap.RLock()
if _, ok := mMap[i]; ok {
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
cacheLru.Remove(i)
i++
}
muMap.RUnlock()
}
}
func Benchmark_InterfaceMapWithLockRemove(b *testing.B) {
for i := 0; i < b.N; i++ {
muMap.Lock()
delete(mMap, i)
muMap.Unlock()
}
}
func Benchmark_IntMapWithLockWithLockSet(b *testing.B) {
for i := 0; i < b.N; i++ {
muInt.Lock()
mInt[i] = i
muInt.Unlock()
}
}
func Benchmark_IntMapWithLockGet(b *testing.B) {
for i := 0; i < b.N; i++ {
muInt.RLock()
if _, ok := mInt[i]; ok {
}
muInt.RUnlock()
}
}
func Benchmark_IntMapWithLockRemove(b *testing.B) {
for i := 0; i < b.N; i++ {
muInt.Lock()
delete(mInt, i)
muInt.Unlock()
}
})
}

View File

@ -11,21 +11,24 @@ import (
"fmt"
"os"
"reflect"
"regexp"
"runtime"
"strings"
"testing"
"github.com/gogf/gf/g/internal/debug"
"github.com/gogf/gf/g/util/gconv"
)
const (
gPATH_FILTER_KEY = "/g/test/gtest/gtest"
)
// Case creates an unit test case.
// The parameter <t> is the pointer to testing.T of stdlib (*testing.T).
// The parameter <f> is the callback function for unit test case.
func Case(t *testing.T, f func()) {
defer func() {
if err := recover(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n%s", err, getStack())
fmt.Fprintf(os.Stderr, "%v\n%s", err, debug.StackWithFilter(gPATH_FILTER_KEY))
t.Fail()
}
}()
@ -262,7 +265,7 @@ func Error(message ...interface{}) {
// Fatal prints <message> to stderr and exit the process.
func Fatal(message ...interface{}) {
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), getStack())
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), debug.StackWithFilter(gPATH_FILTER_KEY))
os.Exit(1)
}
@ -305,53 +308,6 @@ func compareMap(value, expect interface{}) error {
return nil
}
// getStack returns the caller stack content from getStack.
// The parameter <skip> indicates the skip count of the caller stack from getStack.
func getStack(skip ...int) string {
customSkip := 0
if len(skip) > 0 {
customSkip = skip[0]
}
stack := ""
index := 1
from := 0
// Ignore current gtest lines and find the beginning index of caller file.
for i := 0; i < 10; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if reg, _ := regexp.Compile(`gtest\.go$`); !reg.MatchString(file) {
from = i
break
}
}
}
// Converting all file separator to "/".
goRoot := runtime.GOROOT()
if goRoot != "" {
goRoot = strings.Replace(goRoot, "\\", "/", -1)
goRoot = regexp.QuoteMeta(goRoot)
}
for i := from + customSkip; i < 10000; i++ {
if _, file, cline, ok := runtime.Caller(i); ok && file != "" {
if reg, _ := regexp.Compile(`<autogenerated>`); reg.MatchString(file) {
continue
}
if reg, _ := regexp.Compile(`gtest\.go$`); reg.MatchString(file) {
continue
}
if goRoot != "" {
if reg, _ := regexp.Compile("^" + goRoot); reg.MatchString(file) {
continue
}
}
stack += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, "\n")
index++
} else {
break
}
}
return stack
}
// isNil checks whether <value> is nil.
func isNil(value interface{}) bool {
rv := reflect.ValueOf(value)

View File

@ -379,40 +379,40 @@ func Interfaces(i interface{}) []interface{} {
}
// Maps converts <i> to []map[string]interface{}.
func Maps(i interface{}) []map[string]interface{} {
if i == nil {
func Maps(value interface{}, tags ...string) []map[string]interface{} {
if value == nil {
return nil
}
if r, ok := i.([]map[string]interface{}); ok {
if r, ok := value.([]map[string]interface{}); ok {
return r
} else {
array := Interfaces(i)
array := Interfaces(value)
if len(array) == 0 {
return nil
}
list := make([]map[string]interface{}, len(array))
for k, v := range array {
list[k] = Map(v)
list[k] = Map(v, tags...)
}
return list
}
}
// MapsDeep converts <i> to []map[string]interface{} recursively.
func MapsDeep(i interface{}) []map[string]interface{} {
if i == nil {
func MapsDeep(value interface{}, tags ...string) []map[string]interface{} {
if value == nil {
return nil
}
if r, ok := i.([]map[string]interface{}); ok {
if r, ok := value.([]map[string]interface{}); ok {
return r
} else {
array := Interfaces(i)
array := Interfaces(value)
if len(array) == 0 {
return nil
}
list := make([]map[string]interface{}, len(array))
for k, v := range array {
list[k] = MapDeep(v)
list[k] = MapDeep(v, tags...)
}
return list
}

View File

@ -7,6 +7,10 @@
package gvalid
import (
"regexp"
"strconv"
"strings"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/net/gipv4"
@ -14,9 +18,6 @@ import (
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/util/gconv"
"regexp"
"strconv"
"strings"
)
const (

View File

@ -10,7 +10,6 @@ import (
"strings"
"github.com/gogf/gf/g/internal/structs"
"github.com/gogf/gf/g/util/gconv"
)
@ -22,6 +21,8 @@ var (
// 校验struct对象属性object参数也可以是一个指向对象的指针返回值同CheckMap方法。
// struct的数据校验结果信息是顺序的。
func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Error {
// 字段别名记录用于msgs覆盖struct tag的
fieldAliases := make(map[string]string)
params := make(map[string]interface{})
checkRules := make(map[string]string)
customMsgs := make(CustomMsg)
@ -66,22 +67,17 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
checkRules = v
}
// 首先, 按照属性循环一遍将struct的属性、数值、tag解析
tagValue := ""
for _, field := range structs.MapField(object, structTagPriority, true) {
for nameOrTag, field := range structs.MapField(object, structTagPriority, true) {
fieldName := field.Name()
params[fieldName] = field.Value()
tagValue = ""
for _, v := range structTagPriority {
tagValue = field.Tag(v)
if tagValue != "" {
break
}
}
if tagValue != "" {
// MapField返回map[name/tag]*Field当nameOrTag != fieldName时nameOrTag为tag
if nameOrTag != fieldName {
// sequence tag == struct tag, 这里的name为别名
name, rule, msg := parseSequenceTag(tagValue)
name, rule, msg := parseSequenceTag(nameOrTag)
if len(name) == 0 {
name = fieldName
} else {
fieldAliases[fieldName] = name
}
// params参数使用别名**扩容**(而不仅仅使用别名),仅用于验证使用
if _, ok := params[name]; !ok {
@ -89,7 +85,13 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
}
// 校验规则
if _, ok := checkRules[name]; !ok {
checkRules[name] = rule
if _, ok := checkRules[fieldName]; ok {
// tag中存在别名且rules传入的参数中使用了属性命名时进行规则替换并删除该属性的规则
checkRules[name] = checkRules[fieldName]
delete(checkRules, fieldName)
} else {
checkRules[name] = rule
}
errorRules = append(errorRules, name+"@"+rule)
} else {
// 传递的rules规则会覆盖struct tag的规则
@ -116,14 +118,16 @@ func CheckStruct(object interface{}, rules interface{}, msgs ...CustomMsg) *Erro
}
}
}
// 自定义错误消息非必须参数优先级比rules参数中以及struct tag中定义的错误消息更高
if len(msgs) > 0 && len(msgs[0]) > 0 {
if len(customMsgs) > 0 {
for k, v := range msgs[0] {
for k, v := range msgs[0] {
if a, ok := fieldAliases[k]; ok {
// 属性的别名存在时,覆盖别名的错误信息
customMsgs[a] = v
} else {
customMsgs[k] = v
}
} else {
customMsgs = msgs[0]
}
}

View File

@ -17,4 +17,7 @@ func main() {
"id in(?)": g.Slice{1, 2, 3},
}
db.Table("user").Where(conditions).OrderBy("id asc").All()
var params []interface{}
db.Table("user").Where("1=1", params).OrderBy("id asc").All()
}

View File

@ -0,0 +1,2 @@
[redis]
default = "127.0.0.1:6379,10"

View File

@ -22,7 +22,7 @@ func (c *Controller) DoLogin() {
}
func (c *Controller) Main() {
c.Response.WriteJson(c.Session.Data())
c.Response.WriteJson(c.Session.Map())
}
func main() {

View File

@ -0,0 +1,85 @@
package main
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/os/gtime"
)
// 测试SESSION写入
func SessionSet(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.WriteJson("ok")
}
// 测试SESSION读取
func SessionGet(r *ghttp.Request) {
r.Response.WriteJson(r.Session.Map())
}
// 请求处理之前将Redis中的数据读取出来并存储到SESSION对象中。
func RedisHandlerGet(r *ghttp.Request) {
if !r.IsFileRequest() {
id := r.Cookie.GetSessionId()
if id == "" {
return
}
// 当内存中的SESSION存在时不需要读取Redis
if r.Session.Size() > 0 {
return
}
// SESSION不存在时例如服务重启自动从Redis读取并恢复数据
value, err := g.Redis().DoVar("GET", id)
if err != nil {
panic(err)
}
if !value.IsNil() {
if err := r.Session.Restore(value.Bytes()); err != nil {
panic(err)
}
}
}
}
// 请求结束时将SESSION数据存储到Redis中或者在SESSION删除时也删除Redis中的数据。
func RedisHandlerSet(r *ghttp.Request) {
if !r.IsFileRequest() {
id := r.Cookie.GetSessionId()
if id == "" {
return
}
err := (error)(nil)
value := ([]byte)(nil)
if r.Session.Size() > 0 {
if value, err = r.Session.Export(); err == nil {
if len(value) == 0 {
return
} else if !r.Session.IsDirty() {
// 更新过期时间
_, err = g.Redis().Do("EXPIRE", id, r.Server.GetSessionMaxAge())
} else {
// 更新Redis数据
_, err = g.Redis().Do("SETEX", id, r.Server.GetSessionMaxAge(), value)
}
}
} else {
// 清空SESSION后自动删除Redis数据
_, err = g.Redis().Do("DEL", id)
}
if err != nil {
panic(err)
}
}
}
func main() {
s := g.Server()
s.BindHandler("/set", SessionSet)
s.BindHandler("/get", SessionGet)
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
ghttp.HOOK_BEFORE_SERVE: RedisHandlerGet,
ghttp.HOOK_AFTER_SERVE: RedisHandlerSet,
})
s.SetPort(8199)
s.Run()
}

View File

@ -5,7 +5,7 @@ import (
)
func PrintLog(content string) {
glog.Skip(1).Line().Println("line number with skip:", content)
glog.Skip(0).Line().Println("line number with skip:", content)
glog.Line(true).Println("line number without skip:", content)
}

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.8.1"
const VERSION = "v1.8.3"
const AUTHORS = "john<john@goframe.org>"