github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_server_router_group.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/debug/gdebug" 15 16 "github.com/gogf/gf/text/gstr" 17 18 "github.com/gogf/gf/util/gconv" 19 ) 20 21 type ( 22 // RouterGroup is a group wrapping multiple routes and middleware. 23 RouterGroup struct { 24 parent *RouterGroup // Parent group. 25 server *Server // Server. 26 domain *Domain // Domain. 27 prefix string // Prefix for sub-route. 28 middleware []HandlerFunc // Middleware array. 29 } 30 31 // GroupItem is item for router group. 32 GroupItem = []interface{} 33 34 // preBindItem is item for lazy registering feature of router group. preBindItem is not really registered 35 // to server when route function of the group called but is lazily registered when server starts. 36 preBindItem struct { 37 group *RouterGroup 38 bindType string 39 pattern string 40 object interface{} // Can be handler, controller or object. 41 params []interface{} // Extra parameters for route registering depending on the type. 42 source string // Handler is register at certain source file path:line. 43 bound bool // Is this item bound to server. 44 } 45 ) 46 47 const ( 48 groupBindTypeHandler = "HANDLER" 49 groupBindTypeRest = "REST" 50 groupBindTypeHook = "HOOK" 51 groupBindTypeMiddleware = "MIDDLEWARE" 52 ) 53 54 var ( 55 preBindItems = make([]*preBindItem, 0, 64) 56 ) 57 58 // handlePreBindItems is called when server starts, which does really route registering to the server. 59 func (s *Server) handlePreBindItems() { 60 if len(preBindItems) == 0 { 61 return 62 } 63 for _, item := range preBindItems { 64 if item.bound { 65 continue 66 } 67 // Handle the items of current server. 68 if item.group.server != nil && item.group.server != s { 69 continue 70 } 71 if item.group.domain != nil && item.group.domain.server != s { 72 continue 73 } 74 item.group.doBindRoutersToServer(item) 75 item.bound = true 76 } 77 } 78 79 // Group creates and returns a RouterGroup object. 80 func (s *Server) Group(prefix string, groups ...func(group *RouterGroup)) *RouterGroup { 81 if len(prefix) > 0 && prefix[0] != '/' { 82 prefix = "/" + prefix 83 } 84 if prefix == "/" { 85 prefix = "" 86 } 87 group := &RouterGroup{ 88 server: s, 89 prefix: prefix, 90 } 91 if len(groups) > 0 { 92 for _, v := range groups { 93 v(group) 94 } 95 } 96 return group 97 } 98 99 // Group creates and returns a RouterGroup object, which is bound to a specified domain. 100 func (d *Domain) Group(prefix string, groups ...func(group *RouterGroup)) *RouterGroup { 101 if len(prefix) > 0 && prefix[0] != '/' { 102 prefix = "/" + prefix 103 } 104 if prefix == "/" { 105 prefix = "" 106 } 107 group := &RouterGroup{ 108 domain: d, 109 prefix: prefix, 110 } 111 if len(groups) > 0 { 112 for _, v := range groups { 113 v(group) 114 } 115 } 116 return group 117 } 118 119 // Group creates and returns a sub-group of current router group. 120 func (g *RouterGroup) Group(prefix string, groups ...func(group *RouterGroup)) *RouterGroup { 121 if prefix == "/" { 122 prefix = "" 123 } 124 group := &RouterGroup{ 125 parent: g, 126 server: g.server, 127 domain: g.domain, 128 prefix: prefix, 129 } 130 if len(g.middleware) > 0 { 131 group.middleware = make([]HandlerFunc, len(g.middleware)) 132 copy(group.middleware, g.middleware) 133 } 134 if len(groups) > 0 { 135 for _, v := range groups { 136 v(group) 137 } 138 } 139 return group 140 } 141 142 // Clone returns a new router group which is a clone of current group. 143 func (g *RouterGroup) Clone() *RouterGroup { 144 newGroup := &RouterGroup{ 145 parent: g.parent, 146 server: g.server, 147 domain: g.domain, 148 prefix: g.prefix, 149 middleware: make([]HandlerFunc, len(g.middleware)), 150 } 151 copy(newGroup.middleware, g.middleware) 152 return newGroup 153 } 154 155 // Bind does batch route registering feature for router group. 156 func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { 157 group := g.Clone() 158 for _, item := range items { 159 if len(item) < 3 { 160 g.server.Logger().Fatalf("invalid router item: %s", item) 161 } 162 bindType := gstr.ToUpper(gconv.String(item[0])) 163 switch bindType { 164 case groupBindTypeRest: 165 group.preBindToLocalArray(groupBindTypeRest, gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) 166 167 case groupBindTypeMiddleware: 168 group.preBindToLocalArray(groupBindTypeMiddleware, gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) 169 170 default: 171 if strings.EqualFold(bindType, "ALL") { 172 bindType = "" 173 } else { 174 bindType += ":" 175 } 176 if len(item) > 3 { 177 group.preBindToLocalArray(groupBindTypeHandler, bindType+gconv.String(item[1]), item[2], item[3]) 178 } else { 179 group.preBindToLocalArray(groupBindTypeHandler, bindType+gconv.String(item[1]), item[2]) 180 } 181 } 182 } 183 return group 184 } 185 186 // ALL registers a http handler to given route pattern and all http methods. 187 func (g *RouterGroup) ALL(pattern string, object interface{}, params ...interface{}) *RouterGroup { 188 return g.Clone().preBindToLocalArray(groupBindTypeHandler, defaultMethod+":"+pattern, object, params...) 189 } 190 191 // ALLMap registers http handlers for http methods using map. 192 func (g *RouterGroup) ALLMap(m map[string]interface{}) { 193 for pattern, object := range m { 194 g.ALL(pattern, object) 195 } 196 } 197 198 // Map registers http handlers for http methods using map. 199 func (g *RouterGroup) Map(m map[string]interface{}) { 200 for pattern, object := range m { 201 g.preBindToLocalArray(groupBindTypeHandler, pattern, object) 202 } 203 } 204 205 // GET registers a http handler to given route pattern and http method: GET. 206 func (g *RouterGroup) GET(pattern string, object interface{}, params ...interface{}) *RouterGroup { 207 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "GET:"+pattern, object, params...) 208 } 209 210 // PUT registers a http handler to given route pattern and http method: PUT. 211 func (g *RouterGroup) PUT(pattern string, object interface{}, params ...interface{}) *RouterGroup { 212 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "PUT:"+pattern, object, params...) 213 } 214 215 // POST registers a http handler to given route pattern and http method: POST. 216 func (g *RouterGroup) POST(pattern string, object interface{}, params ...interface{}) *RouterGroup { 217 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "POST:"+pattern, object, params...) 218 } 219 220 // DELETE registers a http handler to given route pattern and http method: DELETE. 221 func (g *RouterGroup) DELETE(pattern string, object interface{}, params ...interface{}) *RouterGroup { 222 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "DELETE:"+pattern, object, params...) 223 } 224 225 // PATCH registers a http handler to given route pattern and http method: PATCH. 226 func (g *RouterGroup) PATCH(pattern string, object interface{}, params ...interface{}) *RouterGroup { 227 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "PATCH:"+pattern, object, params...) 228 } 229 230 // HEAD registers a http handler to given route pattern and http method: HEAD. 231 func (g *RouterGroup) HEAD(pattern string, object interface{}, params ...interface{}) *RouterGroup { 232 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "HEAD:"+pattern, object, params...) 233 } 234 235 // CONNECT registers a http handler to given route pattern and http method: CONNECT. 236 func (g *RouterGroup) CONNECT(pattern string, object interface{}, params ...interface{}) *RouterGroup { 237 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "CONNECT:"+pattern, object, params...) 238 } 239 240 // OPTIONS registers a http handler to given route pattern and http method: OPTIONS. 241 func (g *RouterGroup) OPTIONS(pattern string, object interface{}, params ...interface{}) *RouterGroup { 242 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "OPTIONS:"+pattern, object, params...) 243 } 244 245 // TRACE registers a http handler to given route pattern and http method: TRACE. 246 func (g *RouterGroup) TRACE(pattern string, object interface{}, params ...interface{}) *RouterGroup { 247 return g.Clone().preBindToLocalArray(groupBindTypeHandler, "TRACE:"+pattern, object, params...) 248 } 249 250 // REST registers a http handler to given route pattern according to REST rule. 251 func (g *RouterGroup) REST(pattern string, object interface{}) *RouterGroup { 252 return g.Clone().preBindToLocalArray(groupBindTypeRest, pattern, object) 253 } 254 255 // Hook registers a hook to given route pattern. 256 func (g *RouterGroup) Hook(pattern string, hook string, handler HandlerFunc) *RouterGroup { 257 return g.Clone().preBindToLocalArray(groupBindTypeHandler, pattern, handler, hook) 258 } 259 260 // Middleware binds one or more middleware to the router group. 261 func (g *RouterGroup) Middleware(handlers ...HandlerFunc) *RouterGroup { 262 g.middleware = append(g.middleware, handlers...) 263 return g 264 } 265 266 // preBindToLocalArray adds the route registering parameters to internal variable array for lazily registering feature. 267 func (g *RouterGroup) preBindToLocalArray(bindType string, pattern string, object interface{}, params ...interface{}) *RouterGroup { 268 _, file, line := gdebug.CallerWithFilter([]string{stackFilterKey}) 269 preBindItems = append(preBindItems, &preBindItem{ 270 group: g, 271 bindType: bindType, 272 pattern: pattern, 273 object: object, 274 params: params, 275 source: fmt.Sprintf(`%s:%d`, file, line), 276 }) 277 return g 278 } 279 280 // getPrefix returns the route prefix of the group, which recursively retrieves its parent's prefixo. 281 func (g *RouterGroup) getPrefix() string { 282 prefix := g.prefix 283 parent := g.parent 284 for parent != nil { 285 prefix = parent.prefix + prefix 286 parent = parent.parent 287 } 288 return prefix 289 } 290 291 // doBindRoutersToServer does really register for the group. 292 func (g *RouterGroup) doBindRoutersToServer(item *preBindItem) *RouterGroup { 293 var ( 294 bindType = item.bindType 295 pattern = item.pattern 296 object = item.object 297 params = item.params 298 source = item.source 299 ) 300 prefix := g.getPrefix() 301 // Route check. 302 if len(prefix) > 0 { 303 domain, method, path, err := g.server.parsePattern(pattern) 304 if err != nil { 305 g.server.Logger().Fatalf("invalid pattern: %s", pattern) 306 } 307 // If there is already a domain, unset the domain field in the pattern. 308 if g.domain != nil { 309 domain = "" 310 } 311 if bindType == groupBindTypeRest { 312 pattern = prefix + "/" + strings.TrimLeft(path, "/") 313 } else { 314 pattern = g.server.serveHandlerKey( 315 method, prefix+"/"+strings.TrimLeft(path, "/"), domain, 316 ) 317 } 318 } 319 // Filter repeated char '/'. 320 pattern = gstr.Replace(pattern, "//", "/") 321 322 // Convert params to string array. 323 extras := gconv.Strings(params) 324 325 // Check whether it's a hook handler. 326 if _, ok := object.(HandlerFunc); ok && len(extras) > 0 { 327 bindType = groupBindTypeHook 328 } 329 switch bindType { 330 case groupBindTypeHandler: 331 if reflect.ValueOf(object).Kind() == reflect.Func { 332 funcInfo, err := g.server.checkAndCreateFuncInfo(object, "", "", "") 333 if err != nil { 334 g.server.Logger().Error(err.Error()) 335 return g 336 } 337 if g.server != nil { 338 g.server.doBindHandler(pattern, funcInfo, g.middleware, source) 339 } else { 340 g.domain.doBindHandler(pattern, funcInfo, g.middleware, source) 341 } 342 } else { 343 if len(extras) > 0 { 344 if g.server != nil { 345 if gstr.Contains(extras[0], ",") { 346 g.server.doBindObject( 347 pattern, object, extras[0], g.middleware, source, 348 ) 349 } else { 350 g.server.doBindObjectMethod( 351 pattern, object, extras[0], g.middleware, source, 352 ) 353 } 354 } else { 355 if gstr.Contains(extras[0], ",") { 356 g.domain.doBindObject( 357 pattern, object, extras[0], g.middleware, source, 358 ) 359 } else { 360 g.domain.doBindObjectMethod( 361 pattern, object, extras[0], g.middleware, source, 362 ) 363 } 364 } 365 } else { 366 // At last, it treats the `object` as Object registering type. 367 if g.server != nil { 368 g.server.doBindObject(pattern, object, "", g.middleware, source) 369 } else { 370 g.domain.doBindObject(pattern, object, "", g.middleware, source) 371 } 372 } 373 } 374 375 case groupBindTypeRest: 376 if g.server != nil { 377 g.server.doBindObjectRest(pattern, object, g.middleware, source) 378 } else { 379 g.domain.doBindObjectRest(pattern, object, g.middleware, source) 380 } 381 382 case groupBindTypeHook: 383 if h, ok := object.(HandlerFunc); ok { 384 if g.server != nil { 385 g.server.doBindHookHandler(pattern, extras[0], h, source) 386 } else { 387 g.domain.doBindHookHandler(pattern, extras[0], h, source) 388 } 389 } else { 390 g.server.Logger().Fatalf("invalid hook handler for pattern: %s", pattern) 391 } 392 } 393 return g 394 }