程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 一個創建 OData 的新選項: Web API

一個創建 OData 的新選項: Web API

編輯:關於.NET

早在 OData 規范出現以前,Microsoft .NET 開發人員就已能夠創建 OData 源。借助 WCF 數據服務,可使用具象狀態傳輸 (REST) 在 Web 上公開實體數據模型 (EDM)。換句話說,可 經由以下 HTTP 調用使用這些服務: GET、PUT、DELETE 等。隨著創建這些服務的框架的發 展(中途數次更改名稱),輸出也在不斷演變,並最終形成以 OData 規范 (odata.org) 封裝的形態。目前已出現多種可使用 OData 的 客戶端 API,來源如 .NET、PHP、JavaScript 以及其他眾多客戶端。但直到最近,能夠輕松 創建服務的唯一方式仍是借助 WCF 數據服務。

WCF 數據服務是一項 .NET 技術,它 可對 EDM(.edmx,或通過 Code First 定義的模型)進行簡單的封裝然後公開該模型,以供 經由 HTTP 查詢和更新。由於這些調用采用 URI 形式(如 http://mysite.com/mydataservice/­Clients(34)),因此,您甚至可以通過 Web 浏覽 器或 Fiddler 之類的工具執行查詢。為方便創建 WCF 數據服務,Visual Studio 提供了一 個項目模板,可用來構建使用一組 API 的數據服務。

現在又出現了一種創建 OData 源的新方式 — 利用 ASP.NET Web API。在本文中,我將概述這兩種方法之間的某些區別, 並就如何在兩者間作出選擇給予指導。此外,我還將介紹某些與創建 Web API 不同的 OData API 創建方式。

API 對數據服務(宏觀角度)

WCF 數據服務是一種 System.Data.Services.DataService,它包裝已定義的 ObjectContext 或 DbContext。在聲 明該服務類時,它是包含上下文的泛型 DataService(即 DataService<MyDbContext> )。它在初始時呈完全鎖定狀態,因此,應在需要公開該服務的上下文的 DbSets 構造函數 中設置訪問權限。這就是您需要完成的全部操作。其余工作都由底層的 DataService API 負 責處理: 在對該服務的客戶端應用程序 HTTP 請求的響應中直接與上下文交互,以及查詢和 更新數據庫。另外,也可以向該服務添加一些自定義設置,重寫其部分查詢或更新邏輯。但 在大多數情況下,其目的在於讓 DataService 負責與上下文的大部分交互工作。

另 一方面,可借助 Web API 在對 HTTP 請求(PUT、GET 等)的響應中定義上下文交互。由 API 公開方法,由您定義方法邏輯。您不必與實體框架乃至數據庫進行交互。您可以擁有客 戶端正在請求或發送的內存中對象。訪問點不會像在使用 WCF 數據服務時那樣神奇地創建出 來;相反,需要您來控制如何響應這些調用。這是選擇服務而非 API 來公開 OData 的決定 性因素。如果需要公開的大部分操作只是簡單的 Create、Read、Update、Delete (CRUD) 而 無需進行大量自定義設置,那麼數據服務將是您的最佳選擇。如果需要自定義大量行為,那 麼使用 Web API 更為適宜。

我很贊同 Microsoft Integration MVP Matt Milner 在 最近一次聚會上的闡述方式: “WCF 數據服務適合於從數據和模型開始,並且只希望公開它 們的情形。而從 API 開始且需要定義應公開哪些內容時,使用 Web API 會更合適。”

使用標准 Web API 打下基礎

我發現,對於缺乏 Web API 使用經驗的新手 來說,在了解新的 OData 支持之前,最好先學習一些 Web API 的基礎知識,然後弄清它們 與創建 Web API(用於公開 OData)有何關聯。在本文中,我將這樣做:首先創建一個使用 實體框架作為其數據層的簡單 Web API,然後將其轉換為提供 OData 形式的結果。

Web API 的用途之一是作為模型-視圖-控制器 (MVC) 應用程序中標准控制器的替代 品,可將其創建為 ASP.NET MVC 4 項目的一部分。如果不需要前端,則可從空的 ASP.NET Web 應用程序開始,然後添加 Web API 控制器。但是,為了照顧新手,我將從 ASP.NET MVC 4 模板開始(因為它提供了會生成部分起始代碼的基架)。當您了解各部分是如何結合在一 起的之後,就可以直接從空項目開始工作了。

因此,我將創建一個新的 ASP.NET MVC 4 應用程序,然後在系統提示時選擇空模板(不是 Web API 模板,Web API 模板是專為使用 視圖的更強大應用程序而設計的,在這裡就大材小用了)。這將生成一個 Models、Views 和 Controllers 均為空文件夾的 MVC 應用程序項目組織結構。圖 1 將空模板和 Web API 模板 生成的結果進行了對比。您會發現,空模板生成的組織結構要簡單得多,我所需要做的只是 刪除幾個文件夾。

圖 1 使用空模 板和 Web API 模板生成的 ASP.NET MVC 4 項目

我也不需要 Models 文件夾,因為我 將使用現有的域類集合和獨立項目中的 DbContext 來提供模型。接下來,我將借助 Visual Studio 工具創建第一個控制器,它是一個 Web API 控制器,用於與從我的 MVC 項目引用的 DbContext 和域類進行交互。我的模型包括 Airline、Passengers、Flights 類及與航空公 司有關的一些其他數據類型。

由於使用的是空模板,所以需要添加一些引用(分別是 對 System.Data.Entity.dll 和 EntityFramework.dll 的引用),以便調用 DbContext。您 可以通過安裝 EntityFramework NuGet 包來添加這兩個引用。

可以通過與創建標准 MVC 控制器相同的方式創建新的 Web API 控制器: 在解決方案中右鍵單擊 Controllers 文 件夾,然後依次選擇“添加”、“控制器”。此時會顯示用於創建具有 EF 讀取和寫入操作 的 API 控制器模板(如圖 2 所示)。除此以外,還有一個空 API 控制器。我們從 EF 讀取 /寫入操作開始,以便與用於 OData 的控制器(也將采用實體框架)進行對比。

圖 2 一個用於 創建包含預填充操作的 API 控制器的模板

如果您之前創建過 MVC 控制器,就會發現 所生成的類是相似的,只不過提供的不是一組視圖相關的操作方法(如 Index、Add 和 Edit ),而是一組 HTTP 操作罷了。

例如,這裡有兩個 Get 方法(如圖 3 所示)。第一 個方法 Get­Airlines 的簽名不接受任何參數,並且使用 AirlineContext 實例(模板 基架將之命名為 db)返回一個可枚舉的 Airline 實例集合。另一個方法 GetAirline 接受 一個整型參數,然後利用該參數查找並返回一個特定的航空公司。

圖 3 一些由 MVC 基架創建的 Web API 控制器方法

        public class 

AirlineController : ApiController
{
  private AirlineContext db = new AirlineContext2();
  // GET api/Airline
  public IEnumerable<Airline> GetAirlines()
  {
    return db.Airlines.AsEnumerable();
  }
  // GET api/Airline/5
  public Airline GetAirline(int id)
  {
    Airline airline = db.Airlines.Find(id);
    if (airline == null)
    {
      throw new HttpResponseException
        (Request.CreateResponse(HttpStatusCode.NotFound));
    }
    return airline;
  }

模板添加了注釋,以演示如何使用這些方法。

向 Web API 提供一些配 置之後,可以在我的應用程序分配的端口上使用示例語法在浏覽器中直接檢視其操作: http://localhost:1702/api/Airline。這是默認的 HTTP GET 調用,因此,由應用程序路由 以執行 GetAirlines 方法。Web API 通過內容協商來確定如何格式化結果集。我的默認浏覽 器是 Google Chrome,其觸發的協商結果是 XML 格式。來自客戶端的請求控制結果的格式。 例如,Internet Explorer 不會發送任何與其接受何種格式有關的特定標頭信息,因此,Web API 將默認返回 JSON。圖 4 顯示我收到的 XML 結果。

圖 4 Airline WebAPI 對 GET 的響應信息,在我的浏覽器中顯示為 XML

          <ArrayOfAirline 

xmlns:i=http://www.w3.org/2001/XMLSchema-instance 
  xmlns="http://schemas.datacontract.org/2004/07/DomainClasses">
    <Airline>
      <Id>1</Id>
      <Legs/>
      <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
      <Name>Vermont Balloon Transporters</Name>
    </Airline>
    <Airline>
      <Id>2</Id>
      <Legs/>
      <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
      <Name>Olympic Airways</Name>
    </Airline>
    <Airline>
      <Id>3</Id>
      <Legs/>
      <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
      <Name>Salt Lake Flyer</Name>
    </Airline>
</ArrayOfAirline>

遵照 GetAirline 方法的指導信息,如果在請求中添加 一個整型參數,如 http://localhost:1702/api/Airline/3,則只返回鍵值 (ID) 為 3 的航 空公司:

          <Airline xmlns:i=http://www.w3.org/2001/XMLSchema-instance
  xmlns="http://schemas.datacontract.org/2004/07/DomainClasses">
    <Id>3</Id>
    <Legs/>
    <ModifiedDate>2013-02-22T00:00:00</ModifiedDate>
    <Name>Salt Lake Flyer</Name>
</Airline>

如果使用的是可以明確控制到 API 的請求的工具(如 Internet Explorer 或 Fiddler)以確保得到 JSON 格式的結果,則對於 ID 為 3 的 Airline 的請求 結果將以 JSON 格式返回:

          {"Id":3,
  "Name":"Salt Lake Flyer",
  "Legs":[],
  "ModifiedDate":"2013-03-17T00:00:00"
}

這些響應包含對航空公司類型的簡單表述,每一屬性具有以下元素: ID、Legs、 ModifiedDate 和 Name。

此外,該控制器還包含 PutAirline 方法,Web API 在響應 PUT HTTP 請求時會調用該方法。PutAirline 包含使用 AirlineContext 更新航空公司的代 碼。除此以外,還有用於插入操作的 PostAirline 方法和用於刪除操作的 DeleteAirline 方法。這些操作無法在浏覽器 URL 中演示,但您可在 MSDN、Pluralsight 等處找到大量 Web API 的入門資料,因此,我將轉到下一個話題:將其轉換為輸出符合 OData 規范的結果 。

將 Web API 轉變為 OData 提供程序

現在,您已基本了解借助 Web API 使 用實體框架公開數據的方法,下面我們來講解 Web API 的特殊用法:從數據模型創建 OData 提供程序。您可以通過將控制器轉變為 OData 控制器(使用 ASP.NET 和 Web Tools 2012.2 包中提供的類)然後重載其 OData 特有方法,強制 Web API 返回 OData 格式的數據。有了 這種新的控制器類型,您甚至不需要由模板創建的方法。事實上,對於創建 OData 控制器, 更高效的途徑是選擇空 Web API 基架模板(而非創建 CRUD 操作的那個)。

要實現 這一轉換,需要執行四個步驟:

使控制器成為 ODataController 類型並實現其 HTTP 方法。這步我會走個捷徑。

在項目的 WebAPIConfig 文件中定義可用的 EntitySets 。

在 WebAPIConfig 中配置路由。

將控制器類的名稱改為復數形式,以符合 OData 約定。

創建 ODataController 我將使用派生自 ODataController 的 EntitySetController 而非從 ODataController 直接繼承,以便借助一系列虛擬 CRUD 方法 提供更高級別的支持。我使用了 NuGet 來安裝 Microsoft ASP.NET Web API OData 包,以 獲取包含這兩個控制器類的適當程序集。

我的類初始時是下面這樣,它現在從 EntitySetController 繼承,並指定將該控制器用於 Airline 類型:

          

public class AirlinesController : EntitySetController<Airline,int>
{
  private AirlineContext db = new AirlineContext();
  public override IQueryable<Airline> Get()
  {
    return db.Airlines;
  }

查看本欄目

我已重載了 Get 方法,以返回 db.Airlines。請注意,我沒有對 Airlines DbSet 調用 ToList 或 AsEnumerable。Get 方 法需要返回 IQueryable 類型的 Airline,這將由 db.Airlines 實現。這樣,OData 的使用 者可以定義對該集合的查詢,並隨後在數據庫上執行,而不必將所有 Airlines 抓進內存, 然後進行輪詢。

可以重載和添加邏輯的 HTTP 方法有 GET、POST(用於插入)、PUT (用於更新)、PATCH(用於合並更新)和 DELETE。但對於更新,實際上將使用虛擬方法 CreateEntity 來重載針對 POST 調用的邏輯,UpdateEntity 用於針對 PUT 調用的邏輯, PatchEntity 用於處理 PATCH HTTP 調用所需的邏輯。可用作該 OData 提供程序一部分的其 他虛擬方法有: CreateLink、DeleteLink 和 GetEntityByKey。

在 WCF 數據服務中 ,通過配置 SetEntitySetAccessRule 來按每個 EntitySet 控制允許的 CRUD 操作。但在使 用 Web API 時,只需添加需要支持的方法,並忽略不想讓使用者訪問的方法即可。

為 API 指定 EntitySets Web API 需要知道應使哪些 EntitySets 對使用者可用。起初,我 對這條規則感到困惑。我本以為它能夠通過讀取 AirlineContext 自行發現要公開的 EntitySets。但經過反復思索,我發現這與在 WCF 數據服務中使用 SetEntitySetAccessRule 很相似。在 WCF 數據服務中,需要在公開特定集合時定義允許的 CRUD 操作。但在使用 Web API 時,首先需要修改 WebApiConfig.Register 方法,以指定將 成為 API 一部分的集合,然後使用控制器中的方法公開特定的 CRUD 操作。通過 ODataModelBuilder 指定集合 — 這與 DbContext.ModelBuilder 類似(您可能在使用 Code First 時用到過)。下面是 WebApiConfig 文件中 Register 方法的代碼,以使 OData 源公 開 Airlines 和 Legs:

ODataModelBuilder modelBuilder = new 

ODataConventionModelBuilder();
        modelBuilder.EntitySet<Airline>("Airlines");
        modelBuilder.EntitySet<FlightLeg>("Legs");

定義查找 OData 的路由 接下來,Register 方法需要一個指向該模型的路由,以便在調用 Web API 時,提供 對所定義的 EntitySets 的訪問:

          Microsoft.Data.Edm.IEdmModel 

model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute("ODataRoute", "odata", model);

您會發現,許多 演示使用“odata”作為 RoutePrefix 的參數用於定義 API 方法的 URL 前綴。這雖然是個 不錯的標准,但您可以為其指定任意名稱。

為了證明這一點,我將它更改為:

config.Routes.MapODataRoute("ODataRoute", "oairlinedata", 

model);

重命名控制器類 對於控制器,應用程序模板生成的代碼將采用單數命名約 定,例如 AirlineController 和 LegController。但是,OData 的側重點是 EntitySets, 因而通常采用實體名稱的復數形式進行命名。由於 EntitySets 已為復數,所以只需將控制 器類的名稱更改為 AirlinesController,以與 Airlines EntitySet 一致。

使用 OData

現在,我可以使用熟悉的 OData 查詢語法操作 API 了。我將首先使用下面的 請求來請求一個包含可用內容的列表: http://localhost:1702/oairlinedata/。結果如圖 5 所示。

圖 5.請求包含可用數據的列表。

          

http://localhost:1702/oairlinedata/
<service xmlns="http://www.w3.org/2007/app" xmlns:atom=
  "http://www.w3.org/2005/Atom"
  xml:base="http://localhost:1702/oairlinedata /">
    <workspace>
      <atom:title type="text">Default</atom:title>
      <collection href="Airlines">
        <atom:title type="text">Airlines</atom:title>
      </collection>
      <collection href="Legs">
        <atom:title type="text">Legs</atom:title>
      </collection>
    </workspace>
</service>

結果顯示該服務公開了 Airlines 和 Legs。接下來,我將通過 http://localhost:1702/oairlinedata/Airlines 請求一份 OData 形式的 Airlines 列表。 OData 可以 XML 或 JSON 格式返回。Web API 結果的默認格式為 JSON:

         {
  "odata.metadata":
    "http://localhost:1702/oairlinedata/$metadata#Airlines","value":[
    {
      "Id":1,"Name":"Vermont Balloons","ModifiedDate":"2013-02-26T00:00:00"
    },{
      "Id":2,"Name":"Olympic Airways","ModifiedDate":"2013-02-26T00:00:00"
    },{
      "Id":3,"Name":"Salt Lake Flyer","ModifiedDate":"2013-02-26T00:00:00"
    }
  ]
}

OData URI 的眾多功能之一是查詢。默認情況下,Web API 未啟用查詢,因為這 會在服務器上產生額外的負荷。因此,在向適當的方法添加 Queryable 注釋之前,將無法使 用 Web API 的這些查詢功能。例如,我向 Get 方法添加了 Queryable(如下所示):

          [Queryable]
public override IQueryable<Airline> Get()
{
  return db.Airlines;
}

現在,將可以使用 $filter、$inlinecount、$orderby、$sort 和 $top 方法。 下面是一個使用 OData 篩選器方法的查詢:

http://localhost:1702/oairlinedata/Airlines?$filter=startswith (Name,'Vermont')

ODataController 允許限制查詢,以防使用者導致服務器 出現性能問題。例如,可以限制在單次響應中返回的記錄數。有關更多信息,請參閱針對 Web API 的“OData 安全指南”文章 (bit.ly/X0hyv3)。

這只是冰山一角

我 只討論了可借助 Web API OData 支持提供的查詢功能的一部分。還可以使用 EntitySetController 的虛擬方法,以實現對數據庫的更新。除了 PUT、POST 和 DELETE 以 外,另一個有趣的操作是 PATCH:對於只有少量字段發生更改的更新,可以通過它發送明確 而高效的請求,而非發送進行 POST 所需的完整實體。但是,PATCH 方法中的邏輯需要處理 適當的更新,在使用實體框架時,這通常意味著從數據庫檢索當前對象,然後使用新值進行 更新。如何實現該邏輯取決於了解需要在工作流程中的哪個時間點負擔將數據推送上線的開 銷。另一個需要一提的重點是,該版本(使用 ASP.NET 和 Web Tools 2012.2 包)僅支持 OData 功能的一個子集。也就是說,並非所有可對 OData 源作出的 API 調用都適用於使用 Web API 創建的 OData 提供程序。ASP.NET 和 Web Tools 2012.2 包的發行說明列出了支持 的功能。

本專欄文章篇幅有限,除了我分享的這些知識外,還有很多內容值得學習。 我推薦大家閱讀 Web API 官方文檔 (bit.ly/14cfHIm) 中 Mike Wasson 撰寫的 OData 系列 優秀文章。您將學到如何構建所有 CRUD 方法,如何使用 PATCH,甚至是使用注釋來限制允 許在 OData API 中使用的篩選類型,以及如何處理關系。請牢記,還有許多其他 Web API 功能適用於 OData API,例如,如何使用授權限制不同人員對不同操作的訪問權限。此 外,.NET Web 開發與工具博客 (blogs.msdn.com/webdev) 也提供了大量關於 Web API 中 OData 支持的內容翔實的博文。

下載代碼示例

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved