たこし++の備忘録

競技プログラミングの備忘録

【Spring boot】RestControllerのレスポンスボディにThymeleafで動的に値を埋め込んだhtmlを含める

背景

(タイトルがわかり辛すぎるので背景から...)

Spring bootは@RestControllerをつけたクラスのメソッドの返り値がそのままレスポンスボディになります

@RestController
public class HelloRestController {
  @RequestMapping("index")
  public HelloModel index() {
    return new HelloModel(200, "Hello, world!");
  }
}
@Data
@AllArgConstructor
public class HelloModel {
  private Integer status;
  private String message;
}

この場合,indexにGETリクエストを送れば下記のようなレスポンスが得られます

{
  status: 200,
  message: "Hello, world!"
}

ところで,一般的なwebアプリはhtmlの描画をサーバーサイドでやる同期的な描画と,javascriptで非同期通信をし,取ってきたデータをクライアント側で描画する方法に分けることができるかと思います. ここで,javascript側での描画処理を極力抑えて,ほぼすべての描画をサーバーサイドでやりたいという欲が生まれることがあります.理由は色々ありますが,サーバーサイドで描画を行う方法に統一することで テンプレートがサーバーサイドに集約されて保守がしやすいといったことが挙げられます.

Spring bootのサーバーサイドレンダリング@Controllerを使います.

@Controller
public class HelloController {
  @RequestMapping("index")
  public String index(Model model) {
    model.addAttribute("message","Hello, world!");
    return "index";
  }
}

index.html

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
</head>
<body>
<p th:text="${message}"></p>
</body>
</html>

ここで,たとえば

{
  status: 200,
  message: "ここにthymeleafで動的に描画したものを埋め込みたい"
}

というレスポンスを返したい場合,どのように実装すればいいのかというのをこの記事では紹介します. (調べた限り,Spring Bootの標準的な機能としてこのような使い方が見当たりませんでした.もし標準的な機能で出来る場合はコメントにてご指摘いただけると幸いですmm)

結論

こちらのレポジトリに参考実装を置いてあります. github.com

仕組み

Spring BootにおけるControllerの動き

詳しくはこちら terasolunaorg.github.io

レンダリング処理のポイントは以下の2点です

  1. ViewResolverが描画で使うテンプレートを決定し,Viewインスタンスを返す

  2. ViewがModelを受け取って値を動的に埋め込む

RestControllerでも同じ動きを模倣してあげれば動的に埋め込まれたものを生成することができるので, あとはこれを必要に応じてレスポンスの一部に加えてあげれば良いです.