Thymeleafは、Spring MVCなしでも、バッチジョブ、コマンドラインプログラム、メールサービス、レポートパイプラインでHTMLをレンダリングできます。スタンドアロンアプリケーションでは、テンプレートリゾルバーとTemplateEngineを設定し、コンテキストを渡して、レンダリング結果を出力先に書き込みます。
統合レベルを選択する #
Springを使用しないプログラムには、Thymeleafのコアアーティファクトが必要です。
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.1.3.RELEASE</version>
</dependency>Springアプリケーションでは、Spring EL、MessageSource、変換、Spring対応のリンク動作を利用するために、thymeleaf-spring6とSpringTemplateEngineを使用できます。Spring Bootの依存関係管理では、対応するThymeleafのバージョンを選択してください。これらの統合が必要な場合はspring-boot-starter-thymeleafが便利ですが、コアエンジンの実行に必須ではなく、MVCも必要ありません。
リゾルバーとエンジンを設定する #
テンプレートの管理方法に合ったリゾルバーを選択します。ClassLoaderTemplateResolverはJARにパッケージ化された変更されないテンプレートに適しており、FileTemplateResolverは運用担当者が管理するファイルに適しています。
var resolver = new ClassLoaderTemplateResolver();
resolver.setPrefix("reports/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
resolver.setCacheable(true);
var engine = new TemplateEngine();
engine.setTemplateResolver(resolver);エンジンは一度設定して共有します。Thymeleafのドキュメントでは、完全に設定されたTemplateEngineはスレッドセーフとされています。一方、処理開始後に設定を変更することはサポートされていません。安定した本番テンプレートではキャッシュを有効にします。開発中または運用中にファイルを再読み込みする必要がある場合に限り、キャッシュを無効にするか、有効期限を制限したキャッシュを設定してください。
リゾルバーの文字エンコーディングは、テンプレートのバイト列をどのようにデコードするかを制御します。出力Writerで使用するエンコーディングは、生成されるファイルのバイト列を独立して制御するため、両方を明示的に設定してください。
異種のレポートデータをレンダリングする #
JavaでHTMLを構築するのではなく、ドメインに適した形の値を公開します。
record WarningRow(String orderId, String message) {}
record DailyReport(List<OrderSummary> orders, List<WarningRow> warnings) {}
var context = new Context(Locale.ENGLISH);
context.setVariable("report", report);
context.setVariable("generatedAt", generatedAt);
engine.process("daily-report", context, writer);テンプレートでは、異なる行型を持つ独立したセクションをレンダリングできます。
<section>
<h2>Warnings</h2>
<table>
<tr th:each="warning : ${report.warnings}">
<td th:text="${warning.orderId}">order</td>
<td th:text="${warning.message}">message</td>
</tr>
</table>
</section>th:textは、出力モードに応じてテキストをエスケープします。th:utextはエスケープされていないマークアップを出力するため、信頼できないコンテンツを渡してはなりません。HTMLテンプレートモードはHTMLを認識したマークアップを解析して書き出します。対象形式で異なるルールが必要な場合に限り、XML、text、JavaScript、CSSモードを選択してください。
フラグメントとテスト容易性 #
th:fragmentで再利用可能なマークアップを宣言し、th:insertまたはth:replaceで組み立てます。フラグメントのパラメーターを使うと、共有テーブルやヘッダーをグローバル変数名から独立させられます。複数のリゾルバーには順序を付け、解決可能なパターンで制限できます。これにより、広範なファイルシステムリゾルバーが意図せずパッケージ化されたテンプレートを隠すことを防げます。
レンダリングは、レポートモデルとWriterを受け取る純粋なサービスとして保持します。テストではStringWriterを使用し、HTMLパーサーで結果を解析して、構造とエスケープを検証できます。リゾルバーのテストでは、テンプレートが見つからない場合と非ASCIIコンテンツも検証してください。
出力リソースを管理する #
明示的な文字セットで書き込み、Writerを閉じます。
Path temporary = Files.createTempFile(outputDirectory, "daily-", ".html");
try (Writer writer = Files.newBufferedWriter(
temporary, StandardCharsets.UTF_8,
StandardOpenOption.TRUNCATE_EXISTING)) {
engine.process("daily-report", context, writer);
}
Files.move(temporary, destination,
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);アトミックな移動が必要な場合は、出力先と同じファイルシステム上に一時ファイルを作成します。レンダリングまたはアップロードに失敗した場合は一時パスを削除し、処理が成功するまで出力先を公開しないでください。オブジェクトストレージの場合は、サイズに上限を設けたバッファにレンダリングするか、再試行と部分的な失敗の動作が明示されたアップロードAPIを通してストリーミングします。
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)