こんにちはこんばんは!
株式会社情創 技術開発局
リオオリンピックの熱が去って焼き肉が食べたくなった@YAKINIKUです。
今回は引き続き、Controller について解説していきます。
1.@ResponseBody
による戻り値の設定
前回説明しましたが、@ResponseBody
を付けたメソッドは戻り値がそのままレスポンスのコンテンツになります。
また、@RestController
を付けた Controller のメソッドは@ResponseBody
を付けなくても、戻り値がコンテンツになります。
これは、JSON や XML などを返す場合に使用されることが多いです。
前回宣言した通り、本ブログでは Controller は ModelAndView クラスを返すのが基本になります。
しかし、JSON や XML を返す必要がある場合は例外的に、@ResponseBody
を使います。
以降は、@ResponseBody
の記述方法について説明していきます。
1-1.HttpServletResponse でコンテンツを返す
戻り値を void にしても、HttpServletResponse を引数に加えれば、レスポンスのコンテンツを直接書き込むことができます。
@RequestMapping("/text")
@ResponseBody
public void text(HttpServletResponse res) throws IOException {
res.getWriter().write("text content");
}
動作自体は戻り値を String にした場合と変わりませんが、この記述方法は後述するファイルのダウンロードにも使用できます。
1-2.コンテンツタイプを指定する
コンテンツタイプは@RequestMapping
の produces 属性で指定できます。
org.springframework.http.MediaType クラスにコンテンツタイプの定数が定義されているので、こちらを使いましょう。
@RequestMapping(value="/xml", produces=MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public String xml() {
return "<root><content>xml</content></root>";
}
1-3.HTTP ステータスやレスポンスヘッダを指定する
HTTP ステータスやコンテンツタイプ以外のレスポンスヘッダを指定したい場合は、戻り値を ResponseEntity<T> にします。
ResponseEntity はボディ、ヘッダ、ステータスを持つクラスで、型 T にはボディの型を指定します。
@RequestMapping("/responseEntity")
@ResponseBody
public ResponseEntity<String> responseEntity() {
HttpHeaders headers = new HttpHeaders();
headers.add("Connection", "Keep-Alive");
HttpStatus status = HttpStatus.OK;
return new ResponseEntity<String>("text content", headers, status);
}
1-4.JSON を返す
JSON を返す場合は、戻り値は任意のクラスにします。
そうすることで、Spring Boot が Jackson という JSON 処理ライブラリを使用して、JSON 形式に変換してくれます。
例えば String 型のメンバ変数 id と name をもつ User クラスを作り、そのリストを返す場合、メソッドは以下のように記述できます。
@RequestMapping("/json")
@ResponseBody
public List<User> json() {
return Arrays.asList(new User("1", "aaa"), new User("2", "bbb"));
}
このメソッドによって、以下のような JSON データが生成されます。
[{"id":"1","name":"aaa"},{"id":"2","name":"bbb"}]
1-5.ファイルをダウンロードさせる
ファイルをダウンロードさせるには、以下のように HttpServletResponse を使用する方法があります。
@RequestMapping("/file1")
@ResponseBody
public void file1(HttpServletResponse res) throws IOException {
File file = new File("test.xml");
res.setContentLength((int) file.length());
res.setContentType(MediaType.APPLICATION_XML_VALUE);
FileCopyUtils.copy(new FileInputStream(file), res.getOutputStream());
}
また、戻り値に org.springframework.core.io.Resource を使用する方法もあります。
こちらは Content-Length を自動で設定してくれるので、より簡潔に記述できます。
@RequestMapping(value = "/file2", produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public Resource file2() {
return new FileSystemResource("test.xml");
}
2.リクエストパラメータの取得
ここからは前回説明できなかったリクエストパラメータの取得について説明していきます。
これまでは Controller が View に返す内容について触れてきましたが、当然 form などkからリクエストパラメータを受け取る処理も必要になります。
受け取ったデータは扱いやすいようにオブジェクトにバインドしますが、前回紹介した@ModelAttribute
アノテーションを使うことで、リクエスト時に自動的にバインドを行います。
本項では@ModelAttribute
と BindingResult について解説します。
なお本ブログでは、@ModelAttribute
はメソッドの引数に記述し、必ず BindingResult を使用してエラーの有無を検出します。
2-1.@ModelAttribute
@ModelAttribute
は指定したクラスにリクエストパラメータをバインドします。
@ModelAttribute
を付けたメソッドは、リクエストのたびに@RequestMapping
メソッドの前に呼ばれます。
以下のような id と name を submit する場合を例にして説明します。
<form action="modelAttribute" method="post">
<input type="text" name="id"/>
<input type="text" name="name"/>
</form>
これを第○○回で作成した TestBean クラスで受け取ります。
このとき、変数名が submit されるパラメータ名と一致している必要があります。
package com.qiita.demo.domain.tbl;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TestBean implements java.io.Serializable {
private int id;
private String name;
}
@ModelAttribute
メソッドはこの TestForm クラスを new して返します。
@ModelAttribute
TestBean testBean() {
return new TestBean();
}
なお、@ModelAttribute
によってバインドされたクラスは、自動的に Model に追加されます。
このとき、デフォルトでクラス名の頭文字を小文字にしたものが属性名になりますが、@ModelAttribute("属性名")
とすれば指定することもできます。
また、@ModelAttribute
は@RequestMapping
メソッドの引数に直接付けることもできます。
@RequestMapping("/modelAttribute")
public ModelAndView modelAttribute(@ModelAttribute TestBean testBean, ModelAndView mav) {
mav.setViewName("modelAttribute");
return mav;
}
@ModelAttribute
メソッドを用意するより、こちらのほうが一般的のようです。
2-2.BindingResult
@ModelAttribute
を使用してバインドを行う場合、そのクラスの後ろに BindingResult というクラスを引数にすることで、バインドが失敗したかどうかを判定できます。
逆に BindingResult が引数にない場合、バインドのエラーがあった際にメソッドそのものが呼ばれません。
@RequestMapping("/modelAttribute")
public ModelAndView modelAttribute(@ModelAttribute TestBean testBean, BindingResult result, ModelAndView mav) {
if (result.hasErrors()) {
//バインドエラー時
mav.setViewName("error");
return mav;
}
mav.setViewName("success");
return mav;
}
このとき、BindingResult は必ず**@ModelAttribute
を付けた引数の次**に記述しなければならないことに注意してください。
異なる位置に記述していた場合、エラーが発生してメソッドが呼ばれなくなってしまいます。
では今回はこの辺で終わりにします。
最後まで読んでいただきありがとうございました。
それではみなさんまた会いましょう!