v3 (feature): router interface changes (#2176)

* Add(methods []string, path string, handlers ...Handler) Router

* - proposed Router interface changes

Co-authored-by: rocketlaunchr-cto <rocketlaunchr.cloud@gmail.com>
pull/2022/head^2
pj 2022-11-01 18:45:43 +11:00 committed by GitHub
parent f26d9b1d4e
commit 709e0952e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 169 deletions

52
app.go
View File

@ -644,66 +644,66 @@ func (app *App) Use(args ...any) Router {
panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
}
}
app.register(methodUse, prefix, handlers...)
app.register([]string{methodUse}, prefix, nil, handlers...)
return app
}
// Get registers a route for GET methods that requests a representation
// of the specified resource. Requests using GET should only retrieve data.
func (app *App) Get(path string, handlers ...Handler) Router {
return app.Add(MethodGet, path, handlers...)
func (app *App) Get(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodGet}, path, handler, middleware...)
}
// Head registers a route for HEAD methods that asks for a response identical
// to that of a GET request, but without the response body.
func (app *App) Head(path string, handlers ...Handler) Router {
return app.Add(MethodHead, path, handlers...)
func (app *App) Head(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodHead}, path, handler, middleware...)
}
// Post registers a route for POST methods that is used to submit an entity to the
// specified resource, often causing a change in state or side effects on the server.
func (app *App) Post(path string, handlers ...Handler) Router {
return app.Add(MethodPost, path, handlers...)
func (app *App) Post(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodPost}, path, handler, middleware...)
}
// Put registers a route for PUT methods that replaces all current representations
// of the target resource with the request payload.
func (app *App) Put(path string, handlers ...Handler) Router {
return app.Add(MethodPut, path, handlers...)
func (app *App) Put(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodPut}, path, handler, middleware...)
}
// Delete registers a route for DELETE methods that deletes the specified resource.
func (app *App) Delete(path string, handlers ...Handler) Router {
return app.Add(MethodDelete, path, handlers...)
func (app *App) Delete(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodDelete}, path, handler, middleware...)
}
// Connect registers a route for CONNECT methods that establishes a tunnel to the
// server identified by the target resource.
func (app *App) Connect(path string, handlers ...Handler) Router {
return app.Add(MethodConnect, path, handlers...)
func (app *App) Connect(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodConnect}, path, handler, middleware...)
}
// Options registers a route for OPTIONS methods that is used to describe the
// communication options for the target resource.
func (app *App) Options(path string, handlers ...Handler) Router {
return app.Add(MethodOptions, path, handlers...)
func (app *App) Options(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodOptions}, path, handler, middleware...)
}
// Trace registers a route for TRACE methods that performs a message loop-back
// test along the path to the target resource.
func (app *App) Trace(path string, handlers ...Handler) Router {
return app.Add(MethodTrace, path, handlers...)
func (app *App) Trace(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodTrace}, path, handler, middleware...)
}
// Patch registers a route for PATCH methods that is used to apply partial
// modifications to a resource.
func (app *App) Patch(path string, handlers ...Handler) Router {
return app.Add(MethodPatch, path, handlers...)
func (app *App) Patch(path string, handler Handler, middleware ...Handler) Router {
return app.Add([]string{MethodPatch}, path, handler, middleware...)
}
// Add allows you to specify a HTTP method to register a route
func (app *App) Add(method, path string, handlers ...Handler) Router {
return app.register(method, path, handlers...)
// Add allows you to specify multiple HTTP methods to register a route.
func (app *App) Add(methods []string, path string, handler Handler, middleware ...Handler) Router {
return app.register(methods, path, handler, middleware...)
}
// Static will create a file server serving static files
@ -712,10 +712,8 @@ func (app *App) Static(prefix, root string, config ...Static) Router {
}
// All will register the handler on all HTTP methods
func (app *App) All(path string, handlers ...Handler) Router {
for _, method := range intMethod {
_ = app.Add(method, path, handlers...)
}
func (app *App) All(path string, handler Handler, middleware ...Handler) Router {
app.Add(intMethod, path, handler, middleware...)
return app
}
@ -725,7 +723,7 @@ func (app *App) All(path string, handlers ...Handler) Router {
// api.Get("/users", handler)
func (app *App) Group(prefix string, handlers ...Handler) Router {
if len(handlers) > 0 {
app.register(methodUse, prefix, handlers...)
app.register([]string{methodUse}, prefix, nil, handlers...)
}
grp := &Group{Prefix: prefix, app: app}
if err := app.hooks.executeOnGroupHooks(*grp); err != nil {

View File

@ -438,7 +438,7 @@ func Test_App_Add_Method_Test(t *testing.T) {
require.Equal(t, "add: invalid http method JOHN\n", fmt.Sprintf("%v", err))
}
}()
app.Add("JOHN", "/doe", testEmptyHandler)
app.Add([]string{"JOHN"}, "/doe", testEmptyHandler)
}
// go test -run Test_App_GETOnly

View File

@ -64,67 +64,67 @@ func (grp *Group) Use(args ...any) Router {
panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
}
}
grp.app.register(methodUse, getGroupPath(grp.Prefix, prefix), handlers...)
grp.app.register([]string{methodUse}, getGroupPath(grp.Prefix, prefix), nil, handlers...)
return grp
}
// Get registers a route for GET methods that requests a representation
// of the specified resource. Requests using GET should only retrieve data.
func (grp *Group) Get(path string, handlers ...Handler) Router {
func (grp *Group) Get(path string, handler Handler, middleware ...Handler) Router {
path = getGroupPath(grp.Prefix, path)
return grp.app.Add(MethodGet, path, handlers...)
return grp.app.Add([]string{MethodGet}, path, handler, middleware...)
}
// Head registers a route for HEAD methods that asks for a response identical
// to that of a GET request, but without the response body.
func (grp *Group) Head(path string, handlers ...Handler) Router {
return grp.Add(MethodHead, path, handlers...)
func (grp *Group) Head(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodHead}, path, handler, middleware...)
}
// Post registers a route for POST methods that is used to submit an entity to the
// specified resource, often causing a change in state or side effects on the server.
func (grp *Group) Post(path string, handlers ...Handler) Router {
return grp.Add(MethodPost, path, handlers...)
func (grp *Group) Post(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodPost}, path, handler, middleware...)
}
// Put registers a route for PUT methods that replaces all current representations
// of the target resource with the request payload.
func (grp *Group) Put(path string, handlers ...Handler) Router {
return grp.Add(MethodPut, path, handlers...)
func (grp *Group) Put(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodPut}, path, handler, middleware...)
}
// Delete registers a route for DELETE methods that deletes the specified resource.
func (grp *Group) Delete(path string, handlers ...Handler) Router {
return grp.Add(MethodDelete, path, handlers...)
func (grp *Group) Delete(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodDelete}, path, handler, middleware...)
}
// Connect registers a route for CONNECT methods that establishes a tunnel to the
// server identified by the target resource.
func (grp *Group) Connect(path string, handlers ...Handler) Router {
return grp.Add(MethodConnect, path, handlers...)
func (grp *Group) Connect(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodConnect}, path, handler, middleware...)
}
// Options registers a route for OPTIONS methods that is used to describe the
// communication options for the target resource.
func (grp *Group) Options(path string, handlers ...Handler) Router {
return grp.Add(MethodOptions, path, handlers...)
func (grp *Group) Options(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodOptions}, path, handler, middleware...)
}
// Trace registers a route for TRACE methods that performs a message loop-back
// test along the path to the target resource.
func (grp *Group) Trace(path string, handlers ...Handler) Router {
return grp.Add(MethodTrace, path, handlers...)
func (grp *Group) Trace(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodTrace}, path, handler, middleware...)
}
// Patch registers a route for PATCH methods that is used to apply partial
// modifications to a resource.
func (grp *Group) Patch(path string, handlers ...Handler) Router {
return grp.Add(MethodPatch, path, handlers...)
func (grp *Group) Patch(path string, handler Handler, middleware ...Handler) Router {
return grp.Add([]string{MethodPatch}, path, handler, middleware...)
}
// Add allows you to specify a HTTP method to register a route
func (grp *Group) Add(method, path string, handlers ...Handler) Router {
return grp.app.register(method, getGroupPath(grp.Prefix, path), handlers...)
// Add allows you to specify multiple HTTP methods to register a route.
func (grp *Group) Add(methods []string, path string, handler Handler, middleware ...Handler) Router {
return grp.app.register(methods, getGroupPath(grp.Prefix, path), handler, middleware...)
}
// Static will create a file server serving static files
@ -133,10 +133,8 @@ func (grp *Group) Static(prefix, root string, config ...Static) Router {
}
// All will register the handler on all HTTP methods
func (grp *Group) All(path string, handlers ...Handler) Router {
for _, method := range intMethod {
_ = grp.Add(method, path, handlers...)
}
func (grp *Group) All(path string, handler Handler, middleware ...Handler) Router {
grp.Add(intMethod, path, handler, middleware...)
return grp
}
@ -147,7 +145,7 @@ func (grp *Group) All(path string, handlers ...Handler) Router {
func (grp *Group) Group(prefix string, handlers ...Handler) Router {
prefix = getGroupPath(grp.Prefix, prefix)
if len(handlers) > 0 {
_ = grp.app.register(methodUse, prefix, handlers...)
grp.app.register([]string{methodUse}, prefix, nil, handlers...)
}
return grp.app.Group(prefix)
}

View File

@ -6,18 +6,18 @@ package fiber
// Register defines all router handle interface generate by Route().
type Register interface {
All(handlers ...Handler) Register
Get(handlers ...Handler) Register
Head(handlers ...Handler) Register
Post(handlers ...Handler) Register
Put(handlers ...Handler) Register
Delete(handlers ...Handler) Register
Connect(handlers ...Handler) Register
Options(handlers ...Handler) Register
Trace(handlers ...Handler) Register
Patch(handlers ...Handler) Register
All(handler Handler, middleware ...Handler) Register
Get(handler Handler, middleware ...Handler) Register
Head(handler Handler, middleware ...Handler) Register
Post(handler Handler, middleware ...Handler) Register
Put(handler Handler, middleware ...Handler) Register
Delete(handler Handler, middleware ...Handler) Register
Connect(handler Handler, middleware ...Handler) Register
Options(handler Handler, middleware ...Handler) Register
Trace(handler Handler, middleware ...Handler) Register
Patch(handler Handler, middleware ...Handler) Register
Add(method string, handlers ...Handler) Register
Add(methods []string, handler Handler, middleware ...Handler) Register
Static(root string, config ...Static) Register
@ -47,68 +47,68 @@ type Registering struct {
// })
//
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
func (r *Registering) All(handlers ...Handler) Register {
r.app.register(methodUse, r.path, handlers...)
func (r *Registering) All(handler Handler, middleware ...Handler) Register {
r.app.register([]string{methodUse}, r.path, handler, middleware...)
return r
}
// Get registers a route for GET methods that requests a representation
// of the specified resource. Requests using GET should only retrieve data.
func (r *Registering) Get(handlers ...Handler) Register {
r.app.Add(MethodHead, r.path, handlers...).Add(MethodGet, r.path, handlers...)
func (r *Registering) Get(handler Handler, middleware ...Handler) Register {
r.app.Add([]string{MethodHead}, r.path, handler, middleware...).Add([]string{MethodGet}, r.path, handler, middleware...)
return r
}
// Head registers a route for HEAD methods that asks for a response identical
// to that of a GET request, but without the response body.
func (r *Registering) Head(handlers ...Handler) Register {
return r.Add(MethodHead, handlers...)
func (r *Registering) Head(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodHead}, handler, middleware...)
}
// Post registers a route for POST methods that is used to submit an entity to the
// specified resource, often causing a change in state or side effects on the server.
func (r *Registering) Post(handlers ...Handler) Register {
return r.Add(MethodPost, handlers...)
func (r *Registering) Post(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodPost}, handler, middleware...)
}
// Put registers a route for PUT methods that replaces all current representations
// of the target resource with the request payload.
func (r *Registering) Put(handlers ...Handler) Register {
return r.Add(MethodPut, handlers...)
func (r *Registering) Put(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodPut}, handler, middleware...)
}
// Delete registers a route for DELETE methods that deletes the specified resource.
func (r *Registering) Delete(handlers ...Handler) Register {
return r.Add(MethodDelete, handlers...)
func (r *Registering) Delete(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodDelete}, handler, middleware...)
}
// Connect registers a route for CONNECT methods that establishes a tunnel to the
// server identified by the target resource.
func (r *Registering) Connect(handlers ...Handler) Register {
return r.Add(MethodConnect, handlers...)
func (r *Registering) Connect(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodConnect}, handler, middleware...)
}
// Options registers a route for OPTIONS methods that is used to describe the
// communication options for the target resource.
func (r *Registering) Options(handlers ...Handler) Register {
return r.Add(MethodOptions, handlers...)
func (r *Registering) Options(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodOptions}, handler, middleware...)
}
// Trace registers a route for TRACE methods that performs a message loop-back
// test along the r.Path to the target resource.
func (r *Registering) Trace(handlers ...Handler) Register {
return r.Add(MethodTrace, handlers...)
func (r *Registering) Trace(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodTrace}, handler, middleware...)
}
// Patch registers a route for PATCH methods that is used to apply partial
// modifications to a resource.
func (r *Registering) Patch(handlers ...Handler) Register {
return r.Add(MethodPatch, handlers...)
func (r *Registering) Patch(handler Handler, middleware ...Handler) Register {
return r.Add([]string{MethodPatch}, handler, middleware...)
}
// Add allows you to specify a HTTP method to register a route
func (r *Registering) Add(method string, handlers ...Handler) Register {
r.app.register(method, r.path, handlers...)
// Add allows you to specify multiple HTTP methods to register a route.
func (r *Registering) Add(methods []string, handler Handler, middleware ...Handler) Register {
r.app.register(methods, r.path, handler, middleware...)
return r
}

163
router.go
View File

@ -20,19 +20,19 @@ import (
type Router interface {
Use(args ...any) Router
Get(path string, handlers ...Handler) Router
Head(path string, handlers ...Handler) Router
Post(path string, handlers ...Handler) Router
Put(path string, handlers ...Handler) Router
Delete(path string, handlers ...Handler) Router
Connect(path string, handlers ...Handler) Router
Options(path string, handlers ...Handler) Router
Trace(path string, handlers ...Handler) Router
Patch(path string, handlers ...Handler) Router
Get(path string, handler Handler, middleware ...Handler) Router
Head(path string, handler Handler, middleware ...Handler) Router
Post(path string, handler Handler, middleware ...Handler) Router
Put(path string, handler Handler, middleware ...Handler) Router
Delete(path string, handler Handler, middleware ...Handler) Router
Connect(path string, handler Handler, middleware ...Handler) Router
Options(path string, handler Handler, middleware ...Handler) Router
Trace(path string, handler Handler, middleware ...Handler) Router
Patch(path string, handler Handler, middleware ...Handler) Router
Add(method, path string, handlers ...Handler) Router
Add(methods []string, path string, handler Handler, middleware ...Handler) Router
Static(prefix, root string, config ...Static) Router
All(path string, handlers ...Handler) Router
All(path string, handler Handler, middleware ...Handler) Router
Group(prefix string, handlers ...Handler) Router
@ -271,77 +271,86 @@ func (app *App) copyRoute(route *Route) *Route {
}
}
func (app *App) register(method, pathRaw string, handlers ...Handler) Router {
// Uppercase HTTP methods
method = utils.ToUpper(method)
// Check if the HTTP method is valid unless it's USE
if method != methodUse && methodInt(method) == -1 {
panic(fmt.Sprintf("add: invalid http method %s\n", method))
func (app *App) register(methods []string, pathRaw string, handler Handler, middleware ...Handler) Router {
handlers := middleware
if handler != nil {
handlers = append(handlers, handler)
}
// A route requires atleast one ctx handler
if len(handlers) == 0 {
panic(fmt.Sprintf("missing handler in route: %s\n", pathRaw))
}
// Cannot have an empty path
if pathRaw == "" {
pathRaw = "/"
}
// Path always start with a '/'
if pathRaw[0] != '/' {
pathRaw = "/" + pathRaw
}
// Create a stripped path in-case sensitive / trailing slashes
pathPretty := pathRaw
// Case sensitive routing, all to lowercase
if !app.config.CaseSensitive {
pathPretty = utils.ToLower(pathPretty)
}
// Strict routing, remove trailing slashes
if !app.config.StrictRouting && len(pathPretty) > 1 {
pathPretty = strings.TrimRight(pathPretty, "/")
}
// Is layer a middleware?
isUse := method == methodUse
// Is path a direct wildcard?
isStar := pathPretty == "/*"
// Is path a root slash?
isRoot := pathPretty == "/"
// Parse path parameters
parsedRaw := parseRoute(pathRaw)
parsedPretty := parseRoute(pathPretty)
// Create route metadata without pointer
route := Route{
// Router booleans
use: isUse,
star: isStar,
root: isRoot,
for _, method := range methods {
// Path data
path: RemoveEscapeChar(pathPretty),
routeParser: parsedPretty,
Params: parsedRaw.params,
// Public data
Path: pathRaw,
Method: method,
Handlers: handlers,
}
// Increment global handler count
atomic.AddUint32(&app.handlersCount, uint32(len(handlers)))
// Middleware route matches all HTTP methods
if isUse {
// Add route to all HTTP methods stack
for _, m := range intMethod {
// Create a route copy to avoid duplicates during compression
r := route
app.addRoute(m, &r)
// Uppercase HTTP methods
method = utils.ToUpper(method)
// Check if the HTTP method is valid unless it's USE
if method != methodUse && methodInt(method) == -1 {
panic(fmt.Sprintf("add: invalid http method %s\n", method))
}
// A route requires atleast one ctx handler
if len(handlers) == 0 {
panic(fmt.Sprintf("missing handler/middleware in route: %s\n", pathRaw))
}
// Cannot have an empty path
if pathRaw == "" {
pathRaw = "/"
}
// Path always start with a '/'
if pathRaw[0] != '/' {
pathRaw = "/" + pathRaw
}
// Create a stripped path in-case sensitive / trailing slashes
pathPretty := pathRaw
// Case sensitive routing, all to lowercase
if !app.config.CaseSensitive {
pathPretty = utils.ToLower(pathPretty)
}
// Strict routing, remove trailing slashes
if !app.config.StrictRouting && len(pathPretty) > 1 {
pathPretty = strings.TrimRight(pathPretty, "/")
}
// Is layer a middleware?
isUse := method == methodUse
// Is path a direct wildcard?
isStar := pathPretty == "/*"
// Is path a root slash?
isRoot := pathPretty == "/"
// Parse path parameters
parsedRaw := parseRoute(pathRaw)
parsedPretty := parseRoute(pathPretty)
// Create route metadata without pointer
route := Route{
// Router booleans
use: isUse,
star: isStar,
root: isRoot,
// Path data
path: RemoveEscapeChar(pathPretty),
routeParser: parsedPretty,
Params: parsedRaw.params,
// Public data
Path: pathRaw,
Method: method,
Handlers: handlers,
}
// Increment global handler count
atomic.AddUint32(&app.handlersCount, uint32(len(handlers)))
// Middleware route matches all HTTP methods
if isUse {
// Add route to all HTTP methods stack
for _, m := range intMethod {
// Create a route copy to avoid duplicates during compression
r := route
app.addRoute(m, &r)
}
} else {
// Add route to stack
app.addRoute(method, &route)
}
} else {
// Add route to stack
app.addRoute(method, &route)
}
return app
}

View File

@ -278,10 +278,10 @@ func Test_Router_Register_Missing_Handler(t *testing.T) {
app := New()
defer func() {
if err := recover(); err != nil {
require.Equal(t, "missing handler in route: /doe\n", fmt.Sprintf("%v", err))
require.Equal(t, "missing handler/middleware in route: /doe\n", fmt.Sprintf("%v", err))
}
}()
app.register("USE", "/doe")
app.register([]string{"USE"}, "/doe", nil)
}
func Test_Ensure_Router_Interface_Implementation(t *testing.T) {
@ -438,7 +438,7 @@ func registerDummyRoutes(app *App) {
return nil
}
for _, r := range routesFixture.GithubAPI {
app.Add(r.Method, r.Path, h)
app.Add([]string{r.Method}, r.Path, h)
}
}