WCF REST Starter Kit Preview 版の Sample に RequestInterceptor  を使用するサンプル ContentTypeBasedDispatch というプロジェクトが含まれています。今回はサンプルの実装を見て、RequestInterceptorの使用方法を見てみます。Preview版ではCustomBindingとして実現されていますが、最終的にはwebHttpBindingに組み込まれるのかもしれません。

以下の環境で確認しています。

  • 動作環境:IIS6.0 on Windows Server 2003
  • 実装確認: Visaul Studio 2008 Professional
  • .NET 3.5 SP1, WCF REST Starter Kit Preview版

1. RequestInterceptor

ContentTypeBasedDispatch サンプルでは、RequestInspector を継承した ContentTypeRequestInterceptor を使用して、Http の リクエストヘッダに基づいてURLパラメタを設定し、XML,JSON形式を返すオペレーションメソッドにディスパッチされるようにしています。

Sample の ContentTypeRequestInterceptor は次のように実装されています。

public class ContentTypeRequestInterceptor : RequestInterceptor
{
    public override void ProcessRequest(ref RequestContext requestContext)
    {..}
}

RequestInterceptor で ProcessRequest が abstract 宣言されているので、ContentTypeRequestInterceptor で実装しています。中身のやっていることはシンプルです。以下にProcessRequest メソッドの一部、Http ヘッダを解析している部分を掲載します。

HttpRequestMessageProperty prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
string format = null;
string accepts = prop.Headers[HttpRequestHeader.Accept];
if (accepts != null)
{
    if (accepts.Contains("text/xml") || accepts.Contains("application/xml"))
    {
         format = "xml";
    }
    else if (accepts.Contains("application/json"))
    {
         format = "json";
    }
}
else
{
     string contentType = prop.Headers[HttpRequestHeader.ContentType];
     if (contentType != null)
     {
        if (contentType.Contains("text/xml") || contentType.Contains("application/xml"))
        {
            format = "xml";
        }
        else if (contentType.Contains("application/json"))
        {
            format = "json";
        }
     }
}

プログラムを見ても分かるように、 Accept ヘッダもしくは ContentType ヘッダに含まれるコンテンツタイプにしたがってJSON形式,XML形式とするかを判定しています。

最後に ProcessRequest  の末尾にある、適切なサービスインスタンスのオペレーションにディスパッチできるように、URLパラメタを編集する部分の抜粋を掲載します。サンプルを参照すれば分かりますが、サービスクラスは WebGetAttribute の UriTemplate にしたがって、適切なオペレーションにディスパッチされるようになっています。

if (format != null)
{
    UriBuilder toBuilder = new UriBuilder(request.Headers.To);
    if (string.IsNullOrEmpty(toBuilder.Query))
    {
        toBuilder.Query = "format=" + format;
    }
    else if (!toBuilder.Query.Contains("format="))
    {
        toBuilder.Query += "&format=" + format;
    }
    request.Headers.To = toBuilder.Uri;
}

プログラムを見ても分かるように、リクエストパラメタにformat=json もしくは、 format=xml を追加しています。サンプルが動作しない場合は、構成ファイルの aspNetCompatibilityEnabled が true になっていることを確認して下さい。

2. おまけ

以上で解説は終わりです。RequestInspectorの設定方法などは実際にサンプルを見たほうが簡単だと思います。CodePlex のダウンロードのページはWCF REST Starter Kit Preview 解説(Caching)のサンプルのページに載せてあります。

ContentTypeBasedDispatch の翻訳をこりずに載せてみます。

本サンプルで、WCFを使用してHTTPヘッダをベースとしたディスパッチを行う方法を提示しています。サービスはXMLまたはJSON形式でリソースを公開するシングルトンサービスです。JSON形式はURLに?format=jsonとすることで取得できまた、HTTPのAccept Headerに application/json を設定することで取得できます。

AcceptHeaderにapplication/jsonを含めることでJSON形式でリソースを取得できるのは、RequestInterceptorでAccept Headerにapplication/jsonが含まれる場合にURLのクエリ文字列にformat=jsonを追加しているためです。そのため、json形式を返すオペレーションメソッドが実行されるのです。

以上が翻訳です。RequestInterceptor を実装する ContentTypeRequestInterceptor の実装を見ると分かりますが、実際にはAcceptHeaderが存在しない場合は ContentType ヘッダを見て、text/xml もしくは application/xml の場合は、xml 形式を返す用にリクエストパラメタを設定し、json/xmlの場合はjson形式を返すリクエストパラメタを設定しています。

3. おまえその2

RequestInterceptor を使用したほかのサンプルに、XHttpMethodOverride があります。このサンプルでは、ファイアウォールの設定によってPUTやDELETEメソッドを直接使用できない場合に、X-HTTP-Method-Overrideヘッダに実際行いたいメソッドを指定して、RequestInterceptor内で、ヘッダが存在すれば、X-HTTP-Method-Overrideのメソッドで、HttpのGETやPOSTメソッドを置き換える処理をしています。

XHttpMethodOverrideのreadme.txtには次のように記載されています。

本サンプルは、メソッドへのディスパッチが発生する前に、HTTPリクエストを処理するチャネルスタックをプラグインする方法を説明しています。本サンプルの方法では、リクエストのX-HTTP-Method-Overrideが存在した場合に、そのヘッダによって指定されたメソッドにディスパッチを行うRequestInterceptorがプラグインされます。

本サンプルを実行するには
=================
Fiddlerから、HTTP のPOSTリクエストをX-HTTP-Method-OverrideヘッダにPUTもしくはDELETEをセットして送信します。PUTの場合はリソースが更新され、DELETEの場合はリソースが削除されます。