技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

templateキャッシュ【Go】

htmlのtemplate【Go】 - 技術向上で紹介した下記関数ですと、
同じtemplateであっても、処理毎に毎回templateの読み込みが行われてしまいます。

func renderTemplate(w http.ResponseWriter, templ string, p *Page) {
    t, _ := template.ParseFiles(templ + ".html")
    t.Execute(w, p)
}


templateをキャッシュして効率よく処理させる方法があります。

var templates = template.Must(template.ParseFiles("edit.html", "view.html"))    // template情報をキャッシュ

func renderTemplate(w http.ResponseWriter, templ string, p *Page) {
    err := templates.ExecuteTemplate(w, templ + ".html", p)    // キャッシュされたtemplateから対象を呼び出し、pageの内容書き込みを実施する
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)    // 500エラーを返す
    }
}

template.ParseFiles()は可変長引数をとり、その引数としてキャッシュさせたいファイルの名前を指定します。
異なる階層の同じファイル名を複数指定した場合、最後に指定したファイルのみがキャッシュされます。

template.Must()は独自にエラーチェックを行うため、
errorを返り値には持たず、ハンドリングする必要がありません。

全体例です。

type Page struct {
    Title string
    Body  []byte
}

var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

func renderTemplate(w http.ResponseWriter, templ string, p *Page) {
    err := templates.ExecuteTemplate(w, templ + ".html", p)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func load(title string) (*Page, error) {
    filename := title + ".txt"
    body, err := ioutil.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    return &Page{Title: title, Body: body}, nil
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
    title := r.URL.Path[len("/view/"):]
    p, err := load(title)
    if err != nil {
        http.Redirect(w, r, "/edit/"+title, http.StatusFound)
        return
    }
    renderTemplate(w, "view", p)
}

func editHandler(w http.ResponseWriter, r *http.Request) {
    title := r.URL.Path[len("/edit/"):]
    p, err := load(title)
    if err != nil {
        p = &Page{Title: title}
    }
    renderTemplate(w, "edit", p)
}

func main() {
    http.HandleFunc("/view/", viewHandler)
    http.HandleFunc("/edit/", editHandler)
    log.Fatalln(http.ListenAndServe(":8080", nil))
}


template.ParseGlob()と合わせるパターンも多いかもしれません。
template.ParseGlob()【Go】 - 技術向上

階層

main.go
templates
    blabla.gohtml
    tpl1.gohtml
    tpl2.gohtml


var tpl *template.Template

func init() {
    tpl = template.Must(template.ParseGlob("templates/*"))    // templates以下の全ファイルを取り込み、キャッシュ
}

func main() {
    nf, err := os.Create("index.html")
    if err != nil {
        log.Fatal(err)
    }

    err = tpl.ExecuteTemplate(nf, "tpl1.gohtml", nil)    // tpl1.gohtmlの内容をindex.htmlにコピー
    if err != nil {
        log.Fatal(err)
    }

    err = tpl.ExecuteTemplate(os.Stdout, "tpl2.gohtml", nil)    // tpl2.gohtmlの内容を画面出力
    if err != nil {
        log.Fatal(err)
    }
}