次期RailsがPATCHメソッドを採用

先月、Railsのブログにこんな記事が載りました。

なぜか当初とURLが変わっているようで、ブックマークは Edge Rails: PATCH is the new primary HTTP method for updates | Riding Rails のほうが多いです。(たしかこっちはブログのコメントもいっぱいついてたと思うんだが消えている…)

これは簡単に言うと、

ということなんですが、僕が記事を読んだ限りの解釈をもう少し詳しく説明したいと思います。

PUTは冪等

Railsのroutes.rbで

resources :users

と書くと、いくつかのルーティングが生成されますが、その中に

PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}

というものができます。リソース更新のため、HTTP PUTメソッドを使うとアクションメソッドupdateが呼び出されるというものです。
“RESTful”で一見問題ないように見えますが、そもそもPUTメソッドはどういう性質のものなのでしょうか。これはRFC2616に規定されています。

9.1.2 冪等{idempotent} なメソッド

メソッドは、(エラーや期限切れ発行とは別に) 同一のリクエストの N > 0 の副作用が単一のリクエストにおけるものと同じであるような際には "冪等{idempotence}" の性質を持つ事もできる。 GET, HEAD, PUT, DELETE 各メソッドはこの性質を共有する。

http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec9.1.2

PUTは冪等なメソッドということです。「冪等(べきとう)」というのは数学的な用語で少し難しいですが、この説明がわかりやすいです。

例えば、ブラウザからある HTML ファイルに GET リクエストを行ったとします。
この場合、その HTML 文書がブラウザに出力されるでしょう。
この時、もう一度同じファイルに同じ GET リクエストを行ったとしたら、その結果はどうなるでしょうか?
その結果は、当然先程と同じ文書が、同じようにブラウザに出力される事でしょう。
一般に「同じデータについて何度操作が行なわれても、同じ結果になるような操作」の事を冪等な操作と言います。
すなわち、リソースを取得するための GET、リソースに関するヘッダを取得するための HEAD、リソースをアップロードするための PUT、リソースを削除するための DELETE 等は、同じメソッドを何度繰り返しても同様な結果を返すはずなので、これらは冪等なメソッドであると言えます。

http://www.studyinghttp.net/method

冪等だと何がうれしいのかということですが、ものすごく大ざっぱに言うと「リンク・ボタンを連打してもだいじょうぶ」みたいな感じです。例えばモバイル環境などネットワークが不安定でレスポンスがちゃんと返ってこなかった、リクエストが受け付けられたかどうかわからない、という場合でも、冪等が保証されているメソッドであればただもう一度リクエストを送ればいいのです。

updateは冪等?

さて、Railsのupdateアクションは本当に冪等でしょうか?
それはまあ「実装による」わけですが、多くの場合Railsのモデルではcreated_at, updated_atというタイムスタンプ機能を利用します。この機能では更新日時は自動的に書き込まれます。そして、その日時はGETで取得できたり、リソース内のデータとして普通に使っています。
ということは、PUTを2回実行したら、日時が違うわけだから違うデータになってしまいます。あれ、冪等じゃないじゃん。困ったね。

冪等でない更新はPATCH

そこで、昔に提案されていたHTTPメソッドがPATCHです。
PATCHは、部分更新のためのメソッドですが、冪等でないと規定されています*1。ちょうどいいね、ということで、次のバージョンのRails 4ではupdateアクションはPATCHメソッドに結びつくようになりました。ただし、互換性を考えて、PUTメソッドでもupdateアクションが呼び出されます。ルーティングは

PATCH|PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}

となるでしょう。

本当にそれは適切なのかどうか

説明は以上です。この変更は概ね好意的に受け止められているようです。
ただし、上記ではPATCH採用のもう1つの理由である「リソースの部分更新」あたりの説明をあえて省略しました。これはちょっと個人的に思うところがあるからです。

という感じなのですが、この詳細は次回に続くということで…。

*1:冪等であってはいけない、という意味ではない。冪等でもよい