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 }