ghttp增加静态文件目录映射功能;改进gspath目录管理功能;改进gconv的slice转换功能,并增加gconv.Map方法

This commit is contained in:
John
2018-11-30 09:48:57 +08:00
parent aee266eea0
commit b19e47783b
25 changed files with 480 additions and 115 deletions

View File

@ -42,7 +42,7 @@
1. gtcp提供简便的包发送/接收方法(SendPkg/RecvPkg)以解决常见的TCP通信粘包问题并完善文档参考https://www.cnblogs.com/kex1n/p/6502002.html
1. gfile对于文件的读写强行使用了gfpool在某些场景下不合适需要考虑剥离开并为开发者提供单独的指针池文件操作特性
1. 路由增加不区分大小写得匹配方式;
1. gform对于MySQL字段类型为datetime类型的时区问题分析
# DONE
@ -98,4 +98,5 @@
1. WebServer事件回调允许对同一个路由规则绑定多个事件回调
1. gcfg/gview/ghttp等模块加上对临时文件目录的自动添加监听判断基本是开发环境下特别是windows环境去掉临时文件的监听避免临时文件过大引起的运行缓慢占用内存问题
1. 改进gfpool在文件指针变化时的更新
1. ghttp hook回调使用方式在注册路由比较多的时候优先级可能使得开发者混乱考虑方式便于管理
1. ghttp hook回调使用方式在注册路由比较多的时候优先级可能使得开发者混乱考虑方式便于管理
1. gform对于MySQL字段类型为datetime类型的时区问题分析

View File

@ -161,7 +161,7 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
case 0 :
case 1 : min = mid + 1
}
if cmp == 0 || min > max {
if cmp == 0 || min >= max {
break
}
}

View File

@ -154,7 +154,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
case 0 :
case 1 : min = mid + 1
}
if cmp == 0 || min > max {
if cmp == 0 || min >= max {
break
}
}

View File

@ -155,7 +155,7 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
case 0 :
case 1 : min = mid + 1
}
if cmp == 0 || min > max {
if cmp == 0 || min >= max {
break
}
}

View File

@ -48,13 +48,20 @@ func New(value interface{}, safe...bool) *Json {
vc : false ,
}
default:
// 这里效率会比较低
b, _ := Encode(value)
v, _ := Decode(b)
j = &Json{
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false,
v := (interface{})(nil)
if v = gconv.Map(value); v != nil {
j = &Json {
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false,
}
} else {
v = gconv.Interfaces(value)
j = &Json {
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false,
}
}
}
j.mu = rwmutex.New(safe...)

View File

@ -9,12 +9,12 @@ package ghttp
import (
"bytes"
"gitee.com/johng/gf/g/os/gfile"
"net/http"
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/g/encoding/gparser"
"strconv"
"fmt"
"gitee.com/johng/gf/g/encoding/gparser"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/util/gconv"
"net/http"
"strconv"
)
// 服务端请求返回对象。
@ -159,9 +159,7 @@ func (r *Response) WriteStatus(status int, content...string) {
// 静态文件处理
func (r *Response) ServeFile(path string) {
// 首先判断是否给定的path已经是一个绝对路径
if !gfile.Exists(path) {
path, _ = r.Server.paths.Search(path)
}
path = gfile.RealPath(path)
if path == "" {
r.WriteStatus(http.StatusNotFound)
return
@ -172,9 +170,7 @@ func (r *Response) ServeFile(path string) {
// 静态文件下载处理
func (r *Response) ServeFileDownload(path string, name...string) {
// 首先判断是否给定的path已经是一个绝对路径
if !gfile.Exists(path) {
path, _ = r.Server.paths.Search(path)
}
path = gfile.RealPath(path)
if path == "" {
r.WriteStatus(http.StatusNotFound)
return

View File

@ -18,7 +18,6 @@ import (
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gproc"
"gitee.com/johng/gf/g/os/gspath"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/g/util/gregex"
@ -215,26 +214,10 @@ func (s *Server) Start() error {
// 仅在开启静态文件服务的时候有效
if s.config.FileServerEnabled {
// 如果设置了静态文件目录,那么优先按照静态文件目录进行检索,其次是当前可执行文件工作目录;
// 并且如果是开发环境默认也会添加main包的源码目录路径做为二级检索。
if s.config.ServerRoot != "" {
if rp, err := s.paths.Set(s.config.ServerRoot); err != nil {
glog.Error("ghttp.SetServerRoot failed:", err.Error())
return err
} else {
glog.Debug("ghttp.SetServerRoot:", rp)
}
}
// 添加当前可执行文件运行目录到搜索目录
if gfile.SelfDir() != gfile.TempDir() {
s.paths.Add(gfile.SelfDir())
}
// (开发环境)添加main源码包到搜索目录
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
s.paths.Add(p)
s.AddSearchPath(p)
}
// (安全控制)不能访问当前执行文件
s.paths.Remove(gfile.SelfPath())
}
// 底层http server配置
@ -251,18 +234,6 @@ func (s *Server) Start() error {
}
}
// 配置相关相对路径处理
if s.config.HTTPSCertPath != "" && !gfile.Exists(s.config.HTTPSCertPath) {
if t, _ := s.paths.Search(s.config.HTTPSCertPath); t != "" {
s.config.HTTPSCertPath = t
}
}
if s.config.HTTPSKeyPath != "" && !gfile.Exists(s.config.HTTPSKeyPath) {
if t, _ := s.paths.Search(s.config.HTTPSKeyPath); t != "" {
s.config.HTTPSKeyPath = t
}
}
// gzip压缩文件类型
//if s.config.GzipContentTypes != nil {
// for _, v := range s.config.GzipContentTypes {

View File

@ -7,6 +7,8 @@
package ghttp
import (
"fmt"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/glog"
"net/http"
"strconv"
@ -49,7 +51,7 @@ type ServerConfig struct {
ServerAgent string // Server Agent
ServerRoot string // 服务器服务的本地目录根路径(检索优先级比StaticPaths低)
SearchPaths []string // 静态文件搜索目录(包含ServerRoot按照优先级进行排序)
StaticPaths []map[string]string // 静态文件目录映射(按照优先级进行排序)
StaticPaths []staticPathItem // 静态文件目录映射(按照优先级进行排序)
FileServerEnabled bool // 是否允许静态文件服务(总开关,默认开启)
// COOKIE
@ -94,7 +96,7 @@ var defaultServerConfig = ServerConfig {
IndexFolder : false,
ServerAgent : "gf",
ServerRoot : "",
StaticPaths : make([]map[string]string, 0),
StaticPaths : make([]staticPathItem, 0),
FileServerEnabled : true,
CookieMaxAge : gDEFAULT_COOKIE_MAX_AGE,
@ -192,8 +194,16 @@ func (s *Server)EnableHTTPS(certFile, keyFile string) {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
s.config.HTTPSCertPath = certFile
s.config.HTTPSKeyPath = keyFile
certFileRealPath := gfile.RealPath(certFile)
if certFileRealPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: certFile "%s" does not exist`, certFile))
}
keyFileRealPath := gfile.RealPath(keyFile)
if keyFileRealPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] EnableHTTPS failed: keyFile "%s" does not exist`, keyFile))
}
s.config.HTTPSCertPath = certFileRealPath
s.config.HTTPSKeyPath = keyFileRealPath
}
// 设置http server参数 - ReadTimeout

View File

@ -9,11 +9,20 @@
package ghttp
import (
"fmt"
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/util/gconv"
"strings"
)
// 静态文件目录映射关系对象
type staticPathItem struct {
prefix string // 映射的URI前缀
path string // 静态文件目录绝对路径
}
// 设置http server参数 - IndexFiles默认展示文件index.html, index.htm
func (s *Server)SetIndexFiles(index []string) {
if s.Status() == SERVER_STATUS_RUNNING {
@ -50,30 +59,69 @@ func (s *Server)SetServerRoot(root string) {
// RealPath的作用除了校验地址正确性以外还转换分隔符号为当前系统正确的文件分隔符号
path := gfile.RealPath(root)
if path == "" {
glog.Error("invalid root path \"" + root + "\"")
glog.Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: path "%s" does not exist`, root))
}
s.config.ServerRoot = strings.TrimRight(path, gfile.Separator)
s.config.SearchPaths = []string{strings.TrimRight(path, gfile.Separator)}
}
// 添加静态文件搜索目录,必须给定目录的绝对路径
func (s *Server) AddSearchPath(path string) error {
if rp, err := s.paths.Add(path); err != nil {
glog.Error("[ghttp] AddSearchPath failed:", err.Error())
return err
} else {
glog.Debug("[ghttp] AddSearchPath:", rp)
func (s *Server) AddSearchPath(path string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
return nil
// RealPath的作用除了校验地址正确性以外还转换分隔符号为当前系统正确的文件分隔符号
realPath := gfile.RealPath(path)
if realPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: path "%s" does not exist`, path))
}
s.config.SearchPaths = append(s.config.SearchPaths, realPath)
}
// 添加URI与静态目录的映射
func (s *Server) AddStaticPath(prefix string, path string) error {
if rp, err := s.paths.Add(path); err != nil {
glog.Error("[ghttp] AddSearchPath failed:", err.Error())
return err
} else {
glog.Debug("[ghttp] AddSearchPath:", rp)
func (s *Server) AddStaticPath(prefix string, path string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
// RealPath的作用除了校验地址正确性以外还转换分隔符号为当前系统正确的文件分隔符号
realPath := gfile.RealPath(path)
if realPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] AddStaticPath failed: path "%s" does not exist`, path))
}
addItem := staticPathItem {
prefix : prefix,
path : realPath,
}
if len(s.config.StaticPaths) > 0 {
// 先添加item
s.config.StaticPaths = append(s.config.StaticPaths, addItem)
// 按照prefix从长到短进行排序
array := garray.NewSortedArray(0, func(v1, v2 interface{}) int {
s1 := gconv.String(v1)
s2 := gconv.String(v2)
r := len(s2) - len(s1)
if r == 0 {
r = strings.Compare(s1, s2)
}
return r
}, false)
for _, v := range s.config.StaticPaths {
array.Add(v.prefix)
}
// 按照重新排序的顺序重新添加item
paths := make([]staticPathItem, 0)
for _, v := range array.Slice() {
for _, item := range s.config.StaticPaths {
if strings.EqualFold(gconv.String(v), item.prefix) {
paths = append(paths, item)
break
}
}
}
s.config.StaticPaths = paths
} else {
s.config.StaticPaths = []staticPathItem { addItem }
}
return nil
}

View File

@ -10,11 +10,13 @@ package ghttp
import (
"fmt"
"gitee.com/johng/gf/g/encoding/ghtml"
"gitee.com/johng/gf/g/os/gspath"
"gitee.com/johng/gf/g/os/gtime"
"net/http"
"os"
"reflect"
"sort"
"strings"
)
// 默认HTTP Server处理入口http包底层默认使用了gorutine异步处理请求所以这里不再异步执行
@ -62,7 +64,7 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
isStaticDir := false
// 优先执行静态文件检索(检测是否存在对应的静态文件包括index files处理)
if s.config.FileServerEnabled {
staticFile, isStaticDir = s.paths.Search(r.URL.Path, s.config.IndexFiles...)
staticFile, isStaticDir = s.searchStaticFile(r.URL.Path)
if staticFile != "" {
request.isFileRequest = true
}
@ -125,6 +127,31 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
// 查找静态文件的绝对路径
func (s *Server) searchStaticFile(uri string) (filePath string, isDir bool) {
// 优先查找URI映射
if len(s.config.StaticPaths) > 0 {
for _, item := range s.config.StaticPaths {
if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0 : len(item.prefix)]) {
// 防止类似 /static/style 映射到 /static/style.css 的情况
if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
continue
}
return gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
}
}
}
// 其次查找root和search path
if len(s.config.SearchPaths) > 0 {
for _, path := range s.config.SearchPaths {
if filePath, isDir = gspath.Search(path, uri, s.config.IndexFiles...); filePath != "" {
return filePath, isDir
}
}
}
return "", false
}
// 初始化控制器
func (s *Server) callServeHandler(h *handlerItem, r *Request) {
defer func() {

View File

@ -12,10 +12,12 @@ import (
"bytes"
"errors"
"fmt"
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/container/gvar"
"gitee.com/johng/gf/g/encoding/gjson"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/gfsnotify"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gspath"
@ -28,7 +30,7 @@ const (
// 配置管理对象
type Config struct {
name *gtype.String // 默认配置文件名称
paths *gspath.SPath // 搜索目录路径
paths *garray.StringArray // 搜索目录路径
jsons *gmap.StringInterfaceMap // 配置文件对象
vc *gtype.Bool // 层级检索是否执行分隔符冲突检测(默认为false检测会比较影响检索效率)
}
@ -41,7 +43,7 @@ func New(path string, file...string) *Config {
}
c := &Config {
name : gtype.NewString(name),
paths : gspath.New(),
paths : garray.NewStringArray(0, 1),
jsons : gmap.NewStringInterfaceMap(),
vc : gtype.NewBool(),
}
@ -52,18 +54,26 @@ func New(path string, file...string) *Config {
}
// 判断从哪个配置文件中获取内容,返回配置文件的绝对路径
func (c *Config) filePath(file...string) string {
func (c *Config) filePath(file...string) (path string) {
name := c.name.Val()
if len(file) > 0 {
name = file[0]
}
path, _ := c.paths.Search(name)
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ = gspath.Search(v, name); path != "" {
break
}
}
})
if path == "" {
buffer := bytes.NewBuffer(nil)
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name))
for k, v := range c.paths.Paths() {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
c.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
glog.Error(buffer.String())
}
return path
@ -71,13 +81,16 @@ func (c *Config) filePath(file...string) string {
// 设置配置管理器的配置文件存放目录绝对路径
func (c *Config) SetPath(path string) error {
if rp, err := c.paths.Set(path); err != nil {
glog.Error("[gcfg] SetPath failed:", err.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()))
return err
} else {
c.jsons.Clear()
glog.Debug("[gcfg] SetPath:", rp)
}
c.jsons.Clear()
c.paths.Clear()
c.paths.Append(realPath)
glog.Debug("[gcfg] SetPath:", realPath)
return nil
}
@ -90,12 +103,14 @@ func (c *Config) SetViolenceCheck(check bool) {
// 添加配置管理器的配置文件搜索路径
func (c *Config) AddPath(path string) error {
if rp, err := c.paths.Add(path); err != nil {
glog.Debug("[gcfg] AddPath failed:", err.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()))
return err
} else {
glog.Debug("[gcfg] AddPath:", rp)
}
c.paths.Append(realPath)
glog.Debug("[gcfg] AddPath:", realPath)
return nil
}

View File

@ -33,14 +33,36 @@ type SPathCacheItem struct {
isDir bool // 是否目录
}
var (
// 单个目录路径对应的SPath对象指针用于路径检索对象复用
pathsMap = gmap.NewStringInterfaceMap()
)
// 创建一个搜索对象
func New () *SPath {
return &SPath {
paths : garray.NewStringArray(0, 2),
func New(path...string) *SPath {
sp := &SPath {
paths : garray.NewStringArray(0, 1),
cache : gmap.NewStringStringMap(),
}
if len(path) > 0 {
sp.Add(path[0])
}
return sp
}
// 创建/获取一个单例的搜索对象, root必须为目录的绝对路径
func Get(root string) *SPath {
return pathsMap.GetOrSetFuncLock(root, func() interface{} {
return New(root)
}).(*SPath)
}
// 检索root目录(必须为绝对路径)下面的name文件的绝对路径indexFiles用于指定当检索到的结果为目录时同时检索是否存在这些indexFiles文件
func Search(root string, name string, indexFiles...string) (filePath string, isDir bool) {
return Get(root).Search(name, indexFiles...)
}
// 设置搜索路径,只保留当前设置项,其他搜索路径被清空
func (sp *SPath) Set(path string) (realPath string, err error) {
realPath = gfile.RealPath(path)
@ -63,6 +85,7 @@ func (sp *SPath) Set(path string) (realPath string, err error) {
}
sp.paths.Clear()
sp.cache.Clear()
sp.paths.Append(realPath)
sp.updateCacheByPath(realPath)
sp.addMonitorByPath(realPath)

View File

@ -11,6 +11,7 @@ import (
"bytes"
"errors"
"fmt"
"gitee.com/johng/gf/g/container/garray"
"gitee.com/johng/gf/g/encoding/ghash"
"gitee.com/johng/gf/g/encoding/ghtml"
"gitee.com/johng/gf/g/encoding/gurl"
@ -29,7 +30,7 @@ import (
// 视图对象
type View struct {
mu sync.RWMutex
paths *gspath.SPath // 模板查找目录(绝对路径)
paths *garray.StringArray // 模板查找目录(绝对路径)
data map[string]interface{} // 模板变量
funcmap map[string]interface{} // FuncMap
delimiters []string // 模板变量分隔符号
@ -64,7 +65,7 @@ func ParseContent(content string, params Params) ([]byte, error) {
// 生成一个视图对象
func New(path...string) *View {
view := &View {
paths : gspath.New(),
paths : garray.NewStringArray(0, 1),
data : make(map[string]interface{}),
funcmap : make(map[string]interface{}),
delimiters : make([]string, 2),
@ -96,23 +97,28 @@ func New(path...string) *View {
// 设置模板目录绝对路径
func (view *View) SetPath(path string) error {
if rp, err := view.paths.Set(path); err != nil {
glog.Error("[gview] SetPath failed:", err.Error())
realPath := gfile.RealPath(path)
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gview] SetPath failed: %s`, err.Error()))
return err
} else {
glog.Debug("[gview] SetPath:", rp)
}
view.paths.Clear()
view.paths.Append(realPath)
glog.Debug("[gview] SetPath:", realPath)
return nil
}
// 添加模板目录搜索路径
func (view *View) AddPath(path string) error {
if rp, err := view.paths.Add(path); err != nil {
glog.Error("[gview] AddPath failed:", err.Error())
realPath := gfile.RealPath(path)
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gview] AddPath failed: %s`, err.Error()))
return err
} else {
glog.Debug("[gview] AddPath:", rp)
}
view.paths.Append(realPath)
glog.Debug("[gview] AddPath:", realPath)
return nil
}
@ -134,9 +140,24 @@ func (view *View) Assign(key string, value interface{}) {
// 解析模板,返回解析后的内容
func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
path, _ := view.paths.Search(file)
path := ""
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ = gspath.Search(v, file); path != "" {
break
}
}
})
if path == "" {
return nil, errors.New("tpl \"" + file + "\" not found")
buffer := bytes.NewBuffer(nil)
buffer.WriteString(fmt.Sprintf("[gview] cannot find template file \"%s\" in following paths:", file))
view.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
glog.Error(buffer.String())
return nil, errors.New(fmt.Sprintf(`tpl "%s" not found`, file))
}
content := gfcache.GetContents(path)
// 执行模板解析互斥锁主要是用于funcmap

108
g/util/gconv/gconv_map.go Normal file
View File

@ -0,0 +1,108 @@
// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
package gconv
import (
"reflect"
)
// 任意类型转换为 map[string]interface{} 类型,
// 如果给定的输入参数i不是map类型那么转换会失败返回nil.
func Map(i interface{}) map[string]interface{} {
if i == nil {
return nil
}
if r, ok := i.(map[string]interface{}); ok {
return r
} else {
// 仅对常见的几种map组合进行断言最后才会使用反射
m := make(map[string]interface{})
switch i.(type) {
case map[interface{}]interface{}:
for k, v := range i.(map[interface{}]interface{}) {
m[String(k)] = v
}
case map[interface{}]string:
for k, v := range i.(map[interface{}]string) {
m[String(k)] = v
}
case map[interface{}]int:
for k, v := range i.(map[interface{}]int) {
m[String(k)] = v
}
case map[interface{}]uint:
for k, v := range i.(map[interface{}]uint) {
m[String(k)] = v
}
case map[interface{}]float32:
for k, v := range i.(map[interface{}]float32) {
m[String(k)] = v
}
case map[interface{}]float64:
for k, v := range i.(map[interface{}]float64) {
m[String(k)] = v
}
case map[string]bool:
for k, v := range i.(map[string]bool) {
m[k] = v
}
case map[string]int:
for k, v := range i.(map[string]int) {
m[k] = v
}
case map[string]uint:
for k, v := range i.(map[string]uint) {
m[k] = v
}
case map[string]float32:
for k, v := range i.(map[string]float32) {
m[k] = v
}
case map[string]float64:
for k, v := range i.(map[string]float64) {
m[k] = v
}
case map[int]interface{}:
for k, v := range i.(map[int]interface{}) {
m[String(k)] = v
}
case map[int]string:
for k, v := range i.(map[int]string) {
m[String(k)] = v
}
case map[uint]string:
for k, v := range i.(map[uint]string) {
m[String(k)] = v
}
// 不是常见类型,则使用反射
default:
rv := reflect.ValueOf(i)
kind := rv.Kind()
// 如果是指针,那么需要转换到指针对应的数据项,以便识别真实的类型
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
if kind == reflect.Map {
ks := rv.MapKeys()
for _, k := range ks {
m[String(k.Interface())] = rv.MapIndex(k).Interface()
}
} else if kind == reflect.Struct {
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
m[rt.Field(i).Name] = rv.Field(i).Interface()
}
} else {
return nil
}
}
return m
}
}

View File

@ -6,7 +6,10 @@
package gconv
import "fmt"
import (
"fmt"
"reflect"
)
// 任意类型转换为[]int类型
func Ints(i interface{}) []int {
@ -296,6 +299,26 @@ func Interfaces(i interface{}) []interface{} {
for _, v := range i.([]float64) {
array = append(array, v)
}
// 不是常见类型,则使用反射
default:
rv := reflect.ValueOf(i)
kind := rv.Kind()
// 如果是指针,那么需要转换到指针对应的数据项,以便识别真实的类型
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Slice: fallthrough
case reflect.Array:
for i := 0; i < rv.Len(); i++ {
array = append(array, rv.Index(i).Interface())
}
case reflect.Struct:
for i := 0; i < rv.NumField(); i++ {
array = append(array, rv.Field(i).Interface())
}
}
}
if len(array) > 0 {
return array

View File

@ -205,10 +205,12 @@ func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, va
switch structFieldValue.Kind() {
case reflect.Struct:
Struct(value, structFieldValue)
case reflect.Slice:
case reflect.Slice: fallthrough
case reflect.Array:
a := reflect.Value{}
v := reflect.ValueOf(value)
if v.Kind() == reflect.Slice {
if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
a = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len())
for i := 0; i < v.Len(); i++ {
n := reflect.New(structFieldValue.Type().Elem()).Elem()

View File

@ -5,7 +5,7 @@
// You can obtain one at https://gitee.com/johng/gf.
// 单元测试
// go test *.go -bench=".*"
// go test *.go
package gvalid

View File

@ -2,11 +2,12 @@ package main
import "gitee.com/johng/gf/g"
// 静态文件服务器
// 静态文件服务器基本使用
func main() {
s := g.Server()
s.SetIndexFolder(true)
s.SetServerRoot("/Users/john/Temp")
s.AddSearchPath("/Users/john/Documents")
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,14 @@
package main
import "gitee.com/johng/gf/g"
// 静态文件服务器,支持自定义静态目录映射
func main() {
s := g.Server()
s.SetIndexFolder(true)
s.SetServerRoot("/Users/john/Temp")
s.AddSearchPath("/Users/john/Documents")
s.AddStaticPath("/my-doc", "/Users/john/Documents")
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,15 @@
package main
import "gitee.com/johng/gf/g"
// 静态文件服务器,支持自定义静态目录映射
func main() {
s := g.Server()
s.SetIndexFolder(true)
s.SetServerRoot("/Users/john/Temp")
s.AddSearchPath("/Users/john/Documents")
s.AddStaticPath("/my-doc", "/Users/john/Documents")
s.AddStaticPath("/my-doc/test", "/Users/john/Temp")
s.SetPort(8199)
s.Run()
}

View File

@ -11,6 +11,8 @@ func main() {
b, err := v.Parse("gview.tpl", map[string]interface{} {
"k" : "v",
})
fmt.Println(err)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}

View File

@ -10,10 +10,12 @@ import (
func main() {
v := g.View()
// 设置模板目录为当前main.go所在目录下的template目录
v.AddPath(gfile.MainPkgPath() + gfile.Separator + "template")
v.AddPath(gfile.MainPkgPath() + gfile.Separator + "template2")
b, err := v.Parse("index.html", map[string]interface{} {
"k" : "v",
})
fmt.Println(err)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}

View File

@ -1,9 +1,39 @@
package main
import (
"gitee.com/johng/gf/g"
"fmt"
"gitee.com/johng/gf/g/encoding/gparser"
)
func main() {
g.Database()
type DemoInfo struct {
Name string
Age string
}
//r.Response.Write("Hello World")
l := map[interface{}][]DemoInfo{}
el := [...]DemoInfo{
{Name:"Bala", Age:"15"},
{Name:"CeCe", Age:"18"},
{Name:"ChenLo", Age:"28"},
{Name:"Bii", Age:"22"},
{Name:"Ann", Age:"23"},
{Name:"Bmx", Age:"88"},
}
for _,v := range el{
l[string(v.Name[0])] = append(l[string(v.Name[0])],v)
}
//fmt.Println(l)
b, err := gparser.VarToJson(l)
fmt.Println(err)
fmt.Println(string(b))
}

View File

@ -0,0 +1,25 @@
package main
import (
"fmt"
"gitee.com/johng/gf/g/util/gconv"
)
// struct转map
func main() {
type User struct {
Uid int
Name string
}
// 对象
fmt.Println(gconv.Map(User{
Uid : 1,
Name : "john",
}))
// 指针
fmt.Println(gconv.Map(&User{
Uid : 1,
Name : "john",
}))
}

View File

@ -0,0 +1,24 @@
package main
import (
"fmt"
"gitee.com/johng/gf/g/util/gconv"
)
// struct转slice
func main() {
type User struct {
Uid int
Name string
}
// 对象
fmt.Println(gconv.Interfaces(User{
Uid : 1,
Name : "john",
}))
// 指针
fmt.Println(gconv.Interfaces(&User{
Uid : 1,
Name : "john",
}))
}