v3 (feature): new mounting system (#2022)

* 🔥 chore: removed mount from router

*  feat: new mounting system

*  feat: mount for group

*  test: updated and improve for new mount system

* ♻️ refactor: better variable name

* ♻️ refactor: better function naming

* 🐛 fix: not mounted check

*  test: update onmount test if sub app is not mounted to parent

* 🎨 perf: fix allocs problem

*  test: add benchmark for MountPath

*  feat: access to app's mountpath

*  test: remove parent app's mountpath test

* remove some parts

* add comment

* fix

Co-authored-by: Muhammed Efe Çetin <efectn@protonmail.com>
Co-authored-by: RW <rene@gofiber.io>
pull/2216/head
Eren BALCI 2022-11-03 12:55:31 +03:00 committed by GitHub
parent 709e0952e2
commit 24fd165660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 47 deletions

35
app.go
View File

@ -618,33 +618,50 @@ func (app *App) GetRoutes(filterUseOption ...bool) []Route {
// Use registers a middleware route that will match requests
// with the provided prefix (which is optional and defaults to "/").
// Also, you can pass another app instance as a sub-router along a routing path.
// It's very useful to split up a large API as many independent routers and
// compose them as a single service using Use. The fiber's error handler and
// any of the fiber's sub apps are added to the application's error handlers
// to be invoked on errors that happen within the prefix route.
//
// app.Use(func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", handler, func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use(func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", handler, func(c fiber.Ctx) error {
// return c.Next()
// })
// subApp := fiber.New()
// app.Use("/mounted-path", subApp)
//
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
func (app *App) Use(args ...any) Router {
var prefix string
var subApp *App
var handlers []Handler
for i := 0; i < len(args); i++ {
switch arg := args[i].(type) {
case string:
prefix = arg
case *App:
subApp = arg
case Handler:
handlers = append(handlers, arg)
default:
panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
}
}
if subApp != nil {
app.mount(prefix, subApp)
return app
}
app.register([]string{methodUse}, prefix, nil, handlers...)
return app
}

View File

@ -39,32 +39,50 @@ func (grp *Group) Name(name string) Router {
// Use registers a middleware route that will match requests
// with the provided prefix (which is optional and defaults to "/").
// Also, you can pass another app instance as a sub-router along a routing path.
// It's very useful to split up a large API as many independent routers and
// compose them as a single service using Use. The fiber's error handler and
// any of the fiber's sub apps are added to the application's error handlers
// to be invoked on errors that happen within the prefix route.
//
// app.Use(func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", handler, func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use(func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", func(c fiber.Ctx) error {
// return c.Next()
// })
// app.Use("/api", handler, func(c fiber.Ctx) error {
// return c.Next()
// })
// subApp := fiber.New()
// app.Use("/mounted-path", subApp)
//
// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc...
func (grp *Group) Use(args ...any) Router {
prefix := ""
var subApp *App
var handlers []Handler
for i := 0; i < len(args); i++ {
switch arg := args[i].(type) {
case string:
prefix = arg
case *App:
subApp = arg
case Handler:
handlers = append(handlers, arg)
default:
panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg)))
}
}
if subApp != nil {
grp.mount(prefix, subApp)
return grp
}
grp.app.register([]string{methodUse}, getGroupPath(grp.Prefix, prefix), nil, handlers...)
return grp
}

View File

@ -30,7 +30,7 @@ func Test_Hook_OnRoute(t *testing.T) {
subApp := New()
subApp.Get("/test", testSimpleHandler)
app.Mount("/sub", subApp)
app.Use("/sub", subApp)
}
func Test_Hook_OnRoute_Mount(t *testing.T) {
@ -38,7 +38,7 @@ func Test_Hook_OnRoute_Mount(t *testing.T) {
app := New()
subApp := New()
app.Mount("/sub", subApp)
app.Use("/sub", subApp)
subApp.Hooks().OnRoute(func(r Route) error {
require.Equal(t, "/sub/test", r.Path)
@ -77,7 +77,7 @@ func Test_Hook_OnName(t *testing.T) {
subApp.Get("/test", testSimpleHandler)
subApp.Get("/test2", testSimpleHandler)
app.Mount("/sub", subApp)
app.Use("/sub", subApp)
require.Equal(t, "index", buf.String())
}
@ -124,7 +124,7 @@ func Test_Hook_OnGroup_Mount(t *testing.T) {
app := New()
micro := New()
micro.Mount("/john", app)
micro.Use("/john", app)
app.Hooks().OnGroup(func(g Group) error {
require.Equal(t, "/john/v1", g.Prefix)
@ -255,5 +255,5 @@ func Test_Hook_OnMount(t *testing.T) {
return nil
})
app.Mount("/sub", subApp)
app.Use("/sub", subApp)
}

View File

@ -36,7 +36,7 @@ func newMountFields(app *App) *mountFields {
// compose them as a single service using Mount. The fiber's error handler and
// any of the fiber's sub apps are added to the application's error handlers
// to be invoked on errors that happen within the prefix route.
func (app *App) Mount(prefix string, fiber *App) Router {
func (app *App) mount(prefix string, fiber *App) Router {
prefix = strings.TrimRight(prefix, "/")
if prefix == "" {
prefix = "/"
@ -58,8 +58,10 @@ func (app *App) Mount(prefix string, fiber *App) Router {
// Mount attaches another app instance as a sub-router along a routing path.
// It's very useful to split up a large API as many independent routers and
// compose them as a single service using Mount.
func (grp *Group) Mount(prefix string, fiber *App) Router {
// compose them as a single service using Mount. The fiber's error handler and
// any of the fiber's sub apps are added to the application's error handlers
// to be invoked on errors that happen within the prefix route.
func (grp *Group) mount(prefix string, fiber *App) Router {
groupPath := getGroupPath(grp.Prefix, prefix)
groupPath = strings.TrimRight(groupPath, "/")
if groupPath == "" {

View File

@ -21,7 +21,7 @@ func Test_App_Mount(t *testing.T) {
})
app := New()
app.Mount("/john", micro)
app.Use("/john", micro)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil))
require.Equal(t, nil, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
@ -35,9 +35,9 @@ func Test_App_Mount_Nested(t *testing.T) {
two := New()
three := New()
two.Mount("/three", three)
app.Mount("/one", one)
one.Mount("/two", two)
two.Use("/three", three)
app.Use("/one", one)
one.Use("/two", two)
one.Get("/doe", func(c Ctx) error {
return c.SendStatus(StatusOK)
@ -73,9 +73,9 @@ func Test_App_MountPath(t *testing.T) {
two := New()
three := New()
two.Mount("/three", three)
one.Mount("/two", two)
app.Mount("/one", one)
two.Use("/three", three)
one.Use("/two", two)
app.Use("/one", one)
require.Equal(t, "/one", one.MountPath())
require.Equal(t, "/one/two", two.MountPath())
@ -96,7 +96,7 @@ func Test_App_ErrorHandler_GroupMount(t *testing.T) {
app := New()
v1 := app.Group("/v1")
v1.Mount("/john", micro)
v1.Use("/john", micro)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
testErrorResponse(t, err, resp, "1: custom error")
@ -115,7 +115,7 @@ func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) {
app := New()
v1 := app.Group("/v1")
v1.Mount("/", micro)
v1.Use("/", micro)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
testErrorResponse(t, err, resp, "1: custom error")
@ -130,7 +130,7 @@ func Test_App_Group_Mount(t *testing.T) {
app := New()
v1 := app.Group("/v1")
v1.Mount("/john", micro)
v1.Use("/john", micro)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
require.Equal(t, nil, err, "app.Test(req)")
@ -150,7 +150,7 @@ func Test_App_UseMountedErrorHandler(t *testing.T) {
return errors.New("something happened")
})
app.Mount("/api", fiber)
app.Use("/api", fiber)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil))
testErrorResponse(t, err, resp, "hi, i'm a custom error")
@ -168,7 +168,7 @@ func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) {
return errors.New("something happened")
})
app.Mount("/", fiber)
app.Use("/", fiber)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil))
testErrorResponse(t, err, resp, "hi, i'm a custom error")
@ -196,7 +196,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) {
subfiber.Get("/", func(c Ctx) error {
return errors.New("something happened")
})
subfiber.Mount("/third", tripleSubFiber)
subfiber.Use("/third", tripleSubFiber)
f := func(c Ctx, err error) error {
return c.Status(200).SendString("hi, i'm a custom error")
@ -207,9 +207,9 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) {
fiber.Get("/", func(c Ctx) error {
return errors.New("something happened")
})
fiber.Mount("/sub", subfiber)
fiber.Use("/sub", subfiber)
app.Mount("/api", fiber)
app.Use("/api", fiber)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil))
require.Equal(t, nil, err, "/api/sub req")
@ -246,7 +246,7 @@ func Test_Ctx_Render_Mount(t *testing.T) {
})
app := New()
app.Mount("/hello", sub)
app.Use("/hello", sub)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil))
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
@ -301,8 +301,8 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) {
return c.Render("bruh.tmpl", Map{})
})
sub.Mount("/bruh", sub2)
app.Mount("/hello", sub)
sub.Use("/bruh", sub2)
app.Use("/hello", sub)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", nil))
require.Equal(t, StatusOK, resp.StatusCode, "Status code")
@ -348,7 +348,7 @@ func Test_Ctx_Render_MountGroup(t *testing.T) {
app := New()
v1 := app.Group("/v1")
v1.Mount("/john", micro)
v1.Use("/john", micro)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
require.Equal(t, nil, err, "app.Test(req)")

View File

@ -38,8 +38,6 @@ type Router interface {
Route(path string) Register
Mount(prefix string, fiber *App) Router
Name(name string) Router
}