Skip to content

Commit

Permalink
🐛 fix: mounted app views (#1749)
Browse files Browse the repository at this point in the history
* Fix mounted app views.

* Cleaner structure.

Co-authored-by: RW <rene@gofiber.io>

* remove unnecessary lines.

* Add test case for group-with-mount, remove unnecessary lines.

Co-authored-by: RW <rene@gofiber.io>
  • Loading branch information
efectn and ReneWerner87 committed Feb 6, 2022
1 parent 569511e commit c450072
Show file tree
Hide file tree
Showing 7 changed files with 422 additions and 32 deletions.
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

0 comments on commit c450072

Please sign in to comment.