在Web Api2上建立OData v3

原文:Creating an OData v3 Endpoint with Web API 2

OData(Open Data Protocol)是一個Web的存取協定,它提供了一種統一的方式去取得、查詢、操作資料以完成CRUD(create, read, update, and delete),並提供了XML及JSON的輸出格式。

開始實作

建立一個空的Web Api專案

建立實體模型

Modles資料夾下建立一個產品(Prodcut)的實體模型

Proudct.cs

結構如下:

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}

新增Controller

Modles資料夾下建立一個產品(Prodcut)的OData Controller

使用非同步的控制器

加入資料內容類別

怎麼發生錯誤了?

若你在建立實體模型後還沒編譯過,執行到這邊是會出錯的,別擔心,只要重新建置就可以了 :D

完成後,會加入下列檔案

設定Route

打開App_Start資料夾底下,並修改下列檔案

WebApiConfig
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();
            //將實體模型(Product)對應至ProuctsController 
            builder.EntitySet<Product>("Products");
            config.Routes.MapODataRoute("ODataRoute", "odata", builder.GetEdmModel());

             // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}
ODataConventionModelBuilder

ODataConventionModelBuilder會透過EntitySet所傳入的參數與Controller對應,以下的例子EntitySet傳入了"Products"代表會將ProudctsController對應至實體模型(Product)

一個OData endpoint可以對應至多個實體模型

builder.EntitySet<Product>("Products");
MapODataRoute

MapODataRoute在使用時的參數說明

  • ODataRoute
    • Route的名稱
  • odata
    • Uri的前綴字
  • builder.GetEdmModel()
    • EntitySet註冊過的實體模型
config.Routes.MapODataRoute("ODataRoute", "odata", builder.GetEdmModel());
endPoint呼叫的方式

以呼叫ProuductsController為例:

http://hostname/odata/Products

建立DB

使用Code First的方式,需先在套件管理器主控台啟用Migrations

套件管理器主控台
Enable-Migrations

啟用後會新增Migrations資料夾並再它底下新增Configuration.cs

新增預設資料

打開Configuration.cs並加入以下程式碼

Configuration.cs
protected override void Seed(ProductService.Models.ProductServiceContext context)
{
    // 新資料
    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" },
    });
}

回到套件管理器主控台新增剛剛的Migrations

套件管理器主控台
Add-Migration Initial
Update-Database

測試OData Endpoint

測試工具:Fiddler

以下將會假設你的測試專案Port為60868,也就是用以下的格式去測試OData Endpoint

格式:http://localhost:port/odata/

實際呼叫:http://localhost:60868/odata/

預設會回傳以下的Html

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>
回傳JSON

在Header加上下列資訊:

Key Value
Accept application/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"
    }
  ]
}
取得metadata

這裡面會回傳你的服務中用了哪些EntitySetEntityContainer...等資訊。

取得方式
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)的所有資料,可以透過以下的方式做存取:

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"
    }
  ]
}

特定資料

篩選ID為1的資料,存取方式如下

http://localhost:port/odata/Products(1)
回傳的內容
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"
}

序列化的格式

在V3的版本中有提供了下列的格式:

  • Atom Pub (XML)
  • JSON light(OData v3可使用)
  • JSON verbose(OData v2可使用)

預設使用的是 Atom Pub (XML)

Atom Pub (XML)

相較於其它的Json格式,它在描述上顯的有點過於“詳細”,有時會增加不必要的網路傳輸

使用方式

header裡加上下列資訊

Key Value
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>

JSON light

在OData v3以上的版本才能使用

使用方式

header裡加上下列資訊

Key Value
Accept application/json
回傳的內容
{
  "odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element",
  "ID":1,
  "Name":"Hat",
  "Price":"15.00",
  "Category":"Apparel"
}

JSON verbose

在OData v2以上的版本皆能使用,相較於Json light的格式,Json verbose格式會將資料放進名稱d的key值裡,在metadata裡也會連同namespace也一併顯示,若是要縮短傳送的資料長度建議用json light即可。

使用方式

header裡加上下列資訊

Key Value
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"
  }
}

results matching ""

    No results matching ""