Изящно обрабатывать ошибки рендеринга шаблонов в gin-gonic - PullRequest
0 голосов
/ 05 ноября 2018

Я изучаю 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 Ответ

0 голосов
/ 05 ноября 2018

Вы должны быть уверены, что Шаблон отображается правильно, поскольку c.HTML будет писать напрямую в ответ, в это время некоторый байт был отправлен клиенту.

вы можете использовать "html / template" и использовать buff для кэширования данных ответов, вместо того, чтобы записывать их непосредственно в средство записи ответов

...