次の方法で共有


サービス ステーション

WCF を使用した RESTful サービスの概要

Jon Flanders

コードは MSDN コード ギャラリーからダウンロードできます。
オンラインでのコードの参照

目次

REST について
抽象的な例
REST の使用を検討する理由
WCF と REST
WebGetAttribute と WebInvokeAttribute
UriTemplate と UriTemplateTable
WebHttpBinding と WebHttpBehavior
WebServiceHost と WebServiceHostFactory
サンプル コードを使用する

今回は、REST (Representational State Transfer) と呼ばれるアーキテクチャ スタイルを使用した WCF (Windows Communication Foundation) の構築について説明するコラム シリーズの第 1 回です。2009 年はこの「サービス ステーション」コラムで説明する REST の年と言えるようになるかもしれません。その機は十分に熟しています。REST は、2000 年の発売以来、長年高い評価を受けているスタイルです。

この第 1 回のコラムでは、REST の基本概念および WCF を使用した RESTful サービスの実装について説明します。次回以降のコラムでは、REST の基本概念およびその基本テクノロジについてさらに詳細に説明します。

table.one { table-layout:fixed;width: 750px; } .tdwidth1{ width:150px; } .tdwidth2{ width:150px; } tr.heading td { padding : 10px; background-color:white; color: black; vertical-align:top; } tr.fig td { background-color:black; color: white; padding: 10px; font-weight:bold; vertical-align:top; } tr.dark td { background-color: #b9d1dc; color: black; padding: 10px; vertical-align:top; } tr.lite td { background-color: #dde8ee; color: black; padding: 10px; vertical-align:top; } tr.small td { background-color: #dde8ee; color: black; padding: 5px 10px 5px 10px; vertical-align:top; font-size: 10px; }

REST について

アーキテクチャ スタイルは、何かを構築する際に適用可能な制約の集まりです。ソフトウェアのアーキテクチャ スタイルは、ソフトウェア システムの実装のガイドとして使用可能な機能を示します。REST は、ソフトウェア構築のためのアーキテクチャ スタイルです。REST では、クライアント (ユーザー エージェント) からサービス (エンドポイント) を要求できます。REST はクライアント/サーバー アーキテクチャを実装する 1 つの方法であり、クライアント/サーバー型で構築することを明示的に規定しています。

REST という用語は、Roy Thomas Fielding による博士論文 ("Architectural Styles and the Design of Network-based Software Architectures (ネットワークベース ソフトウェア アーキテクチャのアーキテクチャ スタイルと設計)") で初めて提起された概念です。同氏は、現在のインターネットで最も頻繁に利用されるハイパーテキスト転送プロトコル (HTTP) の仕様作成にも参加していました。通常、アーキテクチャ スタイルについて説明する場合、そのスタイルを規定する開発者の背景知識は問題にしませんが、ここでは背景知識が重要です。というのは、REST の基本を理解する最善の方法は Web の概念としくみを検討することだからです。

ここでは、開発者は Web に精通し、日頃から中毒ともいえるような Web のヘビー ユーザー (おそらく筆者のように) であることを前提にする必要があります。おそらく、Web は過去最大の、最もスケーラブルで広く利用されている分散アプリケーションと考えられます。REST の制約は、Web の制御と同じ基本原則に基づいています。その原則を次に示します。

  • ユーザー エージェントがリソースを操作します。名前を指定して表すことができるあらゆる情報がリソースになり得ます。各リソースは一意の URI (Uniform Resource Identifier) によって指定できます。
  • 一意の URI によって検出されたリソースの操作には、HTTP の標準動詞 (GET、POST、PUT、DELETE) の統一インターフェイスを使用します。HTTP Content-Type ヘッダーで指定するリソースのメディアの種類の宣言も操作に重要な情報です (よく知られているメディアの種類に XHTML、XML、JPG、PNG、JSON などがあります)。
  • リソースは自己記述型です。リソースの要求の処理に必要なすべての情報はその要求の内部に記述されているため、サービスをステートレスにすることが可能です。
  • リソースには他のリソースへのリンク (ハイパーメディア) が含まれています。

抽象的な例

REST のアーキテクチャ スタイルを使用するサービスを一般に RESTful サービスまたはエンドポイントと呼びます。このアーキテクチャ スタイルの概念をイメージできるように、簡単な例を示します。ここでは、データを操作して、これまでに MSDN Magazine が発行されたすべての年と各号の記事を取得するサービスを作成する必要があるとします。編集者がこのサービスを使用して新しい記事を追加し、今後発行される号も管理できるようにすることを要件としましょう。

RESTful サービスを作成する手順は、きわめて単純で基本的なものです。

  1. どのようなリソースが必要か
  2. これらのリソースを表すためにどのような URI を使用するか
  3. 各 URI で統一インターフェイス (HTTP 動詞) のどの部分をサポートするか

RESTful エンドポイントの基本的な構成要素となるのは、リソースおよびリソースの表現、これらのリソースの URI、および各 URI が応答する統一インターフェイスの部分です。ステータス コードを明示したり、ハイパーリンクを使用してリソースの状態を管理するなど、さらに高度な機能も利用できますが、この例ではあくまで基本を示します。

次に、これらの手順を使用して、架空のサービスを設計します。リソースは、MSDN Magazine が発行されたすべての年、各年に発行されたすべての号、および各号のすべての記事です。この例ではメディアの種類に application/xml (XML) を指定してリソースを表しますが、RESTful サービスがメディアの種類を XML に限定しているわけではありません。

次に、各リソースの URI を決める必要があります。絶対 URI はエンドポイントをホストする場所によって指定されるため、現時点で必要な URI は相対 URI のみです。発行年の一覧はサービス (/) のルート URI です。構文 /{year} では、各年のすべての発行号が返されます。/{year}/{issue} は各号の URI (各号は発行月で識別します)、/{year}/{issue}/{article} は各記事を表します (各号の各記事には 1 から n の番号が付けられることを前提とします)。

次に、統一インターフェイスに URI をマッピングします。MSDN Magazine の履歴は読み取り専用であるため、ルート リソースは GET のみを公開します。新しい年を追加するには、URI /{year} に PUT します。この例のようにクライアント側で新しいリソースの URI がわかっている場合には、PUT を使用して新しいリソースを作成します。PUT は、URI がわかっている場合の既存リソースの更新にも使用できます。POST は、クライアントで新しいリソースの URI が不明な場合のリソースの作成に使用します。そこで、新しい記事リソースの追加には POST 動詞を使用します。これによって記事リソースが /{year}/{issue} URI に送信されます。

各リソースおよび動詞について説明を続けてもよいのですが、ここまでで RESTful エンドポイントの設計を決定するために実行する手順の感じがつかめていればさいわいです。リソース、URI、動詞の詳しい一覧を図 1 に示します。

図 1 オペレーティング システムのサポート
リソース URI 動詞
すべての年 "/ " GET
特定の年の号 "/{year}" GET、PUT
特定の号 "/{year}/{issue}" GET、PUT
特定の記事 "/{year}/{issue}/{article}" GET、POST (システムによって記事番号が割り当てられる)、PUT、DELETE (発行された号の削除は無効になる)

2006 年 1 月号の記事をクライアントとして使用する場合は、HTTP GET を /2006/January に送信します。編集者が 2008 年 12 月号の記事を追加する場合は、クライアントは記事リソースを /2008/December に POST します。これで新しい記事が 12 月号に追加されます。これは、このサービスをクライアントとして使用できるようにするために繰り返し使用するパターンです。

REST の使用を検討する理由

REST について少し説明してきたところで、では REST を使用する理由がどこにあるのかという疑問をお持ちかもしれません。開発者にはスタイル、テクノロジ、パターンを学習して取り入れる意欲が必要です。この記事の読者の多くは Microsoft テクノロジ スタックの開発者だと思われます。それなら、クライアント/サーバー型アプリケーションの実装に別のアーキテクチャ スタイル、リモート プロシージャ コール (RPC) を使った経験があるかもしれません。DCOM や .NET Remoting などの専用 RPC システムを使用したことがある場合も、ASMX または WCF を使用した SOAP などの相互運用性のある RPC テクノロジの使用経験がある場合も、これらはいずれもマイクロソフト プラットフォームのクライアント/サーバー スタイルの実装です。では、REST を学習または使用する理由はどこにあるのでしょう。

その主な理由は 2 つあります。1 つは、REST には、多くの場合に RPC テクノロジより優れている重要な機能や利点があるということです。もう 1 つは、マイクロソフトが固有の実装の多くを SOAP などの RPC テクノロジから REST に移行する方向に向かっているということです。したがって、REST を使用してシステムを構築することに納得できず、その気にならない場合でも、マイクロソフトをはじめとするベンダが多くのフレームワークやテクノロジを REST に移行するようになると、REST の操作方法を習得せざるを得ません。

その他の利点を次に示します (ここに挙げる利点がすべてではありません)。

キャッシュ HTTP を使用して RESTful エンドポイントにデータを要求する場合、使用する HTTP 動詞は GET です。GET 要求に応答して返されたリソースをさまざまな方法でキャッシュできます。条件付き GET は、クライアントがサービスに対し、取得したデータが最新バージョンであるかどうかを確認する方法であり、処理速度とスケーラビリティを向上させるために RESTful エンドポイントに実装可能なオプションの実装詳細です。

スケールアウト REST では、各リソースが特定の要求の処理に必要なすべての状態を保持することを奨励します。この制約に従い、ステートレスにできるようにすると、RESTful サービスのスケールアウトははるかに容易になります。

副作用 リソースを GET で要求した場合、RESTful サービスには副作用がありません (残念なことに、これは他の REST 制約と比べて違反しやすい制約です)。

アイデムポテント統一インターフェイスの一部として一般的に使われる他の 2 つの主な HTTP 動詞は PUT と DELETE です。PUT はユーザー エージェントがリソースを変更する場合に最もよく使われ、DELETE はその名のとおり、リソースの削除に使われます。アイデムポテントという言葉が意味する重要なことは、これらの 2 つの動詞は特定のリソースで複数回使用可能であり、2 回目以降の結果は 1 回目と同じ (つまり、少なくとも 1 回目より影響が広がることはない) ということです。この性質は、エラー、ネットワーク障害、または遅延によって同じコードが複数回実行される可能性がある環境で信頼性の高い分散システムを構築する場合に安全性を保証します。

相互運用性 SOAP は最も相互運用性の高いクライアント/サーバー プログラムの実装方法として広く認知されていますが、言語や環境によっては SOAP ツールキットが存在しません。または、存在する SOAP ツールキットが、新しい標準を実装するツールキットとの通信が保証されない古い標準に基づいている場合があります。REST の多くの操作に必要なものは HTTP ライブラリのみであり (もちろん XML ライブラリがあれば多くの場合に便利です)、SOAP をはじめとするあらゆる RCP テクノロジより確実に相互運用性に優れています。

簡潔性この利点は、他の利点と比べると主観的な要素が強く、何をもって簡潔とするかは人によって異なります。筆者が REST の簡潔性として評価するのは、リソースを表現する URI と統一インターフェイスの存在です。熟練 Web サーファーである筆者は、取得するリソースごとに異なる URI を入力する必要があることを承知しています (これが URI ハッキングまたは URL ハッキングと呼ばれることがありますが、別に悪意のあるものではありません)。長年 URI を使用してきたため、筆者にとってリソースの URI を設計することは当たり前になっています。統一インターフェイスを使用すると、構築するサービスごとにインターフェイス、コントラクト、API を作成する必要がなくなり、開発が簡素化されます。インターフェイス (クライアントとサービスとの間のデータの受け渡し方法) はアーキテクチャの制約によって設定されます。

前述したように、ここに挙げたものが REST の機能のすべてではなく、REST を唯一のテクノロジとして常時採用することを結論するにはまだ情報不足です。REST の利点を理解し、必要に応じて利用してください。

WCF と REST

WCF は、スタイルやプロトコルを意識せずにネットワーク上で通信するアプリケーションを構築するためのマイクロソフトのフレームワークです。WCF の背景にある概念は、開発者が 1 つのプログラミングおよび構成モデルを習得すれば、そのスキルをさまざまな種類の分散システムに応用できるような拡張可能でプラグ可能なフレームワークを作成することでした。

WCF の大部分は SOAP を使用した RPC を対象にしていますが、当初は .NET Framework 3.0 の一部としてリリースされたため、REST サービスを公開し、使用することができます。ただし、REST を WCF で簡単に使用できるようにするためのプログラミング モデルが必要でした。また、REST で .NET Framework 3.0 を操作できるようにするためにインフラストラクチャの一部を構築する必要もありました。プログラミング モデルとインフラストラクチャの構築が必要な部分は、.NET Framework 3.5 の System.ServiceModel.Web アセンブリで WCF に追加されました。.NET Framework 3.5 SP1 では、いくつかの細かい点も改善されています。

このプログラミング モデルの中心は 2 つの新しい属性、WebGetAttribute と WebInvokeAttribute および各メソッドが応答する URI と動詞を宣言できる URI テンプレート メカニズムです。インフラストラクチャは、REST を使用するための適切なネットワーク スタックを示すバインド (WebHttpBinding) と動作 (WebHttpBehavior) の形式で指定されます。また、カスタム ServiceHost (WebServiceHost) および ServiceHostFactory (WebServiceHostFactory) でも一部のホスティング インフラストラクチャをサポートします。

WebGetAttribute と WebInvokeAttribute

接続されたシステムの構築を WCF で簡素化するには、サービスの実装として定義するクラス インスタンスのメソッドにネットワーク メッセージをルーティングするという方法があります。これにより、開発者は、ネットワーク トラフィックの処理に必要なインフラストラクチャの問題から解放され、サービスのコードのロジックに専念できるようになります。

既定では、WCF はこのルーティング (ディスパッチとも呼ばれる) をアクションの概念に基づいて行います。このディスパッチを処理するには、WCF が代理で受信する各メッセージにアクションが存在する必要があります。一意の各アクションが特定の Action メソッドにマップされます。

Action の値は、メソッド名 (とサービスの名前空間) または OperationContractAttribute.Action プロパティで設定されるカスタム値に基づいて決まります。このルーティング システムは、SOAP 仕様の Action ヘッダーを既定で使用するため、SOAP に密接に結合されています。ただし、WCF の他のほとんどの属性と同様、この既定のディスパッチ インフラストラクチャは置き換え可能です。

WCF で REST インフラストラクチャを使用する場合、既定のディスパッチャは Action ではなく、受信した要求の URI および使用されている HTTP 動詞に基づいてルーティングされるディスパッチャに置き換えられます。WebHttpDispatchOperationSelector というクラスで実行されるこのルーティングにより、RESTful エンドポイントの実装が簡単になります。このディスパッチャは、WebHttpBehavior という名前の動作によって各エンドポイントに設定されます。この設定は、このプログラミング モデルを使用する各エンドポイントに追加する必要があります (ただし、後述するように、多くの場合、この処理を手動で行う必要はありません)。

この処理のポイントは、各種の URI および動詞をメソッドにマップする方法を WebHttpDispatchOperationSelector に通知することです。それには、WCF の ServiceContract 型のメソッドに WebGetAttribute と WebInvokeAttribute を追加する必要があります。

WebGetAttribute は、メソッドが HTTP GET 要求に応答する必要があることをディスパッチャに通知します。WebInvokeAttribute は既定で HTTP POST にマップされますが、WebInvokeAttribute.Method プロパティを設定して、他の HTTP 動詞 (PUT および DELETE の 2 つが最も一般的) をサポートすることができます。既定では、URI は、エンドポイントのベース URI に追加されたメソッドの名前によって指定されます。

メソッド名は動詞を表しているので、この処理は RESTful とは言えません。URI として公開するには、名詞が適しています。そのため、WCF の REST プログラミング モデルでは、WebGetAttribute または WebInvokeAttribute の UriTemplate プロパティで設定可能なテンプレートを使用して、各メソッドの URI をカスタマイズできます。

UriTemplate と UriTemplateTable

メソッドと動詞を組み合わせた URI をカスタマイズできるようにするために、先ほど MSDN Magazine サービス エンドポイントの説明で使用したような特殊なテンプレート構文を使用して各リソースの URI を定義する機能が WCF に追加されました。この構文により、置き換え可能なトークンを使用して、各メソッドで表現する URI 構造と共に HTTP 動詞 (WebGetAttribute または WebInvokeAttribute を使用する) を定義できます。構文については、今後のコラムで詳しく説明します。

図 2 に、MSDN Magazine サービスの WCF ServiceContract の定義を示します。ここでは、属性および UriTemplate を適切にカスタマイズし、前述した機能を適用しています。また、GET に応答する必要がある操作のために、WebGetAttribute を使用して既存の WCF コントラクト定義システムを拡張します。他の動詞に応答するために、操作に WebInvokeAttribute も追加します。

図 2 WCF ServiceContract の定義

[ServiceContract]

public interface IMSDNMagazineService

{

    [OperationContract]

    [WebGet(UriTemplate="/")]

    IssuesCollection GetAllIssues();

    [OperationContract]

    [WebGet(UriTemplate = "/{year}")]

    IssuesData GetIssuesByYear(string year);

    [OperationContract]

    [WebGet(UriTemplate = "/{year}/{issue}")]

    Articles GetIssue(string year, string issue);

    [OperationContract]

    [WebGet(UriTemplate = "/{year}/{issue}/{article}")]

    Article GetArticle(string year, string issue, string article);

    [OperationContract]

    [WebInvoke(UriTemplate = "/{year}/{issue}",Method="POST")]

    Article AddArticle(string year, string issue, Article article);



}

WebInvokeAttribute の既定の動詞は POST なので、図では、わかりやすいように AddArticle メソッドに Method="POST" を追加しました。GET メソッドおよび POST メソッドでは、UriTemplate 属性を使用して URI をカスタマイズしています。UriTemplate 構文では複数の変数パス セグメントを使用可能で、各パス セグメントを引数としてメソッドに渡します。

WebHttpBinding と WebHttpBehavior

WCF では、バインドによって WCF の通信方法が指定されます。バインドは、チャネル スタックと呼ばれる通信スタックを構築する方法を WCF に通知する構成であり、連携して特定のエンドポイントに必要な通信タイプを指定する一連のオブジェクトです。

RESTful エンドポイントで使用するバインドは WebHttpBinding です。他の多くのバインドと異なり、WebHttpBinding はきわめて単純で、使用するコンポーネントは HTTP トランスポートとテキスト メッセージ エンコーダ (この設定により、SOAP ではなくプレーンな XML が処理対象になる) の 2 つのみです。

前述のように、WebHttpBehavior は URI と動詞を組み合わせたディスパッチャを使用するオブジェクトです。そのため、WebHttpBinding と WebHttpBehavior は、ほとんどの場合に組み合わせて使用します。WCF RESTful エンドポイントを自己ホストする場合にこのようなエンドポイントを作成するコードを次に示します。

ServiceHost sh = 

  new ServiceHost(typeof(MSDNMagazineServiceType));

string baseUri = "http://localhost/MagazineService";

ServiceEndpoint se = 

  sh.AddServiceEndpoint(typeof(IMSDNMagazineService),

  new WebHttpBinding(), baseUri);

se.Behaviors.Add(new WebHttpBehavior());

sh.Open();

URI と動詞を組み合わせたディスパッチを機能させるには、ServiceHost にエンドポイントを追加する際に WebHttpBinding を指定すると共に、WebHttpBehavior を明示的にエンドポイントに追加する必要があります。この処理を構成によって行うこともできます (図 3 を参照)。

図 3 構成による URI と動詞のディスパッチ

<configuration>

  <system.serviceModel>

    <services>

      <service name="MSDNMagazine.MSDNMagazineServiceType">

        <endpoint 

          address="http://localhost/MagazineService" 

          binding="webHttpBinding" 

          contract="MSDNMagazine.IMSDNMagazineService" 

          behaviorConfiguration="webby"/>

      </service>

    </services>

    <behaviors>

      <endpointBehaviors>

        <behavior name="webby">

          <webHttp/>

        </behavior>

      </endpointBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

WebServiceHost と WebServiceHostFactory

WCF の短所の 1 つは、複雑すぎる場合があることです。この点は構成の面で特に顕著です。RESTful エンドポイントに関するこの問題を緩和するため (何度も言うようですが、簡潔性はよく挙げられる REST の利点の 1 つです)、マイクロソフトは .NET Framework 3.5 に 2 つの型、WebServiceHost と WebServiceHostFactory を追加しました。

WebServiceHost は ServiceHost の派生型であり、RESTful エンドポイントの自己ホストを簡素化します。WebServiceHost を使用した自己ホストのコードは次のようになります。