Flask の Jinja2 の autoescape について

こんな現象に遭遇したので覚え書き

そもそも

エスケープされない

autoescapeという名前から、全てのテンプレートの処理で自動エスケープされると思っていたが、そうじゃなかった。
たとえばこんなケースだ。

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def index():
   return render_template('index.tmpl',  rawhtml='<a href="http://example.com">hoge</a>')

if __name__ == '__main__':
    app.run()

これをブラウザで出力したとき、rawhtmlは、エスケープされて下記のように出力される事を期待するだろう。

&lt;a href=&#34;http://example.com&#34;&gt;hoge&lt;/a&gt;

ところが、これは期待に反して素のaタグが出力されてしまう。

<a href="http://example.com">hoge</a>

なぜ?

render_templateに渡してるテンプレートファイル名を見てほしい。

index.tmpl

テンプレートファイルになんだか、見慣れない独自の拡張子を使っている。これが原因だ。
テンプレートファイルをリネームして「index.html」に変えて、render_templateの引数を「index.html」に変える。
すると、正しくエスケープされる。

どういうこと?

結論を言うと「autoescape」対象となるテンプレートの拡張子は決まっている。

「flask/app.py」のソースの下記箇所がそれに該当する。

 634     def select_jinja_autoescape(self, filename):
 635         """Returns `True` if autoescaping should be active for the given
 636         template name.
 637 
 638         .. versionadded:: 0.5
 639         """
 640         if filename is None:
 641             return False
 642         return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))

「.html」「.htm」「.xml」「.xhtml」しかautoescapeの対象にならないのだ。

で回避策は?

autoescape対象となる拡張子を追加するには、ソースにベタで拡張子が書いてるのを見ても分かる通り、あまり簡単ではなさそうだ。「.html」なりで統一する事をお勧めします。

けど追加したい、どうしても追加したい、という言う人は、下記のように強引に変える手もあるが、ものすごくバッドノウハウ臭がするのでオススメしません。

def select_jinja_autoescape(filename):
        """ jinja2のautoescape対象の判別メソッドを差し替える
        :refs: Flask.select_jinja_autoescape
        """
        if filename is None:
            return False
        return filename.endswith(('.html', '.htm', '.xml', '.xhtml', '.tmpl'))

app.jinja_env.autoescape = select_jinja_autoescape

ちなみに

  • autoescapeが効いているテンプレート上で、部分的にautoescapeを止めたい時には下記のようにすればよい
{% autoescape false %}

    {{ rawhtml }}

{% endautoescape %}