POSTとPUTの使い分け

REST

POST と PUT の違い、使い分けに関して、できるだけ簡単にまとめてみたいと思います。

はじめに

弊社ではRESTfulなWebサービスを作成していたりするのですが、これに関連して作成した 【図解】RESTful WebサービスにおけるHTTPステータスコード という記事には結構アクセスがなされているようです。

この記事ではリソースの操作 (CRUD) に応じてHTTPメソッドを使い分ける旨記載しているのですが、実はリソースの作成に関しては POST ではなく PUT を利用しても構いません。

但し、この場合も POST, PUT の何れを使うべきかという指針は存在します。

この指針は判りにくいのか、説明してもすぐには理解してもらえなかったり、プロジェクトの中でも誤った利用の仕方をされる場合が結構あるので、ここに書いてみます。

べき等

POST と PUT も含めたそれ以外のHTTPメソッドの大きな違い。
それは 『べき等』 であるか否かです。

※漢字だと 『冪等』、あるいは、『巾等』 とも書く

元々は数学の用語で、『ある操作を何度行っても同じ結果になる場合、この操作はべき等』であるといいます。

各HTTPメソッドのべき等性を以下に示します。

HTTPメソッドべき等性
POSTべき等でない
PUTべき等
DELETEべき等
GETべき等

GET はもともと副作用がないメソッドとして定義されており、あるURIに対して、何度GETを繰り返してもリソースの状態は変化しない事が保証されています。

DELETE も GET に次いで判りやすいでしょう。一度削除しても、複数回削除しても、削除された状態が変わる事はありません=べき等

POST と PUT はどうでしょうか?

リソースの新規作成で考える

例えば社員管理システムをRESTful Webサービスで作成する事を考えます。

社員リソースの URI は以下になります。
http://<サーバ名>/employee/<社員番号>

社員リソースの作成には以下のXMLを利用するとします。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
  <firstname>太郎</firstname>
  <lastname>山田</lastname>
  ( 中略 )
</employee>

Webサービス側では、上記XMLをもとに山田太郎さんの社員リソースを作成します。
この場合、以下のような二種類のシステムが考えられます。

  1. 社員番号をシステムが自動付与するシステム
  2. 社員番号はシステム外で決定し、リソース作成時にこの値を指定するシステム

上の1のシステムにおいては、XML、及び、作成を行うための URI に社員番号は含める事ができません。
同一の XML を利用しても、作成のための処理が行われる度、社員番号が変わります。
これはべき等ではありません。
つまり POST メソッドを利用するのが適当と言えます。

データ保存にDBを利用していて、ID をシーケンスや AUTO INCREMENT にしている場合はこのケースになるでしょう。


一方2のシステムにおいては、システム外で ID を決定後にリソース作成を行うため、何度作成処理を行っても同一のリソースが作成される事になります
( 2回目以降は、同一データで上書きされる、あるいは、既にデータ作成済みとして処理がされない。いずれにしてもリソースの状態は同じになる )。
これはべき等になります。
この場合は PUT メソッドの利用で問題ありません。
※1の場合と異なり、新規作成を行うURIにも社員番号を含める事ができます。


弊社Webサービスの場合には上記1の方式になっています。
従って、リソースの新規作成のメソッドとしてはべき等ではない POST を利用しています。

CRUD以外の処理ではどうか

一般的なCRUD以外の処理ではどうでしょうか。
簡単な口座振り込み処理を考えてみます。
内部処理としては以下のような二種類の処理があったとします。( 実際にあるか/適切かは別として )

  1. A口座からB口座へお金を移動する処理を行う。
  2. A口座、及び、B口座の金額を振込後の金額に設定する処理を行う。
    この処理の事前処理として、A口座、B口座の残高は確認しておく。

1の処理の場合には、複数回実行されれば、そのたびお金が移動します。=べき等ではありません。

2の処理の場合は、金額を設定する処理に関していえば、同じリクエストが何回発行されても、A口座、B口座の残高は同じになります。=べき等です。

べき等に関わる宗教論争

POST、PUT ( あるいはPOST以外 ) の使い分けを行うにあたっては、上記のようにべき等性がどうであるかを考えて決めるべきとされているのですが、以下のようなケースはどうなるのでしょうか?

  • リソース取得であるが、アクセスを記録しており、アクセスログレコードが新規作成される。
  • 同一データで更新リクエストがされた場合でも、最終更新日時を保存している。

GETリクエストを複数回行った場合に、利用者側から見たリソース変更はなくても、システム内部では変更が生じています ( 副作用が発生している )。

最終更新日時を保存しているシステムは結構多いと思います。リソースの更新は普通に考えると PUT ですが ( 最終更新日時以外はべき等になる )、POSTにすべきでしょうか?

こういった場合、

  • べき等性が崩れているからGETではない。POSTにすべき。
    → 反論 : でも、全てのリソースのアクセスログとってたら、GETなんて使えないじゃん?
  • 利用者側からみたらべき等だからGETで問題ない。リソース取得でPOST!?アホか!!
    → 反論 : いや、実際リクエストの度にリソースに変更が発生するんだから、べき等ってはいわないでしょ?
  • 最終更新日時を保存する時点でべき等ではないからPOST!
    → 反論 : この場合も、ほぼ全てのリソースでPUTは利用できなくなる。。。

など、人によって考えは色々で、べき等 ( および、HTTP、REST ) に関する宗教論争になってしまう可能性があります。

私自身は、 『リソースの本質に変更がなければ、完全にはべき等でなくてもPOST以外のメソッドで構わない。』 という考えで使い分ける事にしています。
更新日時やアクセスログが変わったからといって、その他の内容に変更がなければ 同じリソース=リソースに変更はない=べき等 として構わないだろうという事です。

まとめ

べき等に関しては、上述のとおり、『これが絶対正解!!』といったものにはお目にかかった事がありません。
人によって考え方が異なり、面倒臭い論争に巻き込まれる可能性もあるかもしれません。

但し、これといった正解がないからといって、何も決められないわけではありません。
サービスを構築/実装する場合には基本的な指針は決めておいた方がよい事は間違いないでしょう。

  • 利用するHTTPメソッド
    今回の話
  • ステータスコード
    詳細なエラーコードをどうクライアントに伝えるか?
  • リソースURI
    URIもちゃんと決めないと、後でもめたり、はまったりする

等は、初めにきちんと設計するか、あるいは、ある程度しっかりした指針がないとカオスになる可能性があります。

きちんと考えて利用者/開発者双方にやさしいサービスが作れるといいですね。

 

って、簡単にまとめるつもりが、結局まとまってない気が...