このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

訂正補足:HTTPメソッド、URL、そして標準化された動詞

昨日書いた「HTTPメソッド、URL、そして標準化された動詞」、どうも詰めが甘い記事だったので、少し訂正と補足をします。以下で、HTTPクライアントとしては、現存するブラウザを想定しています。

[追記]総括を「そろそろ決着、HTTPメソッド、URL、そして標準化された動詞」に書きました。[/追記]

動詞としてのpost

動詞(あるいは抽象メソッド)とは、具体的なHTTP通信とは離れて、リクエストの意図・意味を記述する記号です。最初に、この動詞を整理して、それをHTTPを使って表現する方法は後から考える段取りにします。しかし、もともとのHTTPメソッドも、その本来の意図・意味を動詞として定義しておきます。混乱を避けるために、現実のHTTPメソッドは大文字、抽象化された動詞は小文字で書くことにします。

HTTPメソッド 意味/使い方 対応する動詞
GET リソース(の表現)を取得する view
PUT 新しいリソースを作る/既存リリースを上書き変更する put
DELETE リソースを削除する delete
HEAD リーソースのメタデータを取得する (今回は考えない)
POST 新しいリソースをサーバー側で決める名前で作る post

昨日の記事では、HTTPメソッドの本来の意味に対応する動詞postを忘れていました。動詞postを入れると; view, viw-as-*, download, edit, process, create, update, append, put, post, delete, exec が典型的動詞です。動詞をこれらに限定する気はありませんが、多くの場面で共通的に使える動詞を確認しておきたいのです。

execとprocess

動詞execと動詞processの区別が曖昧だったので、もう少しハッキリさせます。

  1. exec :サーバー側にあるプログラムを起動する。このプログラム起動自体は、サーバー側での副作用を起こさない。URLは実行すべきプログラムを指し示す。プログラムの実体が何であるかは問わない。例えば、URLが指し示す“プログラム”がHTMLファイルであってもよい。プログラムにパラメータを渡してもよいが、入力データは渡さない。
  2. process :サーバー側の副作用を期待するプログラムを(サーバー側で)実行する。URLは実行すべきプログラムを指し示す。プログラムの実体が何であるかは問わない。プログラムに入力データは必ず渡す(空なデータのときもある)。パラメータを渡してもよい。

execは、ネットワーク越しにブラウザに“プログラムをロードする”感じです。ですから、フォームやJavaScriptを含んだHTMLファイルを“実行する”と言ってもかまいません。一方のprocessは、ブラウザから送ったデータをサーバー側で処理することを依頼します。

こう定義してみると、昨日の表の一部は訂正する必要があります。

動詞 GETメソッド POSTメソッド リソースは存在すべきか
デフォルト view, exec process, exec -
exec 存在

execが、GETメソッドでもPOSTメソッドでも使えるのは変なので、次のようにします。

動詞 GETメソッド POSTメソッド リソースは存在すべきか
デフォルト view, exec process -
exec × 存在

viewとexec

execの意味を上記のように定義すると、「プログラムの実体が何であるかは問わない」ので、単なるHTMLファイルのロードもexecと解釈できます。となると、viewとの区別が極めて曖昧になります。viewとexecを区別するのはもともと困難なのかも知れません。

Windowsデスクトップで、ファイルをクリック(あるいはダブルクリック)すると、そのファイル(リソース)に対してデフォルトの動詞が実行されますが、デフォルト動詞の名前はopenで、意味的にはviewとexecを一緒にしたようなものです。例えば、memo.txt というファイルをクリックすると、(通常の設定では)notepad.exeが起動してmemo.txtを表示します。この事象を、memo.txtを「viewしたのか/execしたのか/editしたのか」と議論してみても、どうも埒が明かない気がします。

現実的には、viewとexecの区別は曖昧なままにしておくか、open, getのようなどっちとも取れる動詞に統合するかでしょう。

view-as-* と追加パラメータ

view-as-htmlのような動詞を使う代わりに、表示フォーマットを別なパラメータに指定する方法もあります。例えば、http://example.jp/samples/hello.java?_verb=view&format=html、 あるいは、動詞viewはデフォルトですから http://example.jp/samples/hello.java?format=html のように。これは、動詞の一部にパラメータを埋め込むか、それとも独立したパラメータにするか、という問題で、あまり決定的な優劣はありません。

Catyの場合でいえば、動詞によるディスパッチはフレームワーク側で処理され、追加のパラメータによる振る舞いの変更は http://example.jp/samples/hello.java に結びつけられたプログラム側で行うという違いがあります。しかし、これはあくまで個別固有の事例に過ぎないので、一般論としては、?_verb=view-as-html と ?_verb=view&format=html の使い分けの基準はハッキリしません。

editに副作用はないのか?

id:yssk22さんのブックマークコメントに「(editには)ロックする、という副作用があるように見える」とありました。確かに、動詞editを受け取ったサーバーは、指定のリソースを編集する準備をします。リソースそのものがただちに変更はされなくても、サーバー側システムの状態は変更されます。

この意味で、「editに副作用はない」と言い切るのは問題ありです。しかし、「editにより、リソースの変更は生じない」とは言えます。さて、このような影響を持つ動詞はGETに乗せるべきかPOSTに乗せるべきか? どちらかを選べと言われれば、僕はGETを選びますが、POSTを否定する理由も見当たりません。editの送信方式は、GETでもPOSTでもいいということになりそうです。

processの曖昧性

動詞processは、HTTPメソッドPOSTにより起動されるサーバ側処理を総称的に表しています。そのため、POSTの曖昧性はそのままprocessの曖昧性に引き継がれ、「processしろ」という動詞を使ったリクエストは具体性がありません。

昨日の記事では:

processはデフォルト動詞であり、createの意味を含む可能性があるので ...

と書いたのですが、これはさすがに拡大解釈のし過ぎで、processを送る先のURLは、既存の処理システム(プロセッサ)を表すと考えた方がいいと思い直しました。動詞がprocessの場合は、目的語はURLではなく、むしろPOSTで送るデータ(エンティティボディ)のほうです。URLは処理の手段なので、process Data with URL って感じでしょうか。

putとpostとappend

「HTTPメソッドの正統的使い方と現実的対処法」で紹介した書籍『RESTful Webサービス』の第4章に、HTTPメソッドPUTとPOSTの違いが詳述されています。

PUTもPOSTもリソースの生成に使えます。違いは、PUTが目的のリソースを表すURLをクライアントが指定するのに対して、POSTに指定するURLが「親」または「ファクトリー」を表すことです。http://example.jp/blog/2010-01-13.entry?_verb=put は、URLで指定されたリソースが生成または上書きされます。ファイルシステムに例えるなら、open, write, closeすることですね。一方、POSTでは、指定するURLは(例えば)http://example.jp/blog となります。http://example.jp/blog?_verb=post とすると、サーバー側が適当に(この場合は日付を使って)リソースを生成します。

appendという動詞は、POSTの用法を細分したものです。appendは、既存リソースの変更をしますが、putのように全部置き換えるのではなくて、既存リソースに追加をします。ただし、追加も実は置換えで、「たまたま以前の内容も含む新データで置き換えられた」とも言えるので、putとappendの区別が明確なわけではありません。[追記]いやっ、後述のベキ等性により区別が付きます。[/追記]

また、http://example.jp/blog をブログエントリーの集まりというコレクション・リソースと考えれば、http://example.jp/blog?_verb=append によって新しいエントリーが「追加」されるのだとも解釈できます。つまり、postとappendの境界も曖昧です。『RESTful Webサービス』には、POSTのほとんどの使用例はappendであると記されています。

createとgenerate

PUTとPOSTでは、指定するURLの意味が変わると言いました。新規リソースを作成する動詞createにも同じ議論が通用します。createの目的語は、新規リソースの名前そのものなのか、それともファクトリーを表すのでしょうか?

仮にgenerateという動詞を導入して、事情を整理しましょう。

動詞 URLは何を表すか リソースは存在すべきか 内容データ
put 上書き変更するリソース 存在しても不在でもよい 必要
post ファクトリー・リソース ファクトリーは存在、リソースは不在 必要
create 新規作成するリソース 不在 不要
generate ファクトリー・リソース ファクトリーは存在、リソースは不在 不要

こうして見ると、generateは、空データを伴ったpostで代用できます。(createを空データを伴ったputで代用すると、既存リソースを意図せず消去してしまう危険があります。)post+空データを特にgenerateと名付けてもいいですが、さほどの必要性は感じません。

ベキ等性の考察

RESTful Webサービス』にベキ等性(冪等性、べき等性)という概念が出てきます。これは、「同じリクエストを続けて2回行っても1回のリクエストと変わらない」ということです。ただし、「変わらない」のはサーバー側のリソース状態のことです。処理システムの状態やクライアント側まで考えれば、ベキ等性はほとんど期待できません。

  1. サーバー側システムがリクエストの回数を勘定していれば、カウンタ値の状態がベキ等になるわけがない。
  2. 同じHTMLファイルを2回リクエストすると、ブラウザのタブ/ウィンドウが増えるかもしれない。
  3. 同じファイルを2回ダウンロードすると、ローカルファイルシステムに同じ内容のファイルが2つできるかもしれない。
  4. ブログエントリーの編集中に、もう一度編集(edit)リクエストすると失敗するかもしれない。

今列挙したようなことは無視して、サーバー側に保持されているリソースが変化するかどうかだけを問題にしてベキ等性を考えます。すると、post, append, generateはベキ等になりません。リクエストのたびに、リソースが追加更新されるからです。

リソース状態に関するベキ等性は、整数変数への代入(破壊的代入)とインクリメントを思い浮かべると理解しやすいでしょう。n = 3; という代入文を2回行っても1回と同じです。n++; というインクリメントを2回行うと1回とは違う結果になります。

まとめ

今回の内容をまとめた表は次のとおりです。「△」が付いている動詞は、意味や用途が曖昧なものです。

動詞 GETメソッド POSTメソッド リソースは存在すべきか 内容データ ベキ等か
デフォルト view, exec process - - -
view△ × 存在 なし
view-as-* × 存在 なし
exec△ × 存在 なし
download × 存在 なし
edit 存在 なし
process△ × プログラムとして存在 必要 状況による
create × 不在 不要
update × 存在 必要
append△ × 存在 必要 ×
put × どちらでもよい 必要
post△ × ファクトリーは存在、リソースは不在 必要 ×
generate△ × ファクトリーは存在、リソースは不在 不要 ×
delete × 存在 不要