mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
improve group router for package ghttp
This commit is contained in:
@ -60,7 +60,7 @@ func Server(name ...interface{}) *ghttp.Server {
|
||||
}
|
||||
} else {
|
||||
// The configuration is not necessary, so it just prints internal logs.
|
||||
intlog.Printf(ctx, `missing configuration for HTTP server "%s"`, instanceName)
|
||||
intlog.Printf(ctx, `missing configuration from configuration component for HTTP server "%s"`, instanceName)
|
||||
}
|
||||
|
||||
// Server logger configuration checks.
|
||||
|
||||
@ -35,9 +35,15 @@ func (d *Domain) BindHandler(pattern string, handler interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Domain) doBindHandler(ctx context.Context, pattern string, funcInfo handlerFuncInfo, middleware []HandlerFunc, source string) {
|
||||
func (d *Domain) doBindHandler(ctx context.Context, in doBindHandlerInput) {
|
||||
for domain, _ := range d.domains {
|
||||
d.server.doBindHandler(ctx, pattern+"@"+domain, funcInfo, middleware, source)
|
||||
d.server.doBindHandler(ctx, doBindHandlerInput{
|
||||
Prefix: in.Prefix,
|
||||
Pattern: in.Pattern + "@" + domain,
|
||||
FuncInfo: in.FuncInfo,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,9 +53,16 @@ func (d *Domain) BindObject(pattern string, obj interface{}, methods ...string)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Domain) doBindObject(ctx context.Context, pattern string, obj interface{}, methods string, middleware []HandlerFunc, source string) {
|
||||
func (d *Domain) doBindObject(ctx context.Context, in doBindObjectInput) {
|
||||
for domain, _ := range d.domains {
|
||||
d.server.doBindObject(ctx, pattern+"@"+domain, obj, methods, middleware, source)
|
||||
d.server.doBindObject(ctx, doBindObjectInput{
|
||||
Prefix: in.Prefix,
|
||||
Pattern: in.Pattern + "@" + domain,
|
||||
Object: in.Object,
|
||||
Method: in.Method,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,13 +72,16 @@ func (d *Domain) BindObjectMethod(pattern string, obj interface{}, method string
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Domain) doBindObjectMethod(
|
||||
ctx context.Context,
|
||||
pattern string, obj interface{}, method string,
|
||||
middleware []HandlerFunc, source string,
|
||||
) {
|
||||
func (d *Domain) doBindObjectMethod(ctx context.Context, in doBindObjectMethodInput) {
|
||||
for domain, _ := range d.domains {
|
||||
d.server.doBindObjectMethod(ctx, pattern+"@"+domain, obj, method, middleware, source)
|
||||
d.server.doBindObjectMethod(ctx, doBindObjectMethodInput{
|
||||
Prefix: in.Prefix,
|
||||
Pattern: in.Pattern + "@" + domain,
|
||||
Object: in.Object,
|
||||
Method: in.Method,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,9 +91,16 @@ func (d *Domain) BindObjectRest(pattern string, obj interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Domain) doBindObjectRest(ctx context.Context, pattern string, obj interface{}, middleware []HandlerFunc, source string) {
|
||||
func (d *Domain) doBindObjectRest(ctx context.Context, in doBindObjectInput) {
|
||||
for domain, _ := range d.domains {
|
||||
d.server.doBindObjectRest(ctx, pattern+"@"+domain, obj, middleware, source)
|
||||
d.server.doBindObjectRest(ctx, doBindObjectInput{
|
||||
Prefix: in.Prefix,
|
||||
Pattern: in.Pattern + "@" + domain,
|
||||
Object: in.Object,
|
||||
Method: in.Method,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,9 +110,15 @@ func (d *Domain) BindHookHandler(pattern string, hook string, handler HandlerFun
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Domain) doBindHookHandler(ctx context.Context, pattern string, hook string, handler HandlerFunc, source string) {
|
||||
func (d *Domain) doBindHookHandler(ctx context.Context, in doBindHookHandlerInput) {
|
||||
for domain, _ := range d.domains {
|
||||
d.server.doBindHookHandler(ctx, pattern+"@"+domain, hook, handler, source)
|
||||
d.server.doBindHookHandler(ctx, doBindHookHandlerInput{
|
||||
Prefix: in.Prefix,
|
||||
Pattern: in.Pattern + "@" + domain,
|
||||
HookName: in.HookName,
|
||||
Handler: in.Handler,
|
||||
Source: in.Source,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,9 @@ import (
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/protocol/goai"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/debug/gdebug"
|
||||
@ -63,11 +66,22 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err
|
||||
return
|
||||
}
|
||||
|
||||
type setHandlerInput struct {
|
||||
Prefix string
|
||||
Pattern string
|
||||
HandlerItem *handlerItem
|
||||
}
|
||||
|
||||
// setHandler creates router item with given handler and pattern and registers the handler to the router tree.
|
||||
// The router tree can be treated as a multilayer hash table, please refer to the comment in following codes.
|
||||
// This function is called during server starts up, which cares little about the performance. What really cares
|
||||
// is the well-designed router storage structure for router searching when the request is under serving.
|
||||
func (s *Server) setHandler(ctx context.Context, pattern string, handler *handlerItem) {
|
||||
func (s *Server) setHandler(ctx context.Context, in setHandlerInput) {
|
||||
var (
|
||||
prefix = in.Prefix
|
||||
pattern = in.Pattern
|
||||
handler = in.HandlerItem
|
||||
)
|
||||
handler.Id = handlerIdGenerator.Add(1)
|
||||
if handler.Source == "" {
|
||||
_, file, line := gdebug.CallerWithFilter(stackFilterKey)
|
||||
@ -78,6 +92,28 @@ func (s *Server) setHandler(ctx context.Context, pattern string, handler *handle
|
||||
s.Logger().Fatalf(ctx, `invalid pattern "%s", %+v`, pattern, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Change the registered route according meta info from its request structure.
|
||||
if handler.Info.Type != nil && handler.Info.Type.NumIn() == 2 {
|
||||
var (
|
||||
objectReq = reflect.New(handler.Info.Type.In(1))
|
||||
)
|
||||
if v := gmeta.Get(objectReq, goai.TagNamePath); !v.IsEmpty() {
|
||||
uri = v.String()
|
||||
}
|
||||
if v := gmeta.Get(objectReq, goai.TagNameMethod); !v.IsEmpty() {
|
||||
method = v.String()
|
||||
}
|
||||
if v := gmeta.Get(objectReq, goai.TagNameDomain); !v.IsEmpty() {
|
||||
domain = v.String()
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix for URI feature.
|
||||
if prefix != "" {
|
||||
uri = prefix + "/" + strings.TrimLeft(uri, "/")
|
||||
}
|
||||
|
||||
if len(uri) == 0 || uri[0] != '/' {
|
||||
s.Logger().Fatalf(ctx, `invalid pattern "%s", URI should lead with '/'`, pattern)
|
||||
return
|
||||
@ -177,7 +213,7 @@ func (s *Server) setHandler(ctx context.Context, pattern string, handler *handle
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
item = e.Value.(*handlerItem)
|
||||
// Checks the priority whether inserting the route item before current item,
|
||||
// which means it has more higher priority.
|
||||
// which means it has higher priority.
|
||||
if s.compareRouterPriority(handler, item) {
|
||||
l.InsertBefore(e, handler)
|
||||
pushed = true
|
||||
@ -211,11 +247,11 @@ func (s *Server) setHandler(ctx context.Context, pattern string, handler *handle
|
||||
|
||||
// compareRouterPriority compares the priority between `newItem` and `oldItem`. It returns true
|
||||
// if `newItem`'s priority is higher than `oldItem`, else it returns false. The higher priority
|
||||
// item will be insert into the router list before the other one.
|
||||
// item will be inserted into the router list before the other one.
|
||||
//
|
||||
// Comparison rules:
|
||||
// 1. The middleware has the most high priority.
|
||||
// 2. URI: The deeper the higher (simply check the count of char '/' in the URI).
|
||||
// 2. URI: The deeper, the higher (simply check the count of char '/' in the URI).
|
||||
// 3. Route type: {xxx} > :xxx > *xxx.
|
||||
func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerItem) bool {
|
||||
// If they're all type of middleware, the priority is according their registered sequence.
|
||||
@ -226,7 +262,7 @@ func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerIte
|
||||
if newItem.Type == HandlerTypeMiddleware && oldItem.Type != HandlerTypeMiddleware {
|
||||
return true
|
||||
}
|
||||
// URI: The deeper the higher (simply check the count of char '/' in the URI).
|
||||
// URI: The deeper, the higher (simply check the count of char '/' in the URI).
|
||||
if newItem.Router.Priority > oldItem.Router.Priority {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -10,10 +10,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/debug/gdebug"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
@ -28,9 +27,6 @@ type (
|
||||
middleware []HandlerFunc // Middleware array.
|
||||
}
|
||||
|
||||
// GroupItem is item for router group.
|
||||
GroupItem = []interface{}
|
||||
|
||||
// preBindItem is item for lazy registering feature of router group. preBindItem is not really registered
|
||||
// to server when route function of the group called but is lazily registered when server starts.
|
||||
preBindItem struct {
|
||||
@ -104,16 +100,17 @@ func (d *Domain) Group(prefix string, groups ...func(group *RouterGroup)) *Route
|
||||
if prefix == "/" {
|
||||
prefix = ""
|
||||
}
|
||||
group := &RouterGroup{
|
||||
routerGroup := &RouterGroup{
|
||||
domain: d,
|
||||
server: d.server,
|
||||
prefix: prefix,
|
||||
}
|
||||
if len(groups) > 0 {
|
||||
for _, v := range groups {
|
||||
v(group)
|
||||
for _, nestedGroup := range groups {
|
||||
nestedGroup(routerGroup)
|
||||
}
|
||||
}
|
||||
return group
|
||||
return routerGroup
|
||||
}
|
||||
|
||||
// Group creates and returns a sub-group of current router group.
|
||||
@ -153,34 +150,26 @@ func (g *RouterGroup) Clone() *RouterGroup {
|
||||
}
|
||||
|
||||
// Bind does batch route registering feature for router group.
|
||||
func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup {
|
||||
func (g *RouterGroup) Bind(handlerOrObject ...interface{}) *RouterGroup {
|
||||
var (
|
||||
ctx = context.TODO()
|
||||
group = g.Clone()
|
||||
)
|
||||
for _, item := range items {
|
||||
if len(item) < 3 {
|
||||
g.server.Logger().Fatalf(ctx, "invalid router item: %s", item)
|
||||
}
|
||||
bindType := gstr.ToUpper(gconv.String(item[0]))
|
||||
switch bindType {
|
||||
case groupBindTypeRest:
|
||||
group.preBindToLocalArray(groupBindTypeRest, gconv.String(item[0])+":"+gconv.String(item[1]), item[2])
|
||||
|
||||
case groupBindTypeMiddleware:
|
||||
group.preBindToLocalArray(groupBindTypeMiddleware, gconv.String(item[0])+":"+gconv.String(item[1]), item[2])
|
||||
for _, v := range handlerOrObject {
|
||||
var (
|
||||
item = v
|
||||
originValueAndKind = utils.OriginValueAndKind(item)
|
||||
)
|
||||
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Func, reflect.Struct:
|
||||
group = group.preBindToLocalArray(
|
||||
groupBindTypeHandler,
|
||||
"/",
|
||||
item,
|
||||
)
|
||||
default:
|
||||
if strings.EqualFold(bindType, "ALL") {
|
||||
bindType = ""
|
||||
} else {
|
||||
bindType += ":"
|
||||
}
|
||||
if len(item) > 3 {
|
||||
group.preBindToLocalArray(groupBindTypeHandler, bindType+gconv.String(item[1]), item[2], item[3])
|
||||
} else {
|
||||
group.preBindToLocalArray(groupBindTypeHandler, bindType+gconv.String(item[1]), item[2])
|
||||
}
|
||||
g.server.Logger().Fatalf(ctx, "invalid bind parameter type: %v", originValueAndKind.InputValue.Type())
|
||||
}
|
||||
}
|
||||
return group
|
||||
@ -188,7 +177,12 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup {
|
||||
|
||||
// ALL registers a http handler to given route pattern and all http methods.
|
||||
func (g *RouterGroup) ALL(pattern string, object interface{}, params ...interface{}) *RouterGroup {
|
||||
return g.Clone().preBindToLocalArray(groupBindTypeHandler, defaultMethod+":"+pattern, object, params...)
|
||||
return g.Clone().preBindToLocalArray(
|
||||
groupBindTypeHandler,
|
||||
defaultMethod+":"+pattern,
|
||||
object,
|
||||
params...,
|
||||
)
|
||||
}
|
||||
|
||||
// ALLMap registers http handlers for http methods using map.
|
||||
@ -312,10 +306,10 @@ func (g *RouterGroup) doBindRoutersToServer(ctx context.Context, item *preBindIt
|
||||
domain = ""
|
||||
}
|
||||
if bindType == groupBindTypeRest {
|
||||
pattern = prefix + "/" + strings.TrimLeft(path, "/")
|
||||
pattern = path
|
||||
} else {
|
||||
pattern = g.server.serveHandlerKey(
|
||||
method, prefix+"/"+strings.TrimLeft(path, "/"), domain,
|
||||
method, path, domain,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -337,57 +331,95 @@ func (g *RouterGroup) doBindRoutersToServer(ctx context.Context, item *preBindIt
|
||||
g.server.Logger().Fatal(ctx, err.Error())
|
||||
return g
|
||||
}
|
||||
if g.server != nil {
|
||||
g.server.doBindHandler(ctx, pattern, funcInfo, g.middleware, source)
|
||||
in := doBindHandlerInput{
|
||||
Prefix: prefix,
|
||||
Pattern: pattern,
|
||||
FuncInfo: funcInfo,
|
||||
Middleware: g.middleware,
|
||||
Source: source,
|
||||
}
|
||||
if g.domain != nil {
|
||||
g.domain.doBindHandler(ctx, in)
|
||||
} else {
|
||||
g.domain.doBindHandler(ctx, pattern, funcInfo, g.middleware, source)
|
||||
g.server.doBindHandler(ctx, in)
|
||||
}
|
||||
} else {
|
||||
if len(extras) > 0 {
|
||||
if g.server != nil {
|
||||
if gstr.Contains(extras[0], ",") {
|
||||
g.server.doBindObject(
|
||||
ctx, pattern, object, extras[0], g.middleware, source,
|
||||
)
|
||||
if gstr.Contains(extras[0], ",") {
|
||||
in := doBindObjectInput{
|
||||
Prefix: prefix,
|
||||
Pattern: pattern,
|
||||
Object: object,
|
||||
Method: extras[0],
|
||||
Middleware: g.middleware,
|
||||
Source: source,
|
||||
}
|
||||
if g.domain != nil {
|
||||
g.domain.doBindObject(ctx, in)
|
||||
} else {
|
||||
g.server.doBindObjectMethod(
|
||||
ctx, pattern, object, extras[0], g.middleware, source,
|
||||
)
|
||||
g.server.doBindObject(ctx, in)
|
||||
}
|
||||
} else {
|
||||
if gstr.Contains(extras[0], ",") {
|
||||
g.domain.doBindObject(
|
||||
ctx, pattern, object, extras[0], g.middleware, source,
|
||||
)
|
||||
in := doBindObjectMethodInput{
|
||||
Prefix: prefix,
|
||||
Pattern: pattern,
|
||||
Object: object,
|
||||
Method: extras[0],
|
||||
Middleware: g.middleware,
|
||||
Source: source,
|
||||
}
|
||||
if g.domain != nil {
|
||||
g.domain.doBindObjectMethod(ctx, in)
|
||||
} else {
|
||||
g.domain.doBindObjectMethod(
|
||||
ctx, pattern, object, extras[0], g.middleware, source,
|
||||
)
|
||||
g.server.doBindObjectMethod(ctx, in)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
in := doBindObjectInput{
|
||||
Prefix: prefix,
|
||||
Pattern: pattern,
|
||||
Object: object,
|
||||
Method: "",
|
||||
Middleware: g.middleware,
|
||||
Source: source,
|
||||
}
|
||||
// At last, it treats the `object` as Object registering type.
|
||||
if g.server != nil {
|
||||
g.server.doBindObject(ctx, pattern, object, "", g.middleware, source)
|
||||
if g.domain != nil {
|
||||
g.domain.doBindObject(ctx, in)
|
||||
} else {
|
||||
g.domain.doBindObject(ctx, pattern, object, "", g.middleware, source)
|
||||
g.server.doBindObject(ctx, in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case groupBindTypeRest:
|
||||
if g.server != nil {
|
||||
g.server.doBindObjectRest(ctx, pattern, object, g.middleware, source)
|
||||
in := doBindObjectInput{
|
||||
Prefix: prefix,
|
||||
Pattern: pattern,
|
||||
Object: object,
|
||||
Method: "",
|
||||
Middleware: g.middleware,
|
||||
Source: source,
|
||||
}
|
||||
if g.domain != nil {
|
||||
g.domain.doBindObjectRest(ctx, in)
|
||||
} else {
|
||||
g.domain.doBindObjectRest(ctx, pattern, object, g.middleware, source)
|
||||
g.server.doBindObjectRest(ctx, in)
|
||||
}
|
||||
|
||||
case groupBindTypeHook:
|
||||
if h, ok := object.(HandlerFunc); ok {
|
||||
if g.server != nil {
|
||||
g.server.doBindHookHandler(ctx, pattern, extras[0], h, source)
|
||||
if handler, ok := object.(HandlerFunc); ok {
|
||||
in := doBindHookHandlerInput{
|
||||
Prefix: prefix,
|
||||
Pattern: pattern,
|
||||
HookName: extras[0],
|
||||
Handler: handler,
|
||||
Source: source,
|
||||
}
|
||||
if g.domain != nil {
|
||||
g.domain.doBindHookHandler(ctx, in)
|
||||
} else {
|
||||
g.domain.doBindHookHandler(ctx, pattern, extras[0], h, source)
|
||||
g.server.doBindHookHandler(ctx, in)
|
||||
}
|
||||
} else {
|
||||
g.server.Logger().Fatalf(ctx, "invalid hook handler for pattern: %s", pattern)
|
||||
|
||||
@ -15,20 +15,41 @@ import (
|
||||
|
||||
// BindHookHandler registers handler for specified hook.
|
||||
func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFunc) {
|
||||
s.doBindHookHandler(context.TODO(), pattern, hook, handler, "")
|
||||
s.doBindHookHandler(context.TODO(), doBindHookHandlerInput{
|
||||
Prefix: "",
|
||||
Pattern: pattern,
|
||||
HookName: hook,
|
||||
Handler: handler,
|
||||
Source: "",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) doBindHookHandler(ctx context.Context, pattern string, hook string, handler HandlerFunc, source string) {
|
||||
s.setHandler(ctx, pattern, &handlerItem{
|
||||
Type: HandlerTypeHook,
|
||||
Name: gdebug.FuncPath(handler),
|
||||
Info: handlerFuncInfo{
|
||||
Func: handler,
|
||||
Type: reflect.TypeOf(handler),
|
||||
type doBindHookHandlerInput struct {
|
||||
Prefix string
|
||||
Pattern string
|
||||
HookName string
|
||||
Handler HandlerFunc
|
||||
Source string
|
||||
}
|
||||
|
||||
func (s *Server) doBindHookHandler(ctx context.Context, in doBindHookHandlerInput) {
|
||||
s.setHandler(
|
||||
ctx,
|
||||
setHandlerInput{
|
||||
Prefix: in.Prefix,
|
||||
Pattern: in.Pattern,
|
||||
HandlerItem: &handlerItem{
|
||||
Type: HandlerTypeHook,
|
||||
Name: gdebug.FuncPath(in.Handler),
|
||||
Info: handlerFuncInfo{
|
||||
Func: in.Handler,
|
||||
Type: reflect.TypeOf(in.Handler),
|
||||
},
|
||||
HookName: in.HookName,
|
||||
Source: in.Source,
|
||||
},
|
||||
},
|
||||
HookName: hook,
|
||||
Source: source,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) BindHookHandlerByMap(pattern string, hookMap map[string]HandlerFunc) {
|
||||
|
||||
@ -26,12 +26,16 @@ func (s *Server) BindMiddleware(pattern string, handlers ...HandlerFunc) {
|
||||
ctx = context.TODO()
|
||||
)
|
||||
for _, handler := range handlers {
|
||||
s.setHandler(ctx, pattern, &handlerItem{
|
||||
Type: HandlerTypeMiddleware,
|
||||
Name: gdebug.FuncPath(handler),
|
||||
Info: handlerFuncInfo{
|
||||
Func: handler,
|
||||
Type: reflect.TypeOf(handler),
|
||||
s.setHandler(ctx, setHandlerInput{
|
||||
Prefix: "",
|
||||
Pattern: pattern,
|
||||
HandlerItem: &handlerItem{
|
||||
Type: HandlerTypeMiddleware,
|
||||
Name: gdebug.FuncPath(handler),
|
||||
Info: handlerFuncInfo{
|
||||
Func: handler,
|
||||
Type: reflect.TypeOf(handler),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -45,12 +49,16 @@ func (s *Server) BindMiddlewareDefault(handlers ...HandlerFunc) {
|
||||
ctx = context.TODO()
|
||||
)
|
||||
for _, handler := range handlers {
|
||||
s.setHandler(ctx, defaultMiddlewarePattern, &handlerItem{
|
||||
Type: HandlerTypeMiddleware,
|
||||
Name: gdebug.FuncPath(handler),
|
||||
Info: handlerFuncInfo{
|
||||
Func: handler,
|
||||
Type: reflect.TypeOf(handler),
|
||||
s.setHandler(ctx, setHandlerInput{
|
||||
Prefix: "",
|
||||
Pattern: defaultMiddlewarePattern,
|
||||
HandlerItem: &handlerItem{
|
||||
Type: HandlerTypeMiddleware,
|
||||
Name: gdebug.FuncPath(handler),
|
||||
Info: handlerFuncInfo{
|
||||
Func: handler,
|
||||
Type: reflect.TypeOf(handler),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -19,9 +19,10 @@ import (
|
||||
)
|
||||
|
||||
// BindHandler registers a handler function to server with given pattern.
|
||||
// The parameter `handler` can be type of:
|
||||
// func(*ghttp.Request)
|
||||
// func(context.Context, Request)(Response, error)
|
||||
//
|
||||
// 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()
|
||||
@ -30,26 +31,49 @@ func (s *Server) BindHandler(pattern string, handler interface{}) {
|
||||
if err != nil {
|
||||
s.Logger().Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
s.doBindHandler(ctx, pattern, funcInfo, nil, "")
|
||||
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, pattern string, funcInfo handlerFuncInfo, middleware []HandlerFunc, source string) {
|
||||
s.setHandler(ctx, pattern, &handlerItem{
|
||||
Name: gdebug.FuncPath(funcInfo.Func),
|
||||
Type: HandlerTypeHandler,
|
||||
Info: funcInfo,
|
||||
Middleware: middleware,
|
||||
Source: source,
|
||||
func (s *Server) doBindHandler(ctx context.Context, in doBindHandlerInput) {
|
||||
s.setHandler(ctx, setHandlerInput{
|
||||
Prefix: in.Prefix,
|
||||
Pattern: in.Pattern,
|
||||
HandlerItem: &handlerItem{
|
||||
Name: gdebug.FuncPath(in.FuncInfo.Func),
|
||||
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, m map[string]*handlerItem) {
|
||||
for p, h := range m {
|
||||
s.setHandler(ctx, p, h)
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,6 +186,7 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, meth
|
||||
return
|
||||
}
|
||||
|
||||
// The request struct should be named as `xxxReq`.
|
||||
if !gstr.HasSuffix(reflectType.In(1).String(), `Req`) {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
@ -171,6 +196,7 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, meth
|
||||
return
|
||||
}
|
||||
|
||||
// The response struct should be named as `xxxRes`.
|
||||
if !gstr.HasSuffix(reflectType.Out(0).String(), `Res`) {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
|
||||
@ -21,8 +21,6 @@ import (
|
||||
//
|
||||
// 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) {
|
||||
var (
|
||||
bindMethod = ""
|
||||
@ -30,97 +28,124 @@ func (s *Server) BindObject(pattern string, object interface{}, method ...string
|
||||
if len(method) > 0 {
|
||||
bindMethod = method[0]
|
||||
}
|
||||
s.doBindObject(context.TODO(), pattern, object, bindMethod, nil, "")
|
||||
s.doBindObject(context.TODO(), doBindObjectInput{
|
||||
Prefix: "",
|
||||
Pattern: pattern,
|
||||
Object: object,
|
||||
Method: bindMethod,
|
||||
Middleware: nil,
|
||||
Source: "",
|
||||
})
|
||||
}
|
||||
|
||||
// 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(context.TODO(), pattern, object, method, nil, "")
|
||||
s.doBindObjectMethod(context.TODO(), doBindObjectMethodInput{
|
||||
Prefix: "",
|
||||
Pattern: pattern,
|
||||
Object: object,
|
||||
Method: method,
|
||||
Middleware: nil,
|
||||
Source: "",
|
||||
})
|
||||
}
|
||||
|
||||
// BindObjectRest registers object in REST API styles 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(context.TODO(), pattern, object, nil, "")
|
||||
s.doBindObjectRest(context.TODO(), doBindObjectInput{
|
||||
Prefix: "",
|
||||
Pattern: pattern,
|
||||
Object: object,
|
||||
Method: "",
|
||||
Middleware: nil,
|
||||
Source: "",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) doBindObject(ctx context.Context, pattern string, object interface{}, method string, middleware []HandlerFunc, source string) {
|
||||
type doBindObjectInput struct {
|
||||
Prefix string
|
||||
Pattern string
|
||||
Object interface{}
|
||||
Method string
|
||||
Middleware []HandlerFunc
|
||||
Source string
|
||||
}
|
||||
|
||||
func (s *Server) doBindObject(ctx context.Context, in doBindObjectInput) {
|
||||
// Convert input method to map for convenience and high performance searching purpose.
|
||||
var (
|
||||
methodMap map[string]bool
|
||||
)
|
||||
if len(method) > 0 {
|
||||
if len(in.Method) > 0 {
|
||||
methodMap = make(map[string]bool)
|
||||
for _, v := range strings.Split(method, ",") {
|
||||
for _, v := range strings.Split(in.Method, ",") {
|
||||
methodMap[strings.TrimSpace(v)] = true
|
||||
}
|
||||
}
|
||||
// If the `method` in `pattern` is `defaultMethod`,
|
||||
// it removes for convenience for next statement control.
|
||||
domain, method, path, err := s.parsePattern(pattern)
|
||||
domain, method, path, err := s.parsePattern(in.Pattern)
|
||||
if err != nil {
|
||||
s.Logger().Fatalf(ctx, `%+v`, err)
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(method, defaultMethod) {
|
||||
pattern = s.serveHandlerKey("", path, domain)
|
||||
in.Pattern = s.serveHandlerKey("", path, domain)
|
||||
}
|
||||
var (
|
||||
m = make(map[string]*handlerItem)
|
||||
v = reflect.ValueOf(object)
|
||||
t = v.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
handlerMap = make(map[string]*handlerItem)
|
||||
reflectValue = reflect.ValueOf(in.Object)
|
||||
reflectType = reflectValue.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
)
|
||||
// If given `object` is not pointer, it then creates a temporary one,
|
||||
// of which the value is `v`.
|
||||
if v.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(t)
|
||||
newValue.Elem().Set(v)
|
||||
v = newValue
|
||||
t = v.Type()
|
||||
if reflectValue.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(reflectType)
|
||||
newValue.Elem().Set(reflectValue)
|
||||
reflectValue = newValue
|
||||
reflectType = reflectValue.Type()
|
||||
}
|
||||
structName := t.Elem().Name()
|
||||
if v.MethodByName("Init").IsValid() {
|
||||
initFunc = v.MethodByName("Init").Interface().(func(*Request))
|
||||
structName := reflectType.Elem().Name()
|
||||
if reflectValue.MethodByName("Init").IsValid() {
|
||||
initFunc = reflectValue.MethodByName("Init").Interface().(func(*Request))
|
||||
}
|
||||
if v.MethodByName("Shut").IsValid() {
|
||||
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
|
||||
if reflectValue.MethodByName("Shut").IsValid() {
|
||||
shutFunc = reflectValue.MethodByName("Shut").Interface().(func(*Request))
|
||||
}
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
pkgPath := reflectType.Elem().PkgPath()
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
methodName := t.Method(i).Name
|
||||
for i := 0; i < reflectValue.NumMethod(); i++ {
|
||||
methodName := reflectType.Method(i).Name
|
||||
if methodMap != nil && !methodMap[methodName] {
|
||||
continue
|
||||
}
|
||||
if methodName == "Init" || methodName == "Shut" {
|
||||
continue
|
||||
}
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
objName := gstr.Replace(reflectType.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
|
||||
funcInfo, err := s.checkAndCreateFuncInfo(v.Method(i).Interface(), pkgPath, objName, methodName)
|
||||
funcInfo, err := s.checkAndCreateFuncInfo(reflectValue.Method(i).Interface(), pkgPath, objName, methodName)
|
||||
if err != nil {
|
||||
s.Logger().Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
|
||||
key := s.mergeBuildInNameToPattern(pattern, structName, methodName, true)
|
||||
m[key] = &handlerItem{
|
||||
key := s.mergeBuildInNameToPattern(in.Pattern, structName, methodName, true)
|
||||
handlerMap[key] = &handlerItem{
|
||||
Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
|
||||
Type: HandlerTypeObject,
|
||||
Info: funcInfo,
|
||||
InitFunc: initFunc,
|
||||
ShutFunc: shutFunc,
|
||||
Middleware: middleware,
|
||||
Source: source,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
}
|
||||
// If there's "Index" method, then an additional route is automatically added
|
||||
// to match the main URI, for example:
|
||||
@ -129,61 +154,70 @@ func (s *Server) doBindObject(ctx context.Context, pattern string, object interf
|
||||
//
|
||||
// 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) {
|
||||
if strings.EqualFold(methodName, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, in.Pattern) {
|
||||
p := gstr.PosRI(key, "/index")
|
||||
k := key[0:p] + key[p+6:]
|
||||
if len(k) == 0 || k[0] == '@' {
|
||||
k = "/" + k
|
||||
}
|
||||
m[k] = &handlerItem{
|
||||
handlerMap[k] = &handlerItem{
|
||||
Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
|
||||
Type: HandlerTypeObject,
|
||||
Info: funcInfo,
|
||||
InitFunc: initFunc,
|
||||
ShutFunc: shutFunc,
|
||||
Middleware: middleware,
|
||||
Source: source,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
}
|
||||
}
|
||||
}
|
||||
s.bindHandlerByMap(ctx, m)
|
||||
s.bindHandlerByMap(ctx, in.Prefix, handlerMap)
|
||||
}
|
||||
|
||||
func (s *Server) doBindObjectMethod(ctx context.Context, pattern string, object interface{}, method string, middleware []HandlerFunc, source string) {
|
||||
type doBindObjectMethodInput struct {
|
||||
Prefix string
|
||||
Pattern string
|
||||
Object interface{}
|
||||
Method string
|
||||
Middleware []HandlerFunc
|
||||
Source string
|
||||
}
|
||||
|
||||
func (s *Server) doBindObjectMethod(ctx context.Context, in doBindObjectMethodInput) {
|
||||
var (
|
||||
m = make(map[string]*handlerItem)
|
||||
v = reflect.ValueOf(object)
|
||||
t = v.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
handlerMap = make(map[string]*handlerItem)
|
||||
reflectValue = reflect.ValueOf(in.Object)
|
||||
reflectType = reflectValue.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
)
|
||||
// If given `object` is not pointer, it then creates a temporary one,
|
||||
// of which the value is `v`.
|
||||
if v.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(t)
|
||||
newValue.Elem().Set(v)
|
||||
v = newValue
|
||||
t = v.Type()
|
||||
if reflectValue.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(reflectType)
|
||||
newValue.Elem().Set(reflectValue)
|
||||
reflectValue = newValue
|
||||
reflectType = reflectValue.Type()
|
||||
}
|
||||
var (
|
||||
structName = t.Elem().Name()
|
||||
methodName = strings.TrimSpace(method)
|
||||
methodValue = v.MethodByName(methodName)
|
||||
structName = reflectType.Elem().Name()
|
||||
methodName = strings.TrimSpace(in.Method)
|
||||
methodValue = reflectValue.MethodByName(methodName)
|
||||
)
|
||||
if !methodValue.IsValid() {
|
||||
s.Logger().Fatalf(ctx, "invalid method name: %s", methodName)
|
||||
return
|
||||
}
|
||||
if v.MethodByName("Init").IsValid() {
|
||||
initFunc = v.MethodByName("Init").Interface().(func(*Request))
|
||||
if reflectValue.MethodByName("Init").IsValid() {
|
||||
initFunc = reflectValue.MethodByName("Init").Interface().(func(*Request))
|
||||
}
|
||||
if v.MethodByName("Shut").IsValid() {
|
||||
shutFunc = v.MethodByName("Shut").Interface().(func(*Request))
|
||||
if reflectValue.MethodByName("Shut").IsValid() {
|
||||
shutFunc = reflectValue.MethodByName("Shut").Interface().(func(*Request))
|
||||
}
|
||||
var (
|
||||
pkgPath = t.Elem().PkgPath()
|
||||
pkgPath = reflectType.Elem().PkgPath()
|
||||
pkgName = gfile.Basename(pkgPath)
|
||||
objName = gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
objName = gstr.Replace(reflectType.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
)
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
@ -194,70 +228,75 @@ func (s *Server) doBindObjectMethod(ctx context.Context, pattern string, object
|
||||
s.Logger().Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
|
||||
key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false)
|
||||
m[key] = &handlerItem{
|
||||
key := s.mergeBuildInNameToPattern(in.Pattern, structName, methodName, false)
|
||||
handlerMap[key] = &handlerItem{
|
||||
Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
|
||||
Type: HandlerTypeObject,
|
||||
Info: funcInfo,
|
||||
InitFunc: initFunc,
|
||||
ShutFunc: shutFunc,
|
||||
Middleware: middleware,
|
||||
Source: source,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
}
|
||||
|
||||
s.bindHandlerByMap(ctx, m)
|
||||
s.bindHandlerByMap(ctx, in.Prefix, handlerMap)
|
||||
}
|
||||
|
||||
func (s *Server) doBindObjectRest(ctx context.Context, pattern string, object interface{}, middleware []HandlerFunc, source string) {
|
||||
func (s *Server) doBindObjectRest(ctx context.Context, in doBindObjectInput) {
|
||||
var (
|
||||
m = make(map[string]*handlerItem)
|
||||
v = reflect.ValueOf(object)
|
||||
t = v.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
handlerMap = make(map[string]*handlerItem)
|
||||
reflectValue = reflect.ValueOf(in.Object)
|
||||
reflectType = reflectValue.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
)
|
||||
// If given `object` is not pointer, it then creates a temporary one,
|
||||
// of which the value is `v`.
|
||||
if v.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(t)
|
||||
newValue.Elem().Set(v)
|
||||
v = newValue
|
||||
t = v.Type()
|
||||
if reflectValue.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(reflectType)
|
||||
newValue.Elem().Set(reflectValue)
|
||||
reflectValue = newValue
|
||||
reflectType = reflectValue.Type()
|
||||
}
|
||||
structName := t.Elem().Name()
|
||||
if v.MethodByName(methodNameInit).IsValid() {
|
||||
initFunc = v.MethodByName(methodNameInit).Interface().(func(*Request))
|
||||
structName := reflectType.Elem().Name()
|
||||
if reflectValue.MethodByName(methodNameInit).IsValid() {
|
||||
initFunc = reflectValue.MethodByName(methodNameInit).Interface().(func(*Request))
|
||||
}
|
||||
if v.MethodByName(methodNameShut).IsValid() {
|
||||
shutFunc = v.MethodByName(methodNameShut).Interface().(func(*Request))
|
||||
if reflectValue.MethodByName(methodNameShut).IsValid() {
|
||||
shutFunc = reflectValue.MethodByName(methodNameShut).Interface().(func(*Request))
|
||||
}
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
methodName := t.Method(i).Name
|
||||
pkgPath := reflectType.Elem().PkgPath()
|
||||
for i := 0; i < reflectValue.NumMethod(); i++ {
|
||||
methodName := reflectType.Method(i).Name
|
||||
if _, ok := methodsMap[strings.ToUpper(methodName)]; !ok {
|
||||
continue
|
||||
}
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
objName := gstr.Replace(reflectType.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
|
||||
funcInfo, err := s.checkAndCreateFuncInfo(v.Method(i).Interface(), pkgPath, objName, methodName)
|
||||
funcInfo, err := s.checkAndCreateFuncInfo(
|
||||
reflectValue.Method(i).Interface(),
|
||||
pkgPath,
|
||||
objName,
|
||||
methodName,
|
||||
)
|
||||
if err != nil {
|
||||
s.Logger().Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
|
||||
key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false)
|
||||
m[key] = &handlerItem{
|
||||
key := s.mergeBuildInNameToPattern(methodName+":"+in.Pattern, structName, methodName, false)
|
||||
handlerMap[key] = &handlerItem{
|
||||
Name: fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, methodName),
|
||||
Type: HandlerTypeObject,
|
||||
Info: funcInfo,
|
||||
InitFunc: initFunc,
|
||||
ShutFunc: shutFunc,
|
||||
Middleware: middleware,
|
||||
Source: source,
|
||||
Middleware: in.Middleware,
|
||||
Source: in.Source,
|
||||
}
|
||||
}
|
||||
s.bindHandlerByMap(ctx, m)
|
||||
s.bindHandlerByMap(ctx, in.Prefix, handlerMap)
|
||||
}
|
||||
|
||||
@ -333,16 +333,16 @@ func Test_Router_DomainGroup(t *testing.T) {
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Group("/app", func(gApp *ghttp.RouterGroup) {
|
||||
gApp.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
|
||||
group.Group("/app", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
|
||||
intlog.Print(r.Context(), "/{table}/list/{page}.html")
|
||||
r.Response.Write(r.Get("table"), "&", r.Get("page"))
|
||||
})
|
||||
gApp.GET("/order/info/{order_id}", func(r *ghttp.Request) {
|
||||
group.GET("/order/info/{order_id}", func(r *ghttp.Request) {
|
||||
intlog.Print(r.Context(), "/order/info/{order_id}")
|
||||
r.Response.Write(r.Get("order_id"))
|
||||
})
|
||||
gApp.DELETE("/comment/{id}", func(r *ghttp.Request) {
|
||||
group.DELETE("/comment/{id}", func(r *ghttp.Request) {
|
||||
intlog.Print(r.Context(), "/comment/{id}")
|
||||
r.Response.Write(r.Get("id"))
|
||||
})
|
||||
|
||||
@ -74,33 +74,3 @@ func Test_Router_Group_Hook2(t *testing.T) {
|
||||
t.Assert(client.PostContent(ctx, "/api/ThisDoesNotExist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Group_Hook3(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Group("/api").Bind([]g.Slice{
|
||||
{"ALL", "handler", func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
}},
|
||||
{"ALL", "/*", func(r *ghttp.Request) {
|
||||
r.Response.Write("0")
|
||||
}, ghttp.HookBeforeServe},
|
||||
{"ALL", "/*", func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
}, ghttp.HookAfterServe},
|
||||
})
|
||||
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
t.Assert(client.GetContent(ctx, "/api/handler"), "012")
|
||||
t.Assert(client.PostContent(ctx, "/api/handler"), "012")
|
||||
t.Assert(client.DeleteContent(ctx, "/api/ThisDoesNotExist"), "02")
|
||||
})
|
||||
}
|
||||
|
||||
@ -78,39 +78,6 @@ func Test_Router_GroupBasic1(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_GroupBasic2(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
obj := new(GroupObject)
|
||||
// 分组路由批量注册
|
||||
s.Group("/api").Bind([]g.Slice{
|
||||
{"ALL", "/handler", Handler},
|
||||
{"ALL", "/obj", obj},
|
||||
{"GET", "/obj/my-show", obj, "Show"},
|
||||
{"REST", "/obj/rest", obj},
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/api/handler"), "Handler")
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/api/obj/delete"), "1Object Delete2")
|
||||
t.Assert(client.GetContent(ctx, "/api/obj/my-show"), "1Object Show2")
|
||||
t.Assert(client.GetContent(ctx, "/api/obj/show"), "1Object Show2")
|
||||
t.Assert(client.DeleteContent(ctx, "/api/obj/rest"), "1Object Delete2")
|
||||
|
||||
t.Assert(client.DeleteContent(ctx, "/ThisDoesNotExist"), "Not Found")
|
||||
t.Assert(client.DeleteContent(ctx, "/api/ThisDoesNotExist"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_GroupBuildInVar(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
|
||||
@ -59,3 +59,139 @@ func Test_Router_Handler_Extended_Handler_WithObject(t *testing.T) {
|
||||
t.Assert(client.GetContent(ctx, "/test/error"), `{"code":50,"message":"error","data":null}`)
|
||||
})
|
||||
}
|
||||
|
||||
type TestForHandlerWithObjectAndMeta1Req struct {
|
||||
g.Meta `path:"/custom-test1" method:"get"`
|
||||
Age int
|
||||
Name string
|
||||
}
|
||||
type TestForHandlerWithObjectAndMeta1Res struct {
|
||||
Id int
|
||||
Age int
|
||||
}
|
||||
|
||||
type TestForHandlerWithObjectAndMeta2Req struct {
|
||||
g.Meta `path:"/custom-test2" method:"get"`
|
||||
Age int
|
||||
Name string
|
||||
}
|
||||
type TestForHandlerWithObjectAndMeta2Res struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
type ControllerForHandlerWithObjectAndMeta1 struct{}
|
||||
|
||||
func (ControllerForHandlerWithObjectAndMeta1) Test1(ctx context.Context, req *TestForHandlerWithObjectAndMeta1Req) (res *TestForHandlerWithObjectAndMeta1Res, err error) {
|
||||
return &TestForHandlerWithObjectAndMeta1Res{
|
||||
Id: 1,
|
||||
Age: req.Age,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ControllerForHandlerWithObjectAndMeta1) Test2(ctx context.Context, req *TestForHandlerWithObjectAndMeta2Req) (res *TestForHandlerWithObjectAndMeta2Res, err error) {
|
||||
return &TestForHandlerWithObjectAndMeta2Res{
|
||||
Id: 1,
|
||||
Name: req.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type TestForHandlerWithObjectAndMeta3Req struct {
|
||||
g.Meta `path:"/custom-test3" method:"get"`
|
||||
Age int
|
||||
Name string
|
||||
}
|
||||
type TestForHandlerWithObjectAndMeta3Res struct {
|
||||
Id int
|
||||
Age int
|
||||
}
|
||||
|
||||
type TestForHandlerWithObjectAndMeta4Req struct {
|
||||
g.Meta `path:"/custom-test4" method:"get"`
|
||||
Age int
|
||||
Name string
|
||||
}
|
||||
type TestForHandlerWithObjectAndMeta4Res struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
type ControllerForHandlerWithObjectAndMeta2 struct{}
|
||||
|
||||
func (ControllerForHandlerWithObjectAndMeta2) Test3(ctx context.Context, req *TestForHandlerWithObjectAndMeta3Req) (res *TestForHandlerWithObjectAndMeta3Res, err error) {
|
||||
return &TestForHandlerWithObjectAndMeta3Res{
|
||||
Id: 1,
|
||||
Age: req.Age,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ControllerForHandlerWithObjectAndMeta2) Test4(ctx context.Context, req *TestForHandlerWithObjectAndMeta4Req) (res *TestForHandlerWithObjectAndMeta4Res, err error) {
|
||||
return &TestForHandlerWithObjectAndMeta4Res{
|
||||
Id: 1,
|
||||
Name: req.Name,
|
||||
}, nil
|
||||
}
|
||||
func Test_Router_Handler_Extended_Handler_WithObjectAndMeta(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Use(ghttp.MiddlewareHandlerResponse)
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", new(ControllerForHandlerWithObjectAndMeta1))
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/"), `{"code":0,"message":"","data":null}`)
|
||||
t.Assert(client.GetContent(ctx, "/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
|
||||
t.Assert(client.GetContent(ctx, "/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
|
||||
t.Assert(client.PostContent(ctx, "/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":null}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Handler_Extended_Handler_Group_Bind(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.Use(ghttp.MiddlewareHandlerResponse)
|
||||
s.Group("/api/v1", func(group *ghttp.RouterGroup) {
|
||||
group.Bind(
|
||||
new(ControllerForHandlerWithObjectAndMeta1),
|
||||
new(ControllerForHandlerWithObjectAndMeta2),
|
||||
)
|
||||
})
|
||||
s.Group("/api/v2", func(group *ghttp.RouterGroup) {
|
||||
group.Bind(
|
||||
new(ControllerForHandlerWithObjectAndMeta1),
|
||||
new(ControllerForHandlerWithObjectAndMeta2),
|
||||
)
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/"), `{"code":0,"message":"","data":null}`)
|
||||
t.Assert(client.GetContent(ctx, "/api/v1/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
|
||||
t.Assert(client.GetContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
|
||||
t.Assert(client.PostContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":null}`)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/api/v1/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
|
||||
t.Assert(client.GetContent(ctx, "/api/v1/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/api/v2/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
|
||||
t.Assert(client.GetContent(ctx, "/api/v2/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
|
||||
t.Assert(client.GetContent(ctx, "/api/v2/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`)
|
||||
t.Assert(client.GetContent(ctx, "/api/v2/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -82,6 +82,7 @@ const (
|
||||
TagNameMethod = `method`
|
||||
TagNameMime = `mime`
|
||||
TagNameType = `type`
|
||||
TagNameDomain = `domain`
|
||||
TagNameValidate = `v`
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user