HTTP メソッド
GET、POST、PUT、PATCH、DELETE、そしてどれが繰り返しても安全か。
メソッドはすべてのリクエストの最初の単語です。URL のリソースに対して何をしたいかをサーバーに伝えます。同じ URL /api/Books/1サインインして表示 でも、GET なら本を返し、PUT や PATCH なら変更し、DELETE なら削除します。ほとんどの API は 5 つのメソッドでほぼすべてをまかない、ほかのいくつかがときどき登場します。
メソッドの段階になると、ブラウザとテストツールの違いが効いてきます。アドレスバーは GET しか送れません。それ以外には curl、Postman、またはこのページにあるような「送信」ボタンが必要です。共有データを変更する例は curl を使っています。このページの「送信」ボタンで、ほかの読者が見ているレコードを変えてしまわないためです。レッスン 6 のcurl と Postman でリクエストを送るで両方のツールをきちんと準備します。まだ curl がなければ、ここでは curl の例を読むだけにして、そのレッスンの後で実行してください。
日常的に使う 5 つのメソッド
| メソッド | すること | 典型的な成功ステータス |
|---|---|---|
GET | リソースや一覧を読む。ボディは送らない。 | 200 |
POST | 新しいリソースを作る、または操作を起動する。 | 201、ときに 200 や 202 |
PUT | 送ったボディでリソースを置き換える。 | 200 または 204 |
PATCH | リソースの一部のフィールドを変更する。 | 200 または 204 |
DELETE | リソースを削除する。 | 200 または 204 |
この API の Methods エンドポイントは、それぞれ 1 つの動詞を受け付け、受け取ったものを説明します。GET を送ってみましょう。
/api/methods/get?x=1ボディ付きの POST も。
/api/methods/post{"hello":"world"}これらのエンドポイントは何も保存しません。メソッドが本物のデータに何をするかを見るにはリソースが必要なので、このレッスンの 残りでは Books を使います。
POST は作る
本を 1 冊作ります。
curl -i -X POST https://api.testingapis.com/api/Books \
-H "Content-Type: application/json" \
-d '{"title":"The Pragmatic Programmer","author":"David Thomas",
"isbn":"9780135957059","genre":"programming","publishedYear":2019,
"pages":352,"language":"English","price":42.5,"rating":4.6,
"available":true,"tags":["career"]}'HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{
"message": "Book created successfully",
"data": {
"id": 29,
"title": "The Pragmatic Programmer",
"author": "David Thomas",
"isbn": "9780135957059",
"genre": "programming",
"publishedYear": 2019,
"pages": 352,
"language": "English",
"price": 42.5,
"rating": 4.6,
"available": true,
"tags": ["career"]
}
}サーバーは 201 Created で応え、ID 29 を割り当てて新しいレコードを返しました。ほかの人も本を作っているので、あなたには別の ID が返るはずです。これがどんな POST でも最初にテストすべきことです。ID を選ぶのはサーバーであり、テストは ID を推測せず、レスポンスから読み取らなければなりません。
多くの API は、201 と一緒に新しいリソースの URL を示す Location ヘッダーも送ります。たとえば Location: /api/Books/29サインインして表示 です。この API は送りません。標準で必須ではありませんが、ドキュメントで約束していれば バグです。
では同じ POST をもう一度実行してください。番号以外はまったく同じ 2 冊目の本が、新しい ID でできます。POST は繰り返しても安全なものではありません。呼ぶたびに新しいものができます。後の表のために覚えておいてください。
PUT は置き換え、PATCH は変更
どちらのメソッドもレコードを更新するので、よく混同されます。違いは、省いたフィールドに表れます。
PATCH
本 29 の価格だけを変えます。
curl -X PATCH https://api.testingapis.com/api/Books/29 \
-H "Content-Type: application/json" \
-d '{"price":39.99}'{
"message": "Book patched successfully",
"changed": ["price"],
"ignored": [],
"data": {
"id": 29,
"title": "The Pragmatic Programmer",
"author": "David Thomas",
"isbn": "9780135957059",
"genre": "programming",
"publishedYear": 2019,
"pages": 352,
"language": "English",
"price": 39.99,
"rating": 4.6,
"available": true,
"tags": ["career"]
}
}変わったのは価格だけです。触れなかったものはすべて元の値のままです。この API は変更したフィールドも教えてくれますが、 これは珍しく、テストにはとても便利です。
PUT
今度はフィールドを 3 つだけにして PUT を送ります。
curl -X PUT https://api.testingapis.com/api/Books/29 \
-H "Content-Type: application/json" \
-d '{"title":"The Pragmatic Programmer","author":"David Thomas","price":39.99}'{
"message": "Book updated successfully",
"data": {
"id": 29,
"title": "The Pragmatic Programmer",
"author": "David Thomas",
"isbn": "",
"genre": "",
"publishedYear": 0,
"pages": 0,
"language": "",
"price": 39.99,
"rating": 0,
"available": false,
"tags": []
}
}ISBN、ジャンル、年、ページ数、言語、評価、タグが消えてしまいました。PUT は「これがこのリソースの新しい完全版です」という 意味です。送らなかったフィールドは、空またはゼロの値に置き換えられました。API は PUT の定義どおりに動いただけなのに、クライアントが一部だけのボディを送ったせいで、レコードのほとんどが消えたのです。
これはアプリで実際によく起きるバグの 1 つです。開発者が 1 つのフィールドを更新するために PUT を使い、そのフィールドだけを送ると、レコードのほかのフィールドが静かにすべて消されてしまいます。更新機能をテストするときは、 一部だけのボディを送ってからレコードを GET し、何が残ったかを確かめてください。
API によっては扱いが違います。欠けたフィールドのある PUT を PATCH のように扱うものや、必須フィールドがないとして 400 で拒否するものもあります。最後のものがおそらくいちばん安全です。何をテストするかはドキュメント次第ですが、テストすること 自体は必ずしてください。
DELETE は削除
本 29 を削除し、もう一度削除してみます。
$ curl -X DELETE https://api.testingapis.com/api/Books/29
{"message":"Book deleted successfully"} (200 OK)
$ curl -X DELETE https://api.testingapis.com/api/Books/29
{"status":404,"error":"Not Found","message":"Book with ID 29 does not exist."} (404 Not Found)1 回目はメッセージ付きの 200 を返します。2 回目は、もう削除するものがないので 404 です。削除の後は、同じ ID への GET が 404 を返すことも確かめてください。レコードに削除済みの印を付けるだけで返し続けるシステムもあり、ドキュメントと違えばそれは バグです。
安全なメソッドと冪等なメソッド
HTTP の標準にある 2 つの性質が、クライアントやテストが各メソッドをどう扱ってよいかを決めます。
安全なメソッドはサーバー上の何も変えません。何度でも呼べます。冪等なメソッドは何かを変えることがありますが、2 回呼んでも 1 回呼んだのと同じ効果しかサーバーに与えません。
| メソッド | 安全 | 冪等 |
|---|---|---|
GET | はい | はい |
HEAD | はい | はい |
OPTIONS | はい | はい |
PUT | いいえ | はい |
DELETE | いいえ | はい |
POST | いいえ | いいえ |
PATCH | いいえ | 保証されない |
PUT が冪等なのは、同じ完全なレコードを 2 回送っても同じレコードが残るからです。DELETE は、2 回目で 404 が返っても冪等です。レスポンスは違っても、サーバーの状態(本 29 は存在しない)はどちらでも同じだからです。冪等性が問題に するのは効果であって、レスポンスではありません。2 回目の POST で 2 冊目の本ができたように、POST は冪等ではありません。PATCH はどちらにもなりえます。価格を 39.99 にするのは冪等ですが、「在庫数に 1 を足す」という patch は冪等ではありません。
これが大事なのは、ネットワークは失敗するものだからです。リクエストがタイムアウトすると、クライアントにはサーバーがそれを 受け取ったかどうかがわかりません。GET や PUT の再試行は無害ですが、POST の再試行ではカードに二重請求が起きかねません。 ブラウザ、プロキシ、HTTP ライブラリは、自分で再試行するかどうかをこのルールで決めています。タイムアウト、リトライ、レート制限では、API が冪等キーで POST を安全に再試行できるようにする方法を紹介します。
テストする側から見ると、これらのルールはテストケースをただでくれます。GET を呼んで、何も変わっていないことを確かめる。PUT を 2 回呼んで、2 回目で何も作られたり重複したりしていないことを確かめる。データを変える GET(実際にあるコードベースもある /api/deleteUser?id=5サインインして表示 のようなもの)は設計上のバグです。クローラー、リンクのプレビュー、ブラウザの先読みは、 どれも断りなく GET リクエストを送るからです。
HEAD と OPTIONS
HEAD はボディのない GET です。サーバーは GET と同じステータスとヘッダーを送り、そこで止まります。クライアントは、 ダウンロードせずに何かが存在するかやファイルの大きさを確かめるのに使います。
HTTP/1.1 200 OK
x-message: HEAD request received. Responses to HEAD carry headers only.
x-method: HEADOPTIONS は、URL で何が許されているかを尋ねます。ブラウザは一部のクロスオリジンリクエストの前にこれを自動で送るので (「プリフライト」)、どのコードも呼んでいなくてもネットワークのログに現れます。CORS の設定に関わる仕事でない限り、直接テストすることはまれでしょう。
/api/methods/options間違ったメソッドを使う
/api/methods/get は GET しか受け付けません。curl -i -X POST https://api.testingapis.com/api/methods/get で POST を送ってみます。
HTTP/1.1 405 Method Not Allowed
Content-Length: 0
allow: GET405 Method Not Allowed は、URL は存在するがそのメソッドには対応していないという意味です。allow ヘッダーに対応しているものが並びます。本は /api/Booksサインインして表示 で作るので、1 冊の本への POST は意味がなく、同じように扱われます。
HTTP/1.1 405 Method Not Allowed
Content-Length: 0
allow: DELETE, GET, HEAD, PATCH, PUTすべてのエンドポイントにすべてのメソッドを送るのは、手軽なのに本物の問題が見つかるテストです。探すのは、拒否されるべき なのに拒否されないメソッド(読み取り専用のはずのエンドポイントで動いてしまう DELETE など)と、405 であるべきところの 500 や 404 です。この API は 405 を空のボディで返しますが、ほかのエラーは JSON であることに注意してください。どの 4xx にも JSON のエラーボディを期待するクライアントは、ここでつまずきます。
演習
curl を使って順にやってみてください(または curl のレッスンの後で戻ってきてください)。
- POST で本を作り、その ID を書き留める。
- タイトルを PATCH で変える。GET して、タイトルだけが変わったことを確かめる。
tags以外のすべてのフィールドを含むボディで PUT する。タグはどうなりましたか。- 同じ PUT をもう一度送る。2 回目で何か変わりましたか。
- DELETE し、GET し、もう一度 DELETE する。3 つのステータスコードをすべて書き留める。
/api/Booksサインインして表示(ID なし)に DELETE を送る。実行する前にステータスコードを予想し、その後allowヘッダーを確かめる。