github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_server_service_controller.go (about)

     1  // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). 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/zhongdalu/gf.
     6  // 服务注册.
     7  
     8  package ghttp
     9  
    10  import (
    11  	"fmt"
    12  	"reflect"
    13  	"strings"
    14  
    15  	"github.com/zhongdalu/gf/g/os/gfile"
    16  	"github.com/zhongdalu/gf/g/os/glog"
    17  	"github.com/zhongdalu/gf/g/text/gregex"
    18  	"github.com/zhongdalu/gf/g/text/gstr"
    19  )
    20  
    21  // 绑定控制器,控制器需要实现 gmvc.Controller 接口,
    22  // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话,
    23  // 第三个参数methods用以指定需要注册的方法,支持多个方法名称,多个方法以英文“,”号分隔,区分大小写.
    24  func (s *Server) BindController(pattern string, c Controller, methods ...string) {
    25  	// 当pattern中的method为all时,去掉该method,以便于后续方法判断
    26  	domain, method, path, err := s.parsePattern(pattern)
    27  	if err != nil {
    28  		glog.Error(err)
    29  		return
    30  	}
    31  	if strings.EqualFold(method, gDEFAULT_METHOD) {
    32  		pattern = s.serveHandlerKey("", path, domain)
    33  	}
    34  
    35  	methodMap := (map[string]bool)(nil)
    36  	if len(methods) > 0 {
    37  		methodMap = make(map[string]bool)
    38  		for _, v := range strings.Split(methods[0], ",") {
    39  			methodMap[strings.TrimSpace(v)] = true
    40  		}
    41  	}
    42  	// 遍历控制器,获取方法列表,并构造成uri
    43  	m := make(handlerMap)
    44  	v := reflect.ValueOf(c)
    45  	t := v.Type()
    46  	sname := t.Elem().Name()
    47  	pkgPath := t.Elem().PkgPath()
    48  	pkgName := gfile.Basename(pkgPath)
    49  	for i := 0; i < v.NumMethod(); i++ {
    50  		mname := t.Method(i).Name
    51  		if methodMap != nil && !methodMap[mname] {
    52  			continue
    53  		}
    54  		if mname == "Init" || mname == "Shut" || mname == "Exit" {
    55  			continue
    56  		}
    57  		ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
    58  		if ctlName[0] == '*' {
    59  			ctlName = fmt.Sprintf(`(%s)`, ctlName)
    60  		}
    61  		if _, ok := v.Method(i).Interface().(func()); !ok {
    62  			if len(methodMap) > 0 {
    63  				// 指定的方法名称注册,那么需要使用错误提示
    64  				glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
    65  					pkgPath, ctlName, mname, v.Method(i).Type().String())
    66  			} else {
    67  				// 否则只是Debug提示
    68  				glog.Debugf(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`,
    69  					pkgPath, ctlName, mname, v.Method(i).Type().String())
    70  			}
    71  			continue
    72  		}
    73  		key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
    74  		m[key] = &handlerItem{
    75  			name:  fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
    76  			rtype: gROUTE_REGISTER_CONTROLLER,
    77  			ctype: v.Elem().Type(),
    78  			fname: mname,
    79  			faddr: nil,
    80  		}
    81  		// 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI,
    82  		// 例如: pattern为/user, 那么会同时注册/user及/user/index,
    83  		// 这里处理新增/user路由绑定。
    84  		// 注意,当pattern带有内置变量时,不会自动加该路由。
    85  		if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) {
    86  			p := gstr.PosR(key, "/index")
    87  			k := key[0:p] + key[p+6:]
    88  			if len(k) == 0 || k[0] == '@' {
    89  				k = "/" + k
    90  			}
    91  			m[k] = &handlerItem{
    92  				name:  fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
    93  				rtype: gROUTE_REGISTER_CONTROLLER,
    94  				ctype: v.Elem().Type(),
    95  				fname: mname,
    96  				faddr: nil,
    97  			}
    98  		}
    99  	}
   100  	s.bindHandlerByMap(m)
   101  }
   102  
   103  // 绑定路由到指定的方法执行, 第三个参数method仅支持一个方法注册,不支持多个,并且区分大小写。
   104  func (s *Server) BindControllerMethod(pattern string, c Controller, method string) {
   105  	m := make(handlerMap)
   106  	v := reflect.ValueOf(c)
   107  	t := v.Type()
   108  	sname := t.Elem().Name()
   109  	mname := strings.TrimSpace(method)
   110  	fval := v.MethodByName(mname)
   111  	if !fval.IsValid() {
   112  		glog.Error("invalid method name:" + mname)
   113  		return
   114  	}
   115  	pkgPath := t.Elem().PkgPath()
   116  	pkgName := gfile.Basename(pkgPath)
   117  	ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
   118  	if ctlName[0] == '*' {
   119  		ctlName = fmt.Sprintf(`(%s)`, ctlName)
   120  	}
   121  	if _, ok := fval.Interface().(func()); !ok {
   122  		glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
   123  			pkgPath, ctlName, mname, fval.Type().String())
   124  		return
   125  	}
   126  	key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
   127  	m[key] = &handlerItem{
   128  		name:  fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
   129  		rtype: gROUTE_REGISTER_CONTROLLER,
   130  		ctype: v.Elem().Type(),
   131  		fname: mname,
   132  		faddr: nil,
   133  	}
   134  	s.bindHandlerByMap(m)
   135  }
   136  
   137  // 绑定控制器(RESTFul),控制器需要实现gmvc.Controller接口
   138  // 方法会识别HTTP方法,并做REST绑定处理,例如:Post方法会绑定到HTTP POST的方法请求处理,Delete方法会绑定到HTTP DELETE的方法请求处理
   139  // 因此只会绑定HTTP Method对应的方法,其他方法不会自动注册绑定
   140  // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
   141  func (s *Server) BindControllerRest(pattern string, c Controller) {
   142  	// 遍历控制器,获取方法列表,并构造成uri
   143  	m := make(handlerMap)
   144  	v := reflect.ValueOf(c)
   145  	t := v.Type()
   146  	sname := t.Elem().Name()
   147  	pkgPath := t.Elem().PkgPath()
   148  	// 如果存在与HttpMethod对应名字的方法,那么绑定这些方法
   149  	for i := 0; i < v.NumMethod(); i++ {
   150  		mname := t.Method(i).Name
   151  		method := strings.ToUpper(mname)
   152  		if _, ok := methodsMap[method]; !ok {
   153  			continue
   154  		}
   155  		pkgName := gfile.Basename(pkgPath)
   156  		ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
   157  		if ctlName[0] == '*' {
   158  			ctlName = fmt.Sprintf(`(%s)`, ctlName)
   159  		}
   160  		if _, ok := v.Method(i).Interface().(func()); !ok {
   161  			glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
   162  				pkgPath, ctlName, mname, v.Method(i).Type().String())
   163  			return
   164  		}
   165  		key := s.mergeBuildInNameToPattern(mname+":"+pattern, sname, mname, false)
   166  		m[key] = &handlerItem{
   167  			name:  fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
   168  			rtype: gROUTE_REGISTER_CONTROLLER,
   169  			ctype: v.Elem().Type(),
   170  			fname: mname,
   171  			faddr: nil,
   172  		}
   173  	}
   174  	s.bindHandlerByMap(m)
   175  }