ものがたり(旧)

atsushieno.hatenablog.com に続く

リクエストの発行

ClientRuntime

クライアントコードでChannelFactoryからサービスインターフェースの実装をCreateChannel()で生成すると、それは実際には ServiceContractであるインターフェースから自動生成されたproxy typeのインスタンスであり、その中にはClientRuntimeが含まれている。

このClientRuntimeはServiceEndpointをもとに生成されるが、その際にServiceEndpointのBehavior (IEndpointBehavior)やContractDescriptionのBehavior(IContractBehavior)、 OperationDescriptionのBehavior(IOperationBehavior)について、 ApplyClientBehavior()が呼び出されることになる。

WebChannelFactoryで生成したServiceEndpointにはWebHttpBindingが自動的に追加されている(WebChannelFactoryを用いず、自ら追加することもできる)。このWebHttpBehaviorの ApplyClientBehavior()が呼び出されると、WebHttpBehaviorは、ClientRuntime.Operationsに含まれるそれぞれのClientOperationについて、Formatterプロパティの値を自らのIClientMessageFormatter 実装に設定する。これによって、通常のSOAPメッセージシリアライゼーションが、RESTfulなサービス呼び出し用のシリアライゼーションに切り替わることになる。

UriTemplate

インターフェース上のランタイムメソッド呼び出しに用いられるパラメータは、 IClientMessageFormatter.SerializeRequest()によってMessageに変換される。 WebHttpBindingで行われるリクエストは、SOAPを利用しない。リクエストパラメータは、HTTPのリクエストURLそのものに表される。従って、そのような可変のURLをあらわす「URIテンプレート文字列」が、何らかの形で提供されなければならない。

UriTemplateは、パラメータのテンプレートを独自のエスケープ構文で記述したURIテンプレート文字列から生成される。URIテンプレート文字列は、たとえばこんな感じだ:

  • /foo?p1={foo}&p2={bar}
  • /foo/{bar}/{baz}

URIテンプレート文字列はoperationごとに、WebGetAttributeあるいはWebInvokeAttributeに含まれる UriTemplateプロパティから与えられる。もしUriTemplateプロパティが空文字列なら、URIテンプレート文字列は次のように自動生成される:

  • void Foo(string s, int i) -> /Foo?s={s}&i={i}

さて、実際のHTTPリクエストでこんなURLが用いられることはない。実際のリクエストのパス/クエリは、以下のようになるだろう:

  • /foo?p1=hoge&p2=fuga
  • /foo/2008/0101

このような実際の呼び出しURLを生成するのに用いられるのがUriTemplateクラスである。このUriTemplateは、ベースアドレスとなる Uriに、特定の文字列パラメータをバインドして、Uriを生成することができる。これによって、クライアント側でHTTP GETリクエストの送り先を決定するために用いられる。

UriTemplateはサービス側の処理でも重要な役割を果たす。

QueryStringConverter

さて、実際にUriTemplateをする前に、もう一つ要求される作業がある。UriTemplateにバインドできるのは文字列でしかないので、ランタイムオブジェクトを文字列に変換しなければならない。この目的で使用されるのがQueryStringConverterである。ランタイムオブジェクトからリクエストパラメータ値となる文字列に変換するにはこれのConvertValueToString()を使用する。

WebHttpBehavior.GetQueryStringConverter()は拡張点のひとつとなっていて、任意の QueryStringConverterを返すことが出来る。WebScriptEnablingBehaviorはこれを拡張して JsonQueryStringConverterを返すようになっている(はずだ)。

QueryStringConverterはサービス側でも重要な役割を果たす。

IRequestChannel.Request()

WebHttpBindingが返すIClientMessageFormatterは、ランタイムオブジェクトであるパラメータを、(全てではないが)UriTemplateを通じてUriに変換してしまう。これはMessage.Headers.Toに設定される。

生成されたMessageは、HttpTransportBindingElementから生成されるIChannelFactoryから生成される IRequestChannel(以下HttpRequsetChannelと仮称する)からRequest()に渡される。 HttpRequestChannel.Request()の中では、WebMessageEncodingBindingElementから生成された MessageEncoderを通じて、MessageからHTTPリクエストのStreamに出力される…が、実際に全てのパラメータが UriTemplateのパラメータとして展開されていれば(HTTP GETの場合はもともとStreamへの出力が許されない)、リクエストURLが全てを表しているので、単純にGETリクエストが発行されるだけである。