BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル RESTアンチパターン

RESTアンチパターン

人々がRESTに挑戦しようとすると、通常、実例を探し始めます。そして、「RESTful」であると主張している多くの実例を探したり、「REST API」と名づけたりするだけでなく、RESTを行っていると主張する特定のサービスが何故失敗するのかに関する多くの議論を集めています。

何故、このようなことが起こるのでしょうか? HTTPは新しいものではありません。しかし、それは様々な方法で適用されています。それらのいくつかはWebデザイナが考えていたことと一致していますが、その多くは異なります。人が利用する、他のプログラムによって利用される、あるいはその両方の目的でそれらを構築するかどうかによらず、あなたのHTTPアプリケーションにRESTの原則を適用するということは、あなたがまさにあべこべのことをするということを意味します。つまり、あなたは「正しく」Webを利用しようとしているのです。あなたがその考えに異議を唱えるのなら、RESTfulな方法において、それはある意味「正しい」ですし、ある意味「誤り」です。多くの人にとって、これは確かにとても新しいアプローチなのです。

通常の標準的な否定が当てはまります。それは、REST、Web、HTTPは同じものではないということです。RESTは、多くの異なる技術で実装することができる一方、HTTPは、RESTアーキテクチャスタイルに従った唯一の具体的なアーキテクチャです。したがって、実際には「RESTful HTTP」を「REST」と区別することに注意を払うべきです。しかし、私はそうしていないので、本稿の残りではその2つが同じものであると仮定することにしましょう。

全ての新しいアプローチで見られるように、それはいくつかの共通のパターンに気づくのに役にたつものです。このシリーズの最初(参考記事)と2つ目の記事(参考記事)で、私は、リソースの収集、正しいリソースとなるような予測結果のマッピング、模範となるイベントへのシンジケーションの活用といった、いくつかの基本的事項についての概説を試みました。そして、将来の記事では、これらと他のパターンに関してさらに詳しく説明しようと考えています。とは言うものの、今回、私はアンチパターンについてフォーカスしたいのです。それらは、RESTの考えを身につけるために問題の発生した、あるいは、しようと努力したが失敗した RESTfulなHTTP活用への試みについての典型的な例についてです。

では、私が考え出したアンチパターンの簡潔なリストから始めましょう。:

  1. 全てをGETでつなぐ
  2. 全てをPOSTでつなぐ
  3. キャッシュを無視する
  4. レスポンスコードを無視する
  5. cookieを乱用する
  6. ハイパーメディアを忘れる
  7. MIMEタイプを無視する
  8. 自己記述性を壊す

それぞれについて詳しく見ることにしましょう。

全てをGETでつなぐ

多くの人々にとって、RESTは単純にあるアプリケーションの機能を公開するためにHTTPを使用することを意味します。基本的で最も重要なオペレーション (厳密に言えば、「動詞」や「メソッド」がより良い表現です)は、HTTPのGETです。GETはURIによって特定されるリソース表現が必要です。しかし、多くの場合、それがすべてではないとしても、既存のHTTPライブラリやサーバープログラミングAPIは、リソースの識別子としてではなくパラメータをエンコードするための便利な手段として見ることがとても多いです。結果、以下のようなURLとなります。:

http://example.com/some-api?method=deleteCustomer&id=1234

実際、URLを作る人は、与えられたシステムの「RESTful具合」について何も言いません。しかし、私たちは特定の場合においてGETが「安全」ではないと推測できます。おそらく、呼び出し側は結果(顧客の削除)に対して責任があるでしょう。しかし、仕様上GETをそういったケースで利用するのは誤りです。

このアプローチに賛成するメリットは、プログラムが簡単で、ブラウザからのテストが取るに足らないという点です。つまり、アドレスバーにURIを貼り付け、いくつかのパラメータに手を加えるだけで、進むことができます。このアンチパターンに関する主要な問題は、以下の通りです。:

  1. リソースがURIによって特定されない。どちらかと言えば、URIがオペレーションとそのパラメータをコード化するのに用いられる。
  2. HTTPメソッドが、その意味に合致していない。
  3. そのようなリンクは、通常、ブックマークされることを目的としない。
  4. 「クローラー」(例えば、Googleのような検索エンジン)が予期しない副作用を引き起こすリスクがある。

このアンチパターンに従ったAPIが、実際に誤ったRestful(リンク)となっているかもしれないということに注意してください。以下の例を見てください。:

http://example.com/some-api?method=findCustomer&id=1234

これは、オペレーションとそのパラメータを特定するURIですか? あるいは、リソースを特定するURIですか? あなたは、その両方のケースについて議論することができます。このURIは、完全に正しいものでブックマーク可能です。その上でGETを行うのは「安全」であるかもしれません。それは、Acceptヘッダーにより異なるフォーマットに応答するかもしれません。そして、それは高度なキャッシュをサポートしています。多くの場合、これは意図的なものではありません。しばしば、APIは「読み込み」インターフェースを公開する方法で始めます。しかし、開発者が「書き込み」機能を追加し始めたとき、突如思い違いが発生します。(顧客の更新が、このURIにPUTを通して発生することはないでしょう。開発者は、おそらく新しいものを作るでしょう。)

全てをPOSTでつなぐ

このアンチパターンは最初のものと非常に似ていますが、今度は、HTTPメソッドのPOSTが使われます。POSTは、エンティティボディを転送します(単にURIではありません)。典型的なシナリオでは、異なる目的を表現するためにPOSTに対する単一のURIや異なるメッセージを使用します。これは、実際、HTTPが「トランスポートプロトコル」として使用される際に、SOAP 1.1のWebサービスがしていることです。それは、実際にSOAPメッセージで、おそらく、WS-AddressingのSOAPヘッダを含んでいて、それは発生するものを決定します。

POST を通してすべてをつなぐことは、GETの問題の全てを分かち合うと主張する人もいるでしょう。それは、単に利用するためのちょっとしたヘッダーで、(確実に)キャッシングを探すこともできません。さらに、ブックマークもサポートしません。実際、それはRESTの原則の全てに違反しているわけではないですが、単純にそれらを無視しています。

キャッシュを無視する

あなたが、利用を目的として動詞を使用するとしても、キャッシングの機会を簡単に壊すことができます。そうする最も簡単な方法は、単純に、HTTPレスポンスに以下のようなヘッダを含めることです。:

Cache-control: no-cache

こうすることで、全てのキャッシュを簡単に破棄することができます。もちろん、これはあなたが意図しておこなったことかもしれません。しかし、多くの場合、それはWebフレームワークで指定されるデフォルト設定です。しかし、効果的なキャッシングと再バリデーションをサポートすることは、RESTful HTTPを利用する上での重要なメリットの一つです。Sam Ruby氏は、RESTfulさを見極めるときに尋ねる重要な質問は、「ETagをサポートしていますか?」(参考記事・英語)であると言っています。(ETagは、暗号法のチェックサムを用いてキャッシュされた表現が現在も妥当であるかをクライアントが確認することができるようにするためのもので、HTTP 1.1で導入されたメカニズムです。)正しいヘッダを作成するための最も簡単な方法は、正しいやり方を「知っている」基盤に、その一部として作業を委譲することです。たとえば、Apache HTTPDのようなWebサーバーによって与えられたディレクトリにファイルを作成することなどがあります。

もちろん、これについてはクライアント側にもあります。あなたがRESTfulサービスのためのクライアントを実装するときに、実際には、利用可能なキャッシュ機能を有効に活用するべきで、そのときは再度表現を取得する必要はありません。たとえば、サーバーは、最初の検索後600秒の間、表現が「新しい」ものであるという情報を送信したかもしれません(例.バックエンドシステムが30分おきにポールされるため)。より短い時間で同じ情報を繰り返しリクエストすることは、全く意味がありません。サーバーサイドでも同様で、クライアントサイドに対するSquidのようなプロキシキャッシュを利用することは、ロジックを自身で構築するよりもより良い選択肢であると思います。

HTTPのキャッシングは、強力ですが複雑です。とても良いガイドとして、Mark Nottingham氏のキャッシュチュートリアル(リンク)を参照してください。

ステータスコードを無視する

多くのWeb開発者に知られていないことですが、HTTPは様々なケースに対処するための非常に豊富なアプリケーションレベルのステータスコード(リンク)セットを持っています。多くの人は、200 (“OK”)、404 (“Not found”)、500 (“Internal server error”)は知っているでしょう。しかし、もっと多くのコードが存在し、それを正しく利用することでクライアントとサーバーが意味的にもよりリッチなレベルで通信することができます。

たとえば、201 (“Created”)のレスポンスは、新しいリソースが作成されたことを示すコードで、そのURIに対するレスポンスのLocation ヘッダに書かれています。409 (“Conflict”)は、たとえば、PUTがより古いバージョンのリソースによるデータを使用されたときに、矛盾がおきたことをクライアントに知らせます。412 (“Precondition Failed”)は、サーバーがクライアントの期待通りに処理することができなかったことを示しています。

ステータスコードを正しく使用することのもう一つの側面は、クライアントへの作用です。異なる種類のステータスコード(たとえば、2xxの範囲全て、5xx の範囲全て)は、共通の包括的なアプローチによって扱われることになっています。たとえば、クライアントは返された特定のコードを扱うためのコード化がなされていなかったとしても、2xxのコード全てを成功のしるしとして処理しなければなりません。

RESTful であると主張する多くのアプリケーションは、200か500のいずれかのみ、あるいは200のみを返します(レスポンスのbody部に失敗の文を含めた状態です。これもSOAPを参照してください)。あなたが望むなら、この「ステータスコード200によるトンネリングのエラー」を呼ぶことができます。あなたが正しい表現であると考えるのであれば何でもよいのです。あなたがリッチアプリケーションでHTTPのステータスコードの意味を十分に活用しないのなら、あなたは、より強化した再利用、より良いインターオペラビリティ、そして疎結合の機会を失っています。

cookieを乱用する

サーバーサイドのあるセッションステートのキーを渡すためにcookieを使用することは、もうひとつのRESTアンチパターンです。

cookie がRESTfulでない確かなしるしであるというのは正しいでしょうか? いいえ、そうとは限りません。RESTの重要な考えの一つにステートレスということがありますが、それは、サーバーがデータを何も格納しないという意味ではありません。リソースステートやクライアントステートであれば、問題ないのです。それは、スケーラビリティ、信頼性、結合度などの理由から許されていないセッションステートのことです。最も典型的なcookieの利用は、メモリ上に保持しているサーバーサイドのあるデータ構造とリンクするためのキーを格納することです。これは、リクエスト毎にブラウザが渡すcookieが、対話的に、セッション、状態を確立するために利用されていることを意味しています。

cookie が、認証トークンといった何らかの情報を格納するために利用されているならば、サーバーはセッション状態への依存なしに正当性を確認することができるので、cookieは申し分ないRESTfulです。しかし、一つ注意点があります。それは、他のより標準化された手段(たとえば、URI、標準的なヘッダ、あるいは稀なケースでメッセージボディ部)で転送された情報をエンコードするのに利用するべきではありません。たとえば、それはRESTfulな HTTPの観点からHTTP認証を使うことが好ましいです。

ハイパーメディアを忘れる

受け入れるのが難しい最初のRESTの考えは、標準的な方式のセットです。RESTのセオリーでは標準セットを作る手段を特定していません。単純に、全てのリソースに適用可能な限られたものが必要です。HTTPは、(主に、少なくとも)GET、PUT、POSTと DELETEでそれらを解決します。そして、あなたのアプリケーションの意味全てをこれら4つの動詞で割り当てるのには、少し慣れが必要です。しかし、一度あなたがそうすれば、人々は実際にRESTを構成している一部を使い始めます。たとえば、WebベースのDRUD(登録、参照、更新、削除)アーキテクチャのいくつかです。このアンチパターンを公開したアプリケーションは、完全に「unRESTful」というわけではありません(そのようなものさえあるならば)。それらは単に、もうひとつのRESTの核となるコンセプト(アプリケーションの状態エンジンとしてのハイパーメディア)を十分に活かすことに失敗しただけです。

ハイパーメディア(ものをリンクする概念)は、WebをWebたらしめるものです。結合したリソースセット、それらはアプリケーションがある状態から次の状態へリンクによって移動します。それは少し難解に聞こえるかもしれませんが、実際に、この原則に従うためのいくつかの妥当な理由があります。

はじめに示した「ハイパーメディアを忘れる」アンチパターンとは、表現の中にリンクがないことです。クライアントサイドでのURI構成の作り方はたくさんありますが、サーバーが単純に何も送らないので、クライアントがリンクを追うことはありません。わずかなより良い変更によって、URI構成とリンク指示の混成を利用し、基礎をなすデータモデルに関連する典型的な表現をリンクします。しかし、理想的には、クライアントは一つのURIだけを知っていればよいのです。それ以外のもの、それらを構成する方法だけでなく個々のURIは不要です。たとえば、クエリーの場合、リソース表現と関連するものとしてハイパーメディアを通して通信します。良い例として、サービスドキュメント(記載するドメイン内のそれぞれのコレクションに対して指定された要素を与えるもの)の考えによるAtomのパブリッシングプロトコルがあります。最終的には、アプリケーションで行うことが可能な状態変化は動的に通信されるべきで、クライアントはできるだけ少ない知識でそれをたどることができるようにすべきです。これの良い例は、HTMLです。それは、ユーザーに完全に動的なインターフェースを提供するためにブラウザに対して十分な情報を含んでいます。

私は、もう一つのアンチパターンとして「人間が読めるURI」を加えることを考えました。私がそうしなかったのは、私が誰にも負けないくらい読むことができ、「ハックできる」URIが好きだからです。しかし、ある人がRESTを始めるとき、「正しい」URI設計についての議論で終わりない時間を浪費することがよくあります。しかし、完全にハイパーメディアの側面を忘れています。したがって、私のアドバイスは、完全なURI設計(結局、それは文字列です)を見出すのに費やす時間に制限をかけ、表現の中でリンクを提供するための良い場所を検討することにいくらかのエネルギーを注ぐことだと思います。

MIMEタイプを無視する

HTTP のコンテンツネゴシエーションの考えは、クライアントがその必要性に応じて異なるリソース表現を得ることを可能にします。たとえば、Java、 JavaScript、Rubyそれぞれで実装されたコンシューマによるコンサンプションのために、リソースがXML、JSON、YAMLといった異なるフォーマットの表現を持っています。あるいは、人間のためのPDFやJPEG版に加えXMLといった「機械が読むことのできる」フォーマットがあるかもしれません。あるいは、あるカスタム表現形式のv1.1とv1.2の両方をサポートしているかもしれません。いずれの場合でも、一つの表現フォーマットのみを持つための十分な理由が存在する間は、多くの場合、それは別の機会を失う兆候です。

より多くの予期しないクライアントがサービスを(再)利用するのは、ほぼ明らかなことです。この理由から、独自のものを作るよりも、既存の、予め定義されていて、広く知られているフォーマットに頼る方が、はるかに良いです。これは、本稿で述べられる最後のアンチパターンにつながる議論です。

自己記述性を壊す

このアンチパターンはとても一般的で、ほぼすべてのRESTアプリケーションに見られるものです。それは、私も含めて自身を「RESTafarians」と呼ぶ人たちによって作られるものでも、そうであることがあります。自己記述性の制約を壊す(それは、一見して考えるかもしれないものですが、AIのサイエンスフィクションではない考えです)理想的には、メッセージ(ヘッダーとボディを含んだHTTPリクエストやHTTPレスポンス)は、それを処理することができるようにするために、すべての一般的なクライアント、サーバー、その仲介者に対して十分な情報を持つ必要があります。たとえば、ブラウザがある保護されたPDF表現を取得したとき、あなたは標準に関する既存の取り決めのすべてがどのように作動しているのかを見ることができます。あるHTTP認証交換がおこなわれ、何らかのキャッシングや再バリデーションがあるかもしれません。そして、コンテンツタイプヘッダーはサーバーの(“application/pdf”)(リンク) トリガーによって送信され、あなたのシステムで登録されているPDFビューアを起動します。そして、最終的には、あなたの画面でPDFが閲覧できるようになります。世界のすべてのユーザーが、同じリクエストを実行するために自身の基盤を利用することができるのです。サーバー開発者が別のコンテンツタイプを追加したときは、サーバーに対するクライアント(あるいはサービスのコンシューマ)のすべてが、単純に適切なビューアがインストールされているかを確認するだけです。

あなたが自身のヘッダー、フォーマット、プロトコルを作るたびに、あなたは、ある程度の自己記述性を壊しています。あなたが極端な立場をとりたいのなら、公式な標準ボディで統一されていないものすべてがこの制約を壊すので、このアンチパターンのケースであると考えることができます。実際には、あなたは可能な限り標準に従う努力をし、いくつかの規定がより小さいドメイン内(たとえば、それに対して特別に開発したサービスとクライアント)に適用できるだろうということを受け入れます。

まとめ

「Gang of Four」が彼らの彼らの本(リンク)(それは、パターンムーブメントの始まりでした)を出版して以来、多くの人がそれを誤解し、できるだけ多くのパターンを適用しようとしました。それは、等しく長い間嘲笑された概念です。パターンは、適合した場合や特定の場合に状況にマッチします。同様に、ある人が与えられたドメインすべてにおいて、アンチパターンのすべてをきちんと避けようとすることができます。多くの場合、全てのルールに違反するための十分な理由が存在します。全ての制約を緩和してください。そうすることは良いのですが、真実に気づくことは有益であり、そこでより詳細な情報を得た上での判断をとってください。

願わくば、本稿で、あなたが最初にRESTプロジェクトを始めるときに、最も一般的な罠のいくつかを避ける手助けとなれれば幸いです。

本稿のドラフトにフィードバックをくれた、Javier Botana氏とBurkhard Neppert氏に感謝します。

Stefan Tilkov(リンク)は、InfoQのSOAコミュニティと共同創設者(Germany/Switzerland-based innoQ(リンク)の主要なコンサルタントとトップRESTafarian)のリードするエディタです。

 

原文はこちらです:http://www.infoq.com/articles/rest-anti-patterns
(このArticleは2008年7月2日に原文が掲載されました)

この記事に星をつける

おすすめ度
スタイル

BT