Web API 2 を使用した OData v3 エンドポイントの作成
作成者: Mike Wasson
Open Data Protocol (OData) は、Web のデータ アクセス プロトコルです。 OData は、CRUD 操作 (作成、読み取り、更新、削除) を使用して、データの構造化、データのクエリ、データセットの操作を行う一貫した方法を提供します。 OData では、AtomPub (XML) 形式と JSON 形式の両方がサポートされています。 また、OData では、データに関するメタデータを公開する方法も定義されています。 クライアントはメタデータを使用して、データ セットの型情報とリレーションシップを検出できます。
ASP.NET Web API を使用すると、データ セットの OData エンドポイントを簡単に作成できます。 エンドポイントがサポートする OData 操作を正確に制御できます。 OData 以外のエンドポイントと共に、複数の OData エンドポイントをホストできます。 データ モデル、バックエンド ビジネス ロジック、データ レイヤーを完全に制御できます。
チュートリアルで使用するソフトウェアのバージョン
- Visual Studio 2013
- Web API 2
- OData-Version 3
- Entity Framework 6
- Fiddler Web デバッグ プロキシ (省略可能)
Web API OData のサポートは、ASP.NET および Web Tools 2012.2 Update で追加されました。 ただし、このチュートリアルでは、Visual Studio 2013 で追加されたスキャフォールディングを使用します。
このチュートリアルでは、クライアントがクエリを実行できる単純な OData エンドポイントを作成します。 エンドポイント用の C# クライアントも作成します。 このチュートリアルの完了後、次の一連のチュートリアルでは、エンティティの関係、アクション、$expand/$selectなどの機能を追加する方法を説明します。
- Visual Studio プロジェクトを作成する
- モデルにエンティティを追加する
- OData コントローラーを追加する
- EDM とルートを追加する
- データベースのシード処理 (省略可能)
- OData エンドポイントの探索
- OData シリアル化形式
Visual Studio プロジェクトを作成する
このチュートリアルでは、基本的な CRUD 操作をサポートする OData エンドポイントを作成します。 エンドポイントは、単一のリソースである製品の一覧を公開します。 後のチュートリアルでは、さらに多くの機能が追加されます。
Visual Studio を起動して、[スタート] ページから [新しいプロジェクト] をクリックします。 [ファイル] メニューの [新規作成] を選択し、[プロジェクト] を選択します。
[テンプレート] ウィンドウで、[インストールされているテンプレート] を選択し、[Visual C#] ノードを展開します。 [Visual C#] で [Web] を選択します。 [ASP.NET Web アプリケーション] テンプレートを選択します。
[新しい ASP.NET プロジェクト] ダイアログで、[空] テンプレートを選択します。 [Add folders and core references for...]\(フォルダーとコア参照の追加...\) で、Web API を確認します。 OK をクリックします。
モデルにエンティティを追加する
"モデル" は、アプリケーションでデータを表すオブジェクトです。 このチュートリアルでは、製品を表すモデルが必要です。 このモデルは、OData エンティティ型に対応しています。
ソリューション エクスプローラーで、[モデル] フォルダーを右クリックします。 コンテキスト メニューの [追加] を選択し、[クラス] を選択します。
[新しい項目の追加] ダイアログで、クラスに "Product" という名前を付けます。
Note
規則により、モデル クラスは Models フォルダーに配置されます。 ご自身のプロジェクトでこの規則に従う必要はありませんが、このチュートリアルではこの規則に従います。
Product.cs ファイルに、次のクラス定義を追加します。
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
ID プロパティはエンティティ キーになります。 クライアントは製品に対して ID でクエリ実行できます。 このフィールドは、バックエンド データベースの主キーでもあります。
プロジェクトを今すぐビルドします。 次の手順では、リフレクションを使用して Product 型を検索する Visual Studio スキャフォールディングをいくつか使用します。
OData コントローラーを追加する
コントローラー は、HTTP 要求を処理するクラスです。 OData サービス内のエンティティ セットごとに個別のコントローラーを定義します。 このチュートリアルでは、単一のコントローラーを作成します。
ソリューション エクスプローラーで、[コントローラー] フォルダーを右クリックします。 [追加]、[コントローラー] の順に選択します。
[スキャフォールディングの追加] ダイアログで、[Entity Framework を使用したアクションがある Web API 2 OData コントローラー] を選択します。
[コントローラーの追加] ダイアログで、コントローラーに "ProductsController" という名前を付けます。 [Use async controller actions]\(非同期コントローラー アクションを使用する\) チェック ボックスをオンにします。 [モデル] ドロップダウン リストで、Product クラスを選択します。
[新しいデータ コンテキスト...] ボタンをクリックします。 データ コンテキストの種類の名前は既定のままにして、[追加] をクリックします。
[コントローラーの追加] ダイアログで [追加] をクリックして、コントローラーを追加します。
注: "型の取得中にエラーが発生しました..." というエラー メッセージが表示される場合は、Product クラスを追加した後に Visual Studio プロジェクトをビルドしたことを確認してください。 スキャフォールディングでは、リフレクションを使用してクラスを検索します。
スキャフォールディングにより、プロジェクトに次の 2 つのコード ファイルが追加されます。
- Products.csは、OData エンドポイントを実装する Web API コントローラーを定義します。
- ProductServiceContext.cs は、Entity Framework を使用して、基になるデータベースに対してクエリを実行するメソッドを提供します。
EDM とルートを追加する
ソリューション エクスプローラーで、App_Start フォルダーを展開し、WebApiConfig.cs という名前のファイルを開きます。 このクラスは、Web API 用の構成コードを保持します。 このコードを次のコードに置き換えます。
using ProductService.Models;
using System.Web.Http;
using System.Web.Http.OData.Builder;
namespace ProductService
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
}
}
}
このコードは 2 つのことを行います:
- OData エンドポイントのエンティティ データ モデル (EDM) を作成します。
- エンドポイントのルートを追加します。
EDM は、データの抽象モデルです。 EDM は、メタデータ ドキュメントを作成し、サービスの URI を定義するために使用されます。 ODataConventionModelBuilder は、既定の名前付け規則 EDM のセットを使用して EDM を作成します。 このアプローチでは、最も少ないコードで済みます。 EDM をより詳細に制御する場合は、ODataModelBuilder クラスを使用して、プロパティ、キー、およびナビゲーション プロパティを明示的に追加して EDM を作成できます。
EntitySet メソッドは、エンティティ セットを EDM に追加します。
modelBuilder.EntitySet<Product>("Products");
文字列 "Products" はエンティティ セットの名前を定義します。 コントローラーの名前は、エンティティ セットの名前と一致している必要があります。 このチュートリアルでは、エンティティ セットに "Products"、コントローラーに ProductsController
という名前を付けます。 エンティティ セットに "ProductSet" という名前を付けた場合は、コントローラーに ProductSetController
という名前を付けます。 エンドポイントには複数のエンティティ セットを含めることができることに注意してください。 エンティティ セットごとに EntitySet<T> を呼び出し、対応するコントローラーを定義します。
MapODataRoute メソッドは、OData エンドポイントのルートを追加します。
config.Routes.MapODataRoute("ODataRoute", "odata", model);
最初のパラメーターは、ルートのフレンドリ名です。 サービスのクライアントには、この名前は表示されません。 2 番目のパラメーターは、エンドポイントの URI プレフィックスです。 このコードを指定すると、Products エンティティ セットの URI は http://hostname/odata/Products になります。 アプリケーションには複数の OData エンドポイントを含めることができます。 エンドポイントごとに MapODataRoute を呼び出し、一意のルート名と一意の URI プレフィックスを指定します。
データベースのシード処理 (省略可能)
この手順では、Entity Framework を使用して、一部のテスト データをデータベースにシード処理します。 この手順は省略可能ですが、OData エンドポイントをすぐにテストできます。
[ツール] メニューで、[NuGet パッケージ マネージャー] を選択し、[パッケージ マネージャー コンソール] を選択します。 [パッケージ マネージャー コンソール] ウィンドウで、次のコマンドを入力します。
Enable-Migrations
これにより、Migrations という名前のフォルダーと、Configuration.cs という名前のコード ファイルが追加されます。
このファイルを開き、Configuration.Seed
メソッドに次のコードを追加します。
protected override void Seed(ProductService.Models.ProductServiceContext context)
{
// New code
context.Products.AddOrUpdate(new Product[] {
new Product() { ID = 1, Name = "Hat", Price = 15, Category = "Apparel" },
new Product() { ID = 2, Name = "Socks", Price = 5, Category = "Apparel" },
new Product() { ID = 3, Name = "Scarf", Price = 12, Category = "Apparel" },
new Product() { ID = 4, Name = "Yo-yo", Price = 4.95M, Category = "Toys" },
new Product() { ID = 5, Name = "Puzzle", Price = 8, Category = "Toys" },
});
}
[パッケージ マネージャー コンソール] ウィンドウで、次のコマンドを入力します。
Add-Migration Initial
Update-Database
これらのコマンドは、データベースを作成するコードを生成し、そのコードを実行します。
OData エンドポイントの探索
このセクションでは、Fiddler Web デバッグ プロキシを使用してエンドポイントに要求を送信し、応答メッセージを調べます。 これは、OData エンドポイントの機能を理解するのに役立ちます。
Visual Studio で F5 キーを押してデバッグを開始します。 既定では、Visual Studio はブラウザーを http://localhost:*port*
に開きます。ここで、ポートはプロジェクト設定で構成されたポート番号を指します。
ポート番号は、プロジェクト設定で変更できます。 ソリューション エクスプローラーで、プロジェクトを右クリックして [プロパティ] を選択します。 [プロパティ] ウィンドウで、[Web] をクリックします。 [プロジェクト URL] にポート番号を 入力します。
サービス ドキュメント
サービス ドキュメント には、OData エンドポイントのエンティティ セットの一覧が含まれています。 サービス ドキュメントを取得するには、サービスのルート URI に GET 要求を送信します。
Fiddler を使用して、[Composer] タブに次の URI を入力します: http://localhost:port/odata/
、ここで、port はポート番号を指します。
[実行] ボタンをクリックします。 Fiddler は HTTP GET 要求をアプリケーションに送信します。 Web セッションの一覧に応答が表示されます。 すべてが機能している場合、状態コードは 200 になります。
Web セッションの一覧で応答をダブルクリックすると、[インスペクター] タブに応答メッセージの詳細が表示されます。
生の HTTP 応答メッセージは、次のようになります。
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/atomsvc+xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 17:51:01 GMT
Content-Length: 364
<?xml version="1.0" encoding="utf-8"?>
<service xml:base="http://localhost:60868/odata"
xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
<workspace>
<atom:title type="text">Default</atom:title>
<collection href="Products">
<atom:title type="text">Products</atom:title>
</collection>
</workspace>
</service></pre>
既定では、Web API は AtomPub 形式でサービス ドキュメントを返します。 JSON を要求するには、HTTP 要求に次のヘッダーを追加します。
Accept: application/json
これで、HTTP 応答に JSON ペイロードが含まれるようになりました。
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 22:59:28 GMT
Content-Length: 136
{
"odata.metadata":"http://localhost:60868/odata/$metadata","value":[
{
"name":"Products","url":"Products"
}
]
}
サービス メタデータ ドキュメント
サービス メタデータ ドキュメントでは、概念スキーマ定義言語 (CSDL) と呼ばれる XML 言語を使用して、サービスのデータ モデルについて説明します。 メタデータ ドキュメントには、サービス内のデータの構造が表示され、クライアント コードの生成に使用できます。
メタデータ ドキュメントを取得するには、GET 要求を http://localhost:port/odata/$metadata
に送信します。 このチュートリアルで示すエンドポイントのメタデータを次に示します。
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:05:52 GMT
Content-Length: 1086
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
<edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<Schema Namespace="ProductService.Models" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<EntityType Name="Product">
<Key>
<PropertyRef Name="ID" />
</Key>
<Property Name="ID" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Price" Type="Edm.Decimal" Nullable="false" />
<Property Name="Category" Type="Edm.String" />
</EntityType>
</Schema>
<Schema Namespace="Default" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
<EntitySet Name="Products" EntityType="ProductService.Models.Product" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
エンティティ セット
Products エンティティ セットを取得するには、GET 要求を http://localhost:port/odata/Products
に送信します。
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:01:31 GMT
Content-Length: 459
{
"odata.metadata":"http://localhost:60868/odata/$metadata#Products","value":[
{
"ID":1,"Name":"Hat","Price":"15.00","Category":"Apparel"
},{
"ID":2,"Name":"Socks","Price":"5.00","Category":"Apparel"
},{
"ID":3,"Name":"Scarf","Price":"12.00","Category":"Apparel"
},{
"ID":4,"Name":"Yo-yo","Price":"4.95","Category":"Toys"
},{
"ID":5,"Name":"Puzzle","Price":"8.00","Category":"Toys"
}
]
}
エンティティ
個々の製品を取得するには、GET 要求 http://localhost:port/odata/Products(1)
にを送信します。ここで、"1" は製品 ID を指します。
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:04:29 GMT
Content-Length: 140
{
"odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element","ID":1,
"Name":"Hat","Price":"15.00","Category":"Apparel"
}
OData シリアル化形式
OData では、複数のシリアル化形式がサポートされています。
- Atom Pub (XML)
- JSON "light" (OData v3 で導入)
- JSON "verbose" (OData v2)
既定では、Web API は AtomPubJSON の "light" 形式を使用します。
AtomPub 形式を取得するには、Accept ヘッダーを "application/atom+xml" に設定します。 次は応答本文の例です。
<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="http://localhost:60868/odata" 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">
<id>http://localhost:60868/odata/Products(1)</id>
<category term="ProductService.Models.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="http://localhost:60868/odata/Products(1)" />
<link rel="self" href="http://localhost:60868/odata/Products(1)" />
<title />
<updated>2013-09-23T23:42:11Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:ID m:type="Edm.Int32">1</d:ID>
<d:Name>Hat</d:Name>
<d:Price m:type="Edm.Decimal">15.00</d:Price>
<d:Category>Apparel</d:Category>
</m:properties>
</content>
</entry>
Atom 形式の明確な欠点の 1 つである、JSON light よりもはるかに冗長であるという点を確認できます。 ただし、AtomPub を認識するクライアントがある場合、クライアントは JSON よりも Atom 形式を優先する可能性があります。
同じエンティティの JSON light バージョンを次に示します。
{
"odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element",
"ID":1,
"Name":"Hat",
"Price":"15.00",
"Category":"Apparel"
}
JSON light 形式は、OData プロトコルのバージョン 3 で導入されました。 下位互換性のために、クライアントは古い JSON "verbose" 形式を要求できます。 詳細 JSON を要求するには、Accept ヘッダーを application/json;odata=verbose
に設定します。 詳細バージョンを次に示します。
{
"d":{
"__metadata":{
"id":"http://localhost:18285/odata/Products(1)",
"uri":"http://localhost:18285/odata/Products(1)",
"type":"ProductService.Models.Product"
},"ID":1,"Name":"Hat","Price":"15.00","Category":"Apparel"
}
}
この形式では、応答本文でより多くのメタデータが伝達されるため、セッション全体にかなりのオーバーヘッドが発生する可能性があります。 また、"d" という名前のプロパティでオブジェクトをラップすることで、間接参照のレベルを追加します。