github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_server_service_handler.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  	"bytes"
    11  	"github.com/gogf/gf/debug/gdebug"
    12  	"github.com/gogf/gf/errors/gcode"
    13  	"github.com/gogf/gf/errors/gerror"
    14  	"reflect"
    15  	"strings"
    16  
    17  	"github.com/gogf/gf/text/gstr"
    18  )
    19  
    20  // BindHandler registers a handler function to server with given pattern.
    21  // The parameter `handler` can be type of:
    22  // func(*ghttp.Request)
    23  // func(context.Context)
    24  // func(context.Context,TypeRequest)
    25  // func(context.Context,TypeRequest) error
    26  // func(context.Context,TypeRequest)(TypeResponse,error)
    27  func (s *Server) BindHandler(pattern string, handler interface{}) {
    28  	funcInfo, err := s.checkAndCreateFuncInfo(handler, "", "", "")
    29  	if err != nil {
    30  		s.Logger().Error(err.Error())
    31  		return
    32  	}
    33  	s.doBindHandler(pattern, funcInfo, nil, "")
    34  }
    35  
    36  // doBindHandler registers a handler function to server with given pattern.
    37  // The parameter <pattern> is like:
    38  // /user/list, put:/user, delete:/user, post:/user@goframe.org
    39  func (s *Server) doBindHandler(pattern string, funcInfo handlerFuncInfo, middleware []HandlerFunc, source string) {
    40  	s.setHandler(pattern, &handlerItem{
    41  		Name:       gdebug.FuncPath(funcInfo.Func),
    42  		Type:       handlerTypeHandler,
    43  		Info:       funcInfo,
    44  		Middleware: middleware,
    45  		Source:     source,
    46  	})
    47  }
    48  
    49  // bindHandlerByMap registers handlers to server using map.
    50  func (s *Server) bindHandlerByMap(m map[string]*handlerItem) {
    51  	for p, h := range m {
    52  		s.setHandler(p, h)
    53  	}
    54  }
    55  
    56  // mergeBuildInNameToPattern merges build-in names into the pattern according to the following
    57  // rules, and the built-in names are named like "{.xxx}".
    58  // Rule 1: The URI in pattern contains the {.struct} keyword, it then replaces the keyword with the struct name;
    59  // Rule 2: The URI in pattern contains the {.method} keyword, it then replaces the keyword with the method name;
    60  // Rule 2: If Rule 1 is not met, it then adds the method name directly to the URI in the pattern;
    61  //
    62  // The parameter <allowAppend> specifies whether allowing appending method name to the tail of pattern.
    63  func (s *Server) mergeBuildInNameToPattern(pattern string, structName, methodName string, allowAppend bool) string {
    64  	structName = s.nameToUri(structName)
    65  	methodName = s.nameToUri(methodName)
    66  	pattern = strings.Replace(pattern, "{.struct}", structName, -1)
    67  	if strings.Index(pattern, "{.method}") != -1 {
    68  		return strings.Replace(pattern, "{.method}", methodName, -1)
    69  	}
    70  	if !allowAppend {
    71  		return pattern
    72  	}
    73  	// Check domain parameter.
    74  	array := strings.Split(pattern, "@")
    75  	uri := array[0]
    76  	uri = strings.TrimRight(uri, "/") + "/" + methodName
    77  	// Append the domain parameter to URI.
    78  	if len(array) > 1 {
    79  		return uri + "@" + array[1]
    80  	}
    81  	return uri
    82  }
    83  
    84  // nameToUri converts the given name to URL format using following rules:
    85  // Rule 0: Convert all method names to lowercase, add char '-' between words.
    86  // Rule 1: Do not convert the method name, construct the URI with the original method name.
    87  // Rule 2: Convert all method names to lowercase, no connecting symbols between words.
    88  // Rule 3: Use camel case naming.
    89  func (s *Server) nameToUri(name string) string {
    90  	switch s.config.NameToUriType {
    91  	case UriTypeFullName:
    92  		return name
    93  
    94  	case UriTypeAllLower:
    95  		return strings.ToLower(name)
    96  
    97  	case UriTypeCamel:
    98  		part := bytes.NewBuffer(nil)
    99  		if gstr.IsLetterUpper(name[0]) {
   100  			part.WriteByte(name[0] + 32)
   101  		} else {
   102  			part.WriteByte(name[0])
   103  		}
   104  		part.WriteString(name[1:])
   105  		return part.String()
   106  
   107  	case UriTypeDefault:
   108  		fallthrough
   109  
   110  	default:
   111  		part := bytes.NewBuffer(nil)
   112  		for i := 0; i < len(name); i++ {
   113  			if i > 0 && gstr.IsLetterUpper(name[i]) {
   114  				part.WriteByte('-')
   115  			}
   116  			if gstr.IsLetterUpper(name[i]) {
   117  				part.WriteByte(name[i] + 32)
   118  			} else {
   119  				part.WriteByte(name[i])
   120  			}
   121  		}
   122  		return part.String()
   123  	}
   124  }
   125  
   126  func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, objName, methodName string) (info handlerFuncInfo, err error) {
   127  	handlerFunc, ok := f.(HandlerFunc)
   128  	if !ok {
   129  		reflectType := reflect.TypeOf(f)
   130  		if reflectType.NumIn() == 0 || reflectType.NumIn() > 2 || reflectType.NumOut() > 2 {
   131  			if pkgPath != "" {
   132  				err = gerror.NewCodef(
   133  					gcode.CodeInvalidParameter,
   134  					`invalid handler: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`,
   135  					pkgPath, objName, methodName, reflect.TypeOf(f).String(),
   136  				)
   137  			} else {
   138  				err = gerror.NewCodef(
   139  					gcode.CodeInvalidParameter,
   140  					`invalid handler: defined as "%s", but "func(*ghttp.Request)" or "func(context.Context)/func(context.Context,Request)/func(context.Context,Request) error/func(context.Context,Request)(Response,error)" is required`,
   141  					reflect.TypeOf(f).String(),
   142  				)
   143  			}
   144  			return
   145  		}
   146  
   147  		if reflectType.In(0).String() != "context.Context" {
   148  			err = gerror.NewCodef(
   149  				gcode.CodeInvalidParameter,
   150  				`invalid handler: defined as "%s", but the first input parameter should be type of "context.Context"`,
   151  				reflect.TypeOf(f).String(),
   152  			)
   153  			return
   154  		}
   155  
   156  		if reflectType.NumOut() > 0 && reflectType.Out(reflectType.NumOut()-1).String() != "error" {
   157  			err = gerror.NewCodef(
   158  				gcode.CodeInvalidParameter,
   159  				`invalid handler: defined as "%s", but the last output parameter should be type of "error"`,
   160  				reflect.TypeOf(f).String(),
   161  			)
   162  			return
   163  		}
   164  	}
   165  	info.Func = handlerFunc
   166  	info.Type = reflect.TypeOf(f)
   167  	info.Value = reflect.ValueOf(f)
   168  	return
   169  }