Secret Staircase

Handlebars.java を使う

May 4, 2013

Handlebars.javaHandlebars の Java 実装です。

ここでは Template Inheritance 機能とデフォルト HTML エスケープの動きを中心に見てゆきます。

セットアップ方法 Handlebars.java のページが詳しいです。Spring MVC の ViewResolver も提供されています。検証に使ったソースコードも参考になると思います。

Template Inheritance

Handlebars.java では Template Inheritance 機能が使えます。親テンプレートで定義された複数のブロックを子テンプレートから上書くことができ、多くのテンプレートエンジンが単一のブロックのみの上書きをサポートしているのに対して、より柔軟なテンプレートを書くことができます。

親レイアウトとなる basic.hbs を用意します。

{% raw %}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="../../../stylesheets/bootstrap.min.css" />
    <title>{{#block "title"}}DEFAULT TITLE{{/block}}</title>
  </head>
  <body>
    {{> layout/navibar}}
    <div class="container">
      {{#block "content"}}DEFAULT CONTENT{{/block}}
    </div>
    {{> layout/footer}}
    <script src="../../../javascripts/jquery-2.0.0.min.js"></script>
    {{#block "script"}}{{/block}}
  </body>
</html>
{% endraw %}

3 つのブロックが定義されてて、これらを子テンプレートで上書きすることができます。

  • {% raw %}{{#block "title"}}DEFAULT TITLE{{/block}}{% endraw %}
  • {% raw %}{{#block "content"}}DEFAULT CONTENT{{/block}}{% endraw %}
  • {% raw %}{{#block "script"}}{{/block}}{% endraw %}

次に、子テンプレート main.hbs を用意します。

{% raw %} {{#partial "title"}}andlebars{{/partial}} {{#partial "content"}}
<article>
  {{#with wikiPage}}
  <h1>{{title}}</h1>
  <div>{{body}}</div>
  {{/#with}}
</article>
{{/partial}} {{#partial "script"}}
<script>
  $('...').click(function() { ... });
</script>
{{/partial}} {{> layout/basic}} {% endraw %}

title, content, script を上書きしています。複数の変数を上書きできるのは多くのテンプレートエンジンでサポートされていますが、Template Inheritance ではこの例のように HTML のブロックで上書きできるので大変便利です。

デフォルト HTML エスケープ

Handlebars では何も設定しなくてもデフォルトで HTML エスケープされます。

前回までに使っていたのと同じモデルを使って動作を検証してみます。

まず title の出力です。

string

エスケープできていますね。

続いて WikiPage の出力。

object

WikiPage#toString() の結果をエスケープできています。 現行バージョンの 0.12.0 は JSP と同様にエスケープされない問題があるのですが修正されたので次バージョンからは問題がありません。上記は 0.13.0-SNAPSHOT の結果です。JSP のは仕様だけど。

今度は明示的にエスケープを回避してみます。{% raw %}{{{...}}}{% endraw %} を使うとエスケープなしで出力できます。{% raw %}{{..}}{% endraw %} と間違えてしまいそう…。デリミタを変更することができるのでたとえば erb スタイルにすれば間違えにくくなるかもしれません。

{{=<% %>=}}
  escape: <% title %>
noescape: <%{ title }%>
<%={{ }}=%>

いずれにしてもエスケープしていない箇所を検出して警告する処理がビルドやデプロイプロセスにあった方が良さそうです。

string noescape

クラス定義でエスケープを回避する機能として SafeString が用意されています。このクラスにラップした文字列はエスケープされません。

モデルクラスでこのように SafeString を返しておくとエスケープされません。

public Handlebars.SafeString getHandlebarsSafeString() {
  return new Handlebars.SafeString(getSafeHtmlValue());
}

object noescape

これはオリジナルの JavaScript 版にならった挙動ですがインターフェースかアノテーションで指定できた方が便利かもしれませんね。

まとめ

Handlebars.java により流行の Mustache/Handlebars を Java でも使えるようになります。Template Inheritance が使え、デフォルトで HTML エスケープされ、機能的には十分に実践で使えるレベルにあるでしょう。

  • 検証に使ったソースコードは github にあります
  • こちらのサイトで動作を確認できます (非常に遅いです)