@PostMappingとリクエストボディ
このレッスンでは、@PostMapping と @RequestBody を使って、新しいリソースを作るエンドポイントを設計できるようになります。
POST は集合に対する操作
GET / PUT / DELETE が 1 件を指すのに対し、POST だけは集合に向けて送ります。
プレーンテキスト
POST /users/users/12 ではなく /users に送るのは、まだ ID が決まっていないからです。「この集合に、こういう内容の要素を 1 つ追加してほしい」という依頼が POST です。ID を決めるのはサーバーの仕事で、決まった ID はレスポンスで教えます。
@RequestBody で本文を受け取る
GET と違い、POST はリクエスト本文を持ちます。JSON で送られてきた本文をオブジェクトに変換して受け取るのが @RequestBody です。
Java
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public UserResponse create(@RequestBody CreateUserRequest request) {
User saved = service.create(request.name(), request.email());
return UserResponse.from(saved);
}
}{"name":"sato","email":"sato@example.com"} という本文が送られてくると、Jackson が CreateUserRequest のプロパティに値を詰めてくれます。この変換は @ResponseBody の逆向きの処理で、同じ HttpMessageConverter の仕組みが担当します。
@RequestParam との違い
| アノテーション | 読む場所 | 主な用途 |
|---|---|---|
@PathVariable | URL のパス | どのリソースかの指定 |
@RequestParam | クエリ文字列 または フォーム | 絞り込み・簡単な値 |
@RequestBody | リクエスト本文 | 構造のあるデータの送信 |
新規作成のように項目が多く入れ子もあるデータは、本文で送るのが自然です。クエリ文字列に長い JSON を詰めるようなことはしません。
送られてきた内容は必ず疑う
ここが最も大事な点です。リクエスト本文はクライアントが自由に作れるので、名前が空だったり、メールの形式が壊れていたり、そもそもキーが無かったりします。変換に成功したことと、業務上正しいことは別です。
Spring では Bean Validation と @Valid を組み合わせて検証を宣言的に書けます。
Java
public record CreateUserRequest(
@NotBlank String name,
@NotBlank @Email String email) {
}
@PostMapping
public UserResponse create(@Valid @RequestBody CreateUserRequest request) { ... }@Valid を付けると、メソッドの中身に入る前に検証が走り、違反があればハンドラは呼ばれずに 400 が返ります。検証の詳細は後のセクションで扱いますが、まずは「本文を受け取ったら必ず検証してから使う」という順番を身につけてください。
作成に成功したときに返すもの
作成が成功したら、次の 3 つを返すのが定石です。
- ステータスコード 201
- 作られたリソースの場所を示す Location ヘッダ
- 本文として、採番された ID を含む作成結果
クライアントは ID を知らずに POST しているので、ID を返してあげないと次の操作ができません。この組み立て方は、このセクションの最後のレッスンで扱います。
演習で確かめます
Spring では @RequestBody が JSON をオブジェクトに変換し、@Valid が入力の検証を引き受けます。ここではその両方を自分で書きます。名前とメールを受け取り、名前が空でなければ採番した ID を付けた作成結果を返し、名前が空なら invalid を返す関数を作ってください。
演習で使うひな形
normalizeName(" taro ") は "taro"、normalizeName(" ") は空文字です。null も空文字になります。normalized.equals("") で検証結果を判断し、有効な場合だけ ID を採番して作成結果を組み立てます。
要件
- 整形後の名前が空文字なら invalid を返すこと
- 有効なら currentCount + 1 で採番すること
- ID、整形済みの名前、メールを指定の形で返すこと