mirror of https://github.com/gofiber/fiber.git
🐛 bug: improve mounting behavior (#2120)
* 🐛 bug: fix mounting doesn't work if when to declare it before routes * 🐛 bug: fix mounting doesn't work if when to declare it before routes * 🐛 bug: fix mounting doesn't work if when to declare it before routes * 🐛 bug: fix mounting doesn't work if when to declare it before routes * 🐛 bug: fix mounting doesn't work if when to declare it before routes * add onMount hooks, mountPath like express.js, better behavior for onName, onRoute, onGroup, onGroupName hooks on mounted apps * add comment * use once * fix views when both app and sub-app have view engine, better behavior for mount path * fix tests * fix tests * make some tasks * make some taskspull/2173/head
parent
5b1885a44c
commit
156b81c768
|
@ -0,0 +1 @@
|
|||
<h1>I'm Bruh</h1>
|
56
app.go
56
app.go
|
@ -18,7 +18,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
@ -106,8 +105,6 @@ type App struct {
|
|||
getBytes func(s string) (b []byte)
|
||||
// Converts byte slice to a string
|
||||
getString func(b []byte) string
|
||||
// Mounted and main apps
|
||||
appList map[string]*App
|
||||
// Hooks
|
||||
hooks *Hooks
|
||||
// Latest route & group
|
||||
|
@ -115,6 +112,8 @@ type App struct {
|
|||
latestGroup *Group
|
||||
// TLS handler
|
||||
tlsHandler *TLSHandler
|
||||
// Mount fields
|
||||
mountFields *mountFields
|
||||
}
|
||||
|
||||
// Config is a struct holding the server settings.
|
||||
|
@ -485,7 +484,6 @@ func New(config ...Config) *App {
|
|||
config: Config{},
|
||||
getBytes: utils.UnsafeBytes,
|
||||
getString: utils.UnsafeString,
|
||||
appList: make(map[string]*App),
|
||||
latestRoute: &Route{},
|
||||
latestGroup: &Group{},
|
||||
}
|
||||
|
@ -493,6 +491,9 @@ func New(config ...Config) *App {
|
|||
// Define hooks
|
||||
app.hooks = newHooks(app)
|
||||
|
||||
// Define mountFields
|
||||
app.mountFields = newMountFields(app)
|
||||
|
||||
// Override config if provided
|
||||
if len(config) > 0 {
|
||||
app.config = config[0]
|
||||
|
@ -549,9 +550,6 @@ func New(config ...Config) *App {
|
|||
// Override colors
|
||||
app.config.ColorScheme = defaultColors(app.config.ColorScheme)
|
||||
|
||||
// Init appList
|
||||
app.appList[""] = app
|
||||
|
||||
// Init app
|
||||
app.init()
|
||||
|
||||
|
@ -582,36 +580,6 @@ func (app *App) SetTLSHandler(tlsHandler *TLSHandler) {
|
|||
app.mutex.Unlock()
|
||||
}
|
||||
|
||||
// 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. 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 {
|
||||
stack := fiber.Stack()
|
||||
prefix = strings.TrimRight(prefix, "/")
|
||||
if prefix == "" {
|
||||
prefix = "/"
|
||||
}
|
||||
|
||||
for m := range stack {
|
||||
for r := range stack[m] {
|
||||
route := app.copyRoute(stack[m][r])
|
||||
app.addRoute(route.Method, app.addPrefixToRoute(prefix, route))
|
||||
}
|
||||
}
|
||||
|
||||
// Support for configs of mounted-apps and sub-mounted-apps
|
||||
for mountedPrefixes, subApp := range fiber.appList {
|
||||
app.appList[prefix+mountedPrefixes] = subApp
|
||||
subApp.init()
|
||||
}
|
||||
|
||||
atomic.AddUint32(&app.handlersCount, fiber.handlersCount)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// Name Assign name to specific route.
|
||||
func (app *App) Name(name string) Router {
|
||||
app.mutex.Lock()
|
||||
|
@ -991,7 +959,7 @@ func (app *App) ErrorHandler(ctx *Ctx, err error) error {
|
|||
mountedPrefixParts int
|
||||
)
|
||||
|
||||
for prefix, subApp := range app.appList {
|
||||
for prefix, subApp := range app.mountFields.appList {
|
||||
if prefix != "" && strings.HasPrefix(ctx.path, prefix) {
|
||||
parts := len(strings.Split(prefix, "/"))
|
||||
if mountedPrefixParts <= parts {
|
||||
|
@ -1041,7 +1009,17 @@ func (app *App) startupProcess() *App {
|
|||
}
|
||||
|
||||
app.mutex.Lock()
|
||||
defer app.mutex.Unlock()
|
||||
|
||||
// add routes of sub-apps
|
||||
app.mountFields.subAppsRoutesAdded.Do(func() {
|
||||
app.appendSubAppLists(app.mountFields.appList)
|
||||
app.addSubAppsRoutes(app.mountFields.appList)
|
||||
app.generateAppListKeys()
|
||||
})
|
||||
|
||||
// build route tree stack
|
||||
app.buildTree()
|
||||
app.mutex.Unlock()
|
||||
|
||||
return app
|
||||
}
|
||||
|
|
161
app_test.go
161
app_test.go
|
@ -246,44 +246,6 @@ func Test_App_ErrorHandler_RouteStack(t *testing.T) {
|
|||
utils.AssertEqual(t, "1: USE error", string(body))
|
||||
}
|
||||
|
||||
func Test_App_ErrorHandler_GroupMount(t *testing.T) {
|
||||
micro := New(Config{
|
||||
ErrorHandler: func(c *Ctx, err error) error {
|
||||
utils.AssertEqual(t, "0: GET error", err.Error())
|
||||
return c.Status(500).SendString("1: custom error")
|
||||
},
|
||||
})
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return errors.New("0: GET error")
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/john", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
testErrorResponse(t, err, resp, "1: custom error")
|
||||
}
|
||||
|
||||
func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) {
|
||||
micro := New(Config{
|
||||
ErrorHandler: func(c *Ctx, err error) error {
|
||||
utils.AssertEqual(t, "0: GET error", err.Error())
|
||||
return c.Status(500).SendString("1: custom error")
|
||||
},
|
||||
})
|
||||
micro.Get("/john/doe", func(c *Ctx) error {
|
||||
return errors.New("0: GET error")
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
testErrorResponse(t, err, resp, "1: custom error")
|
||||
}
|
||||
|
||||
func Test_App_Nested_Params(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
|
@ -307,22 +269,6 @@ func Test_App_Nested_Params(t *testing.T) {
|
|||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
}
|
||||
|
||||
// go test -run Test_App_Mount
|
||||
func Test_App_Mount(t *testing.T) {
|
||||
micro := New()
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
|
||||
app := New()
|
||||
app.Mount("/john", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, uint32(2), app.handlersCount)
|
||||
}
|
||||
|
||||
func Test_App_Use_Params(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
|
@ -1046,23 +992,6 @@ func Test_App_Group_Invalid(t *testing.T) {
|
|||
New().Group("/").Use(1)
|
||||
}
|
||||
|
||||
// go test -run Test_App_Group_Mount
|
||||
func Test_App_Group_Mount(t *testing.T) {
|
||||
micro := New()
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/john", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, uint32(2), app.handlersCount)
|
||||
}
|
||||
|
||||
func Test_App_Group(t *testing.T) {
|
||||
dummyHandler := testEmptyHandler
|
||||
|
||||
|
@ -1538,96 +1467,6 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) {
|
|||
utils.AssertEqual(t, testString, string(body))
|
||||
}
|
||||
|
||||
func Test_App_UseMountedErrorHandler(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
fiber := New(Config{
|
||||
ErrorHandler: func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(500).SendString("hi, i'm a custom error")
|
||||
},
|
||||
})
|
||||
fiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
|
||||
app.Mount("/api", fiber)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil))
|
||||
testErrorResponse(t, err, resp, "hi, i'm a custom error")
|
||||
}
|
||||
|
||||
func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
fiber := New(Config{
|
||||
ErrorHandler: func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(500).SendString("hi, i'm a custom error")
|
||||
},
|
||||
})
|
||||
fiber.Get("/api", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
|
||||
app.Mount("/", fiber)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil))
|
||||
testErrorResponse(t, err, resp, "hi, i'm a custom error")
|
||||
}
|
||||
|
||||
func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
tsf := func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(200).SendString("hi, i'm a custom sub sub fiber error")
|
||||
}
|
||||
tripleSubFiber := New(Config{
|
||||
ErrorHandler: tsf,
|
||||
})
|
||||
tripleSubFiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
|
||||
sf := func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(200).SendString("hi, i'm a custom sub fiber error")
|
||||
}
|
||||
subfiber := New(Config{
|
||||
ErrorHandler: sf,
|
||||
})
|
||||
subfiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
subfiber.Mount("/third", tripleSubFiber)
|
||||
|
||||
f := func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(200).SendString("hi, i'm a custom error")
|
||||
}
|
||||
fiber := New(Config{
|
||||
ErrorHandler: f,
|
||||
})
|
||||
fiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
fiber.Mount("/sub", subfiber)
|
||||
|
||||
app.Mount("/api", fiber)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil))
|
||||
utils.AssertEqual(t, nil, err, "/api/sub req")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err, "iotuil.ReadAll()")
|
||||
utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body")
|
||||
|
||||
resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", nil))
|
||||
utils.AssertEqual(t, nil, err, "/api/sub/third req")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
b, err = ioutil.ReadAll(resp2.Body)
|
||||
utils.AssertEqual(t, nil, err, "iotuil.ReadAll()")
|
||||
utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body")
|
||||
}
|
||||
|
||||
func Test_App_Test_no_timeout_infinitely(t *testing.T) {
|
||||
var err error
|
||||
c := make(chan int)
|
||||
|
|
12
ctx.go
12
ctx.go
|
@ -1384,11 +1384,13 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error {
|
|||
buf := bytebufferpool.Get()
|
||||
defer bytebufferpool.Put(buf)
|
||||
|
||||
// Pass-locals-to-views & bind
|
||||
// Pass-locals-to-views, bind, appListKeys
|
||||
c.renderExtensions(bind)
|
||||
|
||||
rendered := false
|
||||
for prefix, app := range c.app.appList {
|
||||
var rendered bool
|
||||
for i := len(c.app.mountFields.appListKeys) - 1; i >= 0; i-- {
|
||||
prefix := c.app.mountFields.appListKeys[i]
|
||||
app := c.app.mountFields.appList[prefix]
|
||||
if prefix == "" || strings.Contains(c.OriginalURL(), prefix) {
|
||||
if len(layouts) == 0 && app.config.ViewsLayout != "" {
|
||||
layouts = []string{
|
||||
|
@ -1454,6 +1456,10 @@ func (c *Ctx) renderExtensions(bind interface{}) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.app.mountFields.appListKeys) == 0 {
|
||||
c.app.generateAppListKeys()
|
||||
}
|
||||
}
|
||||
|
||||
// Route returns the matched Route struct.
|
||||
|
|
59
ctx_test.go
59
ctx_test.go
|
@ -32,7 +32,6 @@ import (
|
|||
|
||||
"github.com/gofiber/fiber/v2/internal/bytebufferpool"
|
||||
"github.com/gofiber/fiber/v2/internal/storage/memory"
|
||||
"github.com/gofiber/fiber/v2/internal/template/html"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
@ -2792,58 +2791,6 @@ func Test_Ctx_Render(t *testing.T) {
|
|||
utils.AssertEqual(t, false, err == nil)
|
||||
}
|
||||
|
||||
// go test -run Test_Ctx_Render_Mount
|
||||
func Test_Ctx_Render_Mount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sub := New(Config{
|
||||
Views: html.New("./.github/testdata/template", ".gohtml"),
|
||||
})
|
||||
|
||||
sub.Get("/:name", func(ctx *Ctx) error {
|
||||
return ctx.Render("hello_world", Map{
|
||||
"Name": ctx.Params("name"),
|
||||
})
|
||||
})
|
||||
|
||||
app := New()
|
||||
app.Mount("/hello", sub)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil))
|
||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, "<h1>Hello a!</h1>", string(body))
|
||||
}
|
||||
|
||||
func Test_Ctx_Render_MountGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
micro := New(Config{
|
||||
Views: html.New("./.github/testdata/template", ".gohtml"),
|
||||
})
|
||||
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return c.Render("hello_world", Map{
|
||||
"Name": "doe",
|
||||
})
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/john", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, "<h1>Hello doe!</h1>", string(body))
|
||||
}
|
||||
|
||||
func Test_Ctx_RenderWithoutLocals(t *testing.T) {
|
||||
t.Parallel()
|
||||
app := New(Config{
|
||||
|
@ -3149,6 +3096,7 @@ func Test_Ctx_RestartRoutingWithChangedPathAndCatchAll(t *testing.T) {
|
|||
|
||||
type testTemplateEngine struct {
|
||||
templates *template.Template
|
||||
path string
|
||||
}
|
||||
|
||||
func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error {
|
||||
|
@ -3160,7 +3108,10 @@ func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{},
|
|||
}
|
||||
|
||||
func (t *testTemplateEngine) Load() error {
|
||||
t.templates = template.Must(template.ParseGlob("./.github/testdata/*.tmpl"))
|
||||
if t.path == "" {
|
||||
t.path = "testdata"
|
||||
}
|
||||
t.templates = template.Must(template.ParseGlob("./.github/" + t.path + "/*.tmpl"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
30
group.go
30
group.go
|
@ -8,7 +8,6 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Group struct
|
||||
|
@ -19,35 +18,6 @@ type Group struct {
|
|||
Prefix string
|
||||
}
|
||||
|
||||
// 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 {
|
||||
stack := fiber.Stack()
|
||||
groupPath := getGroupPath(grp.Prefix, prefix)
|
||||
groupPath = strings.TrimRight(groupPath, "/")
|
||||
if groupPath == "" {
|
||||
groupPath = "/"
|
||||
}
|
||||
|
||||
for m := range stack {
|
||||
for r := range stack[m] {
|
||||
route := grp.app.copyRoute(stack[m][r])
|
||||
grp.app.addRoute(route.Method, grp.app.addPrefixToRoute(groupPath, route))
|
||||
}
|
||||
}
|
||||
|
||||
// Support for configs of mounted-apps and sub-mounted-apps
|
||||
for mountedPrefixes, subApp := range fiber.appList {
|
||||
grp.app.appList[groupPath+mountedPrefixes] = subApp
|
||||
subApp.init()
|
||||
}
|
||||
|
||||
atomic.AddUint32(&grp.app.handlersCount, fiber.handlersCount)
|
||||
|
||||
return grp
|
||||
}
|
||||
|
||||
// Name Assign name to specific route.
|
||||
func (grp *Group) Name(name string) Router {
|
||||
grp.app.mutex.Lock()
|
||||
|
|
44
hooks.go
44
hooks.go
|
@ -8,6 +8,7 @@ type OnGroupNameHandler = OnGroupHandler
|
|||
type OnListenHandler = func() error
|
||||
type OnShutdownHandler = OnListenHandler
|
||||
type OnForkHandler = func(int) error
|
||||
type OnMountHandler = func(*App) error
|
||||
|
||||
// Hooks is a struct to use it with App.
|
||||
type Hooks struct {
|
||||
|
@ -22,6 +23,7 @@ type Hooks struct {
|
|||
onListen []OnListenHandler
|
||||
onShutdown []OnShutdownHandler
|
||||
onFork []OnForkHandler
|
||||
onMount []OnMountHandler
|
||||
}
|
||||
|
||||
func newHooks(app *App) *Hooks {
|
||||
|
@ -34,6 +36,7 @@ func newHooks(app *App) *Hooks {
|
|||
onListen: make([]OnListenHandler, 0),
|
||||
onShutdown: make([]OnShutdownHandler, 0),
|
||||
onFork: make([]OnForkHandler, 0),
|
||||
onMount: make([]OnMountHandler, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +97,22 @@ func (h *Hooks) OnFork(handler ...OnForkHandler) {
|
|||
h.app.mutex.Unlock()
|
||||
}
|
||||
|
||||
// OnMount is a hook to execute user function after mounting process.
|
||||
// The mount event is fired when sub-app is mounted on a parent app. The parent app is passed as a parameter.
|
||||
// It works for app and group mounting.
|
||||
func (h *Hooks) OnMount(handler ...OnMountHandler) {
|
||||
h.app.mutex.Lock()
|
||||
h.onMount = append(h.onMount, handler...)
|
||||
h.app.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnRouteHooks(route Route) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
route.path = h.app.mountFields.mountPath + route.path
|
||||
route.Path = route.path
|
||||
}
|
||||
|
||||
for _, v := range h.onRoute {
|
||||
if err := v(route); err != nil {
|
||||
return err
|
||||
|
@ -105,6 +123,12 @@ func (h *Hooks) executeOnRouteHooks(route Route) error {
|
|||
}
|
||||
|
||||
func (h *Hooks) executeOnNameHooks(route Route) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
route.path = h.app.mountFields.mountPath + route.path
|
||||
route.Path = route.path
|
||||
}
|
||||
|
||||
for _, v := range h.onName {
|
||||
if err := v(route); err != nil {
|
||||
return err
|
||||
|
@ -115,6 +139,11 @@ func (h *Hooks) executeOnNameHooks(route Route) error {
|
|||
}
|
||||
|
||||
func (h *Hooks) executeOnGroupHooks(group Group) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
group.Prefix = h.app.mountFields.mountPath + group.Prefix
|
||||
}
|
||||
|
||||
for _, v := range h.onGroup {
|
||||
if err := v(group); err != nil {
|
||||
return err
|
||||
|
@ -125,6 +154,11 @@ func (h *Hooks) executeOnGroupHooks(group Group) error {
|
|||
}
|
||||
|
||||
func (h *Hooks) executeOnGroupNameHooks(group Group) error {
|
||||
// Check mounting
|
||||
if h.app.mountFields.mountPath != "" {
|
||||
group.Prefix = h.app.mountFields.mountPath + group.Prefix
|
||||
}
|
||||
|
||||
for _, v := range h.onGroupName {
|
||||
if err := v(group); err != nil {
|
||||
return err
|
||||
|
@ -155,3 +189,13 @@ func (h *Hooks) executeOnForkHooks(pid int) {
|
|||
_ = v(pid)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hooks) executeOnMountHooks(app *App) error {
|
||||
for _, v := range h.onMount {
|
||||
if err := v(app); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -33,6 +33,29 @@ func Test_Hook_OnRoute(t *testing.T) {
|
|||
app.Mount("/sub", subApp)
|
||||
}
|
||||
|
||||
func Test_Hook_OnRoute_Mount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
app := New()
|
||||
subApp := New()
|
||||
app.Mount("/sub", subApp)
|
||||
|
||||
subApp.Hooks().OnRoute(func(r Route) error {
|
||||
utils.AssertEqual(t, "/sub/test", r.Path)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
app.Hooks().OnRoute(func(r Route) error {
|
||||
utils.AssertEqual(t, "/", r.Path)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
app.Get("/", testSimpleHandler).Name("x")
|
||||
subApp.Get("/test", testSimpleHandler)
|
||||
}
|
||||
|
||||
func Test_Hook_OnName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -96,6 +119,24 @@ func Test_Hook_OnGroup(t *testing.T) {
|
|||
utils.AssertEqual(t, "/x/x/a", buf.String())
|
||||
}
|
||||
|
||||
func Test_Hook_OnGroup_Mount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
app := New()
|
||||
micro := New()
|
||||
micro.Mount("/john", app)
|
||||
|
||||
app.Hooks().OnGroup(func(g Group) error {
|
||||
utils.AssertEqual(t, "/john/v1", g.Prefix)
|
||||
return nil
|
||||
})
|
||||
|
||||
v1 := app.Group("/v1")
|
||||
v1.Get("/doe", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Hook_OnGroupName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -200,3 +241,21 @@ func Test_Hook_OnHook(t *testing.T) {
|
|||
|
||||
utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, ":3000", nil))
|
||||
}
|
||||
|
||||
func Test_Hook_OnMount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
app := New()
|
||||
app.Get("/", testSimpleHandler).Name("x")
|
||||
|
||||
subApp := New()
|
||||
subApp.Get("/test", testSimpleHandler)
|
||||
|
||||
subApp.Hooks().OnMount(func(parent *App) error {
|
||||
utils.AssertEqual(t, parent.mountFields.mountPath, "")
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
app.Mount("/sub", subApp)
|
||||
}
|
||||
|
|
|
@ -183,15 +183,6 @@ func (e *Engine) Load() error {
|
|||
|
||||
// Render will execute the template name along with the given values.
|
||||
func (e *Engine) Render(out io.Writer, template string, binding interface{}, layout ...string) error {
|
||||
if !e.loaded || e.reload {
|
||||
if e.reload {
|
||||
e.loaded = false
|
||||
}
|
||||
if err := e.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tmpl := e.Templates.Lookup(template)
|
||||
if tmpl == nil {
|
||||
return fmt.Errorf("render: template %s does not exist", template)
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
||||
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
||||
// 📌 API Documentation: https://docs.gofiber.io
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Put fields related to mounting.
|
||||
type mountFields struct {
|
||||
// Mounted and main apps
|
||||
appList map[string]*App
|
||||
// Ordered keys of apps (sorted by key length for Render)
|
||||
appListKeys []string
|
||||
// check added routes of sub-apps
|
||||
subAppsRoutesAdded sync.Once
|
||||
// Prefix of app if it was mounted
|
||||
mountPath string
|
||||
}
|
||||
|
||||
// Create empty mountFields instance
|
||||
func newMountFields(app *App) *mountFields {
|
||||
return &mountFields{
|
||||
appList: map[string]*App{"": app},
|
||||
appListKeys: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// 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. 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 {
|
||||
prefix = strings.TrimRight(prefix, "/")
|
||||
if prefix == "" {
|
||||
prefix = "/"
|
||||
}
|
||||
|
||||
// Support for configs of mounted-apps and sub-mounted-apps
|
||||
for mountedPrefixes, subApp := range fiber.mountFields.appList {
|
||||
subApp.mountFields.mountPath = prefix + mountedPrefixes
|
||||
app.mountFields.appList[prefix+mountedPrefixes] = subApp
|
||||
}
|
||||
|
||||
// Execute onMount hooks
|
||||
if err := fiber.hooks.executeOnMountHooks(app); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// 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 {
|
||||
groupPath := getGroupPath(grp.Prefix, prefix)
|
||||
groupPath = strings.TrimRight(groupPath, "/")
|
||||
if groupPath == "" {
|
||||
groupPath = "/"
|
||||
}
|
||||
|
||||
// Support for configs of mounted-apps and sub-mounted-apps
|
||||
for mountedPrefixes, subApp := range fiber.mountFields.appList {
|
||||
subApp.mountFields.mountPath = groupPath + mountedPrefixes
|
||||
grp.app.mountFields.appList[groupPath+mountedPrefixes] = subApp
|
||||
}
|
||||
|
||||
// Execute onMount hooks
|
||||
if err := fiber.hooks.executeOnMountHooks(grp.app); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return grp
|
||||
}
|
||||
|
||||
// The MountPath property contains one or more path patterns on which a sub-app was mounted.
|
||||
func (app *App) MountPath() string {
|
||||
return app.mountFields.mountPath
|
||||
}
|
||||
|
||||
// generateAppListKeys generates app list keys for Render, should work after appendSubAppLists
|
||||
func (app *App) generateAppListKeys() {
|
||||
for key := range app.mountFields.appList {
|
||||
app.mountFields.appListKeys = append(app.mountFields.appListKeys, key)
|
||||
}
|
||||
|
||||
sort.Slice(app.mountFields.appListKeys, func(i, j int) bool {
|
||||
return len(app.mountFields.appListKeys[i]) < len(app.mountFields.appListKeys[j])
|
||||
})
|
||||
}
|
||||
|
||||
// appendSubAppLists supports nested for sub apps
|
||||
func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) {
|
||||
for prefix, subApp := range appList {
|
||||
// skip real app
|
||||
if prefix == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(parent) > 0 {
|
||||
prefix = parent[0] + prefix
|
||||
}
|
||||
|
||||
if _, ok := app.mountFields.appList[prefix]; !ok {
|
||||
app.mountFields.appList[prefix] = subApp
|
||||
}
|
||||
|
||||
// The first element of appList is always the app itself. If there are no other sub apps, we should skip appending nested apps.
|
||||
if len(subApp.mountFields.appList) > 1 {
|
||||
app.appendSubAppLists(subApp.mountFields.appList, prefix)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// addSubAppsRoutes adds routes of sub apps nestedly when to start the server
|
||||
func (app *App) addSubAppsRoutes(appList map[string]*App, parent ...string) {
|
||||
for prefix, subApp := range appList {
|
||||
// skip real app
|
||||
if prefix == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(parent) > 0 {
|
||||
prefix = parent[0] + prefix
|
||||
}
|
||||
|
||||
// add routes
|
||||
stack := subApp.stack
|
||||
for m := range stack {
|
||||
for r := range stack[m] {
|
||||
route := app.copyRoute(stack[m][r])
|
||||
app.addRoute(route.Method, app.addPrefixToRoute(prefix, route), true)
|
||||
}
|
||||
}
|
||||
|
||||
atomic.AddUint32(&app.handlersCount, subApp.handlersCount)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
||||
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
||||
// 📌 API Documentation: https://docs.gofiber.io
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gofiber/fiber/v2/internal/template/html"
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
)
|
||||
|
||||
// go test -run Test_App_Mount
|
||||
func Test_App_Mount(t *testing.T) {
|
||||
micro := New()
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
|
||||
app := New()
|
||||
app.Mount("/john", micro)
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, uint32(2), app.handlersCount)
|
||||
}
|
||||
|
||||
// go test -run Test_App_Mount_Nested
|
||||
func Test_App_Mount_Nested(t *testing.T) {
|
||||
app := New()
|
||||
one := New()
|
||||
two := New()
|
||||
three := New()
|
||||
|
||||
two.Mount("/three", three)
|
||||
app.Mount("/one", one)
|
||||
one.Mount("/two", two)
|
||||
|
||||
one.Get("/doe", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
|
||||
two.Get("/nested", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
|
||||
three.Get("/test", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/one/doe", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/nested", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/three/test", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
utils.AssertEqual(t, uint32(6), app.handlersCount)
|
||||
}
|
||||
|
||||
// go test -run Test_App_MountPath
|
||||
func Test_App_MountPath(t *testing.T) {
|
||||
app := New()
|
||||
one := New()
|
||||
two := New()
|
||||
three := New()
|
||||
|
||||
two.Mount("/three", three)
|
||||
one.Mount("/two", two)
|
||||
app.Mount("/one", one)
|
||||
|
||||
utils.AssertEqual(t, "/one", one.MountPath())
|
||||
utils.AssertEqual(t, "/one/two", two.MountPath())
|
||||
utils.AssertEqual(t, "/one/two/three", three.MountPath())
|
||||
utils.AssertEqual(t, "", app.MountPath())
|
||||
}
|
||||
|
||||
func Test_App_ErrorHandler_GroupMount(t *testing.T) {
|
||||
micro := New(Config{
|
||||
ErrorHandler: func(c *Ctx, err error) error {
|
||||
utils.AssertEqual(t, "0: GET error", err.Error())
|
||||
return c.Status(500).SendString("1: custom error")
|
||||
},
|
||||
})
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return errors.New("0: GET error")
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/john", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
testErrorResponse(t, err, resp, "1: custom error")
|
||||
}
|
||||
|
||||
func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) {
|
||||
micro := New(Config{
|
||||
ErrorHandler: func(c *Ctx, err error) error {
|
||||
utils.AssertEqual(t, "0: GET error", err.Error())
|
||||
return c.Status(500).SendString("1: custom error")
|
||||
},
|
||||
})
|
||||
micro.Get("/john/doe", func(c *Ctx) error {
|
||||
return errors.New("0: GET error")
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
testErrorResponse(t, err, resp, "1: custom error")
|
||||
}
|
||||
|
||||
// go test -run Test_App_Group_Mount
|
||||
func Test_App_Group_Mount(t *testing.T) {
|
||||
micro := New()
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return c.SendStatus(StatusOK)
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/john", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, uint32(2), app.handlersCount)
|
||||
}
|
||||
|
||||
func Test_App_UseMountedErrorHandler(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
fiber := New(Config{
|
||||
ErrorHandler: func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(500).SendString("hi, i'm a custom error")
|
||||
},
|
||||
})
|
||||
fiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
|
||||
app.Mount("/api", fiber)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil))
|
||||
testErrorResponse(t, err, resp, "hi, i'm a custom error")
|
||||
}
|
||||
|
||||
func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
fiber := New(Config{
|
||||
ErrorHandler: func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(500).SendString("hi, i'm a custom error")
|
||||
},
|
||||
})
|
||||
fiber.Get("/api", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
|
||||
app.Mount("/", fiber)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil))
|
||||
testErrorResponse(t, err, resp, "hi, i'm a custom error")
|
||||
}
|
||||
|
||||
func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
tsf := func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(200).SendString("hi, i'm a custom sub sub fiber error")
|
||||
}
|
||||
tripleSubFiber := New(Config{
|
||||
ErrorHandler: tsf,
|
||||
})
|
||||
tripleSubFiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
|
||||
sf := func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(200).SendString("hi, i'm a custom sub fiber error")
|
||||
}
|
||||
subfiber := New(Config{
|
||||
ErrorHandler: sf,
|
||||
})
|
||||
subfiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
subfiber.Mount("/third", tripleSubFiber)
|
||||
|
||||
f := func(ctx *Ctx, err error) error {
|
||||
return ctx.Status(200).SendString("hi, i'm a custom error")
|
||||
}
|
||||
fiber := New(Config{
|
||||
ErrorHandler: f,
|
||||
})
|
||||
fiber.Get("/", func(c *Ctx) error {
|
||||
return errors.New("something happened")
|
||||
})
|
||||
fiber.Mount("/sub", subfiber)
|
||||
|
||||
app.Mount("/api", fiber)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil))
|
||||
utils.AssertEqual(t, nil, err, "/api/sub req")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err, "iotuil.ReadAll()")
|
||||
utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body")
|
||||
|
||||
resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", nil))
|
||||
utils.AssertEqual(t, nil, err, "/api/sub/third req")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
b, err = ioutil.ReadAll(resp2.Body)
|
||||
utils.AssertEqual(t, nil, err, "iotuil.ReadAll()")
|
||||
utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body")
|
||||
}
|
||||
|
||||
// go test -run Test_Ctx_Render_Mount
|
||||
func Test_Ctx_Render_Mount(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sub := New(Config{
|
||||
Views: html.New("./.github/testdata/template", ".gohtml"),
|
||||
})
|
||||
|
||||
sub.Get("/:name", func(ctx *Ctx) error {
|
||||
return ctx.Render("hello_world", Map{
|
||||
"Name": ctx.Params("name"),
|
||||
})
|
||||
})
|
||||
|
||||
app := New()
|
||||
app.Mount("/hello", sub)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil))
|
||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, "<h1>Hello a!</h1>", string(body))
|
||||
}
|
||||
|
||||
// go test -run Test_Ctx_Render_Mount_ParentOrSubHasViews
|
||||
func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
engine := &testTemplateEngine{}
|
||||
err := engine.Load()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
engine2 := &testTemplateEngine{path: "testdata2"}
|
||||
err = engine2.Load()
|
||||
utils.AssertEqual(t, nil, err)
|
||||
|
||||
sub := New(Config{
|
||||
Views: html.New("./.github/testdata/template", ".gohtml"),
|
||||
})
|
||||
|
||||
sub2 := New(Config{
|
||||
Views: engine2,
|
||||
})
|
||||
|
||||
app := New(Config{
|
||||
Views: engine,
|
||||
})
|
||||
|
||||
app.Get("/test", func(c *Ctx) error {
|
||||
return c.Render("index.tmpl", Map{
|
||||
"Title": "Hello, World!",
|
||||
})
|
||||
})
|
||||
|
||||
sub.Get("/world/:name", func(c *Ctx) error {
|
||||
return c.Render("hello_world", Map{
|
||||
"Name": c.Params("name"),
|
||||
})
|
||||
})
|
||||
|
||||
sub2.Get("/moment", func(c *Ctx) error {
|
||||
return c.Render("bruh.tmpl", Map{})
|
||||
})
|
||||
|
||||
sub.Mount("/bruh", sub2)
|
||||
app.Mount("/hello", sub)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", nil))
|
||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, "<h1>Hello a!</h1>", string(body))
|
||||
|
||||
resp, err = app.Test(httptest.NewRequest(MethodGet, "/test", nil))
|
||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(body))
|
||||
|
||||
resp, err = app.Test(httptest.NewRequest(MethodGet, "/hello/bruh/moment", nil))
|
||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code")
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, "<h1>I'm Bruh</h1>", string(body))
|
||||
|
||||
}
|
||||
|
||||
func Test_Ctx_Render_MountGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
micro := New(Config{
|
||||
Views: html.New("./.github/testdata/template", ".gohtml"),
|
||||
})
|
||||
|
||||
micro.Get("/doe", func(c *Ctx) error {
|
||||
return c.Render("hello_world", Map{
|
||||
"Name": "doe",
|
||||
})
|
||||
})
|
||||
|
||||
app := New()
|
||||
v1 := app.Group("/v1")
|
||||
v1.Mount("/john", micro)
|
||||
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil))
|
||||
utils.AssertEqual(t, nil, err, "app.Test(req)")
|
||||
utils.AssertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err)
|
||||
utils.AssertEqual(t, "<h1>Hello doe!</h1>", string(body))
|
||||
}
|
13
router.go
13
router.go
|
@ -423,7 +423,13 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router {
|
|||
return app
|
||||
}
|
||||
|
||||
func (app *App) addRoute(method string, route *Route) {
|
||||
func (app *App) addRoute(method string, route *Route, isMounted ...bool) {
|
||||
// Check mounted routes
|
||||
var mounted bool
|
||||
if len(isMounted) > 0 {
|
||||
mounted = isMounted[0]
|
||||
}
|
||||
|
||||
// Get unique HTTP method identifier
|
||||
m := methodInt(method)
|
||||
|
||||
|
@ -441,6 +447,8 @@ func (app *App) addRoute(method string, route *Route) {
|
|||
app.routesRefreshed = true
|
||||
}
|
||||
|
||||
// Execute onRoute hooks & change latestRoute if not adding mounted route
|
||||
if !mounted {
|
||||
app.mutex.Lock()
|
||||
app.latestRoute = route
|
||||
if err := app.hooks.executeOnRouteHooks(*route); err != nil {
|
||||
|
@ -448,12 +456,14 @@ func (app *App) addRoute(method string, route *Route) {
|
|||
}
|
||||
app.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// buildTree build the prefix tree from the previously registered routes
|
||||
func (app *App) buildTree() *App {
|
||||
if !app.routesRefreshed {
|
||||
return app
|
||||
}
|
||||
|
||||
// loop all the methods and stacks and create the prefix tree
|
||||
for m := range intMethod {
|
||||
tsMap := make(map[string][]*Route)
|
||||
|
@ -467,6 +477,7 @@ func (app *App) buildTree() *App {
|
|||
}
|
||||
app.treeStack[m] = tsMap
|
||||
}
|
||||
|
||||
// loop the methods and tree stacks and add global stack and sort everything
|
||||
for m := range intMethod {
|
||||
tsMap := app.treeStack[m]
|
||||
|
|
Loading…
Reference in New Issue