ResponseEntityで組み立てる
このレッスンでは、ResponseEntity を使って本文とステータスコードとヘッダを一体で組み立てられるようになります。
戻り値だけでは足りない場面がある
これまでのコントローラは、本文になるオブジェクトをそのまま返していました。
Java
@GetMapping("/{id}")
public UserResponse get(@PathVariable Long id) {
return service.find(id);
}この書き方は簡潔ですが、返せるのは本文だけです。ステータスコードは 200 に固定され、ヘッダを足すこともできません。@ResponseStatus を付ければコードは変えられますが、それも固定値です。
実際には「見つかれば 200 で本文、見つからなければ 404 で本文なし」のように、同じメソッドの中で状況に応じてコードを変えたいことがあります。そのための型が ResponseEntity です。
本文とコードとヘッダを 1 つにまとめる
Java
@GetMapping("/{id}")
public ResponseEntity<UserResponse> get(@PathVariable Long id) {
return service.find(id)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}ResponseEntity.ok(body) は 200 と本文、ResponseEntity.notFound().build() は 404 で本文なしを表します。ジェネリクスの型引数は本文の型です。
組み立て方は 2 通りあります。
Java
// 手軽な形
ResponseEntity.ok(user);
ResponseEntity.status(HttpStatus.CONFLICT).body(errorBody);
// 段階的に組み立てる形
ResponseEntity.status(HttpStatus.CREATED)
.header("Location", "/users/12")
.body(user);status でコードを決め、header でヘッダを足し、最後に body か build で締めます。本文が無いときは build、あるときは body を呼ぶ、という使い分けです。
201 には Location を付ける
作成に成功したときの定石は、201 と Location ヘッダの組み合わせです。
プレーンテキスト
HTTP/1.1 201 Created
Location: /users/12
Content-Type: application/json
{"id":12,"name":"sato"}Location ヘッダには、いま作られたリソースの URL を入れます。クライアントは POST を送った時点では ID を知らないので、これが次の操作への案内になります。本文にも ID を含めておけば、ヘッダを読まないクライアントでも困りません。
Spring には専用の書き方も用意されています。
Java
@PostMapping
public ResponseEntity<UserResponse> create(@Valid @RequestBody CreateUserRequest req) {
UserResponse created = service.create(req);
return ResponseEntity
.created(URI.create("/users/" + created.id()))
.body(created);
}ResponseEntity.created(uri) は、201 の設定と Location ヘッダの付与を同時に行います。
使い分けの目安
| 戻り値の型 | ステータス | ヘッダ | 向いている場面 |
|---|---|---|---|
| DTO をそのまま | 200 固定 | 付けられない | 単純な取得 |
DTO + @ResponseStatus | 固定値を指定 | 付けられない | 常に 201 を返す作成 |
ResponseEntity | 実行時に決められる | 付けられる | 分岐がある処理・Location が必要な作成 |
毎回 ResponseEntity で包むと記述が増えるので、必要な場所だけで使うのが現実的です。
演習で確かめます
Spring では ResponseEntity がステータスとヘッダと本文をまとめてレスポンスに変換してくれます。ここではその組み立て結果を自分で文字列として作ります。作成された ID を受け取り、201 と Location ヘッダと JSON 本文を縦棒でつないだ 1 行を返してください。
要件
- id が 1 以上のときは 201|Location=/users/12|{"id":12} の形で返すこと
- 本文の JSON はキーを二重引用符で囲み、値は引用符なしの数値にすること
- id が 0 以下のときは invalid を返すこと
入出力例
buildCreated(12) → "201|Location=/users/12|{"id":12}"
buildCreated(1) → "201|Location=/users/1|{"id":1}"
buildCreated(100) → "201|Location=/users/100|{"id":100}"
buildCreated(0) → "invalid"
buildCreated(-5) → "invalid"