Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 fix: mounted app views #1749

Merged
merged 4 commits into from Feb 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/testdata/template/hello_world.gohtml
@@ -0,0 +1 @@
<h1>Hello {{ .Name }}!</h1>
34 changes: 18 additions & 16 deletions app.go
Expand Up @@ -111,8 +111,9 @@ type App struct {
getBytes func(s string) (b []byte)
// Converts byte slice to a string
getString func(b []byte) string
// mount prefix -> error handler
errorHandlers map[string]ErrorHandler

// Mounted and main apps
appList map[string]*App
}

// Config is a struct holding the server settings.
Expand Down Expand Up @@ -460,10 +461,10 @@ func New(config ...Config) *App {
},
},
// Create config
config: Config{},
getBytes: utils.UnsafeBytes,
getString: utils.UnsafeString,
errorHandlers: make(map[string]ErrorHandler),
config: Config{},
getBytes: utils.UnsafeBytes,
getString: utils.UnsafeString,
appList: make(map[string]*App),
}
// Override config if provided
if len(config) > 0 {
Expand Down Expand Up @@ -515,6 +516,9 @@ func New(config ...Config) *App {
app.handleTrustedProxy(ipAddress)
}

// Init appList
app.appList[""] = app

// Init app
app.init()

Expand Down Expand Up @@ -544,20 +548,18 @@ func (app *App) handleTrustedProxy(ipAddress string) {
// 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, "/")
for m := range stack {
for r := range stack[m] {
route := app.copyRoute(stack[m][r])
app.addRoute(route.Method, app.addPrefixToRoute(prefix, route))
}
}

// Save the fiber's error handler and its sub apps
prefix = strings.TrimRight(prefix, "/")
if fiber.config.ErrorHandler != nil {
app.errorHandlers[prefix] = fiber.config.ErrorHandler
}
for mountedPrefixes, errHandler := range fiber.errorHandlers {
app.errorHandlers[prefix+mountedPrefixes] = errHandler
// 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)
Expand Down Expand Up @@ -1002,11 +1004,11 @@ func (app *App) ErrorHandler(ctx *Ctx, err error) error {
mountedPrefixParts int
)

for prefix, errHandler := range app.errorHandlers {
if strings.HasPrefix(ctx.path, prefix) {
for prefix, subApp := range app.appList {
if strings.HasPrefix(ctx.path, prefix) && prefix != "" {
parts := len(strings.Split(prefix, "/"))
if mountedPrefixParts <= parts {
mountedErrHandler = errHandler
mountedErrHandler = subApp.config.ErrorHandler
mountedPrefixParts = parts
}
}
Expand Down
31 changes: 21 additions & 10 deletions ctx.go
Expand Up @@ -1082,18 +1082,28 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error {

}

if c.app.config.Views != nil {
// Render template based on global layout if exists
if len(layouts) == 0 && c.app.config.ViewsLayout != "" {
layouts = []string{
c.app.config.ViewsLayout,
rendered := false
for prefix, app := range c.app.appList {
if prefix == "" || strings.Contains(c.OriginalURL(), prefix) {
if len(layouts) == 0 && app.config.ViewsLayout != "" {
layouts = []string{
app.config.ViewsLayout,
}
}

// Render template from Views
if app.config.Views != nil {
if err := app.config.Views.Render(buf, name, bind, layouts...); err != nil {
return err
}

rendered = true
break
}
}
// Render template from Views
if err := c.app.config.Views.Render(buf, name, bind, layouts...); err != nil {
return err
}
} else {
}

if !rendered {
// Render raw template using 'name' as filepath if no engine is set
var tmpl *template.Template
if _, err = readContent(buf, name); err != nil {
Expand All @@ -1109,6 +1119,7 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error {
return err
}
}

// Set Content-Type to text/html
c.fasthttp.Response.Header.SetContentType(MIMETextHTMLCharsetUTF8)
// Set rendered template to body
Expand Down
54 changes: 54 additions & 0 deletions ctx_test.go
Expand Up @@ -28,6 +28,7 @@ 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"
)
Expand Down Expand Up @@ -2049,6 +2050,59 @@ func Test_Ctx_Render(t *testing.T) {
err = c.Render("./.github/testdata/template-invalid.html", nil)
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{
Expand Down
10 changes: 4 additions & 6 deletions group.go
Expand Up @@ -32,13 +32,11 @@ func (grp *Group) Mount(prefix string, fiber *App) Router {
}
}

// Save the fiber's error handler and its sub apps
// Support for configs of mounted-apps and sub-mounted-apps
groupPath = strings.TrimRight(groupPath, "/")
if fiber.config.ErrorHandler != nil {
grp.app.errorHandlers[groupPath] = fiber.config.ErrorHandler
}
for mountedPrefixes, errHandler := range fiber.errorHandlers {
grp.app.errorHandlers[groupPath+mountedPrefixes] = errHandler
for mountedPrefixes, subApp := range fiber.appList {
grp.app.appList[groupPath+mountedPrefixes] = subApp
subApp.init()
}

atomic.AddUint32(&grp.app.handlersCount, fiber.handlersCount)
Expand Down