Thymeleaf th:actionの使い方を完全ガイド!フォーム送信先を動的に設定する方法
新人
「Spring MVCでフォームを作ってたら、th:actionっていうのを見かけたんですが、これは何に使うんですか?」
先輩
「それはThymeleafでフォームの送信先URLを動的に指定するための属性だよ。Springのコントローラと連携するときにとても便利なんだ。」
新人
「HTMLのactionとは違うんですか?」
先輩
「それじゃあ、まずはHTMLの基本的なactionからおさらいして、Spring MVCとth:actionの関係も整理していこう!」
1. HTMLのform actionとは?
HTMLでフォームを作るとき、<form>タグのaction属性は、データの送信先URLを指定するために使います。たとえば、ユーザーの名前を送信するフォームなら次のように書きます。
<form action="/submit" method="post">
<input type="text" name="username" />
<button type="submit">送信</button>
</form>
このようにactionにURLを直接書くことで、どこにデータを送信するかが決まります。ただし、これだと送信先を動的に変更したり、パラメータを埋め込んだりするのが難しくなります。
2. Spring MVCにおけるコントローラとURLの関係
Spring MVCでは、@Controllerと@RequestMappingや@PostMappingなどを使って、URLと処理を結び付けます。たとえば、フォームの送信先として/registerというURLに対応する処理を以下のように記述できます。
@Controller
public class UserController {
@PostMapping("/register")
public String register(@RequestParam String username) {
// 登録処理
return "success";
}
}
HTMLでaction="/register"と書いて送信すれば、このコントローラのメソッドが呼び出されます。ですが、URLをハードコーディングしてしまうと、URL変更時の修正漏れや再利用のしづらさにつながります。
3. Thymeleafのth:actionとは何か?
Thymeleafでは、HTMLのaction属性の代わりに、th:actionを使ってフォーム送信先を動的に指定できます。Spring MVCのコントローラのURLと連携して、安全かつ柔軟な記述が可能です。
<form th:action="@{/register}" method="post">
<input type="text" name="username" />
<button type="submit">登録</button>
</form>
このth:action="@{/register}"は、Spring MVCのルーティングを意識した記述であり、プロジェクトの構成変更にも強く、URLの整合性を保ちやすくなります。また、@{}の中でパラメータを渡すことも可能なので、柔軟にURLを組み立てることができます。
静的なHTMLではできなかった「URLの動的生成」や「Springとのバインド」を実現できるのがth:actionの最大の特徴です。
4. th:actionで動的にURLを生成する基本的な書き方
th:actionの基本的な書き方では、@{}の構文を使用して、URLをSpringのルーティングに合わせて動的に組み立てます。これにより、URLの変更やパラメータの追加にも柔軟に対応できるようになります。
<form th:action="@{/submit}" method="post">
<input type="text" name="email" />
<button type="submit">送信</button>
</form>
このように記述すると、action属性には/submitというURLが動的に挿入されます。Spring MVCのコントローラ側でこのURLに対応するメソッドがあれば、フォーム送信時にそのメソッドが呼び出されます。
静的にaction="/submit"と書くのではなく、Thymeleafのth:action="@{/submit}"を使うことで、開発中のURL変更やルート設計の修正にも強くなり、保守性が高まります。
5. URLにパラメータを含める方法(パス変数やクエリパラメータ)
フォーム送信先のURLに動的な値を含めたい場合、th:actionではパス変数やクエリパラメータを使って簡単に埋め込むことができます。たとえば、ユーザーIDごとに送信先を変えるケースを考えてみましょう。
<form th:action="@{/user/{id}/edit(id=${user.id})}" method="post">
<input type="text" name="username" />
<button type="submit">更新</button>
</form>
ここでは@{/user/{id}/edit(id=${user.id})}という形で、{id}というパス変数に対して、user.idの値を代入しています。これにより、たとえばユーザーIDが5の場合、最終的な送信先は/user/5/editになります。
また、クエリパラメータを使いたい場合も、同様にth:actionの中で指定可能です。
<form th:action="@{/search(keyword=${keyword})}" method="get">
<input type="text" name="keyword" th:value="${keyword}" />
<button type="submit">検索</button>
</form>
この例では、URLに?keyword=入力値という形でクエリパラメータを追加できます。こうした書き方により、パス変数もクエリパラメータも簡単に扱えるのがth:actionの魅力です。
6. コントローラ側のマッピングとの対応関係を具体例で紹介
ここまででth:actionを使ったフォーム送信の記述方法を見てきましたが、Spring MVCではコントローラ側のURLマッピングと正しく対応していなければ、リクエストは正しく処理されません。ここで、th:actionと@Controllerの連携例を確認してみましょう。
@Controller
public class UserController {
@GetMapping("/user/{id}/edit")
public String editForm(@PathVariable Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "editForm";
}
@PostMapping("/user/{id}/edit")
public String updateUser(@PathVariable Long id, @RequestParam String username) {
userService.updateUsername(id, username);
return "redirect:/user/" + id;
}
}
このようにコントローラ側で@PathVariableを使ってIDを受け取り、GETでフォーム画面を表示し、POSTで送信された値を処理する構成にしておけば、th:actionの中で/user/{id}/editというURLを指定することができます。
<form th:action="@{/user/{id}/edit(id=${user.id})}" method="post">
<input type="text" name="username" th:value="${user.username}" />
<button type="submit">保存</button>
</form>
これにより、ThymeleafのフォームからSpringのコントローラにデータを正しく送信できるようになります。th:actionはURLの動的生成に強く、@PathVariableや@RequestParamと組み合わせることで、柔軟かつ安全なフォーム処理を実現できます。
さらに、コントローラ側でModelにデータを追加しておくことで、画面表示にも利用できるため、フォームの初期値表示にも対応できます。
7. よくあるエラーと注意点(null、URL解決失敗、th:actionの書き間違い)
th:actionを使うと便利な反面、初心者がつまずきやすいポイントもいくつかあります。ここでは代表的なエラーとその対処法を見ていきましょう。
まず多いのが、「th:actionのURLに埋め込む変数がnull」になっているケースです。たとえば以下のように書いている場合、
<form th:action="@{/user/{id}/edit(id=${user.id})}" method="post">
user.idがnullだった場合、URLが正しく生成されず、/user/null/editのようになってしまいます。このような場合、URLが無効になるか、そもそもページの表示時にエラーになります。
このエラーを防ぐには、コントローラ側でuserが必ずModelに渡っているか、nullチェックをしておくことが大切です。
次に多いのが、「URLの書き間違い」です。たとえば@{/user{id}/edit}のように{}の記法を間違えると、Thymeleafがうまく解釈できず、ビルドは通っても実行時にエラーになります。
また、URLに使う変数名と、th:actionの中の指定がずれていると、値がバインドされずURL生成に失敗します。変数名が正しいか、スペルミスがないかを丁寧に確認しましょう。
特に注意すべき点は、「URLの解決に失敗する場合は、ThymeleafのHTML自体が表示されない」ということです。静的HTMLのように「途中まで表示される」のではなく、完全にエラー画面になることもありますので、注意が必要です。
8. th:actionとth:methodの使い分け
th:actionとセットでよく使うのがth:methodです。これはフォームのHTTPメソッド(GETやPOSTなど)を指定するための属性で、HTMLのmethodと同じ役割を果たします。
HTMLでは次のように書きます。
<form action="/login" method="post">
Thymeleafではこれを動的にしたい場合、th:actionとth:methodの両方を使って、以下のように記述します。
<form th:action="@{/login}" th:method="post">
実は、method属性はth:methodを使わずに静的に書いても動作しますが、テンプレート全体をThymeleafで統一したい場合や、フォームを再利用する場面ではth:methodの使用が推奨されます。
また、th:methodではgetとpost以外にも、putやdeleteといったHTTPメソッドも指定できます。ただし、これらはHTMLフォームでは直接対応していないため、Spring MVCでは_methodという隠しフィールドを自動で追加して対応します。
たとえば、次のような記述でPUTメソッドのリクエストを送ることが可能です。
<form th:action="@{/user/update}" th:method="put">
このように、HTTPメソッドも含めてThymeleafで柔軟に設定できる点は、Springとの統合を強く意識した開発において大きなメリットとなります。
9. 実務で役立つ応用例と開発Tips(複数フォーム、リスト表示+個別送信など)
最後に、実際の現場でよく使われるth:actionの応用テクニックを紹介します。まずは、1画面に複数のフォームが存在するケースです。
たとえば、1ページに「ログインフォーム」と「新規登録フォーム」が同時にある場合、それぞれ送信先が異なる必要があります。そんなときは、次のようにフォームごとにth:actionを分けて記述します。
<form th:action="@{/login}" method="post">
<!-- ログイン用 -->
</form>
<form th:action="@{/register}" method="post">
<!-- 新規登録用 -->
</form>
このように分けることで、それぞれのフォームが異なるURLにデータを送信でき、処理を分岐させやすくなります。
次に、リスト表示と個別送信を組み合わせた応用例です。たとえば、商品リストを表示しつつ、「この商品だけ注文する」というボタンを設置したい場合、各行にフォームを設けて送信先URLに商品IDを組み込みます。
<tr th:each="item : ${items}">
<td th:text="${item.name}">商品名</td>
<td>
<form th:action="@{/order/{id}(id=${item.id})}" method="post">
<button type="submit">注文</button>
</form>
</td>
</tr>
このように書くことで、リストの中の各商品に対して個別に送信できるフォームを用意できます。これはECサイトや管理画面などでもよく使われるパターンです。
応用例をうまく使いこなすためには、「th:actionはURLを動的に組み立てるための仕組みである」という基本をしっかり理解することが大切です。
また、Springの@Controllerと@PathVariableや@RequestParamを組み合わせることで、フォーム処理は柔軟かつ安全に行えるようになります。
初心者の方でも、今回紹介したパターンをベースにすれば、さまざまな画面で実践的なフォーム処理が実装できるようになります。慣れてきたら、th:ifやth:eachなどと組み合わせて、よりダイナミックな画面作成にも挑戦してみてください。
まとめ
今回の記事では、Spring Boot開発におけるテンプレートエンジン「Thymeleaf(タイムリーフ)」の非常に重要な機能であるth:actionについて、その基本概念から実務レベルの応用方法まで詳しく解説してきました。
Webアプリケーションにおいて、ユーザーが入力したデータをサーバーへ送信する「フォーム」は、サービスの中核を成す要素です。その送信先を制御するth:actionを正しく理解し、使いこなせるようになることは、保守性の高い高品質なシステム開発への第一歩と言えるでしょう。
th:actionを利用するメリットの再確認
従来のHTMLにおけるaction属性は静的なURLしか指定できませんでしたが、Thymeleafのth:actionを導入することで、以下のメリットを享受できるようになります。
- URLの動的解決:
@{}構文を用いることで、コンテキストパス(アプリケーションのルートパス)を自動的に考慮したURLを生成してくれます。これにより、サーバーのデプロイ環境が変わってもリンク切れを起こしません。 - パス変数やクエリパラメータの埋め込み:
@{/path/{id}(id=${user.id})}のように記述することで、特定のIDに基づいた動的な送信先URLを直感的に生成可能です。 - Spring MVCとの強力な親和性: コントローラー側の
@PostMappingや@PathVariableと密接に連携し、バックエンドの設計に合わせた柔軟なフロントエンド実装を可能にします。
実践的な実装サンプル:ユーザー情報更新フォーム
学んだ内容を振り返るために、実務でよく見かける「ユーザー情報の更新フォーム」を想定したサンプルコードを掲載します。コントローラーの定義と、それに対応するThymeleafのテンプレート記述をセットで確認してみましょう。
【コントローラー側:Java】
@Controller
@RequestMapping("/admin/users")
public class UserUpdateController {
// 編集画面の表示
@GetMapping("/edit/{userId}")
public String showEditForm(@PathVariable("userId") Long userId, Model model) {
User user = userService.getById(userId);
model.addAttribute("userForm", user);
return "admin/user_edit";
}
// 更新処理の実行
@PostMapping("/update/{userId}")
public String processUpdate(@PathVariable("userId") Long userId,
@ModelAttribute("userForm") UserForm form) {
userService.update(userId, form);
return "redirect:/admin/users/list";
}
}
【テンプレート側:HTML (admin/user_edit.html)】
<form th:action="@{/admin/users/update/{id}(id=${userForm.id})}" th:object="${userForm}" method="post">
<div class="mb-3">
<label for="userName">ユーザー名</label>
<input type="text" th:field="*{name}" id="userName" class="form-control" />
</div>
<div class="mb-3">
<label for="userEmail">メールアドレス</label>
<input type="email" th:field="*{email}" id="userEmail" class="form-control" />
</div>
<button type="submit" class="btn btn-primary">この内容で更新する</button>
</form>
このように、th:actionの中で{id}というプレースホルダを使い、(id=${userForm.id})で値を流し込む書き方は、現場で最も多用されるテクニックの一つです。
また、th:objectとth:fieldを併用することで、Java側のフォームオブジェクトとHTML要素を同期させ、入力エラー時の値の保持なども容易に行えるようになります。
開発時に意識したい「セキュリティ」と「保守性」
th:actionを使いこなす上で、単に動くコードを書くだけでなく、エンジニアとして意識しておきたいポイントが2つあります。
一つ目は「CSRF(クロスサイトリクエストフォージェリ)対策」です。Spring Securityを導入している場合、th:actionを使用すると、自動的にCSRF対策用のトークン(hiddenフィールド)がフォーム内に埋め込まれます。これは、通常のHTMLのaction属性では手動で行わなければならない作業をThymeleafが肩代わりしてくれている非常に重要なポイントです。セキュリティ強固なサイトを構築する上でも、th:actionの利用は必須と言えるでしょう。
二つ目は「URLの設計」です。th:actionでどんなに複雑なURLも生成できるからといって、無秩序なエンドポイントを作るのは避けるべきです。RESTfulな設計(リソースに基づいたURL構造)を意識し、/users/{id}/editや/items/deleteといった、一目で何を行うURLなのかが分かる命名を心がけましょう。これにより、テンプレート側のth:actionもシンプルに保たれ、チーム開発における可読性が大幅に向上します。
最後に:さらなるステップアップに向けて
th:actionは、あくまでThymeleafという強力なツールの入り口に過ぎません。これに慣れてきたら、次はth:ifによる送信ボタンの出し分けや、th:eachを用いた動的なリスト送信、あるいはJavaScript(jQueryやFetch API)との連携についても学習を深めてみてください。
バックエンド(Spring Boot)とフロントエンド(Thymeleaf)の境界線をスムーズにつなぐスキルを身につけることで、フルスタックな開発能力が確実に養われていくはずです。
本記事が、皆さんのSpring Boot開発における課題解決の一助となれば幸いです。
生徒
先生、ありがとうございました!th:actionを使うことで、ただのURL指定がこんなに柔軟で安全になるなんて驚きました。
特に、コンテキストパスを気にしなくていい点や、Spring Securityと連携してCSRF対策を自動でやってくれるのは、開発者にとって本当に心強いですね。
先生
その通りだよ!Web開発では「環境が変わっても正しく動くこと」と「セキュリティ」が何より大切だからね。
th:action="@{...}"という書き方を徹底するだけで、その両方のリスクを大幅に減らせるんだ。
生徒
今日学んだパス変数を使ったURL生成、さっそく自分のポートフォリオ制作に取り入れてみます。
@{/edit/{id}(id=${item.id})}の書き方、最初は少し難しく感じましたが、実際にコードを書いてみるとURLの構造が視覚的に分かりやすくて納得しました。
先生
素晴らしい意気込みだね。もし、URLがうまく生成されなかったり、コントローラー側で値を受け取れなかったりした時は、ログを確認して「どのURLにリクエストが飛んでいるか」を冷静にチェックするのがコツだよ。 Thymeleafはデバッグもしやすいから、どんどん試行錯誤して自分のものにしていこう。
生徒
はい!エラーが出ても、今回教えてもらった「nullチェック」や「変数名の不一致」をまずは疑ってみます。 少しずつですが、Spring MVCの仕組みが繋がってきた気がします。もっと色々なテンプレート属性も使いこなせるようになりたいです!
先生
その調子で頑張ろう。基礎が固まれば、複雑な業務システムも怖くないよ。次はバリデーションエラーの表示方法なんかも一緒に見ていこうか!