github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/gf. 6 7 package ghttp 8 9 import ( 10 "context" 11 "fmt" 12 "reflect" 13 "strings" 14 15 "github.com/wangyougui/gf/v2/os/gfile" 16 "github.com/wangyougui/gf/v2/text/gregex" 17 "github.com/wangyougui/gf/v2/text/gstr" 18 ) 19 20 // BindObject registers object to server routes with a given pattern. 21 // 22 // The optional parameter `method` is used to specify the method to be registered, which 23 // supports multiple method names; multiple methods are separated by char ',', case-sensitive. 24 func (s *Server) BindObject(pattern string, object interface{}, method ...string) { 25 var bindMethod = "" 26 if len(method) > 0 { 27 bindMethod = method[0] 28 } 29 s.doBindObject(context.TODO(), doBindObjectInput{ 30 Prefix: "", 31 Pattern: pattern, 32 Object: object, 33 Method: bindMethod, 34 Middleware: nil, 35 Source: "", 36 }) 37 } 38 39 // BindObjectMethod registers specified method of the object to server routes with a given pattern. 40 // 41 // The optional parameter `method` is used to specify the method to be registered, which 42 // does not support multiple method names but only one, case-sensitive. 43 func (s *Server) BindObjectMethod(pattern string, object interface{}, method string) { 44 s.doBindObjectMethod(context.TODO(), doBindObjectMethodInput{ 45 Prefix: "", 46 Pattern: pattern, 47 Object: object, 48 Method: method, 49 Middleware: nil, 50 Source: "", 51 }) 52 } 53 54 // BindObjectRest registers object in REST API styles to server with a specified pattern. 55 func (s *Server) BindObjectRest(pattern string, object interface{}) { 56 s.doBindObjectRest(context.TODO(), doBindObjectInput{ 57 Prefix: "", 58 Pattern: pattern, 59 Object: object, 60 Method: "", 61 Middleware: nil, 62 Source: "", 63 }) 64 } 65 66 type doBindObjectInput struct { 67 Prefix string 68 Pattern string 69 Object interface{} 70 Method string 71 Middleware []HandlerFunc 72 Source string 73 } 74 75 func (s *Server) doBindObject(ctx context.Context, in doBindObjectInput) { 76 // Convert input method to map for convenience and high performance searching purpose. 77 var methodMap map[string]bool 78 if len(in.Method) > 0 { 79 methodMap = make(map[string]bool) 80 for _, v := range strings.Split(in.Method, ",") { 81 methodMap[strings.TrimSpace(v)] = true 82 } 83 } 84 // If the `method` in `pattern` is `defaultMethod`, 85 // it removes for convenience for next statement control. 86 domain, method, path, err := s.parsePattern(in.Pattern) 87 if err != nil { 88 s.Logger().Fatalf(ctx, `%+v`, err) 89 return 90 } 91 if gstr.Equal(method, defaultMethod) { 92 in.Pattern = s.serveHandlerKey("", path, domain) 93 } 94 var ( 95 handlerMap = make(map[string]*HandlerItem) 96 reflectValue = reflect.ValueOf(in.Object) 97 reflectType = reflectValue.Type() 98 initFunc func(*Request) 99 shutFunc func(*Request) 100 ) 101 // If given `object` is not pointer, it then creates a temporary one, 102 // of which the value is `reflectValue`. 103 // It then can retrieve all the methods both of struct/*struct. 104 if reflectValue.Kind() == reflect.Struct { 105 newValue := reflect.New(reflectType) 106 newValue.Elem().Set(reflectValue) 107 reflectValue = newValue 108 reflectType = reflectValue.Type() 109 } 110 structName := reflectType.Elem().Name() 111 if reflectValue.MethodByName(specialMethodNameInit).IsValid() { 112 initFunc = reflectValue.MethodByName(specialMethodNameInit).Interface().(func(*Request)) 113 } 114 if reflectValue.MethodByName(specialMethodNameShut).IsValid() { 115 shutFunc = reflectValue.MethodByName(specialMethodNameShut).Interface().(func(*Request)) 116 } 117 pkgPath := reflectType.Elem().PkgPath() 118 pkgName := gfile.Basename(pkgPath) 119 for i := 0; i < reflectValue.NumMethod(); i++ { 120 methodName := reflectType.Method(i).Name 121 if methodMap != nil && !methodMap[methodName] { 122 continue 123 } 124 if methodName == specialMethodNameInit || methodName == specialMethodNameShut { 125 continue 126 } 127 objName := gstr.Replace(reflectType.String(), fmt.Sprintf(`%s.`, pkgName), "") 128 if objName[0] == '*' { 129 objName = fmt.Sprintf(`(%s)`, objName) 130 } 131 132 funcInfo, err := s.checkAndCreateFuncInfo(reflectValue.Method(i).Interface(), pkgPath, objName, methodName) 133 if err != nil { 134 s.Logger().Fatalf(ctx, `%+v`, err) 135 } 136 137 key := s.mergeBuildInNameToPattern(in.Pattern, structName, methodName, true) 138 handlerMap[key] = &HandlerItem{ 139 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 140 Type: HandlerTypeObject, 141 Info: funcInfo, 142 InitFunc: initFunc, 143 ShutFunc: shutFunc, 144 Middleware: in.Middleware, 145 Source: in.Source, 146 } 147 // If there's "Index" method, then an additional route is automatically added 148 // to match the main URI, for example: 149 // If pattern is "/user", then "/user" and "/user/index" are both automatically 150 // registered. 151 // 152 // Note that if there's built-in variables in pattern, this route will not be added 153 // automatically. 154 var ( 155 isIndexMethod = strings.EqualFold(methodName, specialMethodNameIndex) 156 hasBuildInVar = gregex.IsMatchString(`\{\.\w+\}`, in.Pattern) 157 hashTwoParams = funcInfo.Type.NumIn() == 2 158 ) 159 if isIndexMethod && !hasBuildInVar && !hashTwoParams { 160 p := gstr.PosRI(key, "/index") 161 k := key[0:p] + key[p+6:] 162 if len(k) == 0 || k[0] == '@' { 163 k = "/" + k 164 } 165 handlerMap[k] = &HandlerItem{ 166 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 167 Type: HandlerTypeObject, 168 Info: funcInfo, 169 InitFunc: initFunc, 170 ShutFunc: shutFunc, 171 Middleware: in.Middleware, 172 Source: in.Source, 173 } 174 } 175 } 176 s.bindHandlerByMap(ctx, in.Prefix, handlerMap) 177 } 178 179 type doBindObjectMethodInput struct { 180 Prefix string 181 Pattern string 182 Object interface{} 183 Method string 184 Middleware []HandlerFunc 185 Source string 186 } 187 188 func (s *Server) doBindObjectMethod(ctx context.Context, in doBindObjectMethodInput) { 189 var ( 190 handlerMap = make(map[string]*HandlerItem) 191 reflectValue = reflect.ValueOf(in.Object) 192 reflectType = reflectValue.Type() 193 initFunc func(*Request) 194 shutFunc func(*Request) 195 ) 196 // If given `object` is not pointer, it then creates a temporary one, 197 // of which the value is `v`. 198 if reflectValue.Kind() == reflect.Struct { 199 newValue := reflect.New(reflectType) 200 newValue.Elem().Set(reflectValue) 201 reflectValue = newValue 202 reflectType = reflectValue.Type() 203 } 204 var ( 205 structName = reflectType.Elem().Name() 206 methodName = strings.TrimSpace(in.Method) 207 methodValue = reflectValue.MethodByName(methodName) 208 ) 209 if !methodValue.IsValid() { 210 s.Logger().Fatalf(ctx, "invalid method name: %s", methodName) 211 return 212 } 213 if reflectValue.MethodByName(specialMethodNameInit).IsValid() { 214 initFunc = reflectValue.MethodByName(specialMethodNameInit).Interface().(func(*Request)) 215 } 216 if reflectValue.MethodByName(specialMethodNameShut).IsValid() { 217 shutFunc = reflectValue.MethodByName(specialMethodNameShut).Interface().(func(*Request)) 218 } 219 var ( 220 pkgPath = reflectType.Elem().PkgPath() 221 pkgName = gfile.Basename(pkgPath) 222 objName = gstr.Replace(reflectType.String(), fmt.Sprintf(`%s.`, pkgName), "") 223 ) 224 if objName[0] == '*' { 225 objName = fmt.Sprintf(`(%s)`, objName) 226 } 227 228 funcInfo, err := s.checkAndCreateFuncInfo(methodValue.Interface(), pkgPath, objName, methodName) 229 if err != nil { 230 s.Logger().Fatalf(ctx, `%+v`, err) 231 } 232 233 key := s.mergeBuildInNameToPattern(in.Pattern, structName, methodName, false) 234 handlerMap[key] = &HandlerItem{ 235 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 236 Type: HandlerTypeObject, 237 Info: funcInfo, 238 InitFunc: initFunc, 239 ShutFunc: shutFunc, 240 Middleware: in.Middleware, 241 Source: in.Source, 242 } 243 244 s.bindHandlerByMap(ctx, in.Prefix, handlerMap) 245 } 246 247 func (s *Server) doBindObjectRest(ctx context.Context, in doBindObjectInput) { 248 var ( 249 handlerMap = make(map[string]*HandlerItem) 250 reflectValue = reflect.ValueOf(in.Object) 251 reflectType = reflectValue.Type() 252 initFunc func(*Request) 253 shutFunc func(*Request) 254 ) 255 // If given `object` is not pointer, it then creates a temporary one, 256 // of which the value is `v`. 257 if reflectValue.Kind() == reflect.Struct { 258 newValue := reflect.New(reflectType) 259 newValue.Elem().Set(reflectValue) 260 reflectValue = newValue 261 reflectType = reflectValue.Type() 262 } 263 structName := reflectType.Elem().Name() 264 if reflectValue.MethodByName(specialMethodNameInit).IsValid() { 265 initFunc = reflectValue.MethodByName(specialMethodNameInit).Interface().(func(*Request)) 266 } 267 if reflectValue.MethodByName(specialMethodNameShut).IsValid() { 268 shutFunc = reflectValue.MethodByName(specialMethodNameShut).Interface().(func(*Request)) 269 } 270 pkgPath := reflectType.Elem().PkgPath() 271 for i := 0; i < reflectValue.NumMethod(); i++ { 272 methodName := reflectType.Method(i).Name 273 if _, ok := methodsMap[strings.ToUpper(methodName)]; !ok { 274 continue 275 } 276 pkgName := gfile.Basename(pkgPath) 277 objName := gstr.Replace(reflectType.String(), fmt.Sprintf(`%s.`, pkgName), "") 278 if objName[0] == '*' { 279 objName = fmt.Sprintf(`(%s)`, objName) 280 } 281 282 funcInfo, err := s.checkAndCreateFuncInfo( 283 reflectValue.Method(i).Interface(), 284 pkgPath, 285 objName, 286 methodName, 287 ) 288 if err != nil { 289 s.Logger().Fatalf(ctx, `%+v`, err) 290 } 291 292 key := s.mergeBuildInNameToPattern(methodName+":"+in.Pattern, structName, methodName, false) 293 handlerMap[key] = &HandlerItem{ 294 Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName), 295 Type: HandlerTypeObject, 296 Info: funcInfo, 297 InitFunc: initFunc, 298 ShutFunc: shutFunc, 299 Middleware: in.Middleware, 300 Source: in.Source, 301 } 302 } 303 s.bindHandlerByMap(ctx, in.Prefix, handlerMap) 304 }