Compare commits

..

13 Commits

38 changed files with 903 additions and 275 deletions

View File

@ -85,7 +85,8 @@ func New(config Config) *Redis {
}
return c, nil
},
// 用来测试连接是否可用
// 在被应用从连接池中获取出来之后,用以测试连接是否可用
// 如果返回error那么关闭该连接对象重新创建新的连接。
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err

View File

@ -51,5 +51,5 @@ func ToUTF8(charset string, src string) (dst string, err error) {
// UTF8转指定字符集
func UTF8To(charset string, src string) (dst string, err error) {
return Convert(charset, "UTF-8", src)
return Convert(charset, "UTF-8", src)
}

View File

@ -1,10 +1,16 @@
package gcharset
// Copyright 2018 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.
package gcharset_test
import (
"github.com/gogf/gf/g/encoding/gcharset"
"testing"
)
var testData = []struct {
utf8, other, otherEncoding string
}{
@ -52,7 +58,7 @@ var testData = []struct {
func TestDecode(t *testing.T) {
for _, data := range testData {
str := ""
str, err := Convert("UTF-8", data.otherEncoding, data.other)
str, err := gcharset.Convert("UTF-8", data.otherEncoding, data.other)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
@ -68,7 +74,7 @@ func TestDecode(t *testing.T) {
func TestEncode(t *testing.T) {
for _, data := range testData {
str := ""
str, err := Convert(data.otherEncoding, "UTF-8", data.utf8)
str, err := gcharset.Convert(data.otherEncoding, "UTF-8", data.utf8)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
@ -86,7 +92,7 @@ func TestConvert(t *testing.T) {
dstCharset := "gbk"
dst := "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed"
str, err := Convert(dstCharset, srcCharset, src)
str, err := gcharset.Convert(dstCharset, srcCharset, src)
if err != nil {
t.Errorf("convert error. %v", err)
return

View File

@ -4,11 +4,15 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gjson provides quite flexible and useful API for JSON/XML/YAML/TOML content handling.
// Package gjson provides flexible and useful API for JSON/XML/YAML/TOML content handling.
//
// JSON/XML/YAML/TOML数据格式处理。
package gjson
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/g/encoding/gtoml"
"github.com/gogf/gf/g/encoding/gxml"
@ -38,20 +42,20 @@ type Json struct {
}
// 将变量转换为Json对象进行处理该变量至少应当是一个map或者slice否者转换没有意义
func New(value interface{}, unsafe...bool) *Json {
func New(data interface{}, unsafe...bool) *Json {
j := (*Json)(nil)
switch value.(type) {
switch data.(type) {
case map[string]interface{}, []interface{}, nil:
j = &Json {
p : &value,
p : &data,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
case string, []byte:
j, _ = LoadContent(gconv.Bytes(value))
j, _ = LoadContent(gconv.Bytes(data))
default:
v := (interface{})(nil)
if m := gconv.Map(value); m != nil {
if m := gconv.Map(data); m != nil {
v = m
j = &Json {
p : &v,
@ -59,7 +63,7 @@ func New(value interface{}, unsafe...bool) *Json {
vc : false,
}
} else {
v = gconv.Interfaces(value)
v = gconv.Interfaces(data)
j = &Json {
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
@ -72,41 +76,43 @@ func New(value interface{}, unsafe...bool) *Json {
}
// 创建一个非并发安全的Json对象
func NewUnsafe(value...interface{}) *Json {
if len(value) > 0 {
return New(value[0], true)
func NewUnsafe(data...interface{}) *Json {
if len(data) > 0 {
return New(data[0], true)
}
return New(nil, true)
}
// 识别当前给定内容是否为JSON格式
func Valid (v interface{}) bool {
return json.Valid(gconv.Bytes(v))
func Valid(data interface{}) bool {
return json.Valid(gconv.Bytes(data))
}
// 编码go变量为json字符串并返回json字符串指针
func Encode (v interface{}) ([]byte, error) {
return json.Marshal(v)
func Encode(value interface{}) ([]byte, error) {
return json.Marshal(value)
}
// 解码字符串为interface{}变量
func Decode (b []byte) (interface{}, error) {
var v interface{}
if err := DecodeTo(b, &v); err != nil {
// 解码字符串/[]byte为interface{}变量
func Decode(data interface{}) (interface{}, error) {
var value interface{}
if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
return nil, err
} else {
return v, nil
return value, nil
}
}
// 解析json字符串为go变量注意第二个参数为指针(任意结构的变量)
func DecodeTo (b []byte, v interface{}) error {
return json.Unmarshal(b, v)
// 解析json字符串/[]byte为go变量注意第二个参数为指针(任意结构的变量)
func DecodeTo(data interface{}, v interface{}) error {
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
decoder.UseNumber()
return decoder.Decode(v)
}
// 解析json字符串为gjson.Json对象并返回操作对象指针
func DecodeToJson (b []byte) (*Json, error) {
if v, err := Decode(b); err != nil {
func DecodeToJson(data interface{}) (*Json, error) {
if v, err := Decode(gconv.Bytes(data)); err != nil {
return nil, err
} else {
return New(v), nil
@ -114,48 +120,53 @@ func DecodeToJson (b []byte) (*Json, error) {
}
// 支持多种配置文件类型转换为json格式内容并解析为gjson.Json对象
func Load (path string) (*Json, error) {
func Load(path string) (*Json, error) {
return LoadContent(gfcache.GetBinContents(path), gfile.Ext(path))
}
// 支持的配置文件格式xml, json, yaml/yml, toml,
// 默认为自动识别当无法检测成功时使用json解析。
func LoadContent(data []byte, dataType...string) (*Json, error) {
func LoadContent(data interface{}, dataType...string) (*Json, error) {
var err error
var result interface{}
b := gconv.Bytes(data)
t := "json"
if len(dataType) > 0 {
t = dataType[0]
} else {
if gregex.IsMatch(`<.+>.*</.+>`, data) {
if json.Valid(b) {
t = "json"
} else if gregex.IsMatch(`<.+>.*</.+>`, b) {
t = "xml"
} else if gregex.IsMatch(`\w+\s*:\s*\w+`, data) {
} else if gregex.IsMatch(`\w+\s*:\s*.+`, b) {
t = "yml"
} else if gregex.IsMatch(`\w+\s*=\s*\w+`, data) {
} else if gregex.IsMatch(`\w+\s*=\s*.+`, b) {
t = "toml"
}
}
// 其他数据格式解析
switch t {
case "xml", ".xml":
data, err = gxml.ToJson(data)
if err != nil {
return nil, err
}
case "json", ".json":
// ok
case "xml", ".xml":
b, err = gxml.ToJson(b)
case "yml", "yaml", ".yml", ".yaml":
data, err = gyaml.ToJson(data)
if err != nil {
return nil, err
}
case "yml", "yaml", ".yml", ".yaml":
b, err = gyaml.ToJson(b)
case "toml", ".toml":
data, err = gtoml.ToJson(data)
if err != nil {
return nil, err
}
case "toml", ".toml":
b, err = gtoml.ToJson(b)
default:
err = errors.New("nonsupport type " + t)
}
if err != nil {
return nil, err
}
if result == nil {
if err := json.Unmarshal(data, &result); err != nil {
decoder := json.NewDecoder(bytes.NewReader(b))
decoder.UseNumber()
if err := decoder.Decode(&result); err != nil {
return nil, err
}
}
@ -205,7 +216,7 @@ func (j *Json) GetMap(pattern string) map[string]interface{} {
return nil
}
// 将检索值转换为Json对象指针返回
// 将检索值转换为Json对象指针返回
func (j *Json) GetJson(pattern string) *Json {
result := j.Get(pattern)
if result != nil {
@ -214,18 +225,24 @@ func (j *Json) GetJson(pattern string) *Json {
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (j *Json) GetArray(pattern string) []interface{} {
result := j.Get(pattern)
if result != nil {
if r, ok := result.([]interface{}); ok {
return r
// 将检索值转换为Json对象指针数组返回。
func (j *Json) GetJsons(pattern string) []*Json {
array := j.GetArray(pattern)
if len(array) > 0 {
jsons := make([]*Json, len(array))
for i := 0; i < len(array); i++ {
jsons[i] = New(array[i], !j.mu.IsSafe())
}
return jsons
}
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换。
func (j *Json) GetArray(pattern string) []interface{} {
return gconv.Interfaces(j.Get(pattern))
}
// 返回指定json中的string
func (j *Json) GetString(pattern string) string {
return gconv.String(j.Get(pattern))
@ -724,34 +741,64 @@ func (j *Json) ToXml(rootTag...string) ([]byte, error) {
return gxml.Encode(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlString(rootTag...string) (string, error) {
b, e := j.ToXml(rootTag...)
return string(b), e
}
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlIndentString(rootTag...string) (string, error) {
b, e := j.ToXmlIndent(rootTag...)
return string(b), e
}
func (j *Json) ToJson() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return Encode(*(j.p))
}
func (j *Json) ToJsonString() (string, error) {
b, e := j.ToJson()
return string(b), e
}
func (j *Json) ToJsonIndent() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return json.MarshalIndent(*(j.p), "", "\t")
}
func (j *Json) ToJsonIndentString() (string, error) {
b, e := j.ToJsonIndent()
return string(b), e
}
func (j *Json) ToYaml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gyaml.Encode(*(j.p))
}
func (j *Json) ToYamlString() (string, error) {
b, e := j.ToYaml()
return string(b), e
}
func (j *Json) ToToml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gtoml.Encode(*(j.p))
}
func (j *Json) ToTomlString() (string, error) {
b, e := j.ToToml()
return string(b), e
}
// 转换为指定的struct对象
func (j *Json) ToStruct(o interface{}) error {
j.mu.RLock()

View File

@ -0,0 +1,253 @@
// Copyright 2017 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.
package gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_New(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.New(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
})
}
func Test_NewUnsafe(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j := gjson.NewUnsafe(data)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Valid(t *testing.T) {
data1 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
data2 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]`)
gtest.Case(t, func() {
gtest.Assert(gjson.Valid(data1), true)
gtest.Assert(gjson.Valid(data2), false)
})
}
func Test_Encode(t *testing.T) {
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gjson.Encode(value)
gtest.Assert(err, nil)
gtest.Assert(b, []byte(`[1,2,3]`))
})
}
func Test_Decode(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
v, err := gjson.Decode(data)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
var v interface{}
err := gjson.DecodeTo(data, &v)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_SplitChar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
j.SetSplitChar(byte('#'))
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m#k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a#1"), 2)
})
}
func Test_ViolenceCheck(t *testing.T) {
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("m.a.2"), 3)
gtest.Assert(j.Get("m.v1.v2"), nil)
j.SetViolenceCheck(true)
gtest.Assert(j.Get("m.v1.v2"), 4)
})
}
func Test_GetToVar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
var m map[string]string
var n int
var a []int
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j.GetToVar("n", &n)
j.GetToVar("m", &m)
j.GetToVar("a", &a)
gtest.Assert(n, "123456789")
gtest.Assert(m, g.Map{"k" : "v"})
gtest.Assert(a, g.Slice{1, 2, 3})
})
}
func Test_GetMap(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetMap("n"), g.Map{})
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
gtest.Assert(j.GetMap("a"), g.Map{})
})
}
func Test_GetJson(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j2 := j.GetJson("m")
gtest.AssertNE(j2, nil)
gtest.Assert(j2.Get("k"), "v")
gtest.Assert(j2.Get("a"), nil)
gtest.Assert(j2.Get("n"), nil)
})
}
func Test_GetArray(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.Assert(j.GetArray("n"), g.Array{123456789})
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k":"v"}})
gtest.Assert(j.GetArray("a"), g.Array{1,2,3})
})
}
func Test_GetString(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetString("n"), "123456789")
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
gtest.AssertEQ(j.GetString("i"), "")
})
}
func Test_GetStrings(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
gtest.AssertEQ(j.GetStrings("i"), g.SliceStr{})
})
}
func Test_GetInterfaces(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k":"v"}})
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1,2,3})
})
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Get("a"), g.Map{
"b" : g.Slice{1},
"c" : g.Slice{2},
})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}

View File

@ -0,0 +1,138 @@
// Copyright 2017 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.
package gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Load_JSON(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
// JSON
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// JSON
gtest.Case(t, func() {
path := "test.json"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_XML(t *testing.T) {
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
// XML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
// XML
gtest.Case(t, func() {
path := "test.xml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("doc.n"), "123456789")
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("doc.m.k"), "v")
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("doc.a.1"), 2)
})
}
func Test_Load_YAML(t *testing.T) {
data := []byte(`
a:
- 1
- 2
- 3
m:
k: v
"n": 123456789
`)
// YAML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// YAML
gtest.Case(t, func() {
path := "test.yaml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}
func Test_Load_TOML(t *testing.T) {
data := []byte(`
a = ["1", "2", "3"]
n = "123456789"
[m]
k = "v"
`)
// TOML
gtest.Case(t, func() {
j, err := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
// TOML
gtest.Case(t, func() {
path := "test.toml"
gfile.PutBinContents(path, data)
defer gfile.Remove(path)
j, err := gjson.Load(path)
gtest.Assert(err, nil)
gtest.Assert(j.Get("n"), "123456789")
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
gtest.Assert(j.Get("m.k"), "v")
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
gtest.Assert(j.Get("a.1"), 2)
})
}

View File

@ -8,9 +8,7 @@ package gjson_test
import (
"bytes"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
@ -230,51 +228,3 @@ func Test_Set14(t *testing.T) {
t.Error(err)
}
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Get("a"), g.Map{
"b" : g.Slice{1},
"c" : g.Slice{2},
})
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}

View File

@ -5,6 +5,8 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gxml provides accessing and converting for XML content.
//
// XML数据格式解析。
package gxml
import (
@ -19,9 +21,9 @@ import (
)
// 将XML内容解析为map变量
func Decode(xmlbyte []byte) (map[string]interface{}, error) {
prepare(xmlbyte)
return mxj.NewMapXml(xmlbyte)
func Decode(content []byte) (map[string]interface{}, error) {
prepare(content)
return mxj.NewMapXml(content)
}
// 将map变量解析为XML格式内容
@ -34,9 +36,9 @@ func EncodeWithIndent(v map[string]interface{}, rootTag...string) ([]byte, error
}
// XML格式内容直接转换为JSON格式内容
func ToJson(xmlbyte []byte) ([]byte, error) {
prepare(xmlbyte)
mv, err := mxj.NewMapXml(xmlbyte)
func ToJson(content []byte) ([]byte, error) {
prepare(content)
mv, err := mxj.NewMapXml(content)
if err == nil {
return mv.Json()
} else {
@ -61,10 +63,10 @@ func prepare(xmlbyte []byte) error {
if err != nil {
return err
}
xmlEncode := "UTF-8"
if len(matchStr) == 2 {
xmlEncode = matchStr[1]
xmlEncode = matchStr[1]
}
charset := mahonia.GetCharset(xmlEncode)

View File

@ -75,11 +75,18 @@ func View(name...string) *gview.View {
}
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_VIEW, group)
return instances.GetOrSetFuncLock(key, func() interface{} {
path := cmdenv.Get("gf.gview.path", gfile.Pwd()).String()
view := gview.New(path)
// 添加基于源码的搜索目录检索地址,常用于开发环境调试,只添加入口文件目录
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
view.AddPath(p)
view := gview.New(gfile.Pwd())
// 自定义的环境变量/启动参数路径,优先级最高,覆盖默认的工作目录
if envPath := cmdenv.Get("gf.gview.path").String(); envPath != "" && gfile.Exists(envPath) {
view.SetPath(envPath)
}
// 二进制文件执行目录
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
view.AddPath(selfPath)
}
// 开发环境源码main包目录
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
view.AddPath(mainPath)
}
// 框架内置函数
view.BindFunc("config", funcConfig)
@ -96,21 +103,18 @@ func Config(file...string) *gcfg.Config {
}
return instances.GetOrSetFuncLock(fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_CONFIG, configFile),
func() interface{} {
pwdPath := gfile.Pwd()
envPath := cmdenv.Get("gf.gcfg.path").String()
selfPath := gfile.SelfDir()
mainPath := gfile.MainPkgPath()
config := gcfg.New(pwdPath, configFile)
// 默认当前工作目录
config := gcfg.New(gfile.Pwd(), configFile)
// 自定义的环境变量/启动参数路径,优先级最高,覆盖默认的工作目录
if envPath != "" && gfile.Exists(envPath) {
if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" && gfile.Exists(envPath) {
config.SetPath(envPath)
}
// 二进制文件执行目录
if selfPath != "" && gfile.Exists(selfPath) {
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
config.AddPath(selfPath)
}
// 开发环境源码main包目录
if mainPath != "" && gfile.Exists(mainPath) {
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
config.AddPath(mainPath)
}
return config

View File

@ -81,28 +81,29 @@ func (r *Request) GetVar(key string, def ... interface{}) gvar.VarRead {
// 获取原始请求输入二进制。
func (r *Request) GetRaw() []byte {
err := error(nil)
if r.rawContent == nil {
r.rawContent, _ = ioutil.ReadAll(r.Body)
r.rawContent, err = ioutil.ReadAll(r.Body)
if err != nil {
r.Error("error reading request body: ", err)
}
}
return r.rawContent
}
// 获取原始请求输入字符串。
func (r *Request) GetRawString() string {
if r.rawContent == nil {
r.rawContent, _ = ioutil.ReadAll(r.Body)
}
return string(r.rawContent)
return string(r.GetRaw())
}
// 获取原始json请求输入字符串并解析为json对象
func (r *Request) GetJson() *gjson.Json {
data := r.GetRaw()
if data != nil {
if len(data) > 0 {
if j, err := gjson.DecodeToJson(data); err == nil {
return j
} else {
panic(err)
r.Error(err, ": ", string(data))
}
}
return nil

View File

@ -26,21 +26,21 @@ func (r *Request) setBasicAuth(tips...string) {
}
// 设置HTTP基础账号密码认证如果用户没有提交账号密码那么提示用户输出信息。
// 验证成功之后返回true否则返回false
// 验证成功之后返回true否则返回false
func (r *Request) BasicAuth(user, pass string, tips...string) bool {
auth := r.Header.Get("Authorization")
if auth == "" {
r.setBasicAuth(tips...)
return false
}
auths := strings.SplitN(auth, " ", 2)
if len(auths) != 2 {
authArray := strings.SplitN(auth, " ", 2)
if len(authArray) != 2 {
r.Response.WriteStatus(http.StatusForbidden)
return false
}
switch auths[0] {
switch authArray[0] {
case "Basic":
authStr, err := gbase64.Decode(auths[1])
authStr, err := gbase64.Decode(authArray[1])
if err != nil {
r.Response.WriteStatus(http.StatusForbidden, err.Error())
return false
@ -54,11 +54,12 @@ func (r *Request) BasicAuth(user, pass string, tips...string) bool {
r.setBasicAuth(tips...)
return false
}
return true
default:
r.Response.WriteStatus(http.StatusForbidden)
return false
}
return true
return false
}

View File

@ -0,0 +1,14 @@
// Copyright 2017 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.
package ghttp
import "fmt"
// 打印error日志
func (r *Request) Error(value... interface{}) {
r.Server.handleErrorLog(fmt.Sprint(value...), r)
}

View File

@ -169,7 +169,7 @@ func (r *Request) GetPostMap(def...map[string]string) map[string]string {
}
// 将所有的request参数映射到struct属性上参数object应当为一个struct对象的指针, mapping为非必需参数自定义参数与属性的映射关系
func (r *Request) GetPostToStruct(object interface{}, mapping...map[string]string) {
func (r *Request) GetPostToStruct(object interface{}, mapping...map[string]string) error {
tagmap := r.getStructParamsTagMap(object)
if len(mapping) > 0 {
for k, v := range mapping[0] {
@ -180,5 +180,5 @@ func (r *Request) GetPostToStruct(object interface{}, mapping...map[string]strin
for k, v := range r.GetPostMap() {
params[k] = v
}
gconv.Struct(params, object, tagmap)
return gconv.Struct(params, object, tagmap)
}

View File

@ -177,7 +177,7 @@ func (r *Request) GetQueryMap(def ... map[string]string) map[string]string {
}
// 将所有的get参数映射到struct属性上参数object应当为一个struct对象的指针, mapping为非必需参数自定义参数与属性的映射关系
func (r *Request) GetQueryToStruct(object interface{}, mapping...map[string]string) {
func (r *Request) GetQueryToStruct(object interface{}, mapping...map[string]string) error {
tagmap := r.getStructParamsTagMap(object)
if len(mapping) > 0 {
for k, v := range mapping[0] {
@ -188,5 +188,5 @@ func (r *Request) GetQueryToStruct(object interface{}, mapping...map[string]stri
for k, v := range r.GetQueryMap() {
params[k] = v
}
gconv.Struct(params, object, tagmap)
return gconv.Struct(params, object, tagmap)
}

View File

@ -162,7 +162,7 @@ func (r *Request) GetRequestMap(def...map[string]string) map[string]string {
}
// 将所有的request参数映射到struct属性上参数object应当为一个struct对象的指针, mapping为非必需参数自定义参数与属性的映射关系
func (r *Request) GetRequestToStruct(object interface{}, mapping...map[string]string) {
func (r *Request) GetRequestToStruct(object interface{}, mapping...map[string]string) error {
tagmap := r.getStructParamsTagMap(object)
if len(mapping) > 0 {
for k, v := range mapping[0] {
@ -178,6 +178,6 @@ func (r *Request) GetRequestToStruct(object interface{}, mapping...map[string]st
params = j.ToMap()
}
}
gconv.Struct(params, object, tagmap)
return gconv.Struct(params, object, tagmap)
}

View File

@ -15,7 +15,6 @@ import (
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/genv"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gproc"
"github.com/gogf/gf/g/os/gtimer"
@ -108,8 +107,6 @@ const (
HOOK_AFTER_SERVE = "AfterServe"
HOOK_BEFORE_OUTPUT = "BeforeOutput"
HOOK_AFTER_OUTPUT = "AfterOutput"
HOOK_BEFORE_CLOSE = "BeforeClose"
HOOK_AFTER_CLOSE = "AfterClose"
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
gDEFAULT_SERVER = "default"
@ -187,9 +184,9 @@ func serverProcessInit() {
}
// 是否处于开发环境
if gfile.MainPkgPath() != "" {
glog.Debug("GF notices that you're in develop environment, so error logs are auto enabled to stdout.")
}
//if gfile.MainPkgPath() != "" {
// glog.Debug("GF notices that you're in develop environment, so error logs are auto enabled to stdout.")
//}
}
// 获取/创建一个默认配置的HTTP Server(默认监听端口是80)
@ -217,8 +214,6 @@ func GetServer(name...interface{}) (*Server) {
servedCount : gtype.NewInt(),
logger : glog.New(),
}
// 日志的标准输出默认关闭,但是错误信息会特殊处理
s.logger.SetStdPrint(false)
// 初始化时使用默认配置
s.SetConfig(defaultServerConfig)
// 记录到全局ServerMap中
@ -237,6 +232,11 @@ func (s *Server) Start() error {
return errors.New("server is already running")
}
// 没有注册任何路由,且没有开启文件服务,那么提示错误
if len(s.routesMap) == 0 && !s.config.FileServerEnabled {
glog.Fatal("[ghttp] no router set or static feature enabled, did you forget import the router?")
}
// 底层http server配置
if s.config.Handler == nil {
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
@ -277,7 +277,7 @@ func (s *Server) Start() error {
if gproc.IsChild() {
gtimer.SetTimeout(2*time.Second, func() {
if err := gproc.Send(gproc.PPid(), []byte("exit"), gADMIN_GPROC_COMM_GROUP); err != nil {
glog.Error("ghttp server error in process communication:", err)
glog.Error("[ghttp] server error in process communication:", err)
}
})
}
@ -350,7 +350,10 @@ func (s *Server) GetRouteMap() string {
}
addr := s.config.Addr
if s.config.HTTPSAddr != "" {
addr += ",tls" + s.config.HTTPSAddr
if len(addr) > 0 {
addr += ","
}
addr += "tls" + s.config.HTTPSAddr
}
for _, a := range m {
data := make([]string, 8)

View File

@ -76,8 +76,9 @@ type ServerConfig struct {
// 日志配置
LogPath string // 存放日志的目录路径
LogHandler LogHandler // 自定义日志处理回调方法
ErrorLogEnabled bool // 是否开启error log
AccessLogEnabled bool // 是否开启access log
LogStdPrint bool // 是否打印日志到终端(默认开启)
ErrorLogEnabled bool // 是否开启error log(默认开启)
AccessLogEnabled bool // 是否开启access log(默认关闭)
// 其他设置
NameToUriType int // 服务注册时对象和方法名称转换为URI时的规则
@ -110,12 +111,11 @@ var defaultServerConfig = ServerConfig {
SessionMaxAge : gDEFAULT_SESSION_MAX_AGE,
SessionIdName : gDEFAULT_SESSION_ID_NAME,
LogStdPrint : true,
ErrorLogEnabled : true,
AccessLogEnabled : false,
GzipContentTypes : defaultGzipContentTypes,
DumpRouteMap : true,
RouterCacheExpire : 60,
Rewrites : make(map[string]string),
}

View File

@ -23,6 +23,15 @@ func (s *Server)SetLogPath(path string) {
s.logger.SetPath(path)
}
// 设置日志内容是否输出到终端
func (s *Server)SetLogStdPrint(enabled bool) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
s.config.LogStdPrint = enabled
}
// 设置是否开启access log日志功能
func (s *Server)SetAccessLogEnabled(enabled bool) {
if s.Status() == SERVER_STATUS_RUNNING {

View File

@ -57,15 +57,12 @@ func (s *Server)SetServerRoot(root string) {
return
}
// RealPath的作用除了校验地址正确性以外还转换分隔符号为当前系统正确的文件分隔符号
path := gfile.RealPath(root)
if path == "" {
path = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + root)
realPath, err := gfile.Search(root)
if err != nil {
glog.Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: %s`, err.Error()))
}
if path == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: path "%s" does not exist`, root))
}
glog.Debug("[ghttp] SetServerRoot path:", path)
s.config.SearchPaths = []string{strings.TrimRight(path, gfile.Separator)}
glog.Debug("[ghttp] SetServerRoot path:", realPath)
s.config.SearchPaths = []string{strings.TrimRight(realPath, gfile.Separator)}
s.config.FileServerEnabled = true
}
@ -75,13 +72,9 @@ func (s *Server) AddSearchPath(path string) {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
// RealPath的作用除了校验地址正确性以外,还转换分隔符号为当前系统正确的文件分隔符号
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
}
if realPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: path "%s" does not exist`, path))
realPath, err := gfile.Search(path)
if err != nil {
glog.Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: %s`, err.Error()))
}
s.config.SearchPaths = append(s.config.SearchPaths, realPath)
s.config.FileServerEnabled = true
@ -93,13 +86,9 @@ func (s *Server) AddStaticPath(prefix string, path string) {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
// RealPath的作用除了校验地址正确性以外,还转换分隔符号为当前系统正确的文件分隔符号
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
}
if realPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] AddStaticPath failed: path "%s" does not exist`, path))
realPath, err := gfile.Search(path)
if err != nil {
glog.Fatal(fmt.Sprintf(`[ghttp] AddStaticPath failed: %s`, err.Error()))
}
addItem := staticPathItem {
prefix : prefix,

View File

@ -52,6 +52,13 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
if !request.IsExited() {
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
}
// error log
if e := recover(); e != nil {
request.Response.WriteStatus(http.StatusInternalServerError)
s.handleErrorLog(e, request)
}
// access log
s.handleAccessLog(request)
// 输出Cookie
request.Cookie.Output()
// 输出缓冲区
@ -60,18 +67,8 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
// 事件 - BeforeClose
s.callHookHandler(HOOK_BEFORE_CLOSE, request)
// access log
s.handleAccessLog(request)
// error log使用recover进行判断
if e := recover(); e != nil {
request.Response.WriteStatus(http.StatusInternalServerError)
s.handleErrorLog(e, request)
}
// 更新Session会话超时时间
request.Session.UpdateExpire()
s.callHookHandler(HOOK_AFTER_CLOSE, request)
}()
// ============================================================

View File

@ -9,7 +9,7 @@ package ghttp
import (
"fmt"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gtime"
)
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
@ -22,19 +22,19 @@ func (s *Server) handleAccessLog(r *Request) {
v(r)
return
}
content := fmt.Sprintf(`"%s %s %s %s" %d`,
r.Method, r.Host, r.URL.String(), r.Proto,
content := fmt.Sprintf(`%d "%s %s %s %s"`,
r.Response.Status,
r.Method, r.Host, r.URL.String(), r.Proto,
)
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000)
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
s.logger.Cat("access").Backtrace(false, 2).Println(content)
s.logger.Cat("access").Backtrace(false, 2).StdPrint(s.config.LogStdPrint).Println(content)
}
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
func (s *Server) handleErrorLog(error interface{}, r *Request) {
// 错误输出默认是开启的
if !s.IsErrorLogEnabled() && gfile.MainPkgPath() == "" {
if !s.IsErrorLogEnabled() {
return
}
@ -46,17 +46,11 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) {
// 错误日志信息
content := fmt.Sprintf(`%v, "%s %s %s %s"`, error, r.Method, r.Host, r.URL.String(), r.Proto)
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000)
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
if s.logger.GetPath() == "" {
// 错误信息特殊处理,在未开启日志文件保存时强制强制输出到终端
s.logger.Cat("error").Backtrace(true, 2).StdPrint(true).Error(content)
if r.LeaveTime > r.EnterTime {
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000)
} else {
s.logger.Cat("error").Backtrace(true, 2).Error(content)
// 开发环境下(MainPkgPath)自动输出错误信息到标准输出
if gfile.MainPkgPath() != "" {
s.logger.Cat("error").Backtrace(true, 2).StdPrint(true).Error(content)
}
content += fmt.Sprintf(` %.3f`, float64(gtime.Microsecond() - r.EnterTime)/1000)
}
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
s.logger.Cat("error").Backtrace(true, 2).StdPrint(s.config.LogStdPrint).Error(content)
}

View File

@ -86,10 +86,40 @@ func (c *Config) filePath(file...string) (path string) {
// 设置配置管理器的配置文件存放目录绝对路径
func (c *Config) SetPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gcfg] SetPath failed: %s`, err.Error()))
// 判断相对路径
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path))
c.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
@ -112,10 +142,40 @@ func (c *Config) SetViolenceCheck(check bool) {
// 添加配置管理器的配置文件搜索路径
func (c *Config) AddPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gcfg] AddPath failed: %s`, err.Error()))
// 判断相对路径
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path))
c.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
@ -176,11 +236,11 @@ func (c *Config) getJson(file...string) *gjson.Json {
j.SetViolenceCheck(c.vc.Val())
// 添加配置文件监听,如果有任何变化,删除文件内容缓存,下一次查询会自动更新
gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
c.jsons.Remove(event.Path)
c.jsons.Remove(name)
})
return j
} else {
glog.Errorfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
glog.Criticalfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
}
return nil
})

View File

@ -0,0 +1,63 @@
// Copyright 2017-2018 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.
package gfile
import (
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/container/garray"
)
// 如果给定绝对路径将会去掉其中的相对路径符号后返回;
// 如果是给定的相对路径,那么将会按照以下路径优先级搜索文件(重复路径会去重)
// prioritySearchPaths、当前工作目录、二进制文件目录、源码main包目录(开发环境下)
func Search(name string, prioritySearchPaths...string) (realPath string, err error) {
// 是否绝对路径
realPath = RealPath(name)
if realPath != "" {
return
}
// 相对路径搜索
array := garray.NewStringArray(true)
// 自定义优先路径
array.Append(prioritySearchPaths...)
// 用户工作目录
array.Append(Pwd())
// 二进制所在目录
array.Append(SelfDir())
// 源码main包目录
if path := MainPkgPath(); path != "" {
array.Append(path)
}
// 路径去重
array.Unique()
// 执行相对路径搜索
array.RLockFunc(func(array []string) {
path := ""
for _, v := range array {
path = RealPath(v + Separator + name)
if path != "" {
realPath = path
break
}
}
})
// 目录不存在错误处理
if realPath == "" {
buffer := bytes.NewBuffer(nil)
buffer.WriteString(fmt.Sprintf("cannot find file/folder \"%s\" in following paths:", name))
array.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s", k + 1, v))
}
})
err = errors.New(buffer.String())
}
return
}

View File

@ -24,8 +24,8 @@ import (
"github.com/gogf/gf/g/os/gspath"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/os/gview/internal/text/template"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"strings"
"sync"
)
@ -48,16 +48,10 @@ type FuncMap = map[string]interface{}
// 默认的视图对象
var viewObj *View
// 初始化默认的视图对象
// 初始化默认的视图对象, 默认加载包不会初始化,使用包方法才会初始化模板引擎对象。
func checkAndInitDefaultView() {
if viewObj == nil {
// gfile.MainPkgPath() 用以判断是否开发环境
mainPkgPath := gfile.MainPkgPath()
if gfile.MainPkgPath() == "" {
viewObj = New(gfile.Pwd())
} else {
viewObj = New(mainPkgPath)
}
viewObj = New(gfile.Pwd())
}
}
@ -106,13 +100,40 @@ func New(path...string) *View {
// 设置模板目录绝对路径
func (view *View) SetPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
// 判断相对路径
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gview] SetPath failed: %s`, err.Error()))
buffer := bytes.NewBuffer(nil)
if view.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gview] SetPath failed: cannot find directory \"%s\" in following paths:", path))
view.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gview] SetPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
@ -127,13 +148,40 @@ func (view *View) SetPath(path string) error {
// 添加模板目录搜索路径
func (view *View) AddPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
// 判断相对路径
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gview] AddPath failed: %s`, err.Error()))
buffer := bytes.NewBuffer(nil)
if view.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gview] AddPath failed: cannot find directory \"%s\" in following paths:", path))
view.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gview] AddPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断

View File

@ -8,6 +8,7 @@ package gconv
import (
"github.com/gogf/gf/g/internal/empty"
"github.com/gogf/gf/g/text/gstr"
"reflect"
"strings"
)
@ -102,6 +103,11 @@ func Map(value interface{}, noTagCheck...bool) map[string]interface{} {
rt := rv.Type()
name := ""
for i := 0; i < rv.NumField(); i++ {
// 只转换公开属性
fieldName := rt.Field(i).Name
if !gstr.IsLetterUpper(fieldName[0]) {
continue
}
name = ""
// 检查tag, 支持gconv, json标签, 优先使用gconv
if len(noTagCheck) == 0 || !noTagCheck[0] {
@ -111,7 +117,7 @@ func Map(value interface{}, noTagCheck...bool) map[string]interface{} {
}
}
if name == "" {
name = strings.TrimSpace(rt.Field(i).Name)
name = strings.TrimSpace(fieldName)
} else {
// 支持标准库json特性: -, omitempty
name = strings.TrimSpace(name)

View File

@ -7,6 +7,7 @@
package gconv
import (
"github.com/gogf/gf/g/text/gstr"
"reflect"
)
@ -311,7 +312,12 @@ func Interfaces(i interface{}) []interface{} {
array = append(array, rv.Index(i).Interface())
}
case reflect.Struct:
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
// 只获取公开属性
if !gstr.IsLetterUpper(rt.Field(i).Name[0]) {
continue
}
array = append(array, rv.Field(i).Interface())
}
default:

View File

@ -78,6 +78,10 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string
attrMap := make(map[string]struct{})
elemType := elem.Type()
for i := 0; i < elem.NumField(); i++ {
// 只转换公开属性
if !gstr.IsLetterUpper(elemType.Field(i).Name[0]) {
continue
}
attrMap[elemType.Field(i).Name] = struct{}{}
}
for mapK, mapV := range paramsMap {

View File

@ -112,3 +112,15 @@ func Test_Map_StructWithJsonTag(t *testing.T) {
gtest.Assert(map2["password2"], "456")
})
}
// 私有属性不会进行转换
func Test_Map_PrivateAttribute(t *testing.T) {
type User struct {
Id int
name string
}
gtest.Case(t, func() {
user := &User{1, "john"}
gtest.Assert(gconv.Map(user), g.Map{"Id" : 1})
})
}

View File

@ -7,6 +7,7 @@
package gconv_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/test/gtest"
"testing"
@ -23,3 +24,15 @@ func Test_Slice(t *testing.T) {
gtest.AssertEQ(gconv.Interfaces(value), []interface{}{123.456})
})
}
// 私有属性不会进行转换
func Test_Slice_PrivateAttribute(t *testing.T) {
type User struct {
Id int
name string
}
gtest.Case(t, func() {
user := &User{1, "john"}
gtest.Assert(gconv.Interfaces(user), g.Slice{1})
})
}

View File

@ -8,12 +8,11 @@ package gconv_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"testing"
)
func Test_Struct_Basic1(t *testing.T) {
gtest.Case(t, func() {
type User struct {
@ -302,3 +301,18 @@ func Test_Struct_Attr_Struct_Slice_Ptr(t *testing.T) {
}
})
}
// 私有属性不会进行转换
func Test_Struct_PrivateAttribute(t *testing.T) {
type User struct {
Id int
name string
}
gtest.Case(t, func() {
user := new(User)
err := gconv.Struct(g.Map{"id" : 1, "name" : "john"}, user)
gtest.Assert(err, nil)
gtest.Assert(user.Id, 1)
gtest.Assert(user.name, "")
})
}

View File

@ -7,6 +7,7 @@
package gvalid
import (
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/third/github.com/fatih/structs"
"strings"
@ -59,14 +60,19 @@ func CheckStruct(object interface{}, rules interface{}, msgs...CustomMsg) *Error
case map[string]string:
checkRules = v
}
// 首先, 按照属性循环一遍将strcut的属性、数值、tag解析
// 首先, 按照属性循环一遍将struct的属性、数值、tag解析
for _, field := range fields {
params[field.Name()] = field.Value()
fieldName := field.Name()
// 只检测公开属性
if !gstr.IsLetterUpper(fieldName[0]) {
continue
}
params[fieldName] = field.Value()
if tag := field.Tag("gvalid"); tag != "" {
// sequence tag == struct tag, 这里的name为别名
name, rule, msg := parseSequenceTag(tag)
if len(name) == 0 {
name = field.Name()
name = fieldName
}
// params参数使用别名**扩容**(而不仅仅使用别名),仅用于验证使用
if _, ok := params[name]; !ok {
@ -151,3 +157,4 @@ func CheckStruct(object interface{}, rules interface{}, msgs...CustomMsg) *Error
}
return nil
}

View File

@ -16,4 +16,5 @@ func main() {
redis.Do("SET", "k", "v")
v, _ := redis.Do("GET", "k")
fmt.Println(gconv.String(v))
}
}

View File

@ -1,25 +0,0 @@
package main
import (
"net/http"
"github.com/gogf/gf/g/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s.BindHandler("/log/handler", func(r *ghttp.Request){
r.Response.WriteStatus(http.StatusNotFound, "文件找不到了")
})
s.SetAccessLogEnabled(true)
s.SetErrorLogEnabled(true)
//s.SetLogHandler(func(r *ghttp.Request, error ...interface{}) {
// if len(error) > 0 {
// // 如果是错误日志
// fmt.Println("错误产生了:", error[0])
// }
// // 这里是请求日志
// fmt.Println("请求处理完成,请求地址:", r.URL.String(), "请求结果:", r.Response.Status)
//})
s.SetPort(8199)
s.Run()
}

View File

@ -35,7 +35,7 @@
}
$(function () {
var url = "ws://127.0.0.1:8199/ws";
var url = "wss://127.0.0.1:8199/wss";
var ws = new WebSocket(url);
try {
// ws连接成功

View File

@ -3,12 +3,13 @@ package main
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/glog"
)
func main() {
s := g.Server()
s.BindHandler("/ws", func(r *ghttp.Request) {
s.BindHandler("/wss", func(r *ghttp.Request) {
ws, err := r.WebSocket()
if err != nil {
glog.Error(err)
@ -24,6 +25,8 @@ func main() {
}
}
})
s.SetServerRoot(gfile.MainPkgPath())
s.EnableHTTPS("../../https/server.crt", "../../https/server.key")
s.SetPort(8199)
s.Run()
}

View File

@ -3,6 +3,7 @@ package main
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/glog"
)
@ -24,6 +25,7 @@ func main() {
}
}
})
s.SetServerRoot(gfile.MainPkgPath())
s.SetPort(8199)
s.Run()
}

View File

@ -1,13 +1,18 @@
package main
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/text/gregex"
"bytes"
"encoding/json"
"fmt"
)
func main() {
s := "127.0.0.1:6379,1,nhytaf176tg?maxIdle=1&maxActive=0&idleTimeout=60&maxConnLifetime=60"
array, err := gregex.MatchString(`(.+):(\d+),{0,1}(\d*),{0,1}(.*)\?(.+)`, s)
g.Dump(err)
g.Dump(array)
value := interface{}(nil)
data := []byte(`{"n": 123456789}`)
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.UseNumber()
err := decoder.Decode(&value)
//err := json.Unmarshal(data, &value)
fmt.Println(err)
fmt.Println(value)
}

View File

@ -1,5 +1,5 @@
package gf
const VERSION = "v1.5.18"
const VERSION = "v1.5.21"
const AUTHORS = "john<john@goframe.org>"