はじめに
Webの成功理由はHTTPメソッドにあり
WebAPIの動きを理解するため、HTTPメソッドを頭に刻み込みます。
メソッド | 意味 |
---|---|
GET | リソースの取得 |
POST | 子リソースの作成、リソースへのデータの追加、そのほかの処理 |
PUT | リソースの更新、リソースの作成 |
DELETE | リソースの削除 |
HEAD | リソースのヘッダ(メタデータ)の取得 |
OPTIONS | リソースがサポートしているメソッドの取得 |
※TRACEとCONNECTメソッドはほとんど使われないそうなので、今回は割愛します。
参考書籍
本の目次
第7章 HTTPメソッド
- 7.1 8つしかないメソッド
- 7.2 HTTPメソッドとCRUD
- 7.3 GETーーーリソースの取得
- 7.4 POSTーーーリソースの作成、追加
- リソースの更新
- リソースの作成
- POSTとPUTの使い分け
- 7.5 PUTーーーリソースの更新、作成
- 7.6 DELETE---リソースの削除
- 7.7 HEADーーーリソースのヘッダの削除
- 7.8 OPTIONSーーーリソースがサポートしているメソッドの取得
- 7.9 POSTでPUT/DELETEを代用する方法
- _methodパラメータ
- X-HTTP-Method-Override
- 7.10 条件付きリクエスト
- 7.11 べき等性と安全性
- PUTはべき等
- べき等性の例
- DELETEもべき等
- GETとDELETEもべき等、そのうえ安全
- GETはどこまで安全か
- 安全でもべき等でもないPOST
- 7.12 メソッドの誤用
- GETが安全でなくなる例
- ほかのメソッドでできることにPOSTを誤用した例
- PUTがべき等でなくなる例
- DELETEがべき等でなくなる例
- 7.13 Webの成功理由はHTTPメソッドにあり
学習したこと
HTTPメソッド
本章ではHTTPのリクエストメッセージを特徴づけるメソッドについて解説いたします。
HTTPメソッドは種類こそ8つと少ないですが、重要な役割を果たしています。本章では、メソッドがたった8つでも大丈夫な理由、そしてそこに隠されたHTTPの設計上の工夫を見ていきます。
HTTPメソッドの種類は8つ(実際に使うのは6つ)
HTTPメソッド
メソッド | 意味 |
---|---|
GET | リソースの取得 |
POST | 子リソースの作成、リソースへのデータの追加、そのほかの処理 |
PUT | リソースの更新、リソースの作成 |
DELETE | リソースの削除 |
HEAD | リソースのヘッダ(メタデータ)の取得 |
OPTIONS | リソースがサポートしているメソッドの取得 |
TRACEとCONNECTはほとんど使われない
通常のプログラミング言語の感覚からすると、なぜこんなにもメソッドを限定しているのか、それで大丈夫なのかと不安になることでしょう。しかしメソッドの数をぎりぎりまで削ったからこそ、HTTPが、そしてWebが成功したのです。本章では、ほとんど使われていないTRACEとCONNECTを除いた6つのメソッドについて解説します<
HTTPメソッドとCRUD
CRUD(Create / Read / Update / Delete)
HTTPメソッドのうちGET,POST,PUT,DELETEは、これら4つで「CRUD」という性質を満たすため、代表的なメソッドと言えます。CRUDとは、create(作成)、Read(読み込み)、Update(更新)、Delete(削除)というデータ操作の基本となる4つの処理のことです。CRUDとHTTPメソッドは表7.2のように対応します。
CRUDとHTTPメソッドの対応
CRUD名 | 意味 | メソッド |
---|---|---|
Create | 作成 | POST/PUT |
Read | 読み込み | GET |
Update | 更新 | PUT |
Delete | 削除 | DELETE |
GET(リソースの取得)
GETは指定したURIの情報を取得します。最も利用頻度の高いメソッドで、Webページの取得、画像の取得、映像の取得、フィードの取得など、私たちがブラウザを利用しているときはいつも数多くのGETを発行しています。
GETの例
GET /list HTTP/1.1
Host: example.jp
// リクエストはhttp://example.jp/listに対するGETです。
HTTP/1.1 200 OK
Content-Type: application/json
[
{"uri" : "http://example.jp/list/item1"},
{"uri" : "http://example.jp/list/item2"},
{"uri" : "http://example.jp/list/item3"},
{"uri" : "http://example.jp/list/item4"}
]
// リクエストに対して、
// サーバーは指定されたURIに対応するデータをレスポンスとして返しています。
POST(リソースの作成、追加)
POSTの役割
子リソースの関係
POSTの代表的な機能は、あるリソースに対する子リソースの作成です。
ブログ記事の投稿などの操作で使われます。第12章で解説するAtomPubなど、
POSTをこの目的だけに使うプロトコルも多いです。
POSTの機能①「子リソース作成」
POST /list HTTP/1.1
Host: example.jp
Content-Type: text/plain; charset=utf-8
こんにちは!
// http://example.jp/listに対して新しい子リソースを作成するようにPOSTで指示
// POSTのボディには、新しく作成するリソースの内容を入れる
HTTP/1.1 201 Created // ステータスコード201は新しいリソースが生成されたことを示す
Content-Type: text/plain; charset=utf-8
Location: http://example.jp/list/item5 //Locationヘッダに新しいリソースのURIが入ってる
つまり、listの下に、新たに/list/item5というリソース(子リソース)を生成
こんにちは!
POSTの機能②「リソースへのデータの追加」
子リソースの作成ほど一般的ではありませんが、POSTの代表的な機能の2つめは既存リソースへのデータの追加です。
例としてログリソースを考えてみましょう。まずはリソースをGETしてみます。
GET /log HTTP/1.1
Host: example.jp
HTTP /1.1 200 OK
Content-Type: text/csv; charset=utf-8
2010-10-10T10:10:00Z, GET /list, 200
2010-10-10T10:11:00Z, POST /list, 201
2010-10-10T10:20:00Z, GET /list, 200
このリソースのURIはhttp://example.jp/logで、CSV形式のログを表現します。
このリソースに新しいログを追加するにはPOSTを使います。
POST /log HTTP/1.1
Host: example.jp
2010-10-10T10:13:00Z, GET /log, 200
HTTP/1.1 200 OK // ステータスコード200は、データの追加を意味する。
データ追加としてPOSTしたときに、そのデータをリソースの末尾に追加するのか
先頭に追加するのかはサーバ側の実装に依存します。また、そもそもあるリソースへの
POSTが作成を意味するのかデータ追加を意味するのかも実装に依存します。URIを見ただけでは
POSTの挙動はわかりません。POSTの挙動はWebサービスやWebAPIの仕様書などで表現します
POSTの機能③「ほかのメソッドでは対応できない処理」
例:「検索キーワードが極端に長かった」場合、どうするのか?
POSTの3つめの機能は、ほかのメソッドでは対応できない処理の実行です。
検索結果を表現する次のURIを例に考えてみましょう。
http://example.jp/search?q={キーワード}
通常はこのURIをGETすることで検索を実行しますが、キーワードが非常に長かった場合はどうなるのでしょうか。
URIの長さはキーワードに連動します。第4章で述べたとおり、URIの仕様上は長さ制限がありませんが、
実装上は2000文字などの上限が存在します。そのような長いキーワードの場合、URIにキーワードを入れて
GETする方式は利用できません。この場合、以下のようにPOSTを用います。
POST / search HTTP/1.1
Content-Type: application/x-www-form-urlencoded
q=very+long+keyword+foo+bar+..........
// GETではURIに含めていたキーワードを、POSTではリクエストボディに入れられます。
// これによってどんなに長いキーワードでも実現できます。
PUT (リソースの更新、作成)
PUTの機能①「リソースの更新」
PUTの1つめの機能はリソースの更新です。
まずは先ほどPOSTで作成したitem5をGETするところから始めましょう。
GET /list/item5 HTTP/1.1
Host: example.jp
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
こんにちは!
次にこのリソースをPUTを使って「こんにちは!」から「こんばんは!」に変換してみます
PUT /list/item5 HTTP/1.1
Host: example.jp
Content-Type: text/plain; charset=utf-8
こんばんは!
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
こんばんは!
この例ではPUTへのレスポンスにリソースを更新した結果の表現が入っています。PUTへのレスポンスは、この例のようにボディに結果を入れてもよいですし、ボディには何も入れずに、レスポンスがボディを持たないことを示す204 No Contentを返してもかまいません。
PUTの機能②「リソースの作成」
PUTの2つめの機能はリソースの作成です。
たとえばhttp://example.jp/newitemがまだ存在しないとします。
PUT /newitem HTTP/1.1
Host: example.jp
Content-Type: text/plain; charset=utf-8
新しいリソース/newitemの内容
HTTP/1.1 201 Created
Content-Type: text/plain; charset=utf-8
新しいリソース/newitemの内容
このPUTは存在しないURIへのリクエストのため、サーバはリソースを新しく作成すると解釈し、リクエストが成功した場合は201 Createdを返します。POSTの場合は新しく作成したリソースのURIがLocationヘッダで返りましたが、PUTの場合はすでにリソースのURIを知っているためLocationヘッダを返す必要はありません。
/newitemがすでに存在していた場合は、先述したリソースの更新処理になります。
POSTとPUTの使い分け
POSTとPUTをどのように使い分ければいいか?
正解は存在しないが・・
- 設計上の指針として次の事実がある
-
POST
リソースを作成する場合、リソースのURIの決定はサーバ側
にある -
PUT
でリソースを作成する場合、リソースのURIはクライアント
が決定する
-
(一般的には)サービスの特徴によって、使い分ける
- TwitterのようにつぶやきのURIをサーバ側で自動的に決定するWebサービスの場合は、POSTを用いるのが一般的。
- 逆にWikiのようにクライアントが決めたタイトルがそのままURIになるWebサービスの場合は、PUTを使うほうが適している。
ただし、PUTの場合、リソースの上書きを避けるためにクライアントで事前にURIの存在をチェックしなければならないかもしれません。
(特に理由がなければ)POSTがベター
技術的な負担を軽減するならPOST
一般的に、クライアントがリソースのURIを決定できるということは、クライアントを作るプログラマーがサーバの内部実装(URIにどの文字を許すのか、長さの制限はどれくらいかなど)を熟知していなければなりません。そのため、PUTのほうがどうしてもサーバーとの結合が密になります。特別な理由がない限りは、リソースの作成はPOSTで行いURIもサーバ側で決定する、という設計が望ましいでしょう。
DELETE(リソースの削除)
Deleteはその名のとおり、リソースを削除するメソッドです。
DELETE /list/item2 HTTP/1.1
Host: example.jp
HTTP/1.1 200 OK
HEAD(リソースのヘッダの取得)
HEADはGETによく似たメソッドです。GETはリソースを取得するメソッドですが、HEADはリソースのヘッダ(メタデータ)だけを取得するメソッドです。
HEAD /list/item1 HTTP/1.1
Host: example.jp
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
HEADへのレスポンスにはボディが含まれません。この性質を利用すると、ネットワークの帯域を節約しながらリソースの大きさを調べたり、リソースの更新日時を取得したりできます。
OPTIONS 「リソースがサポートしているメソッドの取得」
最後のメソッドはOPTIONSです。OPTIONSはそのリソースがサポートしているメソッドの一覧を返します。次の2つの例を見てください。
OPTIONS /list HTTP/1.1
Host: example.jp
HTTP/1.1 200 OK
Allow: GET,HEAD, POST //そのリソースが許可するメソッド一覧
OPTIONS /list/item1 HTTP/1.1
Host: example.jp
HTTP/1.1 200 OK
Allow: GET,HEAD,PUT, DELETE //そのリソースが許可するメソッド一覧
OPTIONSを実装する場合、多くのWebアプリケーションフレームワークでは、リソースごとに対応しているメソッドを返すように自前で実装しなければなりません。ApacheのようなWebDAVに対応したWebサーバでは、設定ファイルでOPTIONSの挙動を設定できます。
POSTでPUT/DELETEを代用する方法
現実に一番よく利用されるHTTPメソッドはGETとPOSTの2つ
これまで説明してきたGET、POST、PUT、DELETE、HEAD、OPTIONSの6つが主なHTTPメソッドです。しかし、現実に一番よく利用されているのはGETとPOSTの2つです。これはHTMLのフォームで指定できるメソッドがGETとPOSTだけという制限に起因します。
<form method="GET" action="/list">
・・・
</form>
<form method="POST" action="/list">
・・・
</form>
Ajaxの発展で任意のメソッドを発行できるように
HTMLのこの制限により、WebアプリケーションではGETとPOSTだけを利用する時代が長年続きました。しかし、この制限はAjaxの発展とともに解消されつつあります。Ajaxで用いるXMLHttpRequestというモジュールを利用すると、任意のメソッドを発行できるからです。
ただし、それでもGETとPOSTだけを使わなければならない状況も存在します。たとえばXMLHttpRequestをサポートしない携帯電話向けブラウザはフォームしか利用できませんので、GETとPOST以外使えません。また、セキュリティ上の理由から、プロキシサーバーでGETとPOST以外のアクセスを制限している場合もあります。このような状況でサーバーにPUTやDELETEを伝える手法が2つあります。
_methodパラメータ
1つめは_methodパラメータを用いる方法です。フォームの隠しパラメータ(hidden)に_methodというパラメータを用意し、そこに本来送りたかったメソッドの名前を入れます。_methodパラメータはRuby on Railsが採用しています。
たとえば次のフォームがあったりします。
<form method="POST" action="/list/item1">
<input type="hidden" id="_method" name="_method" value="PUT"/>
<textarea id="body">...</textarea>
</form>
このフォームを送信すると、次のリクエストが送られるでしょう。
POST /list/item1 HTTP/1.1
Host: example.jp
Content-Type: application/x-www-form-urlencoded
_method=PUT&body=...
ボディには、フォームで入力した項目をURIのクエリパラメータと同じ仕様でエンコードした(idと値を「=」でつないで「&」で連結した)テキストが入っています。Content-Typeヘッダの値application/x-www-form-urlencodedは、このフォーマットを表すメディアタイプです。Webアプリケーションフレームワークなどのサーバー側の実装は、_methodパラメータを見て、このリクエスト自体をPUTとして扱います。
X-HTTP-Method-Override
_methodパラメータはフォームを利用してリクエストを送る場合は有効な手法ですが、POSTの内容がXMLなど、application/x-www-form-urlencoded以外の場合は利用できません。このような場合に利用できるのがX-HTTP-Method-Overrideヘッダです。こちらはGoogleのGData(Google Data Protocol)が採用している手法です。
POST /list/item1 HTTP/1.1
Host: example.jp
Content-Type: application/xml; charset=utd-8
X-HTTP-Method-Override: PUT
<body>...</body>
Webアプリケーションフレームワークなどのサーバ側の実装は、X-HTTP-Method-Overrideヘッダを見て、このリクエストをPUTとして扱います。