最近、GoでWebアプリケーションを書く練習をしている。フレームワークとしてEchoを使っている。
アセットをバイナリに埋め込むときいくつか選択肢があったけど、HTMLテンプレートのパースを実装しやすそうなpackrを使ってみている。
以下のようなテンプレートを用意する。
{{/* assets/views/layout.html */}}
{{define "layout" -}}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Task</title>
</head>
<body>
{{template "content" . -}}
</body>
</html>
{{end}}
{{/* assets/views/index.html */}}
{{define "index" -}}
<p>Here is content!</p>
{{end}}
これらのテンプレートは以下のコマンドでバイナリに埋め込むことができる。
$ packr build
次に、EchoでHTMLをレンダリングするためのRendererを実装する。
// renderer/renderer.go
// Renderer is html/template renderer for Echo.
type Renderer struct {
templates *template.Template
}
// New parses templates in box and return a new Renderer.
func New(box packr.Box) *Renderer {
templates := template.New("templates")
for _, name := range box.List() {
text := box.String(name)
templates = template.Must(templates.Parse(text))
}
return &Renderer{templates: templates}
}
// Render renders HTML generated by template.
func (r *Renderer) Render(wr io.Writer, name string, data interface{}, c echo.Context) error {
layout := template.Must(r.templates.Lookup("layout").Clone())
content := template.Must(r.templates.Lookup(name).Clone())
html := template.Must(layout.AddParseTree("content", content.Tree))
return html.ExecuteTemplate(wr, "layout", data)
}
packr
コマンドでバイナリに埋め込んだアセットはbox.String(filename)
のように取得できる。これを使ってテンプレートをパースしていく。
Rendererを初期化するときにテンプレートをすべてパースしておいて、レンダリング時にネストしたテンプレートを組み立ててExecute
するようにしている。
最後に、このRendererを初期化してEchoに設定する。
// main.go
e := echo.New()
viewsBox := packr.NewBox("./assets/views")
e.Renderer = renderer.New(viewsBox)