github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_server_service_object.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package ghttp 8 9 import ( 10 "fmt" 11 "reflect" 12 "strings" 13 14 "github.com/gogf/gf/os/gfile" 15 "github.com/gogf/gf/text/gregex" 16 "github.com/gogf/gf/text/gstr" 17 ) 18 19 // BindObject registers object to server routes with given pattern. 20 // 21 // The optional parameter <method> is used to specify the method to be registered, which 22 // supports multiple method names, multiple methods are separated by char ',', case sensitive. 23 // 24 // Note that the route method should be defined as ghttp.HandlerFunc. 25 func (s *Server) BindObject(pattern string, object interface{}, method ...string) { 26 bindMethod := "" 27 if len(method) > 0 { 28 bindMethod = method[0] 29 } 30 s.doBindObject(pattern, object, bindMethod, nil, "") 31 } 32 33 // BindObjectMethod registers specified method of object to server routes with given pattern. 34 // 35 // The optional parameter <method> is used to specify the method to be registered, which 36 // does not supports multiple method names but only one, case sensitive. 37 // 38 // Note that the route method should be defined as ghttp.HandlerFunc. 39 func (s *Server) BindObjectMethod(pattern string, object interface{}, method string) { 40 s.doBindObjectMethod(pattern, object, method, nil, "") 41 } 42 43 // BindObjectRest registers object in REST API style to server with specified pattern. 44 // Note that the route method should be defined as ghttp.HandlerFunc. 45 func (s *Server) BindObjectRest(pattern string, object interface{}) { 46 s.doBindObjectRest(pattern, object, nil, "") 47 } 48 49 func (s *Server) doBindObject(pattern string, object interface{}, method string, middleware []HandlerFunc, source string) { 50 // Convert input method to map for convenience and high performance searching purpose. 51 var methodMap map[string]bool 52 if len(method) > 0 { 53 methodMap = make(map[string]bool) 54 for _, v := range strings.Split(method, ",") { 55 methodMap[strings.TrimSpace(v)] = true 56 } 57 } 58 // If the `method` in `pattern` is `defaultMethod`, 59 // it removes for convenience for next statement control. 60 domain, method, path, err := s.parsePattern(pattern) 61 if err != nil { 62 s.Logger().Fatal(err) 63 return 64 } 65 if strings.EqualFold(method, defaultMethod) { 66 pattern = s.serveHandlerKey("", path, domain) 67 } 68 var ( 69 m = make(map[string]*handlerItem) 70 v = reflect.ValueOf(object) 71 t = v.Type() 72 initFunc func(*Request) 73 shutFunc func(*Request) 74 ) 75 // If given `object` is not pointer, it then creates a temporary one, 76 // of which the value is `v`. 77 if v.Kind() == reflect.Struct { 78 newValue := reflect.New(t) 79 newValue.Elem().Set(v) 80 v = newValue 81 t = v.Type() 82 } 83 structName := t.Elem().Name() 84 if v.MethodByName("Init").IsValid() { 85 initFunc = v.MethodByName("Init").Interface().(func(*Request)) 86 } 87 if v.MethodByName("Shut").IsValid() { 88 shutFunc = v.MethodByName("Shut").Interface().(func(*Request)) 89 } 90 pkgPath := t.Elem().PkgPath() 91 pkgName := gfile.Basename(pkgPath) 92 for i := 0; i < v.NumMethod(); i++ { 93 methodName := t.Method(i).Name 94 if methodMap != nil && !methodMap[methodName] { 95 continue 96 } 97 if methodName == "Init" || methodName == "Shut" { 98 continue 99 } 100 objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") 101 if objName[0] == '*' { 102 objName = fmt.Sprintf(`(%s)`, objName) 103 } 104 105 funcInfo, err := s.checkAndCreateFuncInfo(v.Method(i).Interface(), pkgPath, objName, methodName) 106 if err != nil { 107 s.Logger().Error(err.Error()) 108 return 109 } 110 111 key := s.mergeBuildInNameToPattern(pattern, structName, methodName, true) 112 m[key] = &handlerItem{ 113 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 114 Type: handlerTypeObject, 115 Info: funcInfo, 116 InitFunc: initFunc, 117 ShutFunc: shutFunc, 118 Middleware: middleware, 119 Source: source, 120 } 121 // If there's "Index" method, then an additional route is automatically added 122 // to match the main URI, for example: 123 // If pattern is "/user", then "/user" and "/user/index" are both automatically 124 // registered. 125 // 126 // Note that if there's built-in variables in pattern, this route will not be added 127 // automatically. 128 if strings.EqualFold(methodName, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) { 129 p := gstr.PosRI(key, "/index") 130 k := key[0:p] + key[p+6:] 131 if len(k) == 0 || k[0] == '@' { 132 k = "/" + k 133 } 134 m[k] = &handlerItem{ 135 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 136 Type: handlerTypeObject, 137 Info: funcInfo, 138 InitFunc: initFunc, 139 ShutFunc: shutFunc, 140 Middleware: middleware, 141 Source: source, 142 } 143 } 144 } 145 s.bindHandlerByMap(m) 146 } 147 148 func (s *Server) doBindObjectMethod(pattern string, object interface{}, method string, middleware []HandlerFunc, source string) { 149 var ( 150 m = make(map[string]*handlerItem) 151 v = reflect.ValueOf(object) 152 t = v.Type() 153 initFunc func(*Request) 154 shutFunc func(*Request) 155 ) 156 // If given `object` is not pointer, it then creates a temporary one, 157 // of which the value is `v`. 158 if v.Kind() == reflect.Struct { 159 newValue := reflect.New(t) 160 newValue.Elem().Set(v) 161 v = newValue 162 t = v.Type() 163 } 164 structName := t.Elem().Name() 165 methodName := strings.TrimSpace(method) 166 methodValue := v.MethodByName(methodName) 167 if !methodValue.IsValid() { 168 s.Logger().Fatal("invalid method name: " + methodName) 169 return 170 } 171 if v.MethodByName("Init").IsValid() { 172 initFunc = v.MethodByName("Init").Interface().(func(*Request)) 173 } 174 if v.MethodByName("Shut").IsValid() { 175 shutFunc = v.MethodByName("Shut").Interface().(func(*Request)) 176 } 177 pkgPath := t.Elem().PkgPath() 178 pkgName := gfile.Basename(pkgPath) 179 objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") 180 if objName[0] == '*' { 181 objName = fmt.Sprintf(`(%s)`, objName) 182 } 183 184 funcInfo, err := s.checkAndCreateFuncInfo(methodValue.Interface(), pkgPath, objName, methodName) 185 if err != nil { 186 s.Logger().Error(err.Error()) 187 return 188 } 189 190 key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false) 191 m[key] = &handlerItem{ 192 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 193 Type: handlerTypeObject, 194 Info: funcInfo, 195 InitFunc: initFunc, 196 ShutFunc: shutFunc, 197 Middleware: middleware, 198 Source: source, 199 } 200 201 s.bindHandlerByMap(m) 202 } 203 204 func (s *Server) doBindObjectRest(pattern string, object interface{}, middleware []HandlerFunc, source string) { 205 var ( 206 m = make(map[string]*handlerItem) 207 v = reflect.ValueOf(object) 208 t = v.Type() 209 initFunc func(*Request) 210 shutFunc func(*Request) 211 ) 212 // If given `object` is not pointer, it then creates a temporary one, 213 // of which the value is `v`. 214 if v.Kind() == reflect.Struct { 215 newValue := reflect.New(t) 216 newValue.Elem().Set(v) 217 v = newValue 218 t = v.Type() 219 } 220 structName := t.Elem().Name() 221 if v.MethodByName(methodNameInit).IsValid() { 222 initFunc = v.MethodByName(methodNameInit).Interface().(func(*Request)) 223 } 224 if v.MethodByName(methodNameShut).IsValid() { 225 shutFunc = v.MethodByName(methodNameShut).Interface().(func(*Request)) 226 } 227 pkgPath := t.Elem().PkgPath() 228 for i := 0; i < v.NumMethod(); i++ { 229 methodName := t.Method(i).Name 230 if _, ok := methodsMap[strings.ToUpper(methodName)]; !ok { 231 continue 232 } 233 pkgName := gfile.Basename(pkgPath) 234 objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "") 235 if objName[0] == '*' { 236 objName = fmt.Sprintf(`(%s)`, objName) 237 } 238 239 funcInfo, err := s.checkAndCreateFuncInfo(v.Method(i).Interface(), pkgPath, objName, methodName) 240 if err != nil { 241 s.Logger().Error(err.Error()) 242 return 243 } 244 245 key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false) 246 m[key] = &handlerItem{ 247 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 248 Type: handlerTypeObject, 249 Info: funcInfo, 250 InitFunc: initFunc, 251 ShutFunc: shutFunc, 252 Middleware: middleware, 253 Source: source, 254 } 255 } 256 s.bindHandlerByMap(m) 257 }