Featured image of post GETとPOSTの間

GETとPOSTの間

MDNの説明によると、HTTPメソッドは主に「リソースに対する」ものだと定義しています。なので書くメソッドの説明としても、GETならリソースの表現、POSTならリソースの作成、PUTならリソースの置き換え、DELETEならリソースの削除のような表現をしていますね。これを元に、CRUDの基本を説明するような資料も多く、学習者は自然にAPIのデザインはリソースの作成、参照、置き換え、削除を基準に考えることになります。あとはリクエストに対する処理が冪等であるかどうか、というのが重要ですね。

理論 != 現実

しかし、理論的にはそうだと認識していても、実際の業務をアプリケーションとして表現していくと、悩ましい場合があります。今回がそうです。「リソースを単純に返すだけに見えるけど、実際は内部的にリソースの作成や修正が伴う処理の場合はどうするか」という疑問がありました。なぜなら、このような要件があったからです。

  • クライアントにとあるファイルのダウンロードを提供するAPIがある。
  • サーバからのファイルの取得には以下の二つのケースがある。
    • ファイルがすでに作成されていて、サーバはそのファイルをクライアントに返すだけ。
    • リクエストに応じて、サーバがDBのレコードをファイルに書き込み(作成し)クライアントに返す。
  • クライアントがファイルをダウンロードすると、サーバはDBを更新する。
    • サーバはDBに「ファイル出力済み」というフラグと、「更新ユーザ」としてログインユーザの情報を登録する。

「ファイルがすでに作成されていて、サーバはそのファイルをクライアントに返すだけ」だと、GETでことは済みます。クライアントの観点からしてもそうであって、サーバもすでにあるリソースを返すだけですね。これは一般的にGETに期待する、「リソースの表現」に合致していて、何の問題もないです。

しかし、「リクエストに応じて、サーバがDBのレコードをファイルに書き込みクライアントに返す」の場合はどうでしょうか?ファイルを新しく作成するのは、「リソースの作成」に当てはまるのでしょうか?それとも、レコードとしてすでに存在しているので、ただ違う形に加工するだけであって「リソースの表現」に当てはまるのでしょうか。または、ファイルの要請だけでなく、クライアントが提供した情報を持ってDBの更新を行うので、「リソースの作成」に当てはまるのでしょうか。

誰の観点から見るべきか

以上の問題は、自分の観点、つまり、バックエンドエンジニア及びアプリケーションとしての問題でもあります。ならば、クライアント(ユーザ)からしてみるとどうでしょうか。以下のことを考えられます。

  • クライアントはファイルがすでに作成されているか、作成してくれるかに興味がない。
    • リクエストは「ファイルをくれ」であり、「ファイルを作ってくれ」ではない。
  • クライアントのリクエストに合わせてファイルを作成するかしないかはサーバ(アプリケーション)の都合である。
    • クライアントはファイルを要請するだけであり、中の処理についてはわからない。

つまり、クライアント側からすると、ファイルをダウンロードするという行為に関しては、あくまで「リソースの表現」を要請することに過ぎなくなります。サーバサイドとはまた都合が違いますね。こういう場合は、どちらの都合に合わせるかは明白ではないでしょうか。優先すべきは、クライアントですね。なぜなら、アプリケーションはそもそもクライアントの要件のために存在するものであるからです。なので、サーバサイドの都合でリクエストをPOSTにする理由はなくなりますね。

Specでは

ただ、クライアント側の観点枯らして、こういう場合はGETが正しい、と決めつけるにはまた違う根拠がいるのではないでしょうか。例えばHTTPの標準とかです。ちょうど、HTTPのスペックに関する文には、以下のような章があります。

4.2.1. Safe Methods

Request methods are considered “safe” if their defined semantics are essentially read-only; i.e., the client does not request, and does not expect, any state change on the origin server as a result of applying a safe method to a target resource. Likewise, reasonable use of a safe method is not expected to cause any harm, loss of property, or unusual burden on the origin server.

4.2.1 安全なメソッド

クライアントがサーバの状態を変えない、変わることを期待しない読み込み専用のリクエストは、「安全」とされる。安全なメソッドを正しく使うことによりサーバにとって害や損となるような事情を発生させなくなる。

これを見ると、クライアントのリクエストによりサーバに何かリソースの変化をもたらすようなことになる場合は、GETではなさそうが気がします。しかし、重要なのはこの次の内容です。

This definition of safe methods does not prevent an implementation from including behavior that is potentially harmful, that is not entirely read-only, or that causes side effects while invoking a safe method. What is important, however, is that the client did not request that additional behavior and cannot be held accountable for it. For example, most servers append request information to access log files at the completion of every response, regardless of the method, and that is considered safe even though the log storage might become full and crash the server.

安全なメソッドが、完璧な読み込み専用でなかったり、副作用を伴うなどで害を招くような実装を防止するわけではない。大事なのは、その動作はクライアントの要請ではなく、責任もまたクライアントにないということである。例えば、多くのサーバはどんなメソッドに対してもアクセスログを記録するが、ログによってサーバのストレージが足りなくなりサーバに障害が生じることもあり得るが、こういう場合でもリクエストは安全とする。

つまり、大事なのは実装上の都合はあくまでサーバサイドの責任であって、クライアントからの行為であるということですね。安全なリクエストのためには、クライアントのリクエストがどのようなものであるかを考えるべき、という結論になります。HTTPメソッドはそういう行為の本質を表すものであり、サーバの都合の表現ではない、ということですね。なのであくまでクライアントからして、「リソースの表現」に対する要請であれば、サーバはGETとして応じるベキになるかと思います。

最後に

実際のウェブサイトを観察してみても、純粋な「リソースの表現」としてのGETはあまりないような気もしています。例えば訪問するだけで「訪問者カウンター」が更新されるサイトがあったりしますね。こういう場合でもクライアントはリソースの作成やリソースを要請してなく、サーバ側で勝手に更新を行っていますが、依然としてGETとされまていて、違和感もないです。むしろこのようなリソースの変更全てをPOSTとして扱うようになるとしたら、GETを使う場面自体がなくなるかもしれません。

ただし、注意しなくてはならないのが、リソースの作成や更新を伴うリクエストを全てGETにするという発想も危険であることです。リソースの扱い方という原則に戻って考えたら、「POSTで作成させた後、GETで取得させる」というやり型もあるのかもしれません。こういう場合は2回のトランザクションが発生するので性能面では劣るかもしれませんが、場合によってはより安全な設計になる可能性もありますね。

なので「全てのケースに置いて」ではなく、アプリケーション全体の設計や今後の方向性を考慮した設計として適切なHTTPメソッドを選んだ方が良い、というのが結論になるかと思います(原論的な話ですが)。重要なのは、「こういう場合は絶対GETだ」、「こういう場合は絶対POST」だと言わないことですね。

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy