リソースの一部更新におけるURL設計

# 概要
Webアプリケーションにて、リソースの一部更新を行う際、どのようにURL設計を行うとシンプルで美しいか（本当はそこまで考えていなかったけど）悩んでいたところ、 @t_wada さんから素敵な設計指針をご教示いただきました。

本記事はその内容に加えて、実際に自分で行ったこと、調べたこと、思った事など、まとめております。

# あらすじ
数週間前にSIピラミッドからヒモなしバンジーを決めてWebの世界に飛び込んだ私は、小さな小さなWebアプリケーションを``rails new``から手探りで作っていました。

そんなとき、簡単なリソースの一部更新機能をどう実装したもんかなーと悩んでました。以下、当時（といっても先週）の超雑なぼやき。

> リンクをクリックしてモデルの一部を変更するのはどうしたらいいんだろう。  
> 例）不参加をクリック -> 某カラムをtrueからfalseへ

> - リクエストオブジェクトに対象カラムの情報を詰めてPATCH
> - オブジェクト丸ごと詰めてPUT

> だれか教えてください。

するとなんと、 @t_wada さんからのレスポンスが……！
以下、頂いた素晴らしいコメントです。

> PUT か PATCH かなら、PATCH を使いましょう。PUT は全置き換え、 PATCH は部分置き換えなので、 Rails4 から更新処理は PATCH リクエストがデフォルトになりました。なので、PATCH がまずはオススメです。しかしさらに踏み込む場合は、 URL 設計をより深く考えましょう。設計によっては、 PUT や DELETE でも同様のことを表現できます。

> URL 設計は

> 1. 何に対して (URL)
> 2. どうするか (HTTP Method)

> です。

> 例を挙げましょう。

> 例えば dropbox のようなファイル共有システムを作っているとして、
そのファイルのロック機能を提供することになったとします。

> /path/to/file に PATCH リクエストを投げても良いのですが、 /path/to/file/lock に PUT リクエストを投げることでロック、 DELETE リクエストを投げることでロック解除、という設計もできます。こうすれば、ロックの有無も GET で取得できるようになります。

> こうすることで、単機能で冪等性のある機能を提供することができるようになるわけです。

> ちなみに、画面にファイル一覧が表示されていてチェックボックスを付けていき、最後に一括ロックボタンを押す、というような画面の場合には、集合処理になりますね。そのような場合はファイルの集合を示す URL に POST リクエストを投げるかたちで実現するでしょう。

あらすじ終わり。ちなみに、本題もだいたい終わりました。
以下、自分なりにまとめていきます。

# 要点
今回の要点は、以下の通りかと思います。

1. URL設計の超基本
2. 更新系メソッドPUTとPATCHの違い
3. リソースの属性をリソースと捉える設計

特に「リソースの属性をリソースと捉える設計」についてが目から鱗でした。

# URL設計の基本
*Simple is best.* これが全ての基本だと感じました。

> URL 設計は

> 1. 何に対して (URL)
> 2. どうするか (HTTP Method)

> です。


ここで大事なことは、

1. URLはリソースであること
2. 処理内容に適したメソッドを選ぶこと

の2点だと思います。

2のメソッド選択については、それぞれのメソッドがどのように使われるか、どのような性質があるかといったことを理解しておく必要があるかと思います。

それについては、良書や良記事がたくさんあるので、そちらをご参照ください。

# 更新系メソッドPUTとPATCHの違い
更新を行うメソッドは、PUTとPATCHの2つが存在します。こちらも調べるとわんさか出てきますので、詳細な解説については毎度のことながら丸投げします。

簡単にまとめると以下の通りです。

|メソッド|概要|
|:--|:--|
|PUT|全置き換え|
|PATCH|部分置き換え|

この性質の違いから、リソースの一部更新をする場合には、そのリソース (URL) へPATCHリクエストを投げるのが、最も自然な設計となります。

# リソースの属性をリソースと捉える設計
本題です。

> 設計によっては、 PUT や DELETE でも同様のことを表現できます。

> /path/to/file に PATCH リクエストを投げても良いのですが、 /path/to/file/lock に PUT リクエストを投げることでロック、 DELETE リクエストを投げることでロック解除、という設計もできます。こうすれば、ロックの有無も GET で取得できるようになります。

これは、リソースの一部（属性）をリソースとして捉えることによって実現しています。 より具体的に言うと、**リソースの属性をそのリソースの子リソースとして捉えている**状態だと思います。

上記の例ですと、ファイルがロックされているかどうかを、「lockリソースの有無」で表現しています。

- GETしてリソースが取れれば、ロック中
- PUTでリソースを作成すれば、ロック開始
- DELETEでリソースを削除すれば、ロック解除

このように、リソースの属性をリソースと捉えることで、非常にシンプルで冪等性のある設計ができる場合があるそうです。

## 適用可能例
リソースが持つ``Boolean``型の属性に対しては、簡単にこの設計が適用できるのではないかと思います。基本的に**属性をリソースとして捉えることができる**場合に有効だと思います。

加えて、

> ちなみに、画面にファイル一覧が表示されていてチェックボックスを付けていき、最後に一括ロックボタンを押す、というような画面の場合には、集合処理になりますね。そのような場合はファイルの集合を示す URL に POST リクエストを投げるかたちで実現するでしょう。

とあるように、集合をリソースとして捉えることもできそうです。

### 集合をリソースとして捉える場合
集合をリソースとして捉える場合、具体的にどのようなURL設計が良いのか、追加で質問してみました。

> 集合を示すURLは /path/to/files/locks といった感じでしょうか。
/path/to/files/locks に file_id の集合をPOSTし、該当の /path/to/file/lock を作成/削除する設計になるのでしょうか。

以下、 @t_wada さんの回答です。

> ロックだけの集合なら /path/to/files/locks 案はシンプルで良いですね。

> ロック以外にもいろいろ(削除とか)やった後に「反映」ボタンがあるような場合はファイルの集合 /path/to/files に POST リクエストを投げてしまいます。

やはりシンプルに、そのリソースに名前(URL)をつけるのが良さそうです。

# Railsでの実装
冒頭でぼやいていたものに対して、アドバイスを元に実際にコーディングをしてみました。

簡単に説明をすると、``users``テーブルの``Boolean``型である``active``カラムの状態を変更するという超シンプルな機能を、「リソースの属性をそのリソースの子リソースとして捉える」設計で実装しました。

子リソースへの``routes.rb``は以下のように記述しました。

```ruby:routes.rb
resources :users do
  resource :activeness, only; [:update, :destroy]
end
```

生成されるURIパターンは``/users/:id/activeness(.:format)``となり、見事に子リソースっぽくなっています。

そして、実際の処理は``activenesses_controller.rb``を用意し、そこへ記述していきました。

```ruby:activenesses_controller.rb
def update
  # PUTの処理
end

def destroy
  # DELETEの処理
end
```


また、以下のように``routes.rb``を書く方法も試してみました。

```ruby:routes.rb
resources :users do
  member do
    put 'activeness'
    delete 'activeness'
  end
end
```

生成されるURIパターンは先ほどと一緒ですが、処理するコントローラーが変わってきます。この場合、PUTでもDELETEでも``users_controller.rb``の``activeness``メソッドに処理が渡されます。
そのため、リクエストのメソッドを判定し、処理を分岐させる必要があります。

```ruby:users_controller.rb
def activeness
  if request.put?
    # PUTの処理
  end
  
  if request.delete?
    # DELETEの処理
  end
end
```

これでも動作しますが、1つのメソッド（アクション）に2つの役割を持たせてしまっているため、あまり良くありません。そこで、下記の用にコントローラーとアクションを指定します。

```ruby:routes.rb
resources :users do
  member do
    put 'activeness' => 'users#activate'
    delete 'activeness' => 'users#deactivate'
  end
end
```

上記の通りに書くと、``users_controller.rb``の``activate``メソッドと``deactivate``メソッドで処理を行えるようになります。

```ruby:users_controller.rb
def activate
  # PUTの処理
end
  
def deactivate
  # DELETEの処理
end
```

また、``put 'activeness' => activenesses#update``のように指定すれば、最初の``resource``での実装と同じように、``activenesses_controller.rb``の``update``メソッドに処理を渡すこともできると思います。

こんな感じで実装してみたところ、シンプルに``/users/:id/activeness``にPUTやDELETEを投げるだけで属性の変更ができるようになりました。

# POSTではなくPUTを使う理由
リソースの新規作成時に、POSTでなくPUTを採用しているのは何故なんだろうと疑問に感じたため調べてみました。

まずは手元にある良書（まだ読み終わってない）の、Webを支える技術を開いてみたところ、P.95にPOSTとPUTの違いが掲載されていました。

> ### POST
> - リソース作成時にクライアントが URIを指定できない
> - URIの決定権はサーバ側にある

> ### PUT
> - リソースのURIはクライアントが決定する

とのことです。この特性の違いについては、冪等性の違いから発生しているのかなと感じました。[HTTP/1.1: Method Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2)によると、POSTは冪等でなく、PUTは冪等であるっぽいです。ちなみに、前述したPATCHは冪等性は保証されてません。

自分なりに噛み砕くと以下の感じになります。大して引用元と変わっていませんが……。

### POST
- どんなURLになるか明確でない
- 冪等性は保証しない

### PUT
-  どんなURLになるか明確である
-  冪等性を保証する

今回の例では、リソースの位置（URL）はとても明確で、``/users/:id/activeness``だと確定しています。また、何度作成や更新を繰り返そうと、同じメソッドを呼び出せば常に同じ状態になるため、完全に冪等です。

そのため、PUTを選択するのが適当だと思います。

また、

> ちなみに、画面にファイル一覧が表示されていてチェックボックスを付けていき最後に一括ロックボタンを押す、というような画面の場合には、集合処理になりますね。そのような場合はファイルの集合を示す URL に POST リクエストを投げるかたちで実現するでしょう。

と、集合をリソースと見なしたものに対しては、POSTを投げると仰っていたのは、

- 集合の要素は動的に変わる（冪等ではない） 

ことが理由かなと感じました。

# まとめ
- URL設計はリソース (URL) と操作 (メソッド)
- 属性の状態や集合もリソースと見なすことができる
- POSTとPATCHは非冪等、PUTは冪等
- PUTは全置き換え、PATCHは部分置き換え 

# 謝辞
@t_wada さんには、非常に勉強になるアドバイスをいただいただけではなく、投稿前に内容の確認もしていただきました。本当にありがとうございます。