📖

すべての Web サービス設計者に捧ぐ「RESTful って結局なんなんだ」

2023/09/10に公開
3

誰もがその戦いの背景にある歴史の深淵と人々の覚悟を見誤っていた。開戦初日に決着が付くと高を括って鼻歌混じりに筆を取った私は、気が付けば夏休みの8割を喪失し、すべての指が腱鞘炎で悲鳴を上げる中で参考文献の海に這いつくばっていた。
                               —— Josh Nobus ——

ぜんぜんわからない 俺たちは雰囲気で REST をやっている

何度調べてもなんだかよく分からない概念ランキングの個人的 Top5 に入る言葉

それが「REST」だ

いろんな Web サービスの REST API はよく使わせてもらっているし、REST API が何かと聞かれれば「その Web サービスの機能を HTTP リクエストで呼び出せる API エンドポイントのことだよ」みたいにそれっぽくお茶を濁すことはできると思うが、じゃあ自分で RESTful なサービスを作ってみろと言われたら私は作れない。作れなかった。

だってどんな条件を満たしたら RESTful であるかの定義を知らないんだもん。

普段使っているサービスの API に雰囲気と機能を似せれば REST っぽい API を作ることはできるだろうけど「それが RESTful なサービスなの?根拠は?」って詰められたらなんも言い返せないと思う。

それではいざサービスを作るときにちょっと困ると思った(というか作ろうと思って実際に困った)ので REST について調べたことをまとめておく。

対象読者はタイトル通りすべての Web サービス設計者です。あなたの求めた答えがきっとここにはあります。

よく使われてるクセにまともな情報がない

あとで参考文献に挙げる通り他にも記事はたくさんありますし、それらは REST の概要や雰囲気を掴めるだけの情報が簡潔にまとまっている素晴らしい記事だと思います。

しかし API の利用者側の立場ならともかくとして、提供者の立場としてはいま一歩かゆいところに手が届かない情報が多いです。たとえば

取得・登録・更新・削除は、HTTPメソッドの GET・POST・PUT(PATCH)・DELETE として提供する

といった説明は利用者にとってほぼ十分な情報ですが、提供者はたとえば

  1. 何についてどこまでの機能をその HTTP メソッドとして提供してよいか
  2. 利用者からその HTTP メソッドのリクエストを受け取ったときに何をすべきで何をしてはいけないか
  3. その HTTP メソッドの処理に失敗したら何をすべきか
  4. これらの HTTP メソッドで表現できない操作はどんなメソッドで提供すべきか

といったことを知らなければなりません。

そんなの場合によりけりだろうと議論を終わらせるのは簡単ですが、場合によって判断を下すというならば判断基準がなければいけません何をしてよくて何をしてはいけないのかの判断基準がないなら、それはルールとしては実質何も規定していないことに等しいです。ひとりで開発するならそれでもよいのですが、チームで開発するとか Web 上に公開してみんなに使ってもらうという状況では早々に破綻するでしょう。

したがって開発者は必要に応じて参照したり参考にできる REST についての「まともな情報」を心の拠り所として求めるはずです。

それでネットの世界を深堀りして調べてみるのですが、どうにもその「まともな情報」がありません。

まともな情報がないというのは1次情報がないという意味です。プログラミングの世界での "まともな情報" とは、究極的には、その技術を管理しているまともな団体が公開しているまともな公式ドキュメントのことですが REST にはそれがありません。

というのも調べてみるとどうも REST とは厳密に仕様が定められた設計やプロトコルではなくただの設計思想(architectural style)を指す言葉だからです[1]

つまり「オブジェクト指向」と同じ部類の宗教です。みんなの心にそれぞれの解釈があって宗派が形成されて収集がつかなくなってるタイプのやつです。

新しい用語を使用する最後の理由は、「REST」がナードの宗教戦争に用いられる用語だからだ。この用語が使用される状況では、通常、真の RESTful アーキテクチャが1つ存在し、それは発言者が支持するアーキテクチャである。
                   ── 引用元『RESTful Webサービス』 ──

1次情報が存在しないのにはこのような背景があるようです。

というのは過去の話

とはいえ先ほどの引用元の本が書かれたのは 2007 年なので、いまはもっとマシな状況になっており、GAFA のようなかなりまともな組織が REST に基づく API 設計についての説明やガイドラインを公開している他、REST という言葉は使われていないものの REST を構成する重要な概念や用語が IETF(Internet Engineering Task Force) により RFC 9110 の『HTTP Semantics』に標準化されました

したがって以下のドキュメントやガイドラインを読み漁る時間と実力がある人には本記事は不要かもしれません。

REST の原典

RFC 9110

REST API についての説明

REST API 設計のガイドライン


本記事は上記のドキュメントや、あとで挙げる参考文献も参照しつつ、主に以下の3つの参考文献について情報をまとめます。

本記事を執筆する際、当初は目標を2つ設定していました。

  1. REST, RESTful とは何なのかを明らかにする
  2. RESTful なサービスの開発者にとって十分な情報を提供する

しかし本記事で達成できたのは 1 のほうだけだと思います。2 については『リソース指向アーキテクチャ』の章を読むことで部分的に達成されるとは思いますが、サービスの実装を試みる人はもっと詳しく知りたいと思うでしょう。本当であれば

まで記事にまとめられれば 2 の目標も達成されるのですが、記事の分量の関係で断念しました。あとで別記事を書くかどうかも未定です[2]

本記事の執筆にあたり、参考文献に挙げた各文書のすべてに目を通したわけではありません。私がなんか変なこと言ってたらコメントで教えてくださいますと幸いです。

また、私の主観で複数のソースからの情報を混ぜてしまうと混乱させてしまうため、本記事はソースごとに章立てて書いています。そのため、同じことを何度も繰り返して書いているように思えるかもしれませんがご了承ください。複数のソースで言及されていることはそれだけ重要な要素とみなされていると考えていただけると幸いです。

参考文献

本記事で直接引用したり参照したりしない文献も含まれます。

REST の原則

REST(REpresentational State Transfer)とは Roy Fielding の博士論文『Architectural Styles and the Design of Network-based Software Architectures』で 2000 年に提唱された World Wide Web 上での分散ハイパーメディアのための設計思想(architectural style)です。REST について1次情報と呼べるのはこの論文でしょう[3]

https://www.ics.uci.edu//~fielding/pubs/dissertation/top.htm

【原文】
The Representational State Transfer (REST) style is an abstraction of the architectural elements within a distributed hypermedia system. REST ignores the details of component implementation and protocol syntax in order to focus on the roles of components, the constraints upon their interaction with other components, and their interpretation of significant data elements. It encompasses the fundamental constraints upon components, connectors, and data that define the basis of the Web architecture, and thus the essence of its behavior as a network-based application.

【日本語訳】
Representational State Transfer (REST)スタイルは、分散ハイパーメディアシステム内のアーキテクチャ要素の抽象化です。RESTは、コンポーネントの実装の詳細やプロトコルの構文を無視し、コンポーネントの役割、他のコンポーネントとの相互作用に対する制約、および重要なデータ要素の解釈に焦点を当てることを目指しています。それは、Webアーキテクチャの基盤となるコンポーネント、コネクタ、およびデータに対する基本的な制約を包括しており、それによってネットワークベースのアプリケーションとしてのWebの振る舞いの本質が定義されています。

REST という語が指す概念を正確に理解するには、まず設計思想(architectural style)と設計(architecture)という用語を正確に理解する必要があります。もうひとつ実装(implementation)という言葉も付け加えておきます。

これら3つの用語の関係性は「ある設計思想に則った設計に基づく実装」という文章を考えれば分かります。設計思想が大元にあって、その設計思想を実現できるように大枠を具体的に規定するのが設計で、さらにその設計を具体的に実現したのが実装です。

ひとつの設計思想にしたがう設計が何種類あっても構いません。また、ひとつの設計を体現する実装が何種類あっても構いません[4]。実装を一段階抽象化したのが設計で、設計をさらに一段階抽象化したのが設計思想です。同じ設計に基づく実装どうしには互換性があるでしょう。同じ設計思想に基づく設計には互換性がないかもしれませんが、根底にある考え方は共通しているかもしれません[5]

重要なのは、お互いのレイヤーどうしで口出ししないということです。設計(アーキテクチャ)はシステムの構成や期待される挙動について定めるかもしれませんが、それをどういったプログラムや回路によって実装するかについては口出ししません。同様に、設計思想はシステムがどんなものであるべきかを定めるかもしれませんが、具体的にどんなシステム構成で設計すべきかについては口出ししません。

REST は設計思想です。Web サービスを「こんな風に作ったらいいんじゃない?」とふんわり抽象的に提案しますが、ここはどういう設計になっているべきだとか、こんな風に実装しろといったことには言及しません。

そして REST という言葉から「REST の設計思想によく当てはまる」を意味する形容詞である RESTful という言葉が作られました[6]。REST の設計思想を体現したアーキテクチャは RESTful なアーキテクチャと呼ばれます。

RESTful Webサービス』で紹介されているリソース指向アーキテクチャ(resource oriented architecture)は RESTful なアーキテクチャのひとつということになります。

RESTful Webサービス』では REST の設計思想が規定する性質をどれだけ満たせば RESTful と呼べるかについての規定はないと述べられています。同書は 2007 年の書籍であり、現在もその状況に変わりはないと思われますが、現在の REST の説明やガイドラインの多くは以下のような原則を提示しています

原則というくらいですから、事実上は以下の原則を満たすのが RESTful なアーキテクチャで、そうでないアーキテクチャは RESTful ではないと言えるでしょう。

文献によって原則が4つだったり5つだったり6つだったりしますが、以下では元論文に記載されている順番で、REST の導出に用いられる6つの制約(一般に英語圏で「原則」と呼ばれている項目)について説明します。

REST の設計思想は実装に依存しない?

元論文には「REST ignores the details of component implementation and protocol syntax」とあるので REST の思想は実装やプロトコルに依存しないのですが、あとで述べる REST の原則の中には「統一されたインターフェイス」があるので使えるプロトコルはひとつ、つまり HTTP で提供される Web サービスが標準となっている現在は事実上 HTTP しか REST としては認めないことになると思われ、ダブルバインドになっています。

これは RESTful なシステムは実装に依存しないがインターフェイスは統一してほしい、つまり将来的に HTTP 以外のプロトコルで置換されても構わないが、置換するなら Web 全体をそのプロトコルで完全に置換しろという勧告の意味合いがあるかもしれません。

また、元論文では REST が Web プロトコル標準を導くフレームワークとなることを想定して開発された概念であることが述べられていますし、

  1. どんな Web サービスも REST による評価に晒されることになる
  2. Web サービスが自身を REST に則っていると主張するためには HTTP を用いねばならない

という圧力をかけることで、要するに「Web サービスの API は RESTful に設計された HTTP で提供しろ」と Web サービスの設計者に遠回しに促しているのではないかと私は推測します。

深読みしすぎかもしれないのでこの話は忘れてください。

「原則」という言葉遣いの出展は分からず

以下で紹介する『クライアントとサーバーの分離』などの項目は、元論文では REST をソフトウェア工学における原則(principles)による制約(constraints)から REST を導出する過程(process of deriving)において「制約」として言及されているものであり「原則」とは呼ばれていません

元論文の中で「REST 設計の原則(design principles of REST)」として言及されている項目もありますが、それは

  • Process View
  • Connector View
  • Data View

の3つであり、これらは今日「REST の原則」として言及されているものとは異なります。

いつからこの「制約」が REST の「原則」と呼ばれるようになったのかは今回調べた限りでは分かりませんでしたが、AWS による REST の解説でも「原則」と呼ばれている(英語版でも principles と明記されている)ので、元論文において「制約」として書かれていたものが歴史上のどこかのタイミングで「REST の原則」ということになったのだとは思います。

日本における「REST の4つの原則」は「リソース指向アーキテクチャの4つの主な特徴」

日本では Wikipedia をはじめとして多くの記事が REST の原則として「4つの原則」を挙げていて、記事によって割と原則の内容もバラバラなため混乱します。なぜか数だけは一致していて4つに整理される傾向がありますが理由は不明です。

AWS による REST の解説では5つの原則が提示され、それらが「REST の原則の一部」であると書かれているので、少なくとも AWS が理解している REST の原則は4つ以上存在していることが分かります。

日本において「REST の4つの原則」とされることが多い以下の項目は『RESTful Webサービス』で説明されている『リソース指向アーキテクチャの4つの主な特徴』です。

  • アドレス可能性(Addressability)
  • ステートレス性(Stateless)
  • 接続性(Connectability)
  • 統一インターフェース(Uniform Interface)

また、関係あるかは不明ですが、他に4という数字に心当たりがあるとすれば、AWS による REST の解説にある『統一されたインターフェイス』の項目の「統一されたインターフェイスは、次の 4 つのアーキテクチャ上の制約を課します」という説明です。これは元論文において以下のように説明されているものです。

REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state. These constraints will be discussed in Section 5.2.

これについても一応本記事で解説を試みます。

1. クライアントとサーバーの分離(Client-Server)

関心の分離(SoC: Separation of Concerns)」というソフトウェア工学の原則から導かれる制約です。

Web アプリケーションを設計する上で現在ではあまりにも当たり前なためか、この項目は原則として説明されていないことがしばしばあります。元論文に基づいて説明すると以下です。

REST スタイルにおいてシステムは Client-Server スタイル(CSスタイル)とします。つまりアプリケーションの全体をクライアントコンポーネントとサーバーコンポーネントに分離します[7]この分離は通常、ユーザーインターフェースの機能をすべてクライアントに集約する形で行われます

コンポーネント 役割 動作
クライアント 実行を希望するサービスのリクエストをサーバーに送信する。 リクエストをサーバーに送信する。
サーバー 一連のサービスを提供し、サービスへのリクエストを受け付ける。 受け取ったリクエストを拒否または実行し、結果をクライアントに返す。

クライアントはトリガー(trigger)となるプロセスであり、それに対してサーバーはリアクティブ(reactive)なプロセスです。すなわちクライアントは自分が好きなタイミングでアクティビティを開始することができますが、サーバーはリクエストがあるまで待機してリクエストがあったら反応を返すだけです[8]

この分離により、両者間のインターフェースが変更されない限り、クライアントとサーバーは独立して開発・進化させることが可能になります

2. ステートレス性(Stateless)

REST スタイルにおいてシステムは Client-Stateless-Server スタイル(CSSスタイル)とします。これは Client-Server スタイルにステートレス性が追加されたもので、サーバーコンポーネントにセッション状態を許さないという追加の制約を持ちます。

必然的にセッションの状態は完全にクライアント側に保持され、クライアントからサーバーへの各リクエストには、リクエストを理解するために必要なすべての情報が含まれることになります。クライアントはリクエストによってサーバー上に保存されたコンテキスト(セッションの状態)を利用することはできません。

この制約を採用する利点は以下です。

  • 可視性の向上
    • リクエストにすべての情報が含まれているので、監視システムはリクエストに対してどのような反応をするか理解するために、そのリクエストを超えてサーバーのコンテキストのような情報を収集する必要がない。
  • 信頼性の向上
    • サーバー内でコンテキスト共有によるプログラムの絡み合いが存在しないため障害が波及しにくく、また部分的な障害に対する復旧が容易になる。
  • スケーラビリティの向上
    • コンテキスト共有によるプログラムの絡み合いが存在しないために実装が簡素化される。
    • リクエスト間で状態を保存する必要がないため、サーバーコンポーネントが迅速にリソースを解放できるようになる。

一方で欠点もあります。サーバー側がステートレスで共有コンテキストにデータを残すことができないため、必要な情報を取得するためにクライアントは何度もリクエストを送信したり、過剰な量のデータを要求せざるを得ず、ネットワークパフォーマンスを低下させる可能性があります

この欠点を解消した GraphQL というスタイルも提案されていて、普及してきています

3. キャッシュ可能性(Cache)

REST スタイルにおいてシステムは Client-Cache-Stateless-Server スタイル(C$SSスタイル)とします[9]。これは Client-Stateless-Server スタイルにキャッシュコンポーネントが追加され、一部のリクエストの実行結果をキャッシュで代替するという制約です。

キャッシュはクライアントとサーバーの間に存在していて、クライアントサイドとサーバーサイドのどちらにでも設置することができ、それぞれ以下のような役割を持ちます。

  • クライアントキャッシュ: クライアントとサーバの通信を削減する
  • サーバーキャッシュ: サーバー側でのレスポンス生成の負荷を削減する

サーバーはレスポンスの中で、そのレスポンス自身がキャッシュ可能であるか不可能であるかを指定します。キャッシュはクライアントとサーバーの間に立ってレスポンスについて適切なキャッシングを行い、以降のクライアントからの同等のリクエストに対してはそのキャッシュをレスポンスとして再利用する権利を持ちます。

キャッシュはクライアントとサーバの通信を部分的または完全に削減することで以下のような利点をもたらします。

  • レンテンシを低減してユーザーが感じるパフォーマンスを向上する
  • ネットワーク帯域を節約してスケーラビリティを向上する

一方で欠点もあり、キャッシュを利用した結果がサーバーからのレスポンスとあまりにも食い違う場合にはサービスとしての信頼性が損なわれます

4. 統一されたインターフェイス(Uniform Interface)

ソフトウェア工学における「一般性の原則(principle of generality)[10]」から導かれる制約です。

REST が一般性の原則にしたがうとはどういうことか

REST が一般性の原則にどうしたがっているかは元論文や参考文献に書かれていませんが、一般性の原則についての説明から解釈するに

  • このインターフェイスは不自然な制約や制限を有していないこと、つまりやろうと思ったことはその枠組みの中で大体できるし、ルールの不備によってそのインターフェイスが破綻したりしないこと
  • いま起こっている問題に場当たり的に対処するのではなく、本質的なソリューションとして提供された枠組みをみんなが利用することでニーズが満たされること

のような意味合いだと思われます。

REST スタイルのシステムはコンポーネント間の通信に「一貫したインターフェイス(uniform interface)[11]」を使用し、「標準化された形式(standard format)」で情報を転送します

この「一貫したインターフェイス」の挙動は、以下の4つの制約により規定されると書かれています。

  1. リソースの識別(identification of resources)
  2. 表現を介したリソースの操作(manipulation of resources through representations)
  3. 自己記述的なメッセージ(self-descriptive messages)
  4. アプリケーション状態のエンジンとしてのハイパーメディア(hypermedia as the engine of application state)

これは元論文では REST の定義であると書かれている↓ので、あとで『REST の定義』の章でもう少し詳しく見ていきます。

REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.

REST は設計思想で具体的な実装に依存しない概念であるため、この統一インターフェイスが具体的にどうであるかについては説明がないのが混乱するポイントです。IBM の説明では、どこからリクエストが来るかに関わらず同じリソースに対するすべての API リクエストは同じように見えるべきだと書かれています。

All API requests for the same resource should look the same, no matter where the request comes from.

抽象的すぎて分かりにくいのですが、REST は分散ハイパーメディアのための設計思想で複数のサーバーが存在して互いに連携していることを想定していますから、サーバー間の通信もクライアント-サーバー間の通信と同じようなインターフェイスで扱えることを意味していると思われます

インターフェイスを統一することでシステム全体のアーキテクチャが単純化され、コンポーネント間の相互作用が分かりやすくなるという利点がある一方で、均一なインターフェイスに情報を押し込める必要があるので通信の効率が低下するという欠点があります。

REST は Web 全体の一般化に特化して最適化された設計思想であり、個別のサービス間通信のパフォーマンス最適化は切り捨てていると言えます[12]

HTTP 1.1

REST は実装に依存しない設計思想であるという建前上、どの説明でも具体的な実装についてやたらぼかして回りくどい説明になっているようなのですが、元論文では

  • REST は Web プロトコルの標準化をガイドするフレームワークとなることを目的として作成された
  • REST は IETF(Internet Engineering Taskforce) と W3C(World Wide Web Consortium) が HTTP, URI, HTML といった Web のための設計標準を定義する取り組みの一部として開発された
  • REST の初版は 1994 年 10 月から 1995 年 8 月の間に作成され、HTTP 1.0 の仕様と HTTP 1.1 の初期の提案を書く際にもともと Web の概念を伝える手段として用いられたもので、既存の問題の指摘や代替案の比較などに役立てられた

と書かれています。

元論文からの該当部分の引用

As described in Chapter 4, the motivation for developing REST was to create an architectural model for how the Web should work, such that it could serve as the guiding framework for the Web protocol standards. REST has been applied to describe the desired Web architecture, help identify existing problems, compare alternative solutions, and ensure that protocol extensions would not violate the core constraints that make the Web successful. This work was done as part of the Internet Engineering Taskforce (IETF) and World Wide Web Consortium (W3C) efforts to define the architectural standards for the Web: HTTP, URI, and HTML.

The first edition of REST was developed between October 1994 and August 1995, primarily as a means for communicating Web concepts as we wrote the HTTP/1.0 specification and the initial HTTP/1.1 proposal. It was iteratively improved over the next five years and applied to various revisions and extensions of the Web protocol standards. REST was originally referred to as the "HTTP object model," but that name would often lead to misinterpretation of it as the implementation model of an HTTP server. The name "Representational State Transfer" is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through the application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use.

つまり言葉をぼかしてはいますが、HTTP には REST の思想を体現できるよう設計されたという歴史があり、統一されたインターフェイスで言及されている通信のための「標準化された形式」とは、事実上 HTTP で REST の原則を満たすように適切に構成された形式のことを指していると思われます[13]

HTTP/1.0 の仕様HTTP/1.1 の仕様を見比べてみると、1.0 から 1.1 にアップデートされるに当たって URI や Cache といった RESTful な項目がより明確にハイライトされており、この時期に HTTP に REST の思想が取り込まれたことが分かります。

5. 階層化されたシステム(Layered System)

REST スタイルにおいてシステムは Layered-Client-Cache-Stateless-Server スタイル(LC$SSスタイル)とします。これは C$SS スタイルのアーキテクチャにプロキシ(proxy)やゲートウェイ(gateway)といったコンポーネントを追加可能にし、階層的なレイヤーでサービスを構成することで、直接相互作用するレイヤーを超えたアクセスを禁止して実際のシステム構成を隠蔽します

これに関しては具体例を挙げたほうが理解しやすく、たとえば実際はサービスを提供する Web サーバーが複数台あったとして、手前にロードバランサーを配置してリクエストを振り分けることでユーザーからは1つのサーバーしかないように見せかけるといった構成を許すということです。他にも、データベースの手前に Web サーバーを設置してユーザーからは直接データベースにアクセスさせないといったことも可能で、現在の一般的なサービス構成はこの「階層化されたシステム」に該当します

システムを階層化することで以下のような利点があります。

  • システムが階層化されることでクライアントからのサービス利用が単純化できる
  • 各コンポーネントの独立性を高めてシステム全体を単純化できる
  • 適切な負荷分散を行うことでスケーラビリティを高めることができる

一方で欠点もあり、システムの階層が深くなることでリクエストからレスポンスまでのオーバーヘッドとレイテンシが増加し、ユーザーが体験するパフォーマンスは低下する可能性があります

6. オンデマンドのコード(Code-On-Demand)

REST において、サーバーはスクリプト(script)やアプレット(applet)[14]の形式でソフトウェアプログラミングコードをクライアントに転送することにより、クライアントの機能を一時的に拡張したりカスタマイズしたりすることができます。これを Code-On-Demand と言います。

Code-On-Demand によりクライアント側で事前に実装する必要がある機能の数が減少するためクライアントの機能が単純化でき、展開後に機能をダウンロードできるのでシステムの拡張性が向上します

ただし、これによりクライアントの挙動の監視が難しくなる他、セキュリティ上の理由でファイアウォールによってプログラムを含む通信が阻まれる場合もあるので、元論文では REST において Code-On-Demand はオプショナルな制約(実装してもしなくてもよい)とされています。

当時は Java Applet が Code-On-Demand の例として挙げられていました[15]が、現在では JavaScript や Wasm を使うことが多いでしょう。

REST の原則まとめ

ここで挙げられている6つの制約(原則)は個別に考慮することもできますが、以下のようにストーリーとして導出されていると考えるとよいです。

  1. まずサービスをクライアントとサーバーに分けます
  2. セッションの状態をすべてクライアントに押し付けてサーバーをステートレスにします
  3. ステートレスにしたせいもあって若干通信が増えて不安なのでキャッシュ可能にします
  4. サーバー間の通信も可能にしますが決まりがないとカオスになるのでインターフェイスは HTTP に統一します
  5. サービス自体が複雑になってもユーザーからは単純に使えるようにサービスを階層化して実装を隠蔽します
  6. クライアント(大抵は Web ブラウザ)はサーバー側から拡張できるようにしておくから好きなアプリ作ってね!

といった感じです。こんな風に作られているサービスは REST の原則を満たしているという意味において RESTful であると言えるということかと思います。

REST の定義

ここまでで説明したのは「REST の原則をどの程度満たしているか」の尺度を基準にした RESTful の説明でした。一方で元論文で『4. 統一されたインターフェイス』に対応する説明の部分では以下のような記載があります。

REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state. These constraints will be discussed in Section 5.2.

数学ではない文脈で「原則」と「定義」のどちらが強い意味を持つのかは知りませんが、文字通りに解釈するならばこの文章は「REST の定義」なので、より直接的に「REST とは何なのか」を示していると言えます。

元論文で REST について説明している5章の中で「define」と検索しても REST 自体を define しているのはこの文章だけなので、元論文にしたがうならこれが「REST の定義」だと言えるでしょう。しかし「Section 5.2. で議論する」と言いつつ明確に個々の項目に分けて言及があるわけではなく、話も抽象的で、正直何を言っているか分からない(というより具体的なことが何も書かれていないので本記事をまとめるにあたり深入りしても仕方がないと判断した)部分も多いです。

なお、日本における「REST の4つの原則」はこの定義よりもむしろ次の章で説明する『ROA の原則』に近いと思われます。

1. リソースの識別(identification of resources)

AWS による解説で以下のように説明されている項目です。

リクエストはリソースを特定する必要があります。これは、Uniform Resource Identifier (URI) を使用することによって行います。

REST においては情報をリソース(resource)という概念に抽象化します。元論文ではリソースについて以下のような言及があります。

  • 名前を付けることができる情報は何でもリソースとして扱うことができる
  • ハイパーテキストで参照される対象となる可能性のあるものはリソースの定義を満たさねばならない

リソースの例として以下が挙げられています。

  • ドキュメントや画像などの単一のデータ
  • 「今日のロサンゼルスの天気」のような時系列性のあるサービス
  • 実在するオブジェクト(人間など)
  • リソースのコレクション[16]

元論文ではリソースの定義と思われる記述があるので解説しておきます。難しいので私の解釈が間違っていたら教えてください。

More precisely, a resource R is a temporally varying membership function MR(t), which for time t maps to a set of entities, or values, which are equivalent. The values in the set may be resource representations and/or resource identifiers. A resource can map to the empty set, which allows references to be made to a concept before any realization of that concept exists -- a notion that was foreign to most hypertext systems prior to the Web [61]. Some resources are static in the sense that, when examined at any time after their creation, they always correspond to the same value set. Others have a high degree of variance in their value over time. The only thing that is required to be static for a resource is the semantics of the mapping, since the semantics is what distinguishes one resource from another.

【日本語訳】
より正確には、リソースRは時系列に変化するメンバーシップ関数M _ R (t)です。メンバーシップ関数M _ R(t)は時刻tを、リソースRに対応する、つまりリソースRと等価とみなすことができる存在、概念[17]、値などの集合に対応づけます。リソースに対応づけられた集合の個々の要素はリソース表現(resource representation)かリソース識別子(resource identifier)のどちらか一方または両方にするとよいです[18]

リソースは空集合への写像を許可します。これにより、参照したい概念が未だ具現化していなくてもその概念を参照することが可能になります。これはハイパーテキストシステム以前の Web ではできなかったことです。

一部のリソースは静的で、一度作成されたあとはいつ参照されても同じ値の集合を返します。そうでないものは時間の経過に伴って頻繁に変動します。

リソースに対して要求される唯一の条件は、その写像のセマンティクスが静的であることです。なぜならばセマンティクスこそが着目しているリソースを他と区別するものだからです。

セマンティクスとは「記号が持っている意味」のことです。たとえば「発表で使用するスライドの最新版」は presentation/slide/latest というリソースとみなすことができます。あなたのデスクトップが時間経過に伴って

  • 発表スライド_草稿.pptx
  • 発表スライド_部長承認済み.pptx
  • 発表スライド_最新版.pptx
  • 発表スライド_最新版ver2_0324.pptx
  • 発表スライド_本番用.pptx
  • 発表スライド_本番用【これを使う!】.pptx

のようにアホみたいな状態になっていたとすると、presentation/slide/latest が指す対象はそれを参照する時間によって 発表スライド_草稿.pptx だったり 発表スライド_本番用.pptx だったりしますが、presentation/slide/latest という記号に与えられた「その時点で最新のスライド」という意味だけは時間によって変わりませんし、リソースとみなす以上は時間によってその意味が変わってはいけません。

REST は実装に捕らわれない枠組みなのでリソース識別子をどのように定義するかまでは規定していませんが、実際はリソース識別子として URI(Uniform Resource Identifier) が用いられています。URI の標準の中で「リソース」という用語を定義する際に REST が用いられています。URI については『リソース指向アーキテクチャ(ROA)』のほうで説明します。

2. 表現を介したリソースの操作(manipulation of resources through representations)

AWS による解説で以下のように説明されている項目です。

クライアントは、必要に応じてリソースを変更または削除するのに十分な情報をリソース表現に持っています。サーバーは、リソースをさらに説明するメタデータを送信することにより、この条件を満たします。

REST のコンポーネントはリソース上でアクションを実行します。クライアントもサーバーもその他のコンポーネントも(実際にリソースに対応する実体にアクションを行いその結果を返すハンドラ以外は)そのリソースの実体がどんなものであるかやリソースに対する操作が具体的にどのような実装になっているかを意識する必要はありません。

REST の各コンポーネントはリソースの現在の状態または意図した状態を表現(representation)によって伝達します。これも元論文の説明は抽象的過ぎるので『リソース指向アーキテクチャ(ROA)』のほうで HTTP による具体的な実装を説明します。

3. 自己記述的なメッセージ(self-descriptive messages)

AWS による解説で以下のように説明されている項目です。

クライアントは、表現をさらに処理する方法に関する情報を受け取ります。サーバーは、クライアントがリソースを最適に使用する方法に関するメタデータを含む自己記述型メッセージを送信することにより、これを実現します。

自己記述的なメッセージ(self-descriptive message)は REST のステートレス性に深く関わっています。REST でやりとりされるメッセージにはそのメッセージを処理するために必要な情報がすべて含まれており、それ以外の情報を参照する必要がありません。「自己記述的」という言葉は元論文で以下のように説明されています。

  • リクエスト間での対話はステートレスである
  • 標準的なメソッドがメッセージの意味を示す
  • メディアタイプが交換される情報を示す
  • レスポンスはキャッシュ可能性を明示的に示す

4. アプリケーション状態のエンジンとしてのハイパーメディア(hypermedia as the engine of application state)

AWS による解説で以下のように説明されている項目だと思われます。

クライアントは、タスクを完了するために必要な他のすべての関連リソースに関する情報を受け取ります。サーバーは、クライアントがより多くのリソースを動的に検出できるように、表現でハイパーリンクを送信することによってこれを実現します。

要するにレスポンスに追加でリソースを含める必要がある場合はハイパーリンクだけ渡せばよいしそうすべきである、と言っているのかと思いますが、正直これに関しては元論文のどこで言及されているのかよく分からない上に、それらしい記述も抽象的すぎて説明しても意味不明になってしまうので記事にまとめるのを諦めました。

多分以下の文章がそれらしい記述なので引用しておきますが、真の意味を理解したい方は頑張って元論文を読んでください。また、『リソース指向アーキテクチャ(ROA)』の『3. 接続性(Connectability)』は本項目をより具体化したもののようなので、そちらを参照したほうがよいでしょう。

The application state is controlled and stored by the user agent and can be composed of representations from multiple servers. In addition to freeing the server from the scalability problems of storing state, this allows the user to directly manipulate the state (e.g., a Web browser's history), anticipate changes to that state (e.g., link maps and prefetching of representations), and jump from one application to another (e.g., bookmarks and URI-entry dialogs).

【ChatGPT による日本語訳】
アプリケーションの状態は、ユーザーエージェントによって制御および保持され、複数のサーバーからの表現で構成されることがあります。サーバーが状態を保持するスケーラビリティの問題から解放されるだけでなく、これによりユーザーは状態を直接操作できる(例: ウェブブラウザの履歴)、その状態の変更を予測できる(例: リンクマップと表現の先読み)、さらに別のアプリケーションに移動できる(例: ブックマークとURI入力ダイアログ)ようになります。

リソース指向アーキテクチャ(ROA)

ここまでの説明は REST の原典である Roy Fielding の『Architectural Styles and the Design of Network-based Software Architectures』 にしたがって説明してきましたが、RESTful なアーキテクチャの説明としては Leonard Richardson と Sam Ruby による『RESTful Webサービス[19]で提唱されたリソース指向アーキテクチャ(ROA: Resource Oriented Architecture)が用いられることもあるようです。

リソース指向アーキテクチャが考案された背景には、REST それ自体は設計思想(architectural style)であって実際の設計(architecture)にまでは口出ししないというスタンスを取っているため、「では具体的にどうやって RESTful なサービスを設計すればよいか」という部分については(意図的に)空白地帯になっているという状況がありました。

そのためエンジニアたちは各々の慣習やノウハウに基づいて「RESTful なアーキテクチャ」を設計していたようで、この記事の冒頭でも引用したように当時は宗教戦争を生んでしまっていたようです。そこで Leonard らはその宗教戦争から逃れるために「リソース指向アーキテクチャ」という言葉を作って RESTful なアーキテクチャを実際に構成するための指針を示しました。

リソース指向アーキテクチャは HTTP と URI を使用して RESTful な Web サービスを構成するための実践的な指針です

特に日本で「REST の4つの原則」といえば『RESTful Webサービス』の第4章で言及されている以下の「ROA の4つの主な特徴」を指していることが多いように思われます。

  1. アドレス可能性(Addressability)
  2. ステートレス性(Stateless)
  3. 接続性(Connectability)
  4. 統一インターフェース(Uniform Interface)

同書を参考にしたリソース指向アーキテクチャの解説記事としては既に詳しい記事があります。本記事も同書にしたがう関係上、説明がオーバーラップする部分も多くなってしまうかと思いますが、補足説明や深掘りなどしておくよう努めます。

https://qiita.com/NagaokaKenichi/items/0f3a55e422d5cc9f1b9c

リソースとは

REST の『リソースの識別(identification of resources)』で説明したリソースは以下の性質を持つものでした。

  • 名前をつけることができる実体を伴う何かである
  • 名前が持つ意味自体は時間によって変わらない
  • その名前が実際に指す対象は時間によって変わってもよい

ROA がこの定義をどこまで引き継いでいるかは不明です。ROA におけるリソース(resource)は

  • それ自体を参照するに値するほどの重要性を持つもの

と説明されています。また、ROA では以下の条件が追加されています。

  • リソースはコンピュータ上に格納することができ、一連のビットで表せるものである[20]
  • リソースは少なくとも URI を1つ持っていなければならない
  • 2つのリソースが同じリソースを表すことはない[21]
では URI を持たない情報は何なのか

RESTful Webサービス』では「URI を持たない情報はリソースではなく、ほかのリソースを説明するデータとして以外に、Web 上に存在することはない」と述べられています。かなり思想が強めに聞こえますが、URI によって存在を特定することができないならば、特定可能なリソースに付随している情報以外はユーザーからはアクセスできないという意味ではないかと思われます。

URI の原理

URI(Uniform Resource Identifier, Universal Resource Identifier) は抽象的または物理的なリソースを識別するためのコンパクトな文字列です[22]

URI が持つべき性質として同書では以下が挙げられています。

  • ひとつの URI が複数のリソースを指してはならない
  • ひとつのリソースに複数の URI を割り当ててもよい
ひとつのリソースに複数の URI を割り当ててもよい

意図的にそうしてもよいし、偶然そうなってもよいようです。たとえば /sales/2004/Q4 で入手可能な販売数を /sales/Q42004 でも入手できるようにしてもよいです。また、最新バージョンが 1.0.3 のソフトウェアについて releases/latest.tar.gz を取得したときには releases/1.0.3.tar.gz と同じリソースを指すことになります。

これはリソースを参照しやすくなる反面、両者が同じリソースを指すことを自動的に証明する方法がないという欠点があります。

この問題の回避方法のひとつは、そのうちひとつを「正規」URIにすることです。クライアントが正規 URI をリクエストするとサーバーはレスポンスコード 200(OK) とともに適切なデータを送信します。クライアントが他の URI の1つをリクエストするとサーバーは正規 URI とともにレスポンスコード 303(See Also) を送信します。

もうひとつの回避方法は、すべての URI を同じものとして公開しますが、非正規 URI がリクエストされた場合には Content-Location レスポンスヘッダーで「正規」URI を渡す方法です。

ここまでの話を数学的に言えば、URI からリソースへの写像は全射的であるということになります。その他にも以下の項目が挙げられています[23]

  • URI は記述的であるべきである[24]
    • リソースと URI は直感的に対応させる
  • URI は構造的でなければならない
    • また、その構造は予測可能な方法で区別されなければならない
    • たとえばクラゲについて調べるために /search/Jellyfish にアクセスするならネズミについても /search/Mouse にアクセスさせるべきであり、/i-want-to-know-about/Mice にアクセスさせるべきではない。

肝心の URI の原理(axioms)について同書では Tim Berners-Lee による『Universal Resource Identifiers -- Axioms of Web Architecture』で詳しく説明されている、と説明をほぼ丸投げしているので本記事はそちらから引用します。

Axiom 0: Universality 1

Any resource anywhere can be given a URI.

どこにあるどんなリソースに対しても、少なくとも1つの URI が与えられる。

Axiom 0a: Universality 2

Any resource of significance should be given a URI.

すべての重要なリソースに URI が割り当てられるべきである。

Axiom 1: Global scope

It doesn't matter to whom or where you specify that URI, it will have the same meaning.

誰がどこでその URI を指定するかは重要ではない。誰がどこで指定しようがその URI は同じ意味を持つ。

Axiom 2a: sameness

A URI will repeatably refer to "the same" thing.

ひとつの URI は何度参照してもある意味で「同じ」ものを参照する。

たとえば「明日の天気」が実際に指すものは毎日変わるが、指している概念としては同じものである。URI はこのように実体としての同一性だけでなく概念としての同一性を許容し、どのような観点から見て「同一」であるかについて制限しない。

Axiom 2b: identity

The significance of identity for a given URI is determined by the person who owns the URI, who first determined what it points to.

与えられた URI の同一性の重要性は、その URI を所有する者によって決定される。その所有者は最初にその URI が何を指すかを決定する。

ただしその「所有者」が何であるかは議論しない。人間かもしれないし機械かもしれないし、どのように URI が割り当てられるかも問わない。URI が作成された時点で「何を指すか」が決まり、その後その意味が変わらないことが重要である。

Axiom 3: non unique

URI space does not have to be the only universal space.

URI 空間は、唯一の普遍的な空間である必要はない。

URI の他に同様のシステムが存在しても構わない。一方で URI はその性質上、他の同様のシステムを部分空間として包含することが可能であり、相互運用できる。たとえば URI の最初を「phone:」で始めることを許せば既存の電話番号のシステムを包含できる。

Axiom: Opacity of URIs

これは URI 自身についての原理ではなく、URI を処理する者が守るべき原理である。

The only thing you can use an identifier for is to refer to an object. When you are not dereferencing, you should not look at the contents of the URI string to gain other information.

識別子はオブジェクトを参照するためだけに用いられる。オブジェクトの参照先を探すという操作時を除いて、他の情報を得るために URI 文字列の中身を覗いてはならない。

つまり URI はリソースを特定してそこに辿り着くためだけに使われるものであって、そのリソースを処理するために URI に含まれる情報を用いてはならない[25]

1. アドレス可能性(Addressability)

アプリケーションがそのデータセットの重要な部分をリソースとして公開する場合、そのアプリケーションはアドレス可能(addressable)であると言います。リソースは URI を通じて提供されるため、アドレス可能なアプリケーションは提供可能な情報ごとに URI を公開します。

エンドユーザーはサービスを利用するに当たって大抵の欠陥には自分で対処できますが、重要な情報についてサービスの提供者がそもそも URI を公開していないという問題については対処しようがありません。

2. ステートレス性(Stateless)

ステートレス性(stateless)とは、すべての HTTP リクエストが完全に分離していることを意味します。クライアントが送信する HTTP リクエストには、サーバーがそのリクエストを処理するために必要な情報がすべて含まれています。サーバーが以前のリクエストの情報に依存することはありません。

プロトコルから状態を取り除くことで、多くのエラー状態を取り除くことができます。クライアントとサーバーのやりとりは1回のリクエストで終了するため、サーバーがクライアントのタイムアウトに配慮する必要はありません。また、クライアントはリクエストごとに必要な情報をすべて送信するため、クライアントの作業履歴やサーバーが隠し持っている状態のせいでクライアントが誤った操作を実行することはありません。

また、ステートレスなリクエストは2つ以上のリクエストが互いに依存しないため、複数のサーバーで独立して処理することができるので、負荷分散用のサーバーを増設するだけで簡単に負荷分散を行うことができます。また、そのリクエストにすべての情報が含まれるのでキャッシュも容易になります。

ステートレス性はクライアントにとっても有効であり、操作履歴を逐一指定しなくても適切な URI を指定するだけであとから同じページに戻ることができます。過去のセッションで有効だった URI は新しいセッションで最初の URI として送信しても同様に有効となります[26]

ステートレス性はサーバーが取りうる状態もリソースであり、独自の URI が割り当てられるべきであることを意味します。何を「状態」とみなせるのかについては以下の2種類が言及されています。

アプリケーション状態

クライアント側で維持される状態のこと

アプリケーションを使用していてサイトの何ページ目を読んでいるとか、ショッピングカートに何が入っているといった情報で、サーバー側はリクエストを通してのみこれらの情報に興味を持ち、それ以外ではクライアント側でどういった操作が行われていてもサーバー側は気にしません。

リソース状態

サーバー上で維持される状態のこと

クライアントに対して提供されるリソースの状態を表す情報で、すべてのクライアントにとって同じでなければならないので、サーバー側で管理する必要があります。たとえば参照されている記事や画像が公開できる状態になっているか削除されているかといった情報などです。

クライアントから送信されてくるアプリケーション状態はしばしば信用できない(たとえば API の呼出し回数はアプリケーション状態とみなしうるが、API 制限回避のためにクライアントはその報告を偽る可能性がある)ので、リソース状態として管理することになります。しかしその場合はクラスター内の複数のマシン間でその状態をレプリケーションしなければならず、スケーラビリティの問題を引き起こす可能性があります[27]

表現

リソースは抽象的な概念なので、サーバーはリソースそのものをクライアントに送信することはできません。サーバーがクライアントに送信するのはリソースの現在の状態に関するデータを特定のファイル形式と特定の言語に変換したバイト列であり、これをリソースの表現(representation)と言います。

表現は Web ページ、XML、コンマ区切りのテキストなどさまざまな形式がありえます。また、ひとつのリソースに対して複数の表現が存在しても構いません。たとえばあるものの年間の売り上げに関するデータは CSV の表のようなデータで返してもよいですし、なんらかのグラフにしてより見やすくした画像形式で返しても構いません。リソースに対する有益な情報になっていれば何でもよいです。

表現はクライアントからサーバーに送信することもできます。新しいリソースの表現をサーバーに送信することでサーバーにリソースを作成させたり、サーバーに既存のリソースの新しい表現を送信し、新しい表現に一致するようにリソースを変更させることもできます。

表現の選択

サーバーがリソースについて複数の表現を適用する場合、ROA では各表現に対して個別の URI を割り当てることを推奨しています。

たとえば /releases/104.en はプレスリリースの英語の表現を指し、 /releases/104.es はスペイン語の表現を指すといった具合です。しかしこれは『URI の原理』の『ひとつのリソースに複数の URI を割り当ててもよい』で説明した問題を引き起こします。回避策も同様[28]です。

他の方法としてコンテンツネゴシエーション(content negotiation)という方法もあるようです。これはリソースに対して releases/104 のようなプラトニック形式の URI のみを提供しておき、どのような表現でレスポンスを受け取りたいかは、対応する特殊な HTTP リクエストヘッダで指定するという方法です。たとえば受け取りたい言語は Accept-Language ヘッダ、受け取りたいファイル形式は Accept ヘッダに指定します。

コンテンツネゴシエーションも RESTful にはなりますが、人間の間でもアプリケーションの間でもリンク先の情報を受け渡すに当たっては URI を伝えることが多く、その過程でリクエストヘッダに含まれる情報が欠落してしまうため、可能な限り情報は URI に含めるほうがよいです。

リンク

リンク(link)はクリックするなどのアクションを通じて他のリソースにアクセスするための仕組みのことです。

たとえば Google 検索の結果はクライアントが欲する情報を持った外部 Web ページへのリンクがたくさん表示されます。

3. 接続性(Connectability)

リソースがリンクを持つという特性を接続性(connectability)といいます。これはサービス内外のリソース(またはその表現)どうしの関係性がリンクによって適切に相互接続されていることを指します。このことから必然的にレスポンスにはリンクが含まれます。

従来の Web サービスはページ間をリンクによって相互接続することでネットサーフィンを容易にしています。これはレスポンスにあったページ中に他のページへのリンクが含まれていて、ページ間が相互に接続されることで実現されています。

RESTful な Web サービスではリソース間の関連性をリンクによって表現することになりますリソースは Web ページよりも広範な概念を包含しているので、リソース間のリンクはより広い概念を表現することができます

たとえばサービスの状態もリソースとして提供されることを思い出せば、状態間遷移もリンクによって表現され URI で指定可能であるということになります。レスポンス中には遷移可能な状態へのリンクが含まれ、クライアントは遷移したい状態への URI を自ら構築しなくとも[29]、レスポンス中の URI を使用して状態遷移が可能です。

接続性は Fielding の論文における『アプリケーション状態のエンジンとしてのハイパーメディア』の項目をシンプルにしたものであることが『RESTful Webサービス』で言及されています。

4. 統一インターフェイス(Uniform Interface)

RESTful Webサービス』では統一インターフェイスの重要性について以下のように述べられています[30]

REST で重要なのは、HTTP が定義する特定の統一インターフェイスを使用することではない。REST は統一インターフェイスを指定するが、どの統一インターフェイスであるかは示さない。GET、PUT、およびその他は、決して完璧なインターフェイスではない。重要なのは、すべてのサービスが HTTP のインターフェイスを同じ方法で使用すること、すなわち統一性である。
肝心なのは、GET が読み取り操作にふさわしい名前であることではなく、どのリソースに使用するかにかかわらず、GET が Web 経由での「読み取り」を意味することである。リソースの URI が与えられたら、表現を取得する方法に疑問の余地はない。HTTP GET リクエストをその URI に送信することである。統一インターフェイスは、任意の2つのサービスを、任意の2つの Web サイトのようにサポートする。統一インターフェイスがなければ、サービスごとに情報を送受信する方法を習得しなければならない。1つのサービス内でさえ、リソースによってルールが異なることもある。

要するに

  • 他のサービスと HTTP の使い方を統一すること
  • サービス内においても各リソースのインターフェイスを統一すること

が重要なようです。

HTTP のセマンティクスについては最近 RFC9110 に『HTTP Semantics』として整備されました。ここでは『RESTful Webサービス』の流れに沿って簡易的に説明します。詳細は『HTTP Semantics』の章で説明します。

HTTP の4つの基本メソッド

リソースに対するもっとも一般的な4つの操作に対して、HTTP では4つの基本メソッドが提供されています。

method 操作
HTTP GET リソースの表現を取得する
新しい URI への HTTP PUT、または既存の URI への HTTP POST 新しいリソースを作成する
既存の URI への HTTP PUT 既存のリソースを変更する
HTTP DELETE 既存のリソースを削除する

ROA ではこの4つのメソッドを使用して思いつくほぼすべての操作を表します。あまり一般的でない操作については他のメソッドを使用することもあります。

安全と冪等

サーバーのリソース状態について変更を要求しないメソッドは安全(safe)であるといいます。安全なメソッドは実行してもリソース状態が変化しないため、理想的には何度実行しても同じ結果が返ります。

安全なメソッドは副作用[31]がないことを意味しません。たとえば「アクセス回数のカウンタ」というリソースは安全なメソッドでアクセスしても、アクセスされるたびにインクリメントされるかもしれません。しかしこの副作用はクライアントが要求したわけではないのでクライアントに責任はありません。また、クライアントがその副作用をあてにしてリクエストを送信してはいけませんし、サーバーもクライアントが想定していないほど甚大な副作用を起こしてはいけません。

1つのリクエストを実行した結果が、同じリクエストを任意の回数実行した結果と同じになる場合、そのメソッドは冪等(idempotent)であるといいます。冪等なメソッドは実行することでサーバーのリソース状態を変化させる可能性がありますが、何度実行しても同じ結果が返ります。安全なメソッドは必ず冪等なメソッドになります。

よくあるケースとしては、現在の状態からの相対的な変更(追記など)を要求するリクエストは結果が冪等でなくなってしまうため、冪等なメソッドにおいては目的とする絶対的な状態を指定しなければなりません。たとえば「リソースを削除された状態にする」というリクエストは何度送っても、そのリソースがもともと存在したかどうかに関わらずリソースが存在しない状態を保てばよいので冪等になります。

以下に各メソッドごとの安全性(safety)と冪等性(idempotence)についての表を記しておきます。

メソッド 安全 冪等
GET yes yes
HEAD yes yes
OPTIONS yes yes
PUT no yes
DELETE no yes
POST no no

ROA では基本的にここに書かれたメソッドのみを使いますが、HTTP のメソッドは拡張可能であり、その他にもたくさんのメソッドがあります。その他のメソッドの安全性と冪等性については『Hypertext Transfer Protocol (HTTP) Method Registry』をご覧ください[32]

GET, PUT, DELETE

リソースに対する一般的な操作はこの3つのメソッドで表現できます。

  • GET: 読み取り
  • PUT: 作成 or 上書き
  • DELETE: 削除

HEAD と OPTIONS

GET と同系列のユーティリティで、リソースに関する情報の一部を取得します。

  • HEAD: リソースのメタデータを取得する
  • OPTIONS: リソースで許可されている操作を取得する

OPTIONS メソッドのレスポンスには Allow: GET, HEAD のように Allow ヘッダに許可される操作が列挙されます。クライアントが認証情報をリクエストに付加した場合、OPTIONS メソッドのリクエストの内容が変化する可能性があります(所有者だけが変更できるリソースなど)。

POST

RFC2616『Hypertext Transfer Protocol -- HTTP/1.1』では、POST について以下のように述べられています[33]

POST は以下の機能をカバーするための統一メソッドとして設計されています。

  • 既存のリソースの注釈
  • 掲示板、ニュースグループ、メーリングリスト、または同様の記事グループへのメッセージの投稿
  • フォーム送信の結果などの、データ処理プロセスへのデータブロックの提供
  • アペンド処理を通じたデータベースの拡張

POST メソッドによって実行される実際の機能はサーバーによって決定され、通常は Request-URI に依存します。

REST と ROA のコンテキストにおいては、さらに REST の制約に適合した POST(a) と、REST の制約を超えて RPC スタイルの要素を取り入れる POST(p) に分かれます。

POST(a)

POST(a) の a は append の頭文字を表しています。

POST(a) は従属リソースの作成に用いられます。従属リソースとはほかの親リソースに関連して存在するリソースであり、ブログを親リソースとしたときそのブログへのコメントなどが従属リソースに該当します。

他の使い分けとして、新しいリソースの URI をクライアントが指定する場合には PUT を、サーバーが自動的に決定する場合には POST を用いるという場合もあります。

POST(a) は必ずしも新しいリソースを作成するとは限らず、既存のリソースを上書きすることもあります。PUT は冪等であることが要求されるためログの追記のような処理には適しませんが、POST(a) はログへの追記のような相対的な変更を行う冪等でない処理も許可します。

POST(p)

POST(p) の p は process の頭文字を表しています。

POST(p) はフォームの送信結果などのデータブロックをサーバーのデータ処理プロセスに提供することでサーバーの機能を呼び出します。これは RPC(Remote Procedure Call) の機能に近く、POST メソッドという意味の範疇を超えてサーバーの特定のメソッドを実行するので ROA ではオーバーロード POST と呼ばれます。

実際のサービス設計においては一部のリソースについて RPC スタイルのほうが適しているという場合において、RPC スタイルの別の窓口を用意することなく統一インターフェースにおける POST メソッドとして提供することができます[34]

ROA に関する説明はこれで終わりです。

Richardson の成熟度モデル

最後に Richardson の成熟度モデル(Richardson Maturity Model)についても説明しておきます。

https://martinfowler.com/articles/richardsonMaturityModel.html

あまり詳しくは書きませんが、ここまでの説明を理解していれば十分理解できると思います。リンク先は短い文章なのでより詳細な説明や具体例が気になる人はそちらを読んでください。

Level 0: The Swamp of POX

通信プロトコルとして HTTP を利用してはいますが、基本的には RPC に基づく独自のリモートインタラクションメカニズムを構築しており、Web のメカニズムは利用していません。

サービスのエンドポイントとして単一(または少数)の URI を公開し、XML, JSON, YAML などリクエストの詳細を含むドキュメントで動作を指定します。また、サービスの機能を呼び出すための HTTP メソッドとして POST メソッドを多用しているでしょう。

※ POX はやり取りに使われるプレーンで古いXML(plain old XML)のことを指します。

Level 1: Resources

サービスにリソースの概念を導入します。これによりすべてのリクエストを単一のエンドポイントに送るのではなく、個々のリソースに対してリクエストを行うようになります。

Level 2: HTTP Verbs

ここまではすべてのインタラクションに HTTP POST メソッドを用いているかもしれませんが、Level2 においては GET や PUT などの各メソッド(HTTP 動詞)を、HTTP 自体でどのように使用されているかにできるだけ近づけて使用します。

安全や冪等などのルールも HTTP のルールに従うほか、レスポンスのステータスコードなども HTTP のルールに則ります。

Level 3: Hypermedia Controls

これは ROA で説明した「接続性」の説明に近いです。クライアントからのリクエストに対して、サーバーはクライアントが次に可能なアクションの URI をレスポンスに含めて返します。このようにアプリケーション状態を制御するためのエンジンとしてハイパーメディアを用いることを HATEOAS(Hypertext As The Engine Of Application State) ということがあるそうです[35]

まとめ

この成熟度モデルにおいては REST の観点から評価したときのサービスの成熟度を以下の3段階のレベルで評価します。

  • Level 1 は分割統治法の考えに基づき、巨大なサービスのエンドポイントを複数のリソースに分割して複雑さを低減します。
  • Level 2 では標準的な HTTP の動詞セットを導入し、同じような状況は同じような方法で取り扱えるようにすることで不必要なバリエーションを排除します。
  • Level 3 ではプロトコルをより自己記述的にすることで探索性を向上させます。

HTTP Semantics

嬉しいことに 2022 年 6 月に、REST を構成する多くの概念や用語が RFC 9110 の『HTTP Semantics』に標準化されました。

RFC はきちんとした文書であり無駄なことは一切書かれていない[36]ため、原文をそのまま読んでいただくか、すべて翻訳されたものを読むべきであって、記事にまとめることは断念しました。

Zalando RESTful API と イベントスキーマのガイドライン

Zalando RESTful API と イベントスキーマのガイドライン』も、やるべきこととやってはいけないことが MUST, SHOULD, MAY で項目として整理されているので、非常に役に立つガイドラインです。

当初の予定ではここからも本記事に関係のある部分をピックアップする予定でしたが、ここまで記事をスクロールするなら直接リンク先を見に行ったほうが早く、いちいち本記事を読み返す人はいないだろうと思ったのでまとめないことにしました。

HTTP Semantics』もそうですが、ここまで読み切った人は REST についてのイメージが大分固まっていると思うので、参考文献のほうを直接読むことができると思います[37]

あとがき

ずいぶんと長くなってしまいましたが、ここまでお付き合いいただきありがとうございました。

私は別に Web サービスの専門家ではない[38]ので、何か至らない部分がございましたら遠慮なくご指摘ください。

ご指摘いただけること自体は私の知見が広がるきっかけになるので大変ありがたいです。また、好意的な感想をいただけることも大変嬉しく執筆活動の励みになります。いつもありがとうございます。

脚注
  1. プログラミングや通信の分野では RFC によって規格が標準化されたりしますが、REST についてはこのような標準化はされていないということです。 ↩︎

  2. 信用に足る情報の在処さえ分かってしまえば、私がその劣化版の記事を作る必要性はありません。その在処はいまみなさんにも教えたので、もう大丈夫ですよね? ↩︎

  3. HTTP の仕様策定の際に複数の専門家の間で洗練・共有され取り入れられているとはいえ、この文書自体はあくまで個人の博士論文であって公式に整備された規格ではありません。また、論文が公開されてから 20 年以上が経過していることからも、多数の開発者によって洗練されてきた現在のガイドラインのほうが開発の実態に即している部分もあるでしょう。 ↩︎

  4. たとえば現在のパソコンで使われる CPU の多くは x86-64 という設計(アーキテクチャ)に基づいていて、Intel や AMD はこのアーキテクチャに基づく実装としてのチップセットを生産しています。CPU アーキテクチャの上位概念に当たる設計思想は、設計思想という用語が使われてはいませんがフォンノイマン型アーキテクチャが対応するでしょう。 ↩︎

  5. REST の設計思想に限って言えば、設計思想としてインターフェースの互換性を要求するので、異なる設計であっても通信には互換性があるべきです。 ↩︎

  6. 〜ful」という接尾辞には「〜な性質を持つ」という形容詞を作る働きがあります。たとえば「beautiful」は beauty な性質を持っている状態を指します。 ↩︎

  7. 明示されるとは限りませんが、クライアントとサーバーを繋ぐコネクターコンポーネントも暗黙的に存在しています。 ↩︎

  8. 現在では WebSocket という技術が規格化されていて、クライアントとサーバーが双方向に通信しあう、すなわちサーバーから能動的にクライアントへ働きかける可能性があるアプリケーションも存在します。たとえば SNS におけるリアルタイムの通知など即応性が重要なアプリケーションで使用されます。 ↩︎

  9. 普通に CCSS と書けばいいと思うのですが、cache にわざわざ $ の記号が割り当てられているのは現金(cash)と発音が同じことからの茶目っ気でしょうか? ↩︎

  10. ソフトウェアを設計は不自然な制約や制限から解放されたものでなければならないという原則です。たとえばシステムが時刻について年を下2桁で記録する設計にすると、1900 年代には正常に稼働していたシステムでも 2000 年になった瞬間に破綻し始めます。これは当時 2000 年問題と呼ばれました。 ↩︎

  11. 「uni(ひとつの)-form(形)」という言葉の構成から分かる通り、やり方が一種類しかないことを指します。既に HTTP が RESTful なインターフェイスとして存在する以上、他のやり方をしたらインターフェイスが uniform ではなくなるので、実装に依存しないとか言っておきながら事実上は HTTP しか認めないことになると思います。ちなみに AWS の説明では「統一されたインターフェース」と書かれているので見出しは合わせましたが、「統一された」だったら「uniformed」のはずなので本文では「一貫した」という訳語に調整しています。 ↩︎

  12. 実際に動画配信のようなネットワーク帯域を圧迫するサービスには HTTP は適しておらず、動画の転送には別のプロトコルを使うかもしれません。 ↩︎

  13. HTTP 自体はただの通信プロトコルであり、RESTful でなければならないという制約はないので、REST として不適切な API を設計すれば HTTP で RESTful でないサービスを作ることももちろんできます。 ↩︎

  14. app(アプリケーションの)-let(小さなまとまり、カケラ、カプセル、モジュール)という意味です。booklet が「冊子」の意味になるのも納得ですね。飛行機の翼の先端の小さく折れ曲がった部分も winglet と言い、語源は同じです。 ↩︎

  15. Java Applet は Java 11 で廃止されました。 ↩︎

  16. コレクションとはリスト、辞書、集合など、もととなるものを集めて作られる高次のデータ構造のことです。 ↩︎

  17. "entity" は日本語における「存在」や「概念」も包含し、実在か架空かも問わない非常に広い範囲の対象を指す単語です。文脈によってどれくらいの範囲を指すかは異なるのですが、この文脈では「明日の天気」のような抽象的な概念すら許されるので「存在、概念」と翻訳しました。 ↩︎

  18. ひとつのリソースに対してリソース表現とリソース識別子の両方を返せという意味か、対応づけられた集合の中にリソース表現とリソース識別子がごちゃ混ぜになっていてもよいという意味かはわかりません。 ↩︎

  19. RESTful なサービスについて解説している書籍がそもそも少ないので貴重な書籍です。重要なこともたくさん書かれているのですが、用語を明確に定義せずに話を進めるせいで分かりにくい部分も多々あります……本記事が執筆された動機でもあります。 ↩︎

  20. RESTful Webサービス』では「りんごのような物体」や「勇気のような抽象概念」をリソースにすると「必ず不本意な結果に終わる」と説明されていますが、ビットの操作だって物理記録媒体への干渉である以上はりんごに対して物理的に干渉する手段(ロボットアームとか)が提供されているならばりんごをリソースにすることもできるでしょう。抽象概念のほうについては同意します。 ↩︎

  21. 「定義上、2つのリソースが同じであることはあり得ない」と述べられていますが、どの定義からどう導かれた性質なのか分かりません。同書に書かれたリソースの説明のみからこの性質を論理的に導くことはできません。 ↩︎

  22. 同書では「URI はリソースの名前とアドレスである」と述べられていますが、抽象的すぎるのでこの説明は RFC3986 から引用しています。 ↩︎

  23. 「これらは REST の絶対的なルールではないが、筆者はそうあるべきだと思っている」という旨の記述があります。 ↩︎

  24. 「記述的」という言葉が何を意味するのか明確には書かれていません。 ↩︎

  25. たとえば URI 自体にリソースをどう処理するか表すメソッド名を含めたり、URI に含まれるクエリをリソースの処理に用いてはならない、ということだと思われます。 ↩︎

  26. セッションの状態をサーバー側で保持してしまうと大抵の場合は「戻る」ボタンが機能しなくなります。 ↩︎

  27. どう解決するかは書かれていません。 ↩︎

  28. 正規 URI を返す。 ↩︎

  29. 遷移したい状態への URI をクライアントが自ら構築しなければならない場合、サービスはアドレス可能性を満たしてはいますが接続性は満たしていないということになります。 ↩︎

  30. 「統一インターフェイス」とは何を指すのかについては明確には説明されていません。この脚注何回書くんでしょう? ↩︎

  31. プログラムにおける副作用とは、ユーザーが意図していない操作や状態の変更のことです。たとえば関数の中に実行状況をログとして出力するような操作を仕込むと、それは副作用になります。副作用があるとプログラムはユーザーにとって不可解な挙動を示すことがあり一般にデバッグが困難になります。たとえばユーザーが完全に正しい引数を指定して関数を呼び出したときに、ログを出力するという操作においてエラーが発生すると、ユーザーにとってはなぜエラーが起こったのか知る術はなく、仮に原因が分かっても対処しようがありません。副作用はバグが生じるポイントになるため、Haskell などの純粋関数型プログラミング言語では副作用の存在を明確に型レベルで分離します。 ↩︎

  32. リンク先は『HTTP Semantics』の『16.1.1. Method Registry』に記載されている、IANA によって管理されている HTTP メソッドの一覧です。 ↩︎

  33. 私が急に参照先を変えたのではなく、『RESTful Webサービス』の説明の中で RFC2616 が参照されています。 ↩︎

  34. サービスの全体を RPC として提供したほうがよい場合は無理に RESTful な設計にする必要はないでしょう。 ↩︎

  35. 不格好な頭字語(ugly acronym)だと言われてしまっています。また、元論文にしたがうのであれば H は Hypertext ではなく Hypermedia が正しいでしょう。 ↩︎

  36. エイプリルフールには毎年のように悪ふざけしているみたいですが。 ↩︎

  37. 英語が苦手でも ChatGPT に「日本語に翻訳して」と言えば翻訳してくれます。 ↩︎

  38. 必要に応じてインフラ構築したりすることはあるけど基本はデータサイエンティストです。Web API 越しに機能提供するにはどうしようかと思ってそういえば REST のこと厳密には知らないなと思って調べました。 ↩︎

Discussion

Toru KawamuraToru Kawamura

RESTを調べるのに原典であるFielding論文を読んでいる人はなかなかいないので、すばらしいと思います!
書籍「RESTful Webサービス」についてですが、著者のひとりであるLeonard RichardsonがMike Amundsenとともに2013年に出版した「RESTful Web APIs」が、正統な後継といえる書籍です。

http://restfulwebapis.com/rws.html

RESTful Web Services is a 2007 book by Leonard Richardson and Sam Ruby. It was the first book-length treatment of RESTful design, and the predecessor to RESTful Web APIs. RESTful Web APIs was designed as a complete replacement for RESTful Web Services, and in our opinion there's no longer any need to buy RESTful Web Services.

【日本語訳】RESTful Web Servicesは、Leonard RichardsonとSam Rubyによる2007年の著書です。RESTfulな設計を扱った初の書籍であり、RESTful Web APIの前身でもあります。RESTful Web APIはRESTful Web Servicesを完全に置き換えるものとして設計されており、RESTful Web Servicesを購入する必要はもはやないと私たちは考えています。

ということで、より現代的なRESTに関する説明や設計の指針は「RESTful Web APIs」を読むのがおすすめです(しかし残念ながら、この本は日本語訳されていません)。前書との違いは、リソース指向から脱却し、最も重要な概念であるハイパーメディアにフォーカスしたところです。さらに付録C「An API Designer's Guide to the Fielding Dissertation」は、Fielding論文の直接の解説であり、「RESTとは何か」を理解するにはうってつけです。

さらに、この内容を踏まえた上での実践的なレシピ集としては同著者(Mike Amundsen)による「RESTful Web API Patterns and Practices Cookbook」があります(日本語訳なし)。
http://www.webapicookbook.com/

Josh NobusJosh Nobus

情報ありがとうございます!ひとりで調べているとやはり辿り着ける情報に限りがあると思っていたので、新しいマイルストーンの情報は本当に助かります!拝読いたします!

Josh NobusJosh Nobus

@ttskch さん、@beijaflor さん

バッジありがとうございます!とても励みになります!