FreeMarker でデフォルト HTML エスケープ

デフォルト HTML エスケープシリーズ。今回は FreeMarker。

デフォルトエスケープの設定

FreeMarker では <#escape> タグで囲った部分を自動でエスケープすることができます。こんな風にファイルの最初と最後にセットしてやればそのファイルではデフォルトでエスケープされます。

<#escape x as x?html>
<!DOCTYPE html>
<html>
  ...
  ${foo.bar}
  ...
</html>
</#escape>

いちいち <#escape>; で囲むのは面倒ですので TemplateLoader でテンプレートをロードするときに自動でつけるようにします。SpringMVC と組み合わせる場合は以下のようになります。(完全なソースコードはこちら)

FreeMarkerConfigurer c = new FreeMarkerConfigurer();
c.setPreTemplateLoaders(
  new SpringTemplateLoader(
    new ServletContextResourceLoader(servletContext),
    "/WEB-INF/freemarker/") {
      @Override
      public Reader getReader(Object templateSource, String encoding)
        throws IOException {
        String s = CharStreams.toString(
          super.getReader(templateSource, encoding));
        return new StringReader(
          "<#escape x as x?html>" + s + "</#escape>");
      }
  });
return c;

ちょっと無理矢理な気もしますがこれで <#escape> を付け忘れてしまう心配はありません。

動作検証

JSP, Velocity の時と同じモデルを使って動作を検証します。

WikiPage wikiPage = new WikiPage(
    "<strong>Tokyo</strong>",
    new WikiText("<strong>Tokyo</strong> is the capital of Japan."));

まず title の出力です。

エスケープできていますね。続いて WikiPage の出力。

WikiPage#toString() の結果をエスケープできています。

今度は明示的にエスケープを回避してみます。<#escape> 内で <#noescape> を使うとエスケープなしで出力できます。

JSP や Velocity のようにクラス定義によりエスケープをスキップする方法はわかりませんでした。

まとめ

<#escape> を自動的にファイルの前後に挿入することによりデフォルト HTML エスケープを実現できました。クラス定義でエスケープを回避できればさらによかったのですがエスケープ漏れはないので優秀ですね。

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