优雅地处理 gin-gonic 中的模板渲染错误
Gracefully handle template rendering errors in gin-gonic
我正在学习 Go 并将 gin-gonic 用于 Web 应用程序。我正在尝试从模板错误中优雅地恢复,但一直无法弄清楚如何缓冲输出或正确重定向以实现此目的。
使用此代码:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
g := gin.New()
g.LoadHTMLGlob("templates/*")
g.Use(func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.HTML(http.StatusInternalServerError, "error.tmpl", nil)
}
}()
c.Next()
})
g.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{"var": 4})
})
g.Run(":80")
}
其中 templates/index.tmpl 是
Before
<br>
Bad: {{.var.x}}
<br>
After
和templates/error.tmpl是
Oops! We encountered an error.
当我加载我的页面时,我看到
Before
Bad: Oops! We encountered an error.
响应代码最终为 200。我更愿意清楚地捕获错误,以便向用户显示的唯一内容是
Oops! We encountered an error.
响应代码为 500,错误被记录在服务器上以供日后调查。
在 gin 中捕获模板错误而不向用户显示部分输出的最佳方法是什么?我已经看到几个通过使用内置 net/http 东西进行缓冲来完成此操作的示例,但是还没有找到任何在杜松子酒中处理它的好方法。
使用解决方案编辑
根据@big pigeon 对已接受答案的评论,我最终自己将模板执行到缓冲区中,如果没有错误,则使用 c.Data()
显示它。这似乎仍然不太理想,因为它绕过了 multitemplate
在开发构建中在运行时动态重新加载已解析模板的功能,但它确实有效。更新后的概念验证代码如下所示:
package main
import (
"bytes"
"html/template"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
g := gin.New()
g.LoadHTMLGlob("templates/*")
g.Use(func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.HTML(http.StatusInternalServerError, "error.tmpl", nil)
}
}()
c.Next()
})
g.GET("/", func(c *gin.Context) {
if tmpl, err := template.ParseFiles("templates/index.tmpl"); err != nil {
panic(err)
} else {
buf := &bytes.Buffer{}
if err = tmpl.Execute(buf, gin.H{"var": 4}); err != nil {
panic(err)
} else {
c.Data(http.StatusOK, "text/html; charset=utf-8", buf.Bytes())
}
}
})
g.Run(":80")
}
使用缓冲池、预解析模板和其他类似的技巧留给未来的读者作为练习。
如果有人知道在不绕过杜松子酒的 parsing/rendering 功能的情况下处理这个问题的更好方法,我很乐意接受。
您必须确保模板正确呈现,因为c.HTML 将直接写入响应,此时一些字节已发送到客户端。
可以使用"html/template",使用buff来缓存响应数据,而不是直接写入响应写入器
我正在学习 Go 并将 gin-gonic 用于 Web 应用程序。我正在尝试从模板错误中优雅地恢复,但一直无法弄清楚如何缓冲输出或正确重定向以实现此目的。
使用此代码:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
g := gin.New()
g.LoadHTMLGlob("templates/*")
g.Use(func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.HTML(http.StatusInternalServerError, "error.tmpl", nil)
}
}()
c.Next()
})
g.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{"var": 4})
})
g.Run(":80")
}
其中 templates/index.tmpl 是
Before
<br>
Bad: {{.var.x}}
<br>
After
和templates/error.tmpl是
Oops! We encountered an error.
当我加载我的页面时,我看到
Before
Bad: Oops! We encountered an error.
响应代码最终为 200。我更愿意清楚地捕获错误,以便向用户显示的唯一内容是
Oops! We encountered an error.
响应代码为 500,错误被记录在服务器上以供日后调查。
在 gin 中捕获模板错误而不向用户显示部分输出的最佳方法是什么?我已经看到几个通过使用内置 net/http 东西进行缓冲来完成此操作的示例,但是还没有找到任何在杜松子酒中处理它的好方法。
使用解决方案编辑
根据@big pigeon 对已接受答案的评论,我最终自己将模板执行到缓冲区中,如果没有错误,则使用 c.Data()
显示它。这似乎仍然不太理想,因为它绕过了 multitemplate
在开发构建中在运行时动态重新加载已解析模板的功能,但它确实有效。更新后的概念验证代码如下所示:
package main
import (
"bytes"
"html/template"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
g := gin.New()
g.LoadHTMLGlob("templates/*")
g.Use(func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
c.HTML(http.StatusInternalServerError, "error.tmpl", nil)
}
}()
c.Next()
})
g.GET("/", func(c *gin.Context) {
if tmpl, err := template.ParseFiles("templates/index.tmpl"); err != nil {
panic(err)
} else {
buf := &bytes.Buffer{}
if err = tmpl.Execute(buf, gin.H{"var": 4}); err != nil {
panic(err)
} else {
c.Data(http.StatusOK, "text/html; charset=utf-8", buf.Bytes())
}
}
})
g.Run(":80")
}
使用缓冲池、预解析模板和其他类似的技巧留给未来的读者作为练习。
如果有人知道在不绕过杜松子酒的 parsing/rendering 功能的情况下处理这个问题的更好方法,我很乐意接受。
您必须确保模板正确呈现,因为c.HTML 将直接写入响应,此时一些字节已发送到客户端。
可以使用"html/template",使用buff来缓存响应数据,而不是直接写入响应写入器