Spring BootのウェブアプリケーションのControllerクラスのメモ。
|
Spring BootのウェブアプリケーションのControllerは、画面遷移の制御を行う。
import org.springframework.stereotype.Controller;
クライアント(ブラウザー)から要求されたURI(パス)に対する処理を記述するのがControllerクラス。
パスと処理(メソッド)の対応付けはControllerクラスに@RequestMappingアノテーションを付けることで行う。(対応付けを行うファイルを別途用意する必要は無い)
処理を行うメソッドは、次の処理(htmlの表示とか)を示すパスを返す。
「http://ホスト名:8080/」(パスが「/」)のリクエストに対してindex.htmlを返す例。
package com.example.demo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/")
public String index() {
return "index";
}
}
Controllerクラスには@Controllerアノテーションを付ける。
どのパスの処理を行うかについては@RequestMappingアノテーションで指定する。
メソッド名は自由に付けてよい。
パラメーターを受け取りたい場合は引数を追加する。
戻り値は「次の処理」を示すパスを返す。
「index」を返した場合、「index.html」が表示される。
htmlで表示する値を渡したい場合は、メソッドの引数にModelを追加し、Modelに対して値をセットする。
自分が処理するURI(パス)を指定するのが@RequestMappingアノテーション。
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMappingアノテーションはクラスとメソッドに付けられる。
「http://ホスト名:8080/hoge/zzz」のリクエストを処理する場合は以下のような書き方が出来る。
@RequestMapping("/hoge")
@Controller
public class HogeController {
@RequestMapping("zzz")
public String zzz() {
return "zzz";
}
} |
@RequestMapping("/hoge/zzz")
@Controller
public class HogeController {
@RequestMapping
public String zzz() {
return "zzz";
}
} |
@Controller
public class HogeController {
@RequestMapping("/hoge/zzz")
public String zzz() {
return "zzz";
}
} |
| 引数名 | 説明 | 例 |
|---|---|---|
| path | 受け付けるパス 複数のパスを記述することも出来る。 *を使う事も出来る。 |
@RequestMapping(path = "/hoge") |
| value | 受け付けるパス(pathの別名) | @RequestMapping("/hoge") |
| method | 受け付けるHTTPメソッド | @RequestMapping(path = "/hoge",
method = RequestMethod.GET) |
Controllerでリクエストを処理するメソッドでパラメーターを取得したい場合は、メソッドの引数を追加する。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Input Thymeleaf</title>
</head>
<body>
<h1>Input Thymeleaf</h1>
<form action="#" th:action="@{/input}" method="post">
<p><input name="param1" type="text" /></p>
<p><input name="param2" type="text" /></p>
<p><input type="submit" value="submit"/></p>
</form>
</body>
</html>
package com.example.demo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class InputController {
@RequestMapping(path = "/input")
public String submit(@ModelAttribute("param1") String param1, @ModelAttribute("param2") String param2) {
return "input";
}
}
HTMLのformのinputの値を受け取る場合は、引数に@ModelAttributeアノテーションを付ける。
→HTTPリクエストのパラメーターを取得したい場合は@RequestParamアノテーションを付ける
HTMLのformの内容を表すJavaBeanを作れば、引数はそれ1つで済む。
public class InputForm {
private String param1;
private String param2;
〜setter, gettter〜
}
@Controller
public class InputController {
@RequestMapping(path = "/input")
public String submit(@ModelAttribute InputForm form) {
String param1 = form.getParam1();
String param2 = form.getParam2();
return "input";
}
}
Controllerでリクエストを処理するメソッドの戻り値は、「次の処理(のパス)」を示す。
例えば"index"を返すと、「index.htmlを表示する」ことになる。
参考: tag1216さんのSpring MVCのコントローラでの戻り値いろいろ
| 遷移方法 | 説明 | 例 |
|---|---|---|
| htmlの表示 | htmlを表示する。"hoge/foo"は、src/main/resources/templates/hoge/foo.htmlを指す。 |
return "index"; |
| フォワード | 次の処理(Controller)へフォワードする。→フォワードのデータ渡し | return "forward:/login"; |
| リダイレクト | 次のURLへリダイレクトする。→リダイレクトのデータ渡し | return "redirect:/login"; |
フォワードとリダイレクトの違いは、Struts1の時代から変わっていないw
リダイレクトは、クライアント(ブラウザー)にリダイレクト先のURLを返す。クライアントは改めてそのURLをリクエストする。つまりリクエストが別になる。
フォワードは、別のControllerに処理を渡す。クライアント(ブラウザー)からのリクエストとしてはそのまま継続となる。
htmlへ値を渡す場合は、Modelを使用する。
メソッドの引数でModelを定義しておき、そのModelに対して値をセットする。
import org.springframework.ui.Model;
@Controller
public class IndexController {
@RequestMapping("/")
public String index(Model model) {
model.addAttribute("message", "hello IndexController");
return "index";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>index Thymeleaf</title>
</head>
<body>
<h1>index Thymeleaf</h1>
<p th:text="${message}">hello</p>
</body>
</html>
フォワード先へデータを渡したい場合は、Modelにデータを追加し、HTTPリクエストのattributeから取得する。[2017-11-01]
import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping(path = "/examplef1")
public String first(Model model) {
model.addAttribute("test", "from1");
return "forward:/examplef2";
}
HttpServletRequestを使う方法
import javax.servlet.http.HttpServletRequest;
@RequestMapping(path = "/examplef2")
public String second(HttpServletRequest request, Model model) {
String test = (String) request.getAttribute("test");
model.addAttribute("message", test);
return "index";
}
RequestAttributeを使う方法[2017-11-03]
import java.util.Optional; import org.springframework.web.bind.annotation.RequestAttribute;
@RequestMapping(path = "/examplef2")
public String second(@RequestAttribute("test") Optional<String> test, Model model) {
test.ifPresent(s -> model.addAttribute("message", s));
return "index";
}
フォワードはクライアント(ブラウザー)からのリクエスト内の一連の処理なので、フォワード前に作ったデータ(インスタンス)がそのままフォワード先に渡される。
リダイレクト先へデータを渡したい場合は、RedirectAttributesにデータを追加し、Modelから取得する。[2017-11-01]
@RequestMapping(path = "/exampler1")
public String first(RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("test", "from1");
return "redirect:/exampler2";
}
@RequestMapping(path = "/exampler2")
public String second(Model model) {
String test = (String) model.asMap().get("test");
model.addAttribute("message", test);
return "index";
}
RedirectAttributesに追加されたデータはリダイレクト先のModelに入ってくる。
なので、上記の例では「test」という名前で保存してリダイレクト先で「message」というデータとして保存し直しているが、
RedirectAttributesに「message」という名前で保存しておけば、リダイレクト先でModelに詰め直す必要は無い。
リダイレクトは、一旦クライアント(ブラウザー)に制御を戻し、指定したURLに改めてアクセスさせる。
このため、RedirectAttributesを使った実際のデータの受け渡しは、HTTPセッションを介して行うようになっているらしい。
Springのアノテーションは、引数のvalueが他の引数の別名(alias)(名前は違うけど同じ機能)になっているものがある。
これは、Javaのアノテーションの仕様によるものだと思われる。
Javaのアノテーションは、引数名がvalueのときは、代入の「value =」を省略できるルールになっている。
例えば、@RequestMappingアノテーションはvalueの別名がpathになっている。
これにより、「@RequestMapping(path = "/hoge")」と「@RequestMapping(value
= "/hoge")」は同じ意味になるが、valueに対しては「@RequestMapping("/hoge")」と省略して書くことが出来る。
逆に言えば、「@RequestMapping("/hoge")」と書けるようにする為にわざわざvalueを定義してあるのだろう。
したがって、「value =」が明示的に出てくる書き方は本来の使い方ではないと思われる。