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  }