mirror of
https://gitee.com/johng/gf
synced 2026-06-10 03:23:59 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1804fc346 | |||
| a3886c2179 | |||
| f258b5bf1c | |||
| afb1adee3d | |||
| 22fa7a37f3 | |||
| 6a58bfc574 | |||
| 64124c60fc | |||
| 9a0066de62 |
38
TODO.MD
38
TODO.MD
@ -1,23 +1,16 @@
|
||||
# ON THE WAY
|
||||
1. 增加图形验证码支持,至少支持数字和英文字母;
|
||||
1. 增加热编译工具,提高开发环境的开发/测试效率(媲美PHP开发效率);
|
||||
1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射;
|
||||
1. ghttp.Response增加输出内容后自动退出当前请求机制,不需要用户手动return,参考beego如何实现;
|
||||
1. Cookie&Session数据池化处理;
|
||||
1. ghttp.Client增加proxy特性;
|
||||
1. gtime增加对时区转换的封装,并简化失去转换时对类似+80500时区的支持;
|
||||
1. orm增加sqlite对Save方法的支持(去掉触发器语句);
|
||||
1. ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps);
|
||||
1. ghttp路由功能增加分组路由特性;
|
||||
1. ghttp增加返回数据压缩机制;
|
||||
1. gview中的template标签失效问题;
|
||||
1. gfile文件stat信息使用gfsnotify进行缓存更新改进;
|
||||
1. ghttp.Server增加proxy功能特性,本地proxy和远程proxy,本地即将路由规则映射;远程即反向代理;
|
||||
1. gjson对大json数据的解析效率问题;
|
||||
1. ghttp增加route name特性,并同时支持backend和template(提供内置函数)引用,可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上;
|
||||
1. ghttp.Client自动Close机制;
|
||||
1. gvalid校验支持当第一个规则失败后便不再校验后续的规则,最好做成链式操作;
|
||||
1. 检查ghttp.Server超时问题;
|
||||
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节);
|
||||
1. ghttp.Request增加对输入参数的自动HtmlEncode机制;
|
||||
1. 常量命名风格根据golint进行修改;
|
||||
@ -36,29 +29,17 @@
|
||||
- https://github.com/Masterminds/sprig
|
||||
1. gform参考 https://gohouse.github.io/gorose/dist/index.html 进行改进
|
||||
1. gtcp提供简便的包发送/接收方法(SendPkg/RecvPkg)以解决常见的TCP通信粘包问题,并完善文档(参考:https://www.cnblogs.com/kex1n/p/6502002.html);
|
||||
1. gfile对于文件的读写强行使用了gfpool,在某些场景下不合适,需要考虑剥离开,并为开发者提供单独的指针池文件操作特性;
|
||||
1. 路由增加不区分大小写得匹配方式;
|
||||
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
|
||||
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
|
||||
1. 改进WebServer获取POST参数处理逻辑,当提交非form数据时,例如json数据,针对某些方法可以直接解析;
|
||||
1. WebServer增加可选择的路由覆盖配置,默认情况下不覆盖;
|
||||
1. gkafka这个包比较重,未来从框架中剥离出来;
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. 增加jumplist的数据结构容器;
|
||||
1. DelayQueue/PriorityQueue;
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag, 完善开发文档;
|
||||
1. 增加SO_REUSEPORT的支持;
|
||||
1. 权限管理模块;
|
||||
1. 从ghttp中剥离SESSION功能构成单独的模块gsession;
|
||||
1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收;
|
||||
1. gdb的Data方法支持struct参数传入;
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
|
||||
1. 更新跨域请求CORS相关功能文档;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gcfg包目前允许添加重复的目录路径,需要在SetPath/AddPath时判断重复性,不能添加重复的路径;
|
||||
1. gdb执行数据写入时,如果参数为struct/[]struct,自动映射与表字段对应关系,不再使用gconv标签标识;
|
||||
|
||||
1. gtcp增加对TLS加密通信的支持;
|
||||
|
||||
|
||||
|
||||
@ -119,4 +100,19 @@
|
||||
1. gform对于MySQL字段类型为datetime类型的时区问题分析;
|
||||
1. 改进证书打开失败时的WebServer错误提示,前置HOOK校验后关闭后续的HOOK逻辑执行;
|
||||
1. 目前WebServer的HOOK是按照优先级执行的,需要增加覆盖特性;
|
||||
|
||||
1. 更新跨域请求CORS相关功能文档;
|
||||
1. ghttp.Response增加输出内容后自动退出当前请求机制,不需要用户手动return,参考beego如何实现;
|
||||
1. gcfg包目前允许添加重复的目录路径,需要在SetPath/AddPath时判断重复性,不能添加重复的路径;
|
||||
1. gdb执行数据写入时,如果参数为struct/[]struct,自动映射与表字段对应关系,不再使用gconv标签标识;
|
||||
1. gdb的Data方法支持struct参数传入;
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag, 完善开发文档;
|
||||
1. 增加SO_REUSEPORT的支持;
|
||||
1. gkafka这个包比较重,未来从框架中剥离出来;
|
||||
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
|
||||
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
|
||||
1. gfile对于文件的读写强行使用了gfpool,在某些场景下不合适,需要考虑剥离开,并为开发者提供单独的指针池文件操作特性;
|
||||
1. ghttp.Client自动Close机制;
|
||||
1. ghttp路由功能增加分组路由特性;
|
||||
1. 增加可选择性的orm tag特性,用以数据表记录与struct对象转换的键名属性映射;
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
253
g/encoding/gjson/gjson_unit_basic_test.go
Normal file
253
g/encoding/gjson/gjson_unit_basic_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
138
g/encoding/gjson/gjson_unit_load_test.go
Normal file
138
g/encoding/gjson/gjson_unit_load_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -56,7 +56,7 @@ func (r *Response) CORS(options CORSOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// 允许请求跨域访问(使用more配置).
|
||||
// 允许请求跨域访问(使用默认配置).
|
||||
func (r *Response) CORSDefault() {
|
||||
r.CORS(r.DefaultCORSOptions())
|
||||
}
|
||||
|
||||
@ -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,7 +107,10 @@ const (
|
||||
HOOK_AFTER_SERVE = "AfterServe"
|
||||
HOOK_BEFORE_OUTPUT = "BeforeOutput"
|
||||
HOOK_AFTER_OUTPUT = "AfterOutput"
|
||||
|
||||
// deprecated.
|
||||
HOOK_BEFORE_CLOSE = "BeforeClose"
|
||||
// deprecated.
|
||||
HOOK_AFTER_CLOSE = "AfterClose"
|
||||
|
||||
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
||||
@ -187,9 +189,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 +219,6 @@ func GetServer(name...interface{}) (*Server) {
|
||||
servedCount : gtype.NewInt(),
|
||||
logger : glog.New(),
|
||||
}
|
||||
// 日志的标准输出默认关闭,但是错误信息会特殊处理
|
||||
s.logger.SetStdPrint(false)
|
||||
// 初始化时使用默认配置
|
||||
s.SetConfig(defaultServerConfig)
|
||||
// 记录到全局ServerMap中
|
||||
@ -237,6 +237,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 +282,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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -74,10 +74,11 @@ type ServerConfig struct {
|
||||
Rewrites map[string]string // URI Rewrite重写配置
|
||||
|
||||
// 日志配置
|
||||
LogPath string // 存放日志的目录路径
|
||||
LogHandler LogHandler // 自定义日志处理回调方法
|
||||
ErrorLogEnabled bool // 是否开启error log
|
||||
AccessLogEnabled bool // 是否开启access log
|
||||
LogPath string // 存放日志的目录路径(默认为空,表示不写文件)
|
||||
LogHandler LogHandler // 自定义日志处理回调方法(默认为空)
|
||||
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),
|
||||
}
|
||||
|
||||
@ -10,7 +10,10 @@ import (
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
|
||||
// 设置日志目录
|
||||
// 设置日志目录,只有在设置了日志目录的情况下才会输出日志到日志文件中。
|
||||
// 日志文件路径格式为:
|
||||
// 1. 请求日志: access/YYYY-MM-DD.log
|
||||
// 2. 错误日志: error/YYYY-MM-DD.log
|
||||
func (s *Server)SetLogPath(path string) {
|
||||
if s.Status() == SERVER_STATUS_RUNNING {
|
||||
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
|
||||
@ -23,6 +26,16 @@ func (s *Server)SetLogPath(path string) {
|
||||
s.logger.SetPath(path)
|
||||
}
|
||||
|
||||
// 设置日志内容是否输出到终端,默认情况下只有错误日志才会自动输出到终端。
|
||||
// 如果需要输出请求日志到终端,默认情况下使用SetAccessLogEnabled方法开启请求日志特性即可。
|
||||
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 {
|
||||
@ -51,7 +64,7 @@ func (s *Server) SetLogHandler(handler LogHandler) {
|
||||
}
|
||||
|
||||
// 获取日志写入的回调函数
|
||||
func (s *Server) GetLogHandler() LogHandler {
|
||||
func (s *Server)GetLogHandler() LogHandler {
|
||||
return s.config.LogHandler
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -35,6 +35,12 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path = rewrite
|
||||
}
|
||||
}
|
||||
|
||||
// URI默认值
|
||||
if r.URL.Path == "" {
|
||||
r.URL.Path = "/"
|
||||
}
|
||||
|
||||
// 去掉末尾的"/"号
|
||||
if r.URL.Path != "/" {
|
||||
for r.URL.Path[len(r.URL.Path) - 1] == '/' {
|
||||
@ -52,6 +58,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 +73,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)
|
||||
}()
|
||||
|
||||
// ============================================================
|
||||
|
||||
@ -9,7 +9,6 @@ package ghttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
)
|
||||
|
||||
@ -23,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)
|
||||
}
|
||||
|
||||
// 处理服务错误信息,主要是panic,http请求的status由access log进行管理
|
||||
func (s *Server) handleErrorLog(error interface{}, r *Request) {
|
||||
// 错误输出默认是开启的
|
||||
if !s.IsErrorLogEnabled() && gfile.MainPkgPath() == "" {
|
||||
if !s.IsErrorLogEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
@ -53,15 +52,5 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) {
|
||||
content += fmt.Sprintf(` %.3f`, float64(gtime.Microsecond() - 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)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
s.logger.Cat("error").Backtrace(true, 2).StdPrint(s.config.LogStdPrint).Error(content)
|
||||
}
|
||||
|
||||
@ -43,14 +43,22 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) {
|
||||
if mname == "Init" || mname == "Shut" || mname == "Exit" {
|
||||
continue
|
||||
}
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if ctlName[0] == '*' {
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
if len(methodMap) > 0 {
|
||||
// 指定的方法名称注册,那么需要使用错误提示
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required`,
|
||||
pkgPath, ctlName, mname, v.Method(i).Type().String())
|
||||
} else {
|
||||
// 否则只是Debug提示
|
||||
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`,
|
||||
pkgPath, ctlName, mname, v.Method(i).Type().String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
@ -93,16 +101,17 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
|
||||
glog.Error("invalid method name:" + mname)
|
||||
return
|
||||
}
|
||||
if _, ok := fval.Interface().(func()); !ok {
|
||||
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, fval.Type().String())
|
||||
return
|
||||
}
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if ctlName[0] == '*' {
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
if _, ok := fval.Interface().(func()); !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required`,
|
||||
pkgPath, ctlName, mname, fval.Type().String())
|
||||
return
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
@ -132,15 +141,16 @@ func (s *Server)BindControllerRest(pattern string, c Controller) {
|
||||
if _, ok := methodsMap[method]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, v.Method(i).Type().String())
|
||||
return
|
||||
}
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if ctlName[0] == '*' {
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required`,
|
||||
pkgPath, ctlName, mname, v.Method(i).Type().String())
|
||||
return
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(mname + ":" + pattern, sname, mname, false)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
|
||||
@ -49,15 +49,23 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) {
|
||||
if mname == "Init" || mname == "Shut" {
|
||||
continue
|
||||
}
|
||||
faddr, ok := v.Method(i).Interface().(func(*Request))
|
||||
if !ok {
|
||||
glog.Errorfln(`invalid method definition "%s", while "func(*Request))" is required`, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
faddr, ok := v.Method(i).Interface().(func(*Request))
|
||||
if !ok {
|
||||
if len(methodMap) > 0 {
|
||||
// 指定的方法名称注册,那么需要使用错误提示
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required`,
|
||||
pkgPath, objName, mname, v.Method(i).Type().String())
|
||||
} else {
|
||||
// 否则只是Debug提示
|
||||
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)"`,
|
||||
pkgPath, objName, mname, v.Method(i).Type().String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
@ -103,11 +111,6 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
|
||||
glog.Error("invalid method name:" + mname)
|
||||
return
|
||||
}
|
||||
faddr, ok := fval.Interface().(func(*Request))
|
||||
if !ok {
|
||||
glog.Errorfln(`invalid method definition "%s", while "func(*Request)" is required`, fval.Type().String())
|
||||
return
|
||||
}
|
||||
finit := (func(*Request))(nil)
|
||||
fshut := (func(*Request))(nil)
|
||||
if v.MethodByName("Init").IsValid() {
|
||||
@ -122,6 +125,12 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
faddr, ok := fval.Interface().(func(*Request))
|
||||
if !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required`,
|
||||
pkgPath, objName, mname, fval.Type().String())
|
||||
return
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
|
||||
m[key] = &handlerItem{
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
@ -158,16 +167,17 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) {
|
||||
if _, ok := methodsMap[method]; !ok {
|
||||
continue
|
||||
}
|
||||
faddr, ok := v.Method(i).Interface().(func(*Request))
|
||||
if !ok {
|
||||
glog.Errorfln(`invalid method definition "%s", while "func(*Request)" is required`, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
faddr, ok := v.Method(i).Interface().(func(*Request))
|
||||
if !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required`,
|
||||
pkgPath, objName, mname, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(mname + ":" + pattern, sname, mname, false)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
|
||||
@ -221,8 +221,11 @@ func (c *Conn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
// 不能使用c.conn.RemoteAddr(),其返回为nil,
|
||||
// 这里使用c.raddr获取远程连接地址。
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
//return c.conn.RemoteAddr()
|
||||
return c.raddr
|
||||
}
|
||||
|
||||
func (c *Conn) Close() error {
|
||||
|
||||
@ -57,7 +57,7 @@ func TestCache_LRU(t *testing.T) {
|
||||
|
||||
gtest.Assert(cache.Size(), 10)
|
||||
gtest.Assert(cache.Get(6), 6)
|
||||
time.Sleep(3*time.Second)
|
||||
time.Sleep(4*time.Second)
|
||||
gtest.Assert(cache.Size(), 2)
|
||||
gtest.Assert(cache.Get(6), 6)
|
||||
gtest.Assert(cache.Get(1), nil)
|
||||
|
||||
@ -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
|
||||
}
|
||||
// 重复判断
|
||||
|
||||
63
g/os/gfile/gfile_search.go
Normal file
63
g/os/gfile/gfile_search.go
Normal 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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
// 重复判断
|
||||
|
||||
@ -20,7 +20,7 @@ func (c *User) Test(r *ghttp.Request, value interface{}) {
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindObjectMethod("/user", new(User), "Test")
|
||||
s.BindObject("/user", new(User))
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ func main() {
|
||||
data, err := conn.Recv(-1)
|
||||
if len(data) > 0 {
|
||||
if err := conn.Send(append([]byte("> "), data...)); err != nil {
|
||||
glog.Error(err)
|
||||
glog.Error(err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
fmt.Println(json.Valid([]byte("111")))
|
||||
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)
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.5.20"
|
||||
const VERSION = "v1.5.22"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user