Изящно обрабатывать ошибки рендеринга шаблонов в gin-gonic
Я изучаю Go и использую gin-gonic для веб-приложения. Я пытаюсь изящно восстановиться после ошибок шаблона и не смог выяснить, как буферизовать вывод или перенаправить правильно для достижения этой цели.
С этим кодом:
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, но я не смог найти ничего, что помогло бы справиться с этим в gin.
Отредактировано с решением
Основываясь на комментариях @big голубя к принятому ответу, я сам запустил шаблон в буфер и использовал c.Data()
отображать его, если не было ошибок. Это все еще кажется далеко не идеальным, поскольку оно обходит такие функции, как multitemplate
Способность динамически перезагружать проанализированный шаблон во время выполнения в сборках 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")
}
Использование пула буферов, шаблоны предварительного разбора и другие подобные тонкости оставляются в качестве упражнения для будущих читателей.
Если кто-нибудь знает лучший способ справиться с этим, не обходя возможности парсинга / рендеринга джина, я очень открыт для этого.
1 ответ
Вы должны быть уверены, что Шаблон отображается правильно, потому что c.HTML будет писать прямо в ответ, в это время некоторый байт был отправлен клиенту.
Вы можете использовать "html/template" и использовать buff для кэширования данных ответов, вместо того, чтобы записывать их непосредственно в средство записи ответов