松崎 剛 Blog

This Blog's theme : エンタープライズ開発 (Server side)、Office サーバ開発

Web Api (REST サービス) を検索 (Query) 可能にする (および、OData への対応)

Web Api (REST サービス) を検索 (Query) 可能にする (および、OData への対応)

  • Comments 0

(2012/03 : サンプル コードを、WCF Web Api から ASP.NET Web Api に変更)

環境 :
Visual Studio 2010
ASP.NET Web API Beta (ASP.NET MVC 4 Beta)
WCF Web API Preview5

REST サービス / Web Api の実践

ここでは、応用的なテーマをとりあげます。基本的な構築手順については、「REST サービスの作成」 (WCF の場合)、または 「Getting Started with ASP.NET Web Api」 (ASP.NET の場合) を参照してください。(2012/02 変更 : 「WCF Web Api」は、今後、「ASP.NET Web API」としてリリース予定です。)

こんにちは。

今回は、REST で返すデータを、クライアント側から検索 (Query) 可能にする方法について、具体的なサンプル コードを使って説明します。なお、今回も、前回同様、ASP.NET Web API WCF Web API (Preview 5) を使います。

 

プロジェクトの準備

前回同様、準備として、まず、ASP.NET Web API WCF Web API を使用した簡単な REST サービスを構築しておきましょう。(この投稿では、詳細の手順は省略します。)

今回のサービスでは、コレクションを扱うようにしたいので、下記の通り、Contact クラスの List を返すようにします。

. . .

public class ContactController : ApiController
{
    private static List<Contact> contacts = new List<Contact>()
    {
        new Contact { Id = 1, Name = "Ichiro Matsuzaki", Rating = 3},
        new Contact { Id = 2, Name = "Jiro Matsuzaki", Rating = 1 },
        new Contact { Id = 3, Name = "Saburo Matsuzaki", Rating = 3 },
        new Contact { Id = 4, Name = "Ichiro Suzuki", Rating = 2 },
        new Contact { Id = 5, Name = "Jiro Suzuki", Rating = 1 }
    };

    // GET /api/contact
    public List<Contact> Get()
    {
        return contacts;
    }
}

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Rating { get; set; }
}
. . .

 

Query 可能な REST Services

上記のサービス (ContactController) を、クライアント側からクエリー可能となるように変更してみましょう。

ASP.NET Web API WCF Web API で、クエリーをサポートする方法は、驚くほど簡単です!上記のコードを下記の太字の通り変更すれば完了です。


. . .

public class ContactController : ApiController
{
    . . .

    // GET /api/contact
    public IQueryable<Contact> Get()
    {
        return contacts.AsQueryable();
    }
}
. . .

上記のコードを見ると、いったんデータを全件取得して、そのあとで絞り込むように動作するように思われますが、そうではありません。
Linq を使ったことがある方はご存じの通り、IQuerable を使うと、yield return により検索条件が遅延評価され、その結果が一覧として返されます。(上記の場合、List の Generic クラスが、内部で、そうした処理を実行しています。) クライアントにすべてのデータを転送してから検索をおこなうのとは異なり、無駄のない、最適なスループットを実現できます。

では、実際に動作を確認してみましょう。

まず、Id が 4 の Contact を取得してみましょう。下記で、eq は、Equal を意味しています。(下記で、%20 は、空白文字を URL エンコードした文字列です。以降も同様です。)

GET http://localhost:16826/api/contact?$filter=Id%20eq%204 HTTP/1.1
User-Agent: Fiddler
Host: localhost:16826

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 05 Mar 2012 06:59:01 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 44

[
  {"Id":4,"Name":"Ichiro Suzuki","Rating":2}
]

つぎに、Rating が 3 未満 (2 以下) の Contact の一覧を取得してみます。下記で、lt は、"Less Than" を意味しています。

GET http://localhost:16826/api/contact?$filter=Rating%20lt%203 HTTP/1.1
User-Agent: Fiddler
Host: localhost:16826

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 05 Mar 2012 07:04:12 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 129

[
  {"Id":2,"Name":"Jiro Matsuzaki","Rating":1},
  {"Id":4,"Name":"Ichiro Suzuki","Rating":2},
  {"Id":5,"Name":"Jiro Suzuki","Rating":1}
]

つぎに、Rating が 3 未満で、かつ、Name に "Matsu" という文字列が含まれているデータに絞り込んでみましょう。下記の通り、and や or を組み合わせることができます。

GET http://localhost:16826/api/contact?$filter=Rating%20lt%203%20and%20substringof('Matsu',Name) HTTP/1.1
User-Agent: Fiddler
Host: localhost:16826

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 05 Mar 2012 07:11:37 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 45

[
  {"Id":2,"Name":"Jiro Matsuzaki","Rating":1}
]

また、検索 (クエリー) だけでなく、ソートもおこなうことができます。
以下は、Rating でソートしています。

GET http://localhost:16826/api/contact?$orderby=Rating HTTP/1.1
User-Agent: Fiddler
Host: localhost:16826

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 05 Mar 2012 07:13:18 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 221

[
  {"Id":2,"Name":"Jiro Matsuzaki","Rating":1},
  {"Id":5,"Name":"Jiro Suzuki","Rating":1},
  {"Id":4,"Name":"Ichiro Suzuki","Rating":2},
  {"Id":1,"Name":"Ichiro Matsuzaki","Rating":3},
  {"Id":3,"Name":"Saburo Matsuzaki","Rating":3}
]

また、以下は、Rating でソートしたうちの、上位 3 件を取得しています。

GET http://localhost:16826/api/contact?$orderby=Rating&$top=3 HTTP/1.1
User-Agent: Fiddler
Host: localhost:16826

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 05 Mar 2012 07:14:38 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 129

[
  {"Id":2,"Name":"Jiro Matsuzaki","Rating":1},
  {"Id":5,"Name":"Jiro Suzuki","Rating":1},
  {"Id":4,"Name":"Ichiro Suzuki","Rating":2}
]

 

Microsoft のテクノロジーに詳しい方は、もうお分かりと思いますが、これらのクエリー オプション (上記の $filter、$orderby など) は、すべて、OData の仕様で定義されるクエリー オプションそのものです。(下記に記載されています。)

[MSDN] REST エンドポイントを使用する OData システム クエリ オプション
http://msdn.microsoft.com/ja-jp/library/gg309461.aspx

補足 : Atom 形式の OData フォーマットを処理できる MediaTypeFormatter は、現時点 (ASP.NET MVC 4 Beta) の ASP.NET Web API では提供されていません。

 

OData フォーマットへの対応

また、WCF Web Api が標準で提供している ODataMediaTypeFormatter を使うと、atom フォーマットの OData も簡単に扱うことができます。(下記参照。なお、MediaTypeFormatter の設定方法については、前回の投稿 を参照してください。)

GET http://localhost:32224/contacts?$filter=Id%20eq%204 HTTP/1.1
User-Agent: Fiddler
Host: localhost:32224
Accept: application/atom+xml

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Thu, 13 Oct 2011 06:55:06 GMT
X-AspNet-Version: 4.0.30319
DataServiceVersion: 3.0;
Content-Length: 1181
Cache-Control: private
Content-Type: application/atom+xml; type=feed
Connection: Close

<?xml version="1.0" encoding="utf-8"?>
<feed xml:base="http://www.tempuri.org/"
  xmlns="http://www.w3.org/2005/Atom"
  xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
  xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
  xmlns:georss="http://www.georss.org/georss"
  xmlns:gml="http://www.opengis.net/gml">
  <entry>
    <id>http://schemas.datacontract.org/2004/07/QueryRestSample/Contact(4)</id>
    <category term="http://schemas.datacontract.org/2004/07/QueryRestSample/Contact"
      scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <link rel="self" href="http://schemas.datacontract.org/2004/07/QueryRestSample/Contact(4)" />
    <title />
    <updated>2011-10-13T06:55:06Z</updated>
    <author>
      <name />
    </author>
    <content type="application/xml">
      <m:properties>
        <d:Id m:type="Edm.Int32">4</d:Id>
        <d:Name>Ichiro Suzuki</d:Name>
        <d:Rating m:type="Edm.Int32">2</d:Rating>
      </m:properties>
    </content>
  </entry>
  <id>http://schemas.datacontract.org/2004/07/QueryRestSample/Contact</id>
  <title />
  <updated>2011-10-13T06:55:06Z</updated>
</feed>

このため、こうした要素を組み合わせることで、OData に完全準拠した、Full Customize の (WCF Data Services を使用しない) REST サービスも簡単に構築できます。

補足 : ただし、現在の WCF Web Api (Preview 5) は、OData の $expand 操作を簡単に実現できるフレームワークは提供していないようです。また、OData の batch (バッチ処理) に対応させるには、WCF Web Api で提供されている System.Net.Http.MultipartContent クラスなどを使って実装できます。(このサンプル コードは、WCF Web Api のサンプルに入っています。)

 

WCF Web API のリリースが、待ち遠しいですね。。。

 

Leave a Comment
  • Please add 2 and 6 and type the answer here:
  • Post