Изящно обрабатывать ошибки рендеринга шаблонов в 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 для кэширования данных ответов, вместо того, чтобы записывать их непосредственно в средство записи ответов

Другие вопросы по тегам