[Go言語]テンプレートの共通部分を抜き出す冴えたやり方

こんにちは。KOUKIです。とある企業でGo言語を使った開発業務に従事しています。

Go言語は私の大好きな言語なのですが、日本ではあまり流行っておりません^^;そのため、啓蒙活動の一環で、Go言語の素晴らしい機能を本ブログで紹介しています。

今回は、Go言語のhtml/templateパッケージを使ってWebアプリを作った時に得た知見を紹介しようと思います。

紹介したいこと

本記事では、テンプレートの共通化について触れたいと思います。

Go言語のhtml/templateパッケージは、任意の形式のファイルからHTML文章を生成してくれる便利なツールです。しかも、テンプレートという形で処理するので、プログラムで生成したデータをテンプレートに渡して、画面に表示することもできます。

Djangoを触ったことがある人ならテンプレートシステムを使った経験があると思いますが、その機能と同じです。

Go言語で作成したWebサーバーでクライアントからリクエストを受け取る -> テンプレートから描画するデータを作成 -> 画面に返す、みたいなことをGo言語の基本機能だけで実装できます

このテンプレートには、基本的にはHTML文章を記述するわけですが、共通部分が多くでてきます。例えば、DOCTYPEやheadタグなどです。

重複コードはプログラムのメンテナンス性を著しく下げるので、共通部分を一つのファイルに括り出して、他のテンプレートから参照できるようにします

ワークスペースの準備

作業に必要なファイルを用意してください。

ざっとWebアプリを作る

最初に、簡単なWebアプリを作ります。

main.go

render.go

handlers.go

home.page.tmpl

about.page.tmpl

home.page.tmplとabouut.page.tmplが、テンプレートファイルです。

ご覧の通り、重複する箇所が多くあると思います。

動作確認

次のコマンドで、アプリを動かします。

ブラウザから「http://localhost:8080/」にアクセスします。

同様に「http://localhost:8080/about」にアクセスします。

こんな感じで、Go言語の機能だけでUIを作り、ルーティングさせることが可能です。

テンプレートの共通化に立ちはだかる問題

テンプレートの共通箇所をbase.layout.tmplに移します。

defineやblockなどは、テンプレート構文です。

その他のテンプレートは、以下のように修正しましょう。

共通箇所を括り出すことで、だいぶスッキリしました。

しかし、このコードは、ページ読み込み時に以下のエラーを出力します。

base.layout.tmplが読み込めていないようですね^^;

ページも真っ白になります。

解決方法

先に解決したコードを以下に記載します。

render.goを以下のように書き換えてください。

このコードでは、home/aboutテンプレートを先に読み込み、それらにbaseテンプレートをマージして画面出力しています。

この処理で、問題なく表示されるようになります。

解説

ソースコードの解説に移ります。

CreateTemplateCache関数

CreateTemplateCache関数の中で、最初にキャッシュを定義しています。

myCacheには、テンプレートのファイル名とパースしたテンプレート情報が入ります。

次に、home/aboutのテンプレートを読みこみます。

これで、templatesフォルダ配下にある「*.page.tmpl」パターンに一致したファイルを全て取得しています。

取得したファイル情報一つ一つにbaseをマージするので、pagesをループします。

forループの中では、最初にファイル名を取得します。

次に、取得した名前を元に新しいテンプレートインスタンスを作成します。

「template.New」は、渡したファイル名で新しいテンプレートを作ります。

Funcs」は、引数に指定したfunctionの実行結果をテンプレートに埋め込みます。今回は、特に埋め込むものもなかったので、以下のように空の「FuncMap」を渡しています。

FuncMapには、関数をMapに持たせることができるようです。

ParseFiles(page)の処理は、引数に渡したテンプレート情報を新しく作成したテンプレートインスタンスにマージするようです。

このようにして、tsにはhome/aboutのパースされたテンプレート情報が格納されます。

そして、最後にhome/aboutのテンプレートとbaseのテンプレートのマージを「ParseGlob」で行います。

RenderTemplate関数

RenderTemplate関数では、最初にCreateTemplateCache関数を呼びます。

RenderTemplate関数の引数にはテンプレートのパスが渡されるので、以下のようにしてキャッシュからリクエストに応じたテンプレート情報を取り出せます。

テンプレートインスタンスは「Execute」メソッドを持っており、bufferにテンプレート情報を吐き出します。

そして、このバッファをResponseWriterに書き込めば、Web上で表示できるようになります。

これで完成です。

おわりに

作ろうと思えば、Go言語だけでWebアプリケーションをサクッと作れてしまいます。

しかもGo言語の文法は平易で、覚えやすく、並行処理プログラミングも簡単にマスターできるので、初心者にも大変おすすめな言語です^^

日本に広く普及して、Go言語エンジニアの価値も上がることを祈っています。

それでは、また!

おまけ

ここからは、文字通りおまけです。

キャッシュの導入

キャッシュ機能を導入してみましょう。

現時点ではページがリクエストされる度にCreateTemplateCache関数が実行され、テンプレートが読み込まれます。

例えば、Aboutページを表示します。

この状態で、about.page.tmplにh2タグをつけて、アプリケーションを再起動しないでページを再表示してください。

「Confirm Cache」が表示されましたね。これは、毎回テンプレートを読み込んでいるからです。この動作をキャッシュを使って回避します。

コンフィグファイルを定義

アプリケーション全体で使うconfig設定をconfig.goに実装します。

このように設定ファイルを用意すると実装がスマートになります。

UseCacheは、キャッシュのON/OFFを決めるパラメータです。この値がONの場合は、キャッシュが有効になります

TemplateCacheには、キャッシュされたテンプレートのインスタンス情報が入ります。

render.goの書き換え

render.goに先ほど定義したコンフィグを内包するNewTemplates関数を定義し、RenderTemplate関数を書き換えてください。

RenderTemplate関数内では、キャッシュのON/OFFによって、読み込まれるテンプレートインスタンスを切り替えています。

handlers.goの書き換え

handlers.goは、全体的に書き換えます。

ここでは「App *config.AppConfig」を特に使っていませんが、アプリケーション全体の設定という位置付けなので、情報として持たせています。

main.goの修正

main.goを以下のように修正します。

ここでは、他のファイルに実装したNew関数を読み込んでいます。

そして、「app.UseCache = true」にすることでキャッシュが有効になります。

コメントを残す