必须先配置e.Renderer,再注册路由;模板需一次性ParseFiles加载所有文件,路径与Render参数严格一致,data用map更灵活。

Context.Render() 是唯一入口,但必须先配好 Renderer
直接调用 c.Render(200, "index.html", data) 会 panic:「renderer not registered」。Echo 不内置模板引擎,echo.Context.Render 只是转发接口,背后依赖你提前赋值的 e.Renderer。漏掉这步,所有模板调用都失败。
关键动作只有两行:
- 定义一个实现
echo.Renderer接口的结构体(比如Template) - 把它的实例赋给
e.Renderer,且必须在注册路由 之前
顺序错——比如先 e.GET(...) 再 e.Renderer = t——会导致部分路由无法渲染,且无明确报错,只返回空响应或 500。
template.ParseGlob 不能多次调用,否则模板丢失
常见错误写法:t.templates = template.Must(template.ParseGlob("views/layouts/*.html")),然后又写一行 t.templates = template.Must(template.ParseGlob("views/pages/*.html"))。第二行会完全覆盖第一行解析结果,所有 layouts 里定义的 {{define "header"}} 全部消失,后续 {{template "header"}} 直接 panic。
正确做法是合并路径一次性加载:
- 用
filepath.Glob分别收集 layout、page、partial 等目录下的文件路径 - 用
append合并成一个[]string列表(注意 layouts 必须排在前面) - 传给
template.ParseFiles,而不是反复调用ParseGlob
否则你看到的典型现象是:首页能打开,点进子页面就 500,日志里只有 template: "header" is not defined。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
模板文件名必须和 Render 第二个参数严格一致
c.Render(200, "user/profile.html", data) 要求磁盘上存在 views/user/profile.html(假设你 ParseGlob("views/**/*.html"))。路径不匹配、大小写不符、扩展名多写一个 .tmpl,都会导致 ExecuteTemplate: "user/profile.html" is undefined。
尤其注意 Windows/macOS 开发时路径分隔符习惯(/ vs \),ParseGlob 在不同系统对通配符行为一致,但硬编码路径字符串容易出错。建议统一用 /,Go 的 filepath 包会自动适配。
另一个坑:如果你用了 template.New("base"),那第一个参数是模板树根名,和文件名无关;真正匹配的是 ExecuteTemplate(w, name, data) 的 name 参数——它必须是某个 {{define "xxx"}} 的名字,或完整文件路径(取决于你如何组织 ParseFiles)。
data 参数类型决定模板内 . 的含义,别默认传 string
写 c.Render(200, "hello.html", "World") 模板里用 {{.}} 能取到值,但一旦要传多个字段(比如用户姓名+头像URL),就得换结构体或 map:
- 传
struct{ Name string; Avatar string },模板用{{.Name}}、{{.Avatar}} - 传
map[string]interface{}{"name": "A", "avatar": "/a.jpg"},模板用{{.name}}(注意 key 小写)
混用会导致模板静默失败:比如传了 struct 却在模板里写 {{.name}}(首字母小写),字段不可导出,渲染为空字符串,没有任何错误提示。
更隐蔽的问题是安全上下文:如果 data 是 map,你可以在自定义 Renderer 中往里面塞函数(如 "urlFor": c.Echo().Reverse),但 struct 不支持动态加字段。需要 URL 构造或 CSRF token 时,map 更灵活。

















