REST について調べたまとめ

普段仕事でRails を使っている身ですが、Rails 2.x 系を使っているものが1つもない。結構前からRails 3 の話題がでてきている今、そろそろRails 2.x をまともに使っておきたいと思ったので、まずはREST について調べました。最初にREST について調べたのは、REST がRails 2.x (実際には1.2.x から)で導入された最も大きな概念だからです。

REST とは

REST とはアーキテクチャスタイルである

アーキテクチャスタイルとはデザインパターンのようなもので、システムを設計する上での方針をまとめたものである。

REST は「REpresentational State Transfer」の略である

直訳すると、「(リソースの)表現可能な状態の転送」。あるリソースの状態を表現したものがサーバからクライアントに転送されるのがREST。ここにでてきた「リソース」「表現」「状態」は全てREST において重要な概念なので後述する。
Web を例にすると、「商品在庫一覧ページ」は、商品という「リソース」の在庫であるという「状態」をHTML 形式で「表現」したものと見なすことができる。この表現(HTML ファイル)をサーバがブラウザに「転送」している。これがREST の概念である。

REST はRoy Fielding によって提案された

Roy Fielding はHTTP プロトコルの作成者の1人であり、REST はWeb と密に関係している。

REST とWorld Wide Web

REST の提案者Roy Fielding はWeb 及びHTTP の構造を調査する過程の中で、ネットワークアーキテクチャに共通して適応できる原理として抽出したものをREST として発表した

よって、REST は主にWorld Wide Web とHTTP に適用され、Web を組織化、構造化するための最善の方法に対する統一理論である。実際に、Web ではREST に準拠した通信が可能である。一方、REST の原理に違反することも可能である。

REST とWeb アプリケーション

Web アプリケーションを設計する際にもREST を使うことができる

HTTP 層と関係する部分、すなわち、クライアントとやりとりする部分の設計に適応することができる。具体的に、次に示すようなものにおいて、REST を適応できる。

  • URL の定義
  • HTTP メソッドの使い方
  • HTTP レスポンスステータスコードの使い方
  • HTTP キャッシュの利用

REST における重要要素

リソース
  • リソースに関する明確な定義は存在しない
    • 最も一般的な定義は「アイデンティティを持つ(= 名前をつけることができる)何か」である
  • リソースは「名詞」で表現することができる
  • 処理や振る舞いはリソースではない
    • 例えば、「登録フォーム」というリソースは存在するが、「登録を処理する」というリソースは存在しない
  • リソースの例として以下のようなものがある
    • 電子文書
      • 例えば、このエントリー
    • 画像
    • 商品
      • 例えば、りんご
    • サービス
      • 例えば、現在の日本の時刻
状態
  • リソースの状態
  • 状態の例として以下のようなものがある
    • (商品の)在庫
    • (作成前の)商品
      • 商品登録しようとしてフォームを開いている時の状態。Rails 上では「/products/new」というURL で表現される
URL
  • Web 上にあるリソースを一意に特定するもの
  • URL から見れば、リソースとはネットワーク上でアドレス指定が可能なもの


「リソース」「状態」「URL」の3つについて書きましたが、ここから、REST において「リソース」とは、最初に説明したような「(名詞のみで表現される狭義の)リソース」と、その「状態」を合わせたものと見てよさそうです。つまり、「/products/new」は「作成前の商品」というリソースだと見なすことができ、URL で指定することが可能です。

表現
  • リソースの表現
  • REST では、リソースとその表現は明確に区別される
  • 通信を通じてやりとりされるものは、リソースではなくその表現
    • レスポンスを通じて取得できるものも当然リソースの表現であり、クライアントも表現を送信する
  • 現時点で、表現は以下のどちらかの方法で指定することができる
    • 「index.html」のような拡張子
    • HTTP のAccept ヘッダ
  • 表現の例として以下のようなものがある
HTTP メソッド
  • リソースに対する操作を表現する動詞として使用される
  • REST では、HTTP メソッドは制限された領域である
    • 表現はコンテンツタイプとして拡張可能であり、リソースは無限に存在するが、動詞としてのHTTP メソッドは固定されている
    • WebDAV 等による拡張されたメソッドは使用するべきではない
  • REST では、URL で表現された「リソース」に対して「HTTP メソッド」でアクションを指定することでサーバとやりとりする
  • REST において使用できるHTTP メソッドは以下の4つである

以下、説明において「これ」はリクエストのボディを表し、「そこ」は操作を行うURL を表す。

    • GET
      • 「そこにあるものを取得する」
      • GET は副作用がない、つまりセーフであると位置付けられており、リソースを作成、更新する目的には使われない
      • GET はべき等であり、他のアクションによってリソースが更新されない限り、アクションを何度繰り返しても得られる結果(アクション実行後のリソースの状態とレスポンス)は同じである
    • PUT
      • 「これをそこに格納する」
      • PUT はべき等である
    • DELETE
      • 「そこにあるものを削除する」
      • DELETE はべき等である
    • POST
      • 「そこにあるものにこれを処理させる」
      • POST はセーフでもべき等でもない
      • POST は何でもできる最終手段のメソッドであり、上記の3つのメソッドで行えないことのみを担当する
      • POST を使う代表的な例は、「新しいリソースの作成」と「既存リソースへの注釈」である
レスポンスステータスコード
  • サーバはクライアントのリクエストに対するレスポンスに適切なレスポンスステータスコードを返さなければならない
  • レスポンスステータスコードの例として以下のようなものがある
    • 200
      • リクエストが成功したことを示す
    • 201
      • リクエストの結果、新しいリソースが作成されたことを示す
    • 404
      • リクエストさえたリソースが存在しないことを示す
    • 422
      • クライアントから送信されたエンティティが意味的に無効であったことを示す
      • 例えば、金額に対し負の値を送信したような場合のレスポンスは422 になる
      • 422 はHTTP/1.1 の標準の仕様には含まれていないが、Roy Fielding 自身もこのような場合に最も適切なステータスコードは422 であると認めている


ちなみに、Rails もvalidation 失敗に対するステータスコードを422 としている。

REST のステートレス性

REST とCookie

この節に関しては「である」調では書きません。このトピックはREST の中でも議論に上ることが多いので、明確に記述することが難しいためです。あくまで調べた上での「自分の」理解としてまとめておきます。間違いに気付いたら都度直していきます。


REST の原則の1つに「ステートレス性」があります。そもそもHTTP はステートレスなプロトコルなので、この原則は自然です。
ただし実際には、多くのWeb アプリケーションではCookie を使ってセッションを実現していて、厳密にはREST に従っていません。例えば、認証が必要なページのURL に対してCookie なしにリクエストを送信した場合、大抵は認証ページにリダイレクトされてしまいます。これは、サーバが同じURL に対して異なる表現を返していて、そのURL はアドレス可能性を失っていることになるからです。だからと言って、REST の原則に従うようにセッション付きURL を使うことはセキュリティの観点から無理なわけです。
では、Cookie を使うことを前提に、それでもなるべくREST に違反しないようにするにはどうしたらいいでしょうか。「実践 Rails p.209」には以下のように書かれています。

REST によれば、アプリケーション状態はすべてクライアント側で管理しなければならない。これがステートレスの意味することである。つまり、アプリケーションに状態がないのではなく、リクエストをそれぞれ独立させることができる。クライアント/サーバセッション自体は状態を維持しない。


これに対する1つのアプローチがRails におけるCookie ベースでのセッション管理だと思います。Rails 2.x はデフォルトでセッションデータそのものをCookie に格納します。つまり、状態をなるべくクライアントに持たせる実装になっています。これによって、サーバ側がそのCookie が有効かどうかの状態を管理する必要がなくなります。この認証は、HTTP に組み込まれているBasic 認証等のようにステートレスな認証に近い形になります。デメリットとして、セキュリティ面での問題が起こりやすくなります。


さらに同書には、原則から少し外れるアプローチも書かれていました(具体的には「p.211 なぜステートレスなのか」)。そこによれば、ステートレスのそもそもの目的は「シェアードナッシングアーキテクチャによるスケーラビリティの確保」にあります。つまり、スケーラビリティをなるべく確保できる方向に設計することが大切。
その実現方法の1つとして、スティッキーセッションが上げられていました。スティッキーセッションならば、リクエストを処理するサーバをスケールアウトしていくことは可能です。サーバを増やした際に無効になるセッションが存在する可能性はありますが、認証のためだけに用いられているセッションならば許容できる場合が多いはずです。

REST を使うことのメリット

良い設計原理への誘導
  • 基本的なHTTP メソッド以外を使用する必要を感じた場合、それはリソースが隠れていることを示唆する
  • REST の原理に従うことで感じる苦痛は全て、Web の自然なアーキテクチャとして設計が間違っていることへの暗示だと捉えるべきである
  • REST の設計ではアーキテクチャ上の決定は名詞の領域に集まる。名詞の選択はドメイン主体となる傾向にあるが、RPC では実装上の詳細である手続きがインターフェースとして公開されるため、より実装の変更による影響を受けやすい
統一性
  • REST が実現する統一性は標準化作業に大きく貢献する。よってシステム間での連携がしやすい
  • 動詞としてのHTTP メソッドは全てのアプリケーションドメインで統一されている
    • これは、標準化の問題が「コンテンツタイプ」と「名詞」というアプリケーションドメイン内では標準化しやすい領域に限定されることに役立つ
  • Web 上の重要なすべてのものがURL で指定できる
    • 例えば、はてなブックマークの件数画像を取得するAPI はHTTP ベースなので、「img タグ」を使って利用することができる。これはとても簡単である
スケーラビリティ
  • ステートレスを原則とするREST では、クライアント - サーバ間で何も共有しない。これは負荷に応じて水平方向へスケールしやすいことを示す
キャッシュ
  • REST を通じてHTTP キャッシュを利用することができる
  • HTTP キャッシュの要件には「透過性」があり、キャッシュが関与しているいないに関らず転送される情報が同じになることが意識されている

REST を使う上で

ここまではREST の概念を中心に書いてきましたが、残りはREST を使う上で身につけなければいけない知識や考え方、REST について調べた上での感想について書いていきたいと思います。

リソースモデリング

REST に従ったアプリケーション、すなわちRESTful アプリケーションの設計はURL の設計が大きな部分を占めることになります。リソースに対するメソッドの設計も名前も既に決められているからです。REST においては、適切なリソースをどう導出するかが重要であり、この過程をリソースモデリングといいます。
アプリケーションの設計では、「何をやるか」という処理の方に意識が向けられがちですが、REST においては、「その処理によって得られるリソースは何なのか」を意識する必要があります。これを「Resource Oriented Architecture」といいます。
例えば、検索をしたいなら、「検索結果」というリソースに対してGET します。また、ログインは、セッションというリソースに対するCreate であると捉えます。最後の例では、セッションはDB に格納されないものである点も注目です。リソースとは必ずしもDB に格納されているものだけではないことを示しています。

リソースレイヤーの増加

リソースの概念が導入されたREST では、リソースレイヤーが従来のレイヤーに加わることになります。従来のレイヤーとは、UI 層、DB 層とその間を繋ぐプログラム層から成ります。リソース層はUI 層とプログラム層の間に入ることになり、これは以前、プログラム(オブジェクト)層とDB 層の間であったインピーダンスミスマッチが、リソース層とプログラム層で発生する可能性があることを示しています。
例えば、検索結果というリソース、確認画面に対するリソースをどう表現するのかについて悩むことになります。さらに、リソース層、DB 層での操作がCRUD しかないことから、その間を繋ぐプログラム層の設計がより重要になってきます。

HTTP メソッドに対する正しい設計

PUT はべき等です。特定のページに対するブックマークの追加を、既存のリソース(そのページのブックマーク数)に対して+1 をPUT するように実現してはいけません。何故なら、PUT リクエストをする度にブックマーク数が増えてしまい、べき等ではなくなるからです。この場合、「そのページのブックマーク数を11 で更新する」のように実現する必要があります。
PUT を使うべき場所でPOST を使ってしまう場合もあります。Amazon S3 では、バケット(ファイルシステムディレクトリのようなもの)に新たなオブジェクトを追加する際にPUT メソッドを使います。REST ではリソースの作成にPUT を使用することができます。PUT を用いる場合というのは、リソースのURL をクライアントが既に知っている場合です。S3 に追加するオブジェクトのキーも値もクライアントによって決定されるので、この場合はPUT が適切なメソッドとなります。一方、商品が「/products/123」のようなURL で表現されるなら、新しい商品を追加するような場合は、POST メソッドを用いることになります。クライアントは新しく作成される商品を表現するURL を知らないからです。

雑感

今回ざっくりREST について調べましたが、とても面白かったです。
まず、REST によってもたらされる制約が面白い。設計を良い方向に導く制約はRails に似たものがあり、Rails ユーザとしてはその効果に期待せずにはいられません。アプリケーションに関わるエンジニアの設計視点が統一されるのも良いことだと思います。過去に、URL にadd 等の動詞を入れてしまったり、delete とdestroy、confirm とconfirmation 等同じことを意味するであろう操作に異るURL が割り振ってしまい、その結果、そのURL に対応するメソッド名の一貫性を失なわせてしまった経験がある身としては、REST の原則は非常に良いと感じます。

他にも、Rails と絡めて感じたことは、コントローラ層がリクエストのメソッドを意識しなくてよくなったのがとても良いと思いました。今まではそのためにverify を使っていたり、ポストバックによって1つのメソッドに2つの役割を持たせたこともありました。これからは、URL とHTTP メソッドによってアクションが定まるので、そういった心配はなくなり、コントローラ層がすっきりします。あ、before_filter が増えるのは若干微妙な気もしますが…。親リソースをロードするためには仕方ないのかなぁ。あれはDRY というか、Once And Only Once に近いし、あまり好きにはなれない。

REST を勉強してさらに良かったのは、設計に対する意識が高まったことです。例えば、今までは、ActiveRecord を継承したモデル内に、モデルのレコードを調査してメールを送信するバッチ用メソッドを生やしたりしていました。でも、モデルの仕事はせいぜいバッチ対象となるレコードを取得する部分くらいであって、メール送信はそれこそサービス層でやるのが正しいんだと思います。DB の1テーブルを司るクラスの責任にメール送信があるとは思えません。
と、そんなことを考えながら本を読んでいました。何はともあれ、このような新しい概念を使っていく上で学ぶことができるRails はやはり素晴しいフレームワークだなぁと思いました。