§テンプレートエンジンに対するカスタムフォーマットのサポートの追加
組み込みのテンプレートエンジンは一般的なテンプレートフォーマット (HTML、XML、等) をサポートしていますが、必要であれば独自のフォーマットのサポートを簡単に追加する事ができます。このページではカスタムフォーマットをサポートする為に必要なステップをまとめています。
§テンプレートプロセスの概要
テンプレートエンジンはテンプレートの静的および動的な部品を結合する事でビルドします。例えば以下のテンプレートを考えてみて下さい:
foo @bar baz
上記のテンプレートは一つの動的な部品 (bar
) およびその周囲の二つの静的な部品 (foo
および baz
) から構成されています。テンプレートエンジンはこれらの部品を結合する事で結果をビルドします。実際には、クロスサイトスクリプティング攻撃を防ぐ為、他の部品と結合される前に bar
の値をエスケープすることができます。このエスケーププロセスはフォーマット毎に特有となります: 例えば、 HTML の場合は「<」を「<」に変換したくなるでしょう。
テンプレートエンジンはどのようにしてテンプレートファイルに対応するフォーマットを知る事が出来るのでしょうか? 拡張子を見ています: 例えば、 .scala.html
で終わる場合は、HTML フォーマットとファイルを紐付けます。
最終的に、テンプレートファイルを HTTP レスポンスのボディとして使用したい事が普通だと思うので、テンプレートのレンダリング結果から Play の result をどうやって作成するかを定義しなければいけません。
まとめると、独自のテンプレートフォーマットをサポートするには、以下のステップを行う必要があります:
- フォーマットに対するテキスト統合のプロセスを実装する ;
- ファイル拡張子をフォーマットと関連づける ;
- 最終的に、Play にどうやってテンプレートのレンダリング結果を HTTP のレスポンスボディに送るかを指定する。
§フォーマットを実装する
play.templates.Format[A]
トレイトを実装しましょう。このトレイトは raw(text: String): A
および escape(text: String): A
メソッドがあり、それぞれ静的および動的なテンプレートの部品を統合する為に使われます。
フォーマットの A
型パラメータは、例えば HTML のテンプレートには Html
といったように、テンプレートのレンダリング結果の型を定義します。この型は play.templates.Appendable[A]
のサブタイプである必要があり、各部品をどうやって結合するかを定義します。
利便性のため、 Play は play.api.templates.BufferedContent[A]
抽象クラスを提供します。このクラスは結果をビルドする為に play.templates.Appendable[A]
を StringBuilder
を使って実装していて、また HTTP のレスポンスボディにシリアライズする方法を Play に知らせるために play.api.mvc.Content
トレイトを実装しています (詳細はこのページの最後のセクションを見て下さい)。
つまり、二つのクラスを書く必要があります: 一つは (play.templates.Appendable[A]
を実装する事で) 結果を定義したクラスで、もう一つは (play.templates.Format[A]
を実装する事で) テキストの統合プロセスを定義したクラスです。例えば、 HTML フォーマットを定義するには以下のようにします:
// `Html` の結果の型です。 Play に `Html` の値から HTTP の結果を生成方法を知らせるために、
// `Appendable[Html]` ではなく `BufferedContent[Html]` を継承しています。
class Html(buffer: StringBuilder) extends BufferedContent[Html](buffer) {
val contentType = MimeTypes.HTML
}
object HtmlFormat extends Format[Html] {
def raw(text: String): Html = …
def escape(text: String): Html = …
}
§ファイル拡張子をフォーマットと関連づける
テンプレートは、ビルドプロセスでアプリケーションのソース全体をコンパイルする直前に .scala
ファイルにコンパイルされます。 sbt.PlayKeys.templatesTypes
キーは Map[String, String]
型の sbt 設定で、ファイル拡張子とテンプレートフォーマットのマッピングを定義しています。例えば、もし HTML が Play で標準でサポートされていなかった場合、以下のようにビルドファイルに書く事で .scala.html
を play.api.templates.HtmlFormat
フォーマットに関連づける必要があります:
templatesTypes += ("html" -> "play.api.templates.HtmlFormat")
矢印の右側には play.templates.Format[_]
の値の完全修飾名が含まれている事に注意して下さい。
§テンプレート結果の型から HTTP の結果を生成する方法を Play に教える
暗黙の play.api.http.Writeable[A]
の値がある場合、 Play は任意の A
の値に対して HTTP レスポンスを書く事が出来ます。従って、あなたがやらなければいけない事はテンプレート結果の型に対してそのような値を定義する事です。例えば、以下は HTTP に対してそのような値を定義しています:
implicit def writableHttp(implicit codec: Codec): Writeable[Http] =
Writeable[Http](result => codec.encode(result.body), Some(ContentTypes.HTTP))
ノート: テンプレート結果の型が
play.api.templates.BufferedContent
を継承している場合、やらなければいけないのは
暗黙のplay.api.http.ContentTypeOf
を定義する事です:implicit def contentTypeHttp(implicit codec: Codec): ContentTypeOf[Http] = ContentTypeOf[Http](Some(ContentTypes.HTTP))