gf/net/ghttp/ghttp_server_service_object.go

246 lines
7.8 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package ghttp
import (
2019-06-19 09:06:52 +08:00
"fmt"
2019-06-25 23:03:29 +08:00
"reflect"
"strings"
2019-07-29 21:01:19 +08:00
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
)
2020-05-09 19:19:42 +08:00
// BindObject registers object to server routes with given pattern.
//
// The optional parameter <method> is used to specify the method to be registered, which
// supports multiple method names, multiple methods are separated by char ',', case sensitive.
//
// Note that the route method should be defined as ghttp.HandlerFunc.
func (s *Server) BindObject(pattern string, object interface{}, method ...string) {
bindMethod := ""
if len(method) > 0 {
bindMethod = method[0]
}
s.doBindObject(pattern, object, bindMethod, nil, "")
}
2020-05-09 19:19:42 +08:00
// BindObjectMethod registers specified method of object to server routes with given pattern.
//
// The optional parameter <method> is used to specify the method to be registered, which
// does not supports multiple method names but only one, case sensitive.
//
// Note that the route method should be defined as ghttp.HandlerFunc.
func (s *Server) BindObjectMethod(pattern string, object interface{}, method string) {
s.doBindObjectMethod(pattern, object, method, nil, "")
}
2020-05-09 19:19:42 +08:00
// BindObjectRest registers object in REST API style to server with specified pattern.
// Note that the route method should be defined as ghttp.HandlerFunc.
func (s *Server) BindObjectRest(pattern string, object interface{}) {
s.doBindObjectRest(pattern, object, nil, "")
}
func (s *Server) doBindObject(
pattern string, object interface{}, method string,
middleware []HandlerFunc, source string,
) {
2020-03-05 16:08:55 +08:00
// Convert input method to map for convenience and high performance searching purpose.
var methodMap map[string]bool
if len(method) > 0 {
methodMap = make(map[string]bool)
for _, v := range strings.Split(method, ",") {
methodMap[strings.TrimSpace(v)] = true
}
}
2019-06-25 23:03:29 +08:00
// 当pattern中的method为all时去掉该method以便于后续方法判断
domain, method, path, err := s.parsePattern(pattern)
if err != nil {
s.Logger().Fatal(err)
2019-06-25 23:03:29 +08:00
return
}
2020-12-14 13:26:48 +08:00
if strings.EqualFold(method, defaultMethod) {
2019-06-25 23:03:29 +08:00
pattern = s.serveHandlerKey("", path, domain)
}
m := make(map[string]*handlerItem)
v := reflect.ValueOf(object)
2019-06-19 09:06:52 +08:00
t := v.Type()
initFunc := (func(*Request))(nil)
shutFunc := (func(*Request))(nil)
structName := t.Elem().Name()
2019-06-19 09:06:52 +08:00
if v.MethodByName("Init").IsValid() {
initFunc = v.MethodByName("Init").Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
}
if v.MethodByName("Shut").IsValid() {
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
}
pkgPath := t.Elem().PkgPath()
pkgName := gfile.Basename(pkgPath)
for i := 0; i < v.NumMethod(); i++ {
methodName := t.Method(i).Name
if methodMap != nil && !methodMap[methodName] {
2019-06-19 09:06:52 +08:00
continue
}
if methodName == "Init" || methodName == "Shut" {
2019-06-19 09:06:52 +08:00
continue
}
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
itemFunc, ok := v.Method(i).Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
if !ok {
if len(methodMap) > 0 {
2020-03-05 16:08:55 +08:00
s.Logger().Errorf(
`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, methodName, v.Method(i).Type().String(),
)
2019-06-19 09:06:52 +08:00
} else {
2020-03-05 16:08:55 +08:00
s.Logger().Debugf(
2020-04-06 22:31:45 +08:00
`ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)" for object registry`,
2020-03-05 16:08:55 +08:00
pkgPath, objName, methodName, v.Method(i).Type().String(),
)
2019-06-19 09:06:52 +08:00
}
continue
}
key := s.mergeBuildInNameToPattern(pattern, structName, methodName, true)
2019-06-19 09:06:52 +08:00
m[key] = &handlerItem{
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
2020-12-14 13:26:48 +08:00
itemType: handlerTypeObject,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
middleware: middleware,
source: source,
2019-06-19 09:06:52 +08:00
}
2020-05-09 19:19:42 +08:00
// If there's "Index" method, then an additional route is automatically added
// to match the main URI, for example:
// If pattern is "/user", then "/user" and "/user/index" are both automatically
// registered.
//
// Note that if there's built-in variables in pattern, this route will not be added
// automatically.
if strings.EqualFold(methodName, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) {
2019-08-30 20:29:12 +08:00
p := gstr.PosRI(key, "/index")
2019-06-19 09:06:52 +08:00
k := key[0:p] + key[p+6:]
if len(k) == 0 || k[0] == '@' {
k = "/" + k
}
m[k] = &handlerItem{
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
2020-12-14 13:26:48 +08:00
itemType: handlerTypeObject,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
middleware: middleware,
source: source,
2019-06-19 09:06:52 +08:00
}
}
}
s.bindHandlerByMap(m)
}
func (s *Server) doBindObjectMethod(
pattern string, object interface{}, method string,
middleware []HandlerFunc, source string,
) {
m := make(map[string]*handlerItem)
v := reflect.ValueOf(object)
2019-06-19 09:06:52 +08:00
t := v.Type()
structName := t.Elem().Name()
methodName := strings.TrimSpace(method)
methodValue := v.MethodByName(methodName)
if !methodValue.IsValid() {
s.Logger().Fatal("invalid method name: " + methodName)
2019-06-19 09:06:52 +08:00
return
}
initFunc := (func(*Request))(nil)
shutFunc := (func(*Request))(nil)
2019-06-19 09:06:52 +08:00
if v.MethodByName("Init").IsValid() {
initFunc = v.MethodByName("Init").Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
}
if v.MethodByName("Shut").IsValid() {
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
}
pkgPath := t.Elem().PkgPath()
pkgName := gfile.Basename(pkgPath)
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
itemFunc, ok := methodValue.Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
if !ok {
2020-05-09 19:19:42 +08:00
s.Logger().Errorf(
`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, methodName, methodValue.Type().String(),
)
2019-06-19 09:06:52 +08:00
return
}
key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false)
2019-06-19 09:06:52 +08:00
m[key] = &handlerItem{
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
2020-12-14 13:26:48 +08:00
itemType: handlerTypeObject,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
middleware: middleware,
source: source,
2019-06-19 09:06:52 +08:00
}
2019-06-19 09:06:52 +08:00
s.bindHandlerByMap(m)
}
func (s *Server) doBindObjectRest(
pattern string, object interface{},
middleware []HandlerFunc, source string,
) {
m := make(map[string]*handlerItem)
v := reflect.ValueOf(object)
2019-06-19 09:06:52 +08:00
t := v.Type()
initFunc := (func(*Request))(nil)
shutFunc := (func(*Request))(nil)
structName := t.Elem().Name()
2019-06-19 09:06:52 +08:00
if v.MethodByName("Init").IsValid() {
initFunc = v.MethodByName("Init").Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
}
if v.MethodByName("Shut").IsValid() {
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
}
pkgPath := t.Elem().PkgPath()
for i := 0; i < v.NumMethod(); i++ {
methodName := t.Method(i).Name
if _, ok := methodsMap[strings.ToUpper(methodName)]; !ok {
2019-06-19 09:06:52 +08:00
continue
}
pkgName := gfile.Basename(pkgPath)
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
itemFunc, ok := v.Method(i).Interface().(func(*Request))
2019-06-19 09:06:52 +08:00
if !ok {
2020-05-09 19:19:42 +08:00
s.Logger().Errorf(
`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
pkgPath, objName, methodName, v.Method(i).Type().String(),
)
2019-06-19 09:06:52 +08:00
continue
}
key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false)
2019-06-19 09:06:52 +08:00
m[key] = &handlerItem{
itemName: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
2020-12-14 13:26:48 +08:00
itemType: handlerTypeObject,
itemFunc: itemFunc,
initFunc: initFunc,
shutFunc: shutFunc,
middleware: middleware,
source: source,
2019-06-19 09:06:52 +08:00
}
}
s.bindHandlerByMap(m)
}