RESTという設計方針
このレッスンでは、扱いたいデータと操作から URL と HTTP メソッドを機械的に導けるようになります。
URL に動詞を書かない
Web API の設計で最初につまずくのが URL の付け方です。素直に書くと、こうなりがちです。
プレーンテキスト
GET /getUsers
GET /getUserById?id=12
POST /createUser
POST /updateUser
POST /deleteUser動いてはいますが、これは REST とは呼びません。URL の中に getUser や createUser という動詞が入っていて、しかも操作の種類が全部 GET と POST に押し込まれています。API が育つほど名前が増え、どれがどの操作なのか URL を読んでも分からなくなります。
REST の考え方はとても単純です。URL は名詞、操作は HTTP メソッド、この 2 つを分けます。先ほどの 5 つを書き直すと次のようになります。
プレーンテキスト
GET /users 一覧を取る
GET /users/12 1 件を取る
POST /users 新しく作る
PUT /users/12 まるごと置き換える
DELETE /users/12 消すURL に出てくる名前は users だけになりました。操作の違いはすべてメソッド名が担当しています。
リソースは複数形の名詞にする
/users のように複数形を使うのが慣習です。理由は、一覧と単体を同じ語で自然に表せるからです。/users は「利用者の集合」、/users/12 は「その集合の中の 12 番」と読めます。ここが /user だと、一覧を返す /user が単数なのに複数返す、という気持ち悪さが残ります。
名前の付け方をまとめると次の通りです。
| 良い | 良くない | 理由 |
|---|---|---|
/users | /getUsers | URL に動詞を入れている |
/users/12 | /users?id=12 | ID は集合の中の 1 件を指すのでパスに置く |
/orders | /orderList | List は表現の都合であってリソース名ではない |
/users/12/orders | /getOrdersOfUser | 所属関係はパスの階層で表す |
メソッドごとの意味
| メソッド | 意味 | 対象 | 何度呼んでも同じ結果か |
|---|---|---|---|
| GET | 取得する | 集合 または 1 件 | はい |
| POST | 新しく作る | 集合 | いいえ |
| PUT | まるごと置き換える | 1 件 | はい |
| PATCH | 一部だけ更新する | 1 件 | 場合による |
| DELETE | 削除する | 1 件 | はい |
注目してほしいのは対象の列です。POST だけが集合に対する操作で、それ以外の更新系はすべて 1 件を指します。新しく作る時点ではまだ ID が決まっていないので、ID をパスに書きようがないからです。作成した ID はサーバーが採番して返します。
Spring では属性ではなく型で表れる
Spring Boot では、この対応づけがアノテーションの種類そのものになります。
Java
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping
public List<UserResponse> list() { ... }
@GetMapping("/{id}")
public UserResponse get(@PathVariable Long id) { ... }
@PostMapping
public UserResponse create(@RequestBody CreateUserRequest req) { ... }
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) { ... }
}クラスに付けた @RequestMapping("/users") が共通の接頭辞になり、メソッド側では差分だけを書きます。GET と POST の違いはアノテーション名が持っているので、メソッド名は list でも findAll でも API の形には影響しません。ここが動詞入り URL との決定的な違いです。メソッド名を変えても外から見た API は変わらない、という状態が作れます。
演習で確かめます
Spring では上のようにアノテーションが担っている「リソース名と操作から URL とメソッドを決める」という判断を、今回は自分の手で書きます。リソース名と操作の種類を受け取り、GET /users や DELETE /users/{id} のような 1 行を組み立てる関数を作ってください。単数形で渡されても複数形に直す、というところまでやります。
要件
- resource の末尾が s でなければ s を足して複数形にすること
- list は GET /users、get は GET /users/{id}、create は POST /users、update は PUT /users/{id}、delete は DELETE /users/{id} の形で返すこと
- list / get / create / update / delete 以外の action が来たら invalid を返すこと
入出力例
route("user", "list") → "GET /users"
route("users", "get") → "GET /users/{id}"
route("order", "create") → "POST /orders"
route("book", "update") → "PUT /books/{id}"
route("user", "delete") → "DELETE /users/{id}"
route("user", "search") → "invalid"