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  }