diff --git a/.example/other/test.go b/.example/other/test.go index d619cbe86..a7691fd1a 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -1,41 +1,12 @@ package main import ( - "github.com/gogf/gf/frame/g" - "github.com/gogf/gf/os/glog" + "fmt" + "regexp" ) -func AddAPKCmdTask11(assistantId int, cmd int32, cmdData []byte, FromClientId string, desc string, priority int, status int32, cmdkey string) (int64, error) { - //var res, err = g.DB("test").Insert("assistant_tasks", g.Map{ - // "assistant_id": assistantId, - // "cmd": cmd, - // "cmdData": cmdData, - // "status": status, - // "FromClientId": FromClientId, - // "desc": desc, - // "priority": priority, - // "cmdkey": cmdkey, - //}) - var res, err = g.DB("test").Table("assistant_tasks").Data(g.Map{ - "assistant_id": assistantId, - "cmd": cmd, - "cmdData": cmdData, - "status": status, - "FromClientId": FromClientId, - "desc": desc, - "priority": priority, - "cmdkey": cmdkey, - }).Insert() - - if err != nil { - glog.Error("插入手机任务队列报错", err.Error()) - return 0, err - } - taskId, err := res.LastInsertId() - return taskId, err -} - func main() { - g.DB().SetDebug(true) - AddAPKCmdTask11(1, 2058, []byte(""), "", "", 60, 0, "") + replaceCharReg, err := regexp.Compile(`[\-\.\_\s]+`) + fmt.Println(err) + fmt.Println(replaceCharReg.ReplaceAllString("s--s.s.a b", "")) } diff --git a/frame/g/g_object.go b/frame/g/g_object.go index 37d95232c..3908ea43b 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -22,7 +22,7 @@ import ( // Server returns an instance of http server with specified name. func Server(name ...interface{}) *ghttp.Server { - return ghttp.GetServer(name...) + return gins.Server(name...) } // TCPServer returns an instance of tcp server with specified name. diff --git a/frame/gins/gins.go b/frame/gins/gins.go index aa8808773..343057574 100644 --- a/frame/gins/gins.go +++ b/frame/gins/gins.go @@ -9,6 +9,8 @@ package gins import ( "fmt" + "github.com/gogf/gf/internal/intlog" + "github.com/gogf/gf/net/ghttp" "github.com/gogf/gf/os/gfile" @@ -28,6 +30,7 @@ import ( const ( gFRAME_CORE_COMPONENT_NAME_REDIS = "gf.core.component.redis" gFRAME_CORE_COMPONENT_NAME_LOGGER = "gf.core.component.logger" + gFRAME_CORE_COMPONENT_NAME_SERVER = "gf.core.component.server" gFRAME_CORE_COMPONENT_NAME_DATABASE = "gf.core.component.database" ) @@ -100,7 +103,6 @@ func I18n(name ...string) *gi18n.Manager { // Log returns an instance of glog.Logger. // The parameter is the name for the instance. func Log(name ...string) *glog.Logger { - config := Config() instanceName := glog.DEFAULT_NAME if len(name) > 0 && name[0] != "" { instanceName = name[0] @@ -108,9 +110,12 @@ func Log(name ...string) *glog.Logger { instanceKey := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_LOGGER, instanceName) return instances.GetOrSetFuncLock(instanceKey, func() interface{} { logger := glog.Instance(instanceName) - if m := config.GetMap("logging"); m != nil { - if err := logger.SetConfigWithMap(m); err != nil { - glog.Error(err) + // To avoid file no found error while it's not necessary. + if Config().FilePath() != "" { + if m := Config().GetMap("logging"); m != nil { + if err := logger.SetConfigWithMap(m); err != nil { + glog.Panic(err) + } } } return logger @@ -130,13 +135,13 @@ func Database(name ...string) gdb.DB { if gdb.GetConfig(group) != nil { db, err := gdb.Instance(group) if err != nil { - glog.Error(err) + glog.Panic(err) } return db } m := config.GetMap("database") if m == nil { - glog.Error(`database init failed: "database" node not found, is config file or configuration missing?`) + glog.Panic(`database init failed: "database" node not found, is config file or configuration missing?`) return nil } // Parse as map-slice. @@ -173,7 +178,7 @@ func Database(name ...string) gdb.DB { if db, err := gdb.New(name...); err == nil { return db } else { - glog.Error(err) + glog.Panic(err) } return nil }) @@ -191,7 +196,7 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode { node := &gdb.ConfigNode{} err := gconv.Struct(nodeMap, node) if err != nil { - glog.Error(err) + glog.Panic(err) } if value, ok := nodeMap["link"]; ok { node.LinkInfo = gconv.String(value) @@ -210,7 +215,7 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode { // Redis returns an instance of redis client with specified configuration group name. func Redis(name ...string) *gredis.Redis { config := Config() - group := "default" + group := gredis.DEFAULT_GROUP_NAME if len(name) > 0 && name[0] != "" { group = name[0] } @@ -225,16 +230,16 @@ func Redis(name ...string) *gredis.Redis { if v, ok := m[group]; ok { redisConfig, err := gredis.ConfigFromStr(gconv.String(v)) if err != nil { - glog.Error(err) + glog.Panic(err) return nil } addConfigMonitor(instanceKey, config) return gredis.New(redisConfig) } else { - glog.Errorf(`configuration for redis not found for group "%s"`, group) + glog.Panicf(`configuration for redis not found for group "%s"`, group) } } else { - glog.Errorf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath()) + glog.Panicf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath()) } return nil }) @@ -244,10 +249,30 @@ func Redis(name ...string) *gredis.Redis { return nil } +// Server returns an instance of http server with specified name. +func Server(name ...interface{}) *ghttp.Server { + instanceKey := fmt.Sprintf("%s.%v", gFRAME_CORE_COMPONENT_NAME_SERVER, name) + return instances.GetOrSetFuncLock(instanceKey, func() interface{} { + s := ghttp.GetServer(name...) + // To avoid file no found error while it's not necessary. + if Config().FilePath() != "" { + if m := Config().GetMap("server"); m != nil { + if err := s.SetConfigWithMap(m); err != nil { + panic(err) + } + } + } + return s + }).(*ghttp.Server) +} + func addConfigMonitor(key string, config *gcfg.Config) { if path := config.FilePath(); path != "" && gfile.Exists(path) { - gfsnotify.Add(path, func(event *gfsnotify.Event) { + _, err := gfsnotify.Add(path, func(event *gfsnotify.Event) { instances.Remove(key) }) + if err != nil { + intlog.Error(err) + } } } diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index 58c146ba2..de3e1ccc3 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -234,7 +234,9 @@ func (r *Response) ClearBuffer() { // Output outputs the buffer content to the client. func (r *Response) Output() { - r.Header().Set("Server", r.Server.config.ServerAgent) + if r.Server.config.ServerAgent != "" { + r.Header().Set("Server", r.Server.config.ServerAgent) + } //r.handleGzip() r.Writer.OutputBuffer() } diff --git a/net/ghttp/ghttp_response_view.go b/net/ghttp/ghttp_response_view.go index 77d6c3bf1..8b2cd42b1 100644 --- a/net/ghttp/ghttp_response_view.go +++ b/net/ghttp/ghttp_response_view.go @@ -8,7 +8,7 @@ package ghttp import ( - "github.com/gogf/gf/frame/gins" + "github.com/gogf/gf/os/gcfg" "github.com/gogf/gf/os/gview" "github.com/gogf/gf/util/gmode" ) @@ -57,14 +57,14 @@ func (r *Response) ParseTplContent(content string, params ...gview.Params) (stri // 内置变量/对象 func (r *Response) buildInVars(params ...map[string]interface{}) map[string]interface{} { - vars := map[string]interface{}(nil) + var vars map[string]interface{} if len(params) > 0 && params[0] != nil { vars = params[0] } else { vars = make(map[string]interface{}) } // 当配置文件不存在时就不赋值该模板变量,不然会报错 - if c := gins.Config(); c.FilePath() != "" { + if c := gcfg.Instance(); c.FilePath() != "" { vars["Config"] = c.GetMap(".") } vars["Get"] = r.Request.GetQueryMap() diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 5d226a1f3..9097ddc23 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -227,7 +227,9 @@ func GetServer(name ...interface{}) *Server { logger: glog.New(), } // 初始化时使用默认配置 - s.SetConfig(c) + if err := s.SetConfig(c); err != nil { + panic(err) + } // 记录到全局ServerMap中 serverMapping.Set(serverName, s) return s @@ -368,7 +370,7 @@ func (s *Server) GetRouteMap() string { m[item.domain].Add(item) } } - itemFunc := s.config.Addr + itemFunc := s.config.Address if s.config.HTTPSAddr != "" { if len(itemFunc) > 0 { itemFunc += "," @@ -424,9 +426,9 @@ func (s *Server) startServer(fdMap listenerFdMap) { // HTTPS // ================ if len(s.config.HTTPSAddr) == 0 { - if len(s.config.Addr) > 0 { - s.config.HTTPSAddr = s.config.Addr - s.config.Addr = "" + if len(s.config.Address) > 0 { + s.config.HTTPSAddr = s.config.Address + s.config.Address = "" } else { s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR } @@ -464,14 +466,14 @@ func (s *Server) startServer(fdMap listenerFdMap) { // HTTP // ================ // 当HTTPS服务未启用时,默认HTTP地址才会生效 - if !httpsEnabled && len(s.config.Addr) == 0 { - s.config.Addr = gDEFAULT_HTTP_ADDR + if !httpsEnabled && len(s.config.Address) == 0 { + s.config.Address = gDEFAULT_HTTP_ADDR } var array []string if v, ok := fdMap["http"]; ok && len(v) > 0 { array = strings.Split(v, ",") } else { - array = strings.Split(s.config.Addr, ",") + array = strings.Split(s.config.Address, ",") } for _, v := range array { if len(v) == 0 { diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index a7b390674..8cae3649e 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -37,7 +37,7 @@ type LogHandler func(r *Request, err ...error) // HTTP Server 设置结构体,静态配置 type ServerConfig struct { - Addr string // 监听IP和端口,监听本地所有IP使用":端口"(支持多个地址,使用","号分隔) + Address string // Server listening address like ":port", multiple addresses separated using ',' HTTPSAddr string // HTTPS服务监听地址(支持多个地址,使用","号分隔) HTTPSCertPath string // HTTPS证书文件路径 HTTPSKeyPath string // HTTPS签名文件路径 @@ -81,7 +81,7 @@ type ServerConfig struct { // 默认HTTP Server配置 var defaultServerConfig = ServerConfig{ - Addr: "", + Address: "", HTTPSAddr: "", Handler: nil, ReadTimeout: 60 * time.Second, @@ -116,12 +116,12 @@ func Config() ServerConfig { } // 通过Map创建Config配置对象,Map没有传递的属性将会使用模块的默认值 -func ConfigFromMap(m map[string]interface{}) ServerConfig { +func ConfigFromMap(m map[string]interface{}) (ServerConfig, error) { config := defaultServerConfig if err := gconv.Struct(m, &config); err != nil { - panic(err) + return config, err } - return config + return config, nil } // http server setting设置。 @@ -140,24 +140,28 @@ func (s *Server) SetConfig(c ServerConfig) error { // 通过map设置http server setting。 // 注意使用该方法进行http server配置时,需要配置所有的配置项,否则没有配置的属性将会默认变量为空 -func (s *Server) SetConfigWithMap(m map[string]interface{}) { - s.SetConfig(ConfigFromMap(m)) +func (s *Server) SetConfigWithMap(m map[string]interface{}) error { + config, err := ConfigFromMap(m) + if err != nil { + return err + } + return s.SetConfig(config) } // 设置http server参数 - Addr func (s *Server) SetAddr(address string) { - s.config.Addr = address + s.config.Address = address } // 设置http server参数 - Port func (s *Server) SetPort(port ...int) { if len(port) > 0 { - s.config.Addr = "" + s.config.Address = "" for _, v := range port { - if len(s.config.Addr) > 0 { - s.config.Addr += "," + if len(s.config.Address) > 0 { + s.config.Address += "," } - s.config.Addr += ":" + strconv.Itoa(v) + s.config.Address += ":" + strconv.Itoa(v) } } } diff --git a/net/ghttp/ghttp_unit_config_test.go b/net/ghttp/ghttp_unit_config_test.go index 63be0277a..4bf2173a5 100644 --- a/net/ghttp/ghttp_unit_config_test.go +++ b/net/ghttp/ghttp_unit_config_test.go @@ -21,19 +21,35 @@ import ( func Test_ConfigFromMap(t *testing.T) { gtest.Case(t, func() { m := g.Map{ - "addr": ":8199", + "address": ":8199", "readTimeout": "60s", "indexFiles": g.Slice{"index.php", "main.php"}, "errorLogEnabled": true, "cookieMaxAge": "1y", } - config := ghttp.ConfigFromMap(m) + config, err := ghttp.ConfigFromMap(m) + gtest.Assert(err, nil) d1, _ := time.ParseDuration(gconv.String(m["readTimeout"])) d2, _ := time.ParseDuration(gconv.String(m["cookieMaxAge"])) - gtest.Assert(config.Addr, m["addr"]) + gtest.Assert(config.Address, m["address"]) gtest.Assert(config.ReadTimeout, d1) gtest.Assert(config.CookieMaxAge, d2) gtest.Assert(config.IndexFiles, m["indexFiles"]) gtest.Assert(config.ErrorLogEnabled, m["errorLogEnabled"]) }) } + +func Test_SetConfigWithMap(t *testing.T) { + gtest.Case(t, func() { + m := g.Map{ + "address": ":8199", + "readTimeout": "60s", + "indexFiles": g.Slice{"index.php", "main.php"}, + "errorLogEnabled": true, + "cookieMaxAge": "1y", + } + s := g.Server() + err := s.SetConfigWithMap(m) + gtest.Assert(err, nil) + }) +} diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index cc50653f5..07319b729 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -10,12 +10,19 @@ import ( "errors" "fmt" "reflect" + "regexp" "strings" "github.com/gogf/gf/internal/structs" "github.com/gogf/gf/internal/utilstr" ) +var ( + // replaceCharReg is the regular expression object for replacing chars + // in map keys and attribute names. + replaceCharReg, _ = regexp.Compile(`[\-\.\_\s]+`) +) + // Struct maps the params key-value pairs to the corresponding struct object's properties. // The third parameter is unnecessary, indicating the mapping rules between the custom key name // and the attribute name(case sensitive). @@ -35,6 +42,7 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin if pointer == nil { return errors.New("object pointer cannot be nil") } + // paramsMap is the map[string]interface{} type variable for params. paramsMap := Map(params) if paramsMap == nil { return fmt.Errorf("invalid params: %v", params) @@ -53,7 +61,7 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin } elem = rv.Elem() } - // Auto create struct object. + // It automatically creates struct object if necessary. // For example, if is **User, then is *User, which is a pointer to User. if elem.Type().Kind() == reflect.Ptr && (!elem.IsValid() || elem.IsNil()) { e := reflect.New(elem.Type().Elem()).Elem() @@ -61,13 +69,14 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin elem = e } // It only performs one converting to the same attribute. - // doneMap is used to check repeated converting. - doneMap := make(map[string]bool) + // doneMap is used to check repeated converting, its key is the attribute name of the struct. + doneMap := make(map[string]struct{}) // It first checks the passed mapping rules. if len(mapping) > 0 && len(mapping[0]) > 0 { for mapK, mapV := range mapping[0] { + // mapV is the the attribute name of the struct. if v, ok := paramsMap[mapK]; ok { - doneMap[mapV] = true + doneMap[mapV] = struct{}{} if err := bindVarToStructAttr(elem, mapV, v); err != nil { return err } @@ -77,65 +86,67 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin // It secondly checks the tags of attributes. tagMap := structs.TagMapName(pointer, structTagPriority, true) for tagK, tagV := range tagMap { + // tagV is the the attribute name of the struct. if _, ok := doneMap[tagV]; ok { continue } if v, ok := paramsMap[tagK]; ok { - doneMap[tagV] = true + doneMap[tagV] = struct{}{} if err := bindVarToStructAttr(elem, tagV, v); err != nil { return err } } } // It finally do the converting with default rules. - attrMap := make(map[string]struct{}) + // The key of the map is the attribute name of the struct, + // and the value is its replaced name for later comparison to improve performance. + attrMap := make(map[string]string) elemType := elem.Type() + tempName := "" for i := 0; i < elem.NumField(); i++ { // Only do converting to public attributes. if !utilstr.IsLetterUpper(elemType.Field(i).Name[0]) { continue } - attrMap[elemType.Field(i).Name] = struct{}{} + tempName = elemType.Field(i).Name + attrMap[tempName] = replaceCharReg.ReplaceAllString(tempName, "") } if len(attrMap) == 0 { return nil } + var attrName, checkName string for mapK, mapV := range paramsMap { - name := "" - for _, checkName := range []string{ - utilstr.UcFirst(mapK), - utilstr.ReplaceByMap(mapK, map[string]string{ - "_": "", - "-": "", - " ": "", - })} { - if _, ok := doneMap[checkName]; ok { - continue - } - if _, ok := tagMap[checkName]; ok { - continue - } - // Loop to find the matched attribute name. - for value, _ := range attrMap { - if strings.EqualFold(checkName, value) { - name = value - break - } - if strings.EqualFold(checkName, strings.Replace(value, "_", "", -1)) { - name = value - break - } - } - doneMap[checkName] = true - if name != "" { + attrName = "" + checkName = replaceCharReg.ReplaceAllString(mapK, "") + // Loop to find the matched attribute name with or without + // string cases and chars like '-'/'_'/'.'/' '. + for attrK, attrV := range attrMap { + // Eg: + // UserName eq user_name + // User-Name eq username + // username eq userName + // etc. + if strings.EqualFold(checkName, attrV) { + attrName = attrK break } } + // If the attribute name is already checked converting, then skip it. + if attrName != "" { + if _, ok := doneMap[attrName]; ok { + continue + } + if _, ok := tagMap[attrName]; ok { + continue + } + } // No matching, give up this attribute converting. - if name == "" { + if attrName == "" { continue } - if err := bindVarToStructAttr(elem, name, mapV); err != nil { + // Mark it done. + doneMap[attrName] = struct{}{} + if err := bindVarToStructAttr(elem, attrName, mapV); err != nil { return err } } diff --git a/util/gutil/gutil_map.go b/util/gutil/gutil_map.go index 2dcd162b1..d49c9afab 100644 --- a/util/gutil/gutil_map.go +++ b/util/gutil/gutil_map.go @@ -7,11 +7,42 @@ // Package gutil provides utility functions. package gutil -// CopyMap does memory from map to . -func CopyMap(data map[string]interface{}) (copy map[string]interface{}) { +import ( + "regexp" + "strings" +) + +var ( + // replaceCharReg is the regular expression object for replacing chars in map keys. + replaceCharReg, _ = regexp.Compile(`[\-\.\_\s]+`) +) + +// MapCopy does memory from map to . +func MapCopy(data map[string]interface{}) (copy map[string]interface{}) { copy = make(map[string]interface{}, len(data)) for k, v := range data { copy[k] = v } return } + +// MapValueForPossibleKey tries to find the possible value for given key with or without +// cases or chars '-'/'_'/'.'/' '. +// +// Note that this function might be of low performance. +func MapPossibleValueForKey(data map[string]interface{}, key string) interface{} { + if v, ok := data[key]; ok { + return v + } + replacedKey := replaceCharReg.ReplaceAllString(key, "") + if v, ok := data[replacedKey]; ok { + return v + } + // Loop for check. + for k, v := range data { + if strings.EqualFold(replaceCharReg.ReplaceAllString(k, ""), replacedKey) { + return v + } + } + return nil +}