在gin gonic中优雅地处理模板渲染错误

在gin gonic中优雅地处理模板渲染错误,go,go-gin,Go,Go Gin,我正在学习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)

我正在学习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
当我加载页面时,我看到

Before
Bad: Oops! We encountered an error.
响应代码最终为200。我更愿意清楚地捕捉错误,这样,向用户显示的唯一内容就是

Oops! We encountered an error.
响应代码显示为500,并将错误记录在服务器上,以供以后调查

在gin中,在不向用户显示部分输出的情况下捕获模板错误的最佳方法是什么?我见过几个使用内置net/http的缓冲来实现这一点的例子,但是还没有找到一个好的方法来处理它

用解决方案编辑 在@big dige对已接受答案的评论的基础上,我最终将自己的模板执行到一个缓冲区中,并使用
c.Data()
在没有错误的情况下显示它。这似乎还不够理想,因为它绕过了
multi模板
在dev构建中运行时动态重新加载已解析模板的功能,但它可以工作。更新后的概念验证代码如下所示:

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")
}
使用缓冲池、预解析模板和其他类似的细节留给未来的读者练习


如果有人知道在不绕过gin的解析/呈现功能的情况下处理此问题的更好方法,我对此持开放态度。

您必须确保模板呈现正确,因为c.HTML将直接写入响应,此时一些字节已发送到客户端


您可以使用“html/template”,并使用buff缓存响应数据,而不是直接写入响应编写器

Bad:{{.var.x}}
可能必须读取{.var}}@NorbertvanNobelen确实可以修复错误。我有意将其包括在内,以显示出现模板错误时会发生什么情况。:)可能重复:。@icza.gonic将模板执行调用保留为内部调用。我正在寻找一种专门用杜松子酒的方法。也许答案是不可能。啊,现在我明白你的问题了。go本身的优雅错误处理非常清楚。由@icza链接的惯用方法是正确的。不幸的是,gin gonic将模板执行调用保留在内部。我正在寻找一种专门用杜松子酒的方法。也许答案是,如果不执行两次模板,使用gin是不可能的。@Parnic使用context.Data方法,它将向response.writer写入字节数据context.Data提示帮助了我,谢谢。最后,我自己执行html模板,如果没有错误,则将结果写入数据,否则通过html()显示错误页面。
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")
}