Ahmad Hamdi
Ahmad Hamdi

Reputation: 1270

Echo response issue with Templ ( Golang )

Echo & Templ Response Issue

I am trying to build an admin dashboard for my project using Echo V5 & Templ.

My current setup

// RegisterRoutes -> a route registeration method used to connect routes to handler funcs dynamically.
func RegisterRoutes(router *echo.Echo) {

    returnHome := func(c echo.Context) error {
        return c.Redirect(http.StatusTemporaryRedirect, "/admin/workspace/analytics")
    }

    router.GET("admin/sign-in", signInHandler)
    router.POST("admin/sign-in", signInHandler)
    router.GET("admin/sign-up", signUpHandler)
    router.POST("admin/sign-up", signUpHandler)
    
    // This is the problematic line.
    router.GET("admin/workspace/*", adminNavigator)

    router.GET("admin/*", returnHome)
}

as for the adminNavigator function it's implemented like this

func adminNavigator(c echo.Context) error {
    if c.Request().Method != http.MethodGet {
        return echo.ErrMethodNotAllowed
    }

    // Extract the word immediately following /admin/workspace/ and normalize it
    // A good upgrade would be to accept /admin/word directly or accept typos in the word itself.
    // Word being the workspace dashboard itself.
    word := strings.TrimPrefix(c.Request().URL.Path, "/admin/workspace/")
    //word = strings.TrimSuffix(word, ".gohtml") // Safely ignore .gohtml suffix if present

    // Compile a regex pattern to match files that include the specific 'word' with any prefix or suffix
    pattern := regexp.MustCompile(`(?i)^[^-]*-?` + regexp.QuoteMeta(word) + `-?.*$`)

    // Attempt to find the first matching template file
    return findMatchingTemplate(&c, RouteMap, pattern)
}

findMatchingTemplate itself is defined as the following:

func findMatchingTemplate(c *echo.Context, routes map[string]func(c *echo.Context) error, pattern *regexp.Regexp) error {
    for route, handler := range routes {
        if pattern.MatchString(route) {
            return handler(c)
        }
    }
    return nil
}

Where it simply looks up a route/handler map defined as follows:

var RouteMap = map[string]func(c *echo.Context) error{
    "analytics":    CreatePopulateFunc(analytics.PageData),
    "presence":     CreatePopulateFunc(presence.PageData),
    "catalog":      CreatePopulateFunc(products.PageData),
    "help":         CreatePopulateFunc(help.PageData),
    "invoice":      CreatePopulateFunc(invoices.PageData),
    "invoices":     CreatePopulateFunc(invoices.PageData),
    "new":          CreatePopulateFunc(new2.PageData),
    "offers":       CreatePopulateFunc(offers.PageData),
    "onboarding":   CreatePopulateFunc(onboarding.PageData),
    "orders":       CreatePopulateFunc(orders.PageData),
    "renewal":      CreatePopulateFunc(renewal.PageData),
    "reports":      CreatePopulateFunc(reports.PageData),
    "settings":     CreatePopulateFunc(settings.PageData),
    "sign-in":      CreatePopulateFunc(signin.PageData),
    "sign-up":      CreatePopulateFunc(signup.PageData),
    "store":        CreatePopulateFunc(store.PageData),
    "uploaders":    CreatePopulateFunc(uploaders.PageData),
    "users":        CreatePopulateFunc(users.PageData),
    "vendorDetail": CreatePopulateFunc(vendors.PageData),
}
// CreatePopulateFunc -> a wrapper that returns a dynamic echo handler func.
func CreatePopulateFunc(pageDataFunc func(c *echo.Context) any) func(c *echo.Context) error {
    return func(c *echo.Context) error {
        data := pageDataFunc(c)
        pageData, _ := ToMapStringAny(data)
        _ = admin.Render(c, p.Index(pageData))
        return nil
    }
}

admin.Render itself is defined as:

// Render -> admin.Render used to render templ components.
func Render(ctx *echo.Context, cmp templ.Component) error {
    context := *ctx
    logger.LogDuringDev("\nInside admin.Render: %+v\n", context)
    err := cmp.Render(context.Request().Context(), context.Response())
    if err != nil {
        return err
    }
    return nil
}

Whenever I try to fix this I get the following error:

Inside admin.Render: &{request:0xc000550500 response:0xc00084a240 route:0xc0007ddc70 path:/admin/workspace/* pathParams:0xc000848018 currentParams:[] query:map[] store:map[execStart:{wall:13953535859364049284 ext:12233197001 loc:0x2937480}] echo:0xc00040fd40 lock:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:{_:{} v:0} readerWait:{_:{} v:0}}}

2024/08/07 11:45:49 server.go:3489: http: superfluous response.WriteHeader call from github.com/labstack/echo/v5.(*Response).WriteHeader (response.go:64)

{"time":"2024-08-07T12:27:17.642138+03:00","level":"ERROR","prefix":"echo","message":"response already committed"}

What am I doing wrong here? also please know that using gohtml works fine, its just when I try to render templ components with echo that I get this error.

Upvotes: 1

Views: 85

Answers (0)

Related Questions