mirror of
https://gitee.com/johng/gf.git
synced 2024-12-02 12:17:53 +08:00
210 lines
6.3 KiB
Go
210 lines
6.3 KiB
Go
// 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 (
|
|
"bytes"
|
|
"context"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/gogf/gf/v2/errors/gcode"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/text/gstr"
|
|
)
|
|
|
|
// BindHandler registers a handler function to server with a given pattern.
|
|
//
|
|
// Note that the parameter `handler` can be type of:
|
|
// 1. func(*ghttp.Request)
|
|
// 2. func(context.Context, BizRequest)(BizResponse, error)
|
|
func (s *Server) BindHandler(pattern string, handler interface{}) {
|
|
var ctx = context.TODO()
|
|
funcInfo, err := s.checkAndCreateFuncInfo(handler, "", "", "")
|
|
if err != nil {
|
|
s.Logger().Fatalf(ctx, `%+v`, err)
|
|
}
|
|
s.doBindHandler(ctx, doBindHandlerInput{
|
|
Prefix: "",
|
|
Pattern: pattern,
|
|
FuncInfo: funcInfo,
|
|
Middleware: nil,
|
|
Source: "",
|
|
})
|
|
}
|
|
|
|
type doBindHandlerInput struct {
|
|
Prefix string
|
|
Pattern string
|
|
FuncInfo handlerFuncInfo
|
|
Middleware []HandlerFunc
|
|
Source string
|
|
}
|
|
|
|
// doBindHandler registers a handler function to server with given pattern.
|
|
//
|
|
// The parameter `pattern` is like:
|
|
// /user/list, put:/user, delete:/user, post:/user@goframe.org
|
|
func (s *Server) doBindHandler(ctx context.Context, in doBindHandlerInput) {
|
|
s.setHandler(ctx, setHandlerInput{
|
|
Prefix: in.Prefix,
|
|
Pattern: in.Pattern,
|
|
HandlerItem: &handlerItem{
|
|
Type: HandlerTypeHandler,
|
|
Info: in.FuncInfo,
|
|
Middleware: in.Middleware,
|
|
Source: in.Source,
|
|
},
|
|
})
|
|
}
|
|
|
|
// bindHandlerByMap registers handlers to server using map.
|
|
func (s *Server) bindHandlerByMap(ctx context.Context, prefix string, m map[string]*handlerItem) {
|
|
for pattern, handler := range m {
|
|
s.setHandler(ctx, setHandlerInput{
|
|
Prefix: prefix,
|
|
Pattern: pattern,
|
|
HandlerItem: handler,
|
|
})
|
|
}
|
|
}
|
|
|
|
// mergeBuildInNameToPattern merges build-in names into the pattern according to the following
|
|
// rules, and the built-in names are named like "{.xxx}".
|
|
// Rule 1: The URI in pattern contains the {.struct} keyword, it then replaces the keyword with the struct name;
|
|
// Rule 2: The URI in pattern contains the {.method} keyword, it then replaces the keyword with the method name;
|
|
// Rule 2: If Rule 1 is not met, it then adds the method name directly to the URI in the pattern;
|
|
//
|
|
// The parameter `allowAppend` specifies whether allowing appending method name to the tail of pattern.
|
|
func (s *Server) mergeBuildInNameToPattern(pattern string, structName, methodName string, allowAppend bool) string {
|
|
structName = s.nameToUri(structName)
|
|
methodName = s.nameToUri(methodName)
|
|
pattern = strings.Replace(pattern, "{.struct}", structName, -1)
|
|
if strings.Index(pattern, "{.method}") != -1 {
|
|
return strings.Replace(pattern, "{.method}", methodName, -1)
|
|
}
|
|
if !allowAppend {
|
|
return pattern
|
|
}
|
|
// Check domain parameter.
|
|
array := strings.Split(pattern, "@")
|
|
uri := array[0]
|
|
uri = strings.TrimRight(uri, "/") + "/" + methodName
|
|
// Append the domain parameter to URI.
|
|
if len(array) > 1 {
|
|
return uri + "@" + array[1]
|
|
}
|
|
return uri
|
|
}
|
|
|
|
// nameToUri converts the given name to the URL format using the following rules:
|
|
// Rule 0: Convert all method names to lowercase, add char '-' between words.
|
|
// Rule 1: Do not convert the method name, construct the URI with the original method name.
|
|
// Rule 2: Convert all method names to lowercase, no connecting symbols between words.
|
|
// Rule 3: Use camel case naming.
|
|
func (s *Server) nameToUri(name string) string {
|
|
switch s.config.NameToUriType {
|
|
case UriTypeFullName:
|
|
return name
|
|
|
|
case UriTypeAllLower:
|
|
return strings.ToLower(name)
|
|
|
|
case UriTypeCamel:
|
|
part := bytes.NewBuffer(nil)
|
|
if gstr.IsLetterUpper(name[0]) {
|
|
part.WriteByte(name[0] + 32)
|
|
} else {
|
|
part.WriteByte(name[0])
|
|
}
|
|
part.WriteString(name[1:])
|
|
return part.String()
|
|
|
|
case UriTypeDefault:
|
|
fallthrough
|
|
|
|
default:
|
|
part := bytes.NewBuffer(nil)
|
|
for i := 0; i < len(name); i++ {
|
|
if i > 0 && gstr.IsLetterUpper(name[i]) {
|
|
part.WriteByte('-')
|
|
}
|
|
if gstr.IsLetterUpper(name[i]) {
|
|
part.WriteByte(name[i] + 32)
|
|
} else {
|
|
part.WriteByte(name[i])
|
|
}
|
|
}
|
|
return part.String()
|
|
}
|
|
}
|
|
|
|
func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, methodName string) (info handlerFuncInfo, err error) {
|
|
handlerFunc, ok := f.(HandlerFunc)
|
|
if !ok {
|
|
reflectType := reflect.TypeOf(f)
|
|
if reflectType.NumIn() != 2 || reflectType.NumOut() != 2 {
|
|
if pkgPath != "" {
|
|
err = gerror.NewCodef(
|
|
gcode.CodeInvalidParameter,
|
|
`invalid handler: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" or "func(context.Context, BizRequest)(BizResponse, error)" is required`,
|
|
pkgPath, structName, methodName, reflect.TypeOf(f).String(),
|
|
)
|
|
} else {
|
|
err = gerror.NewCodef(
|
|
gcode.CodeInvalidParameter,
|
|
`invalid handler: defined as "%s", but "func(*ghttp.Request)" or "func(context.Context, BizRequest)(BizResponse, error)" is required`,
|
|
reflect.TypeOf(f).String(),
|
|
)
|
|
}
|
|
return
|
|
}
|
|
|
|
if reflectType.In(0).String() != "context.Context" {
|
|
err = gerror.NewCodef(
|
|
gcode.CodeInvalidParameter,
|
|
`invalid handler: defined as "%s", but the first input parameter should be type of "context.Context"`,
|
|
reflect.TypeOf(f).String(),
|
|
)
|
|
return
|
|
}
|
|
|
|
if reflectType.Out(1).String() != "error" {
|
|
err = gerror.NewCodef(
|
|
gcode.CodeInvalidParameter,
|
|
`invalid handler: defined as "%s", but the last output parameter should be type of "error"`,
|
|
reflect.TypeOf(f).String(),
|
|
)
|
|
return
|
|
}
|
|
|
|
// The request struct should be named as `xxxReq`.
|
|
if !gstr.HasSuffix(reflectType.In(1).String(), `Req`) {
|
|
err = gerror.NewCodef(
|
|
gcode.CodeInvalidParameter,
|
|
`invalid struct naming for request: defined as "%s", but it should be named with "Req" suffix like "xxxReq"`,
|
|
reflectType.In(1).String(),
|
|
)
|
|
return
|
|
}
|
|
|
|
// The response struct should be named as `xxxRes`.
|
|
if !gstr.HasSuffix(reflectType.Out(0).String(), `Res`) {
|
|
err = gerror.NewCodef(
|
|
gcode.CodeInvalidParameter,
|
|
`invalid struct naming for response: defined as "%s", but it should be named with "Res" suffix like "xxxRes"`,
|
|
reflectType.Out(0).String(),
|
|
)
|
|
return
|
|
}
|
|
}
|
|
info.Func = handlerFunc
|
|
info.Type = reflect.TypeOf(f)
|
|
info.Value = reflect.ValueOf(f)
|
|
return
|
|
}
|