程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> OData 和 AtomPub: 使用WCF數據服務綁定 AtomPub 服務器

OData 和 AtomPub: 使用WCF數據服務綁定 AtomPub 服務器

編輯:關於.NET

如果您不熟悉開放數據協議 (OData),我要告訴您它很美妙。OData(在 odata.org 上有詳細介紹)以下列各種基於 HTTP 的功能優勢 為基礎:用於發布數據的 Atom;用於創建、更新和刪除數據的 AtomPub;以及用於定義數據類型的 Microsoft 實體數據模型 (EDM)。

如果您擁有 JavaScript 客戶端,則可以采用 JSON 格式(而不是 Atom 格式)直接返回數據;如果您擁有其他客戶端(包括 Excel、 .Microsoft NET Framework、PHP、AJAX 等),則可以使用客戶端庫來形成 OData 請求和處理 OData 響應。如果您在服務器端使用 .NET Framework,則 Microsoft 還提供一個易於使用的庫,該庫稱為 WCF 數據服務,用於公開 Microsoft 實體框架支持的 .NET Framework 類型或數據庫作為 OData 源。這樣,您就可以采用基於 HTTP 的方式和標准方式,通過 Internet 輕松公開您的數據。

話雖如此,您也可能希望使用 OData 執行一些並不完全屬於現成功能的任務,如將 OData 與現有基於 Atom 和 AtomPub 的閱讀器和 編寫器集成。這正是我們要嘗試的功能。

一個簡單的博客

例如,假設我要構建一個簡單的博客系統(事實上,此工作基於我對 sellsbrothers.com 上的內容管理系統的重新編寫)。我對 Visual Studio 2010 中的模型優先支持十分著迷,因此創建了一個 ASP.NET MVC 2.0 項目,添加了一個名為 MyBlogDB.edmx 的 ADO.NET EDM 文件,並設計了一個 Post 實體,如圖 1 所示。

圖 1 在 Visual Studio 2010 中創建的 Post 實體

博客軟件越復雜,需要跟蹤的數據越多,但圖 1 顯示了一些基本數據。右鍵單擊設計器圖面時,可以選擇“根據模型生成數據庫”, 這會顯示將創建的 SQL 文件(在本例中為 MyBlogDB.sql)以及將為創建數據庫而生成的 SQL。單擊“完成”將創建 SQL 文件並將數據庫 綁定到我在 EDM 設計器中創建的實體。SQL 的重要部分如圖 2 所示。

圖 2 使用“根據模型生成數據庫”生成的 SQL 代碼

...
USE [MyBlogDB];
GO
...
-- Dropping existing tables
IF OBJECT_ID(N'[dbo].[Posts]', 'U') IS NOT NULL
   DROP TABLE [dbo].[Posts];
GO
...
-- Creating table 'Posts'
CREATE TABLE [dbo].[Posts] (
   [Id] int IDENTITY(1,1) NOT NULL,
   [Title] nvarchar(max) NOT NULL,
   [PublishDate] datetime NULL,
   [Content] nvarchar(max) NOT NULL
);
GO
...
-- Creating primary key on [Id] in table 'Posts'
ALTER TABLE [dbo].[Posts]
ADD CONSTRAINT [PK_Posts]
   PRIMARY KEY CLUSTERED ([Id] ASC);
GO

基本而言,我們做的只是按預期方式根據我們的這個實體創建一個表,並將字段映射到 SQL 類型。請注意,PublishDate 設置為 NULL ,這不是默認設置。我在 EDM 設計器中明確選擇了該設置,因為我希望在沒有發布日期的情況下也可以正常發布(某些工具默認情況下不 提供發布日期)。

若要執行此 SQL 並創建數據庫,操作很簡單,只需在 Visual Studio 文本編輯器中右鍵單擊該 SQL,然後選擇“執行 SQL”。系統會 提示您輸入連接信息和數據庫名稱。因為這是一個新數據庫,所以需要鍵入新名稱(例如 MyBlogDB),然後在出現提示時單擊“確定”以 進行創建。創建數據庫以後,可以在服務器資源管理器中,在 Visual Studio 剛剛為您創建的連接下浏覽該數據庫。

為了方便測試,可以在表中直接添加數據,方法是右鍵單擊 Posts 並選擇“顯示表數據”,這會顯示一個小網格,如圖 3 所示。

圖 3“顯示表數據”網格可方便測試

這雖然不是世界上最好的編輯體驗,但是要好過通過編寫 SQL 語句最終得到並運行端到端編輯解決方案(即將討論這種方法,請往下 讀!)。

我們已經有了一些數據,現在便可以通過更新 HomeController.cs 來編寫一些 ASP.NET 代碼,從而顯示這些數據(請在 asp.net/mvc/ 閱讀有關 MVC 的更多內容):

...
namespace ODataBloggingSample.Controllers {
  [HandleError]
  public class HomeController : Controller {
   MyBlogDBContainer blogDB = new MyBlogDBContainer();

   public ActionResult Index() {
    return View(blogDB.Posts);
   }

   public ActionResult About() {
    return View();
   }
  }
}

我在此處所做的工作只是創建了 MyBlogDBContainer 類的一個實例,該類是從 MyBlogDB.edmx 文件創建的頂級 ObjectContext 派生 類,我們通過它來訪問新數據庫。(如果您不熟悉實體框架,請參見 msdn.com/data/aa937723)。在對 HomeController 類調用 Index 方法時,表示有人正請求我們的新 Web 應用程序的主頁,我們希望使用該主頁顯示新博客文章,因此將 Posts 集合從數據庫路由到 Home/Index.aspx 視圖的一個實例,我們對該視圖進行了如下修改:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
   Inherits="System.Web.Mvc.ViewPage<IEnumerable<ODataBloggingSample.Post>>" %>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
   Home Page 
</asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
   <% foreach (var post in Model) { %>
    <h1><%= post.Title %></h1>
    <div><%= post.Content %></div>
    <p><i>Posted <%= post.PublishDate %></i></p>
   <% } %>
</asp:Content>

在這裡,我們將基類更改為采用生成的 Post 類型集合(以及 MyBlogDBContainer 類)來對 Posts 表進行建模。我們還將主頁內容替 換為一個 foreach 語句,以顯示每篇文章的標題、內容和發布日期。

這就是我們需要的全部操作。現在,當我們執行該項目(“調試”|“啟動調試”)時,浏覽器會啟動並顯示博客文章(如果您沒有在 數據庫中放入更多文章,那麼就只有一篇文章),如圖 4 所示。

圖 4 完成的網頁

在我告訴您所有這些之 後,現在我可以這樣跟您說:OData 的神奇之處在於,只需幾下簡單的鼠標和鍵盤操作,我便可以公開這些數據的完整編程接口,從而可 以通過 JavaScript、.NET Framework、PHP 等訪問這些數據。若要親眼見證這種神奇,請在解決方案資源管理器中右鍵單擊您的項目,選 擇“添加”|“新建項”,選擇“WCF 數據服務”,選擇一個名稱(我使用 odata.svc),然後單擊 “添加”。您將在一個文件(在本例中為 odata.svc.cs)中獲得一個代碼框架,如果暫時忽略安全性,這個框架如下所示:

using System.Data.Services; 
using System.Data.Services.Common; 
using ODataBloggingSample; 
 
namespace ODataBloggingSample {
 public class odata : DataService<MyBlogDBContainer> {
  public static void InitializeService(DataServiceConfiguration config) {
    config.SetEntitySetAccessRule("*", EntitySetRights.All); 
   config.DataServiceBehavior.MaxProtocolVersion =   
    DataServiceProtocolVersion.V2; 
  } 
 } 
}

請注意,我們引入 MyBlogDBContainer(頂級數據庫訪問類)作為 DataService 類的模板參數,而 DataService 類是服務器端 WCF 數據服務的核心(請參 見 msdn.com/data/bb931106)。通過在 OData 協議中定義的基於 HTTP 謂詞的創建、讀取、更新和刪除 (CRUD) 操作,可以使用 DataService 類輕松公開我們的數據庫。對於傳遞給 DataService 的類型,將檢查其用於公開集合的公共屬性。在我們的示例中,實體框 架生成的對象上下文類包含非常符合要求的 Posts 集合:

...
namespace ODataBloggingSample {
  ...
  public partial class MyBlogDBContainer : ObjectContext {
   ...
   public ObjectSet<Post> Posts {...}
   ...
  }

  ...
  public partial class Post : EntityObject {
   ...
   public global::System.Int32 Id { get { ... } set { ... } }
   public global::System.String Title { get { ... } set { ... } }
   public Nullable<global::System.DateTime> PublishDate {
    get { ... } set { ... } }
   public global::System.String Content { get { ... } set { ... } }
   ...
  }
}

請注意,生成的 MyBlogDBContainer 公開一個名為 Posts 的 ObjectSet(只是一種集合),其中包含 Post 類型的實例。而且,Post 類型經過定義,在 Id、Title、PublishDate 和 Content 屬性與前面所建 Posts 表上的基礎列之間提供映射。

隨著 odata.svc 准備就緒,我們可以浏覽到服務文檔,該文檔通過在 URL 中使用數據服務終結點文件的名稱(例如 localhost:54423/odata.svc)來公開對象上下文集合屬性:

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<service xml:base="http://localhost:54423/odata.svc/" xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:app="http://www.w3.org/2007/app" xmlns="http://www.w3.org/2007/app">
   <workspace>
    <atom:title>Default</atom:title>
    <collection>
     <atom:title>Posts</atom:title>
    </collection>
   </workspace>
</service>

這個文件完全按 AtomPub 規范 (ietf.org/rfc/rfc5023.txt) 定義。更深入一層,可以看到我們的博客文章在 localhost:54423/odata.svc/Posts 作為一組 Atom 條目公開,如圖 5 所示。

圖 5 作為一組 Atom 條目公開的博客文章

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<feed xml:base="http://localhost:54423/odata.svc/"
  xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
  xmlns:m=
   "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
  xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Posts</title>
  <id>http://localhost:54423/odata.svc/Posts</id>
  <updated>2010-03-15T00:26:40Z</updated>
  <link rel="self" title="Posts" href="Posts" />
  <entry>
   <id>http://localhost:54423/odata.svc/Posts(1)</id>
   <title type="text" />
   <updated>2010-03-15T00:26:40Z</updated>
   <author>
    <name />
   </author>
   <link rel="edit" title="Post" href="Posts(1)" />
   <category term="MyBlogDB.Post"
    scheme=
     "http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
   <content type="application/xml">
    <m:properties>
     <d:Id m:type="Edm.Int32">1</d:Id>
     <d:Title>My first blog post</d:Title>
     <d:PublishDate m:type=
      "Edm.DateTime">2010-03-14T00:00:00</d:PublishDate>
     <d:Content>Hi! How are you?</d:Content>
    </m:properties>
   </content>
  </entry>
</feed>

這個文件幾乎是一個徹頭徹尾的傳統 Atom (ietf.org/rfc/rfc4287.txt),唯一不同的就是基於 Microsoft 的 URI,這些 URI 用於將 OData 功能分層到 Atom 之中。具體而言,您需要注意“content”元素中的“properties”元素。您會看出這些屬性就是以前在 Post 實 體和相應 Posts 表中定義的相同屬性。此數據包含在由 Atom 定義並通過 CRUD 注釋公開的信封之中,這些注釋本身由 AtomPub 定義,

使您可以通過 HTTP 方法 POST、GET、PUT 和 DELETE 分別執行創建、讀取、更新和刪除操作。問題在於這不是完全傳統的 Atom。例 如,如果我們在 Atom 閱讀器(如 Internet Explorer 8)中浏覽至 odata.svc/Posts,則標題和內容不會正確顯示,如圖 6 所示。

圖 6 在 Atom 閱讀器中查看博客文章時未顯示標題和內容

您可以看到數據就在其中(請注意,日期是正確的,而且顯示了類別),但是看不到標題和內容。這是因為 Internet Explorer 查找 標題和內容的位置(邏輯上為每個條目中的“title”和“content”元素)不包含應包含的信息。“title”元素為空,而“content”元 素采用的格式使 Internet Explorer 無法識別。Internet Explorer 真正需要的格式如下:

<feed ...>
  <title type="text">Posts</title>
  <id>http://localhost:54423/atompub.svc/Posts</id>
  <updated>2010-03-15T00:42:32Z</updated>
  <link rel="self" title="Posts" href="Posts" />
  <entry>
   <id>http://localhost:54423/atompub.svc/Posts(1)</id>
   <title type="text">My first blog post</title>
   <updated>2010-03-15T00:42:32Z</updated>
   ...
   <content type="html">Hi! How are you?</content>
   <published>2010-03-14T00:00:00-08:00</published>
  </entry>
</feed>

請注意,“title”元素包含的信息過去隱藏在“content”元素中 OData“properties”元素的 Title 屬性之中,“content”元素已 由 Content 屬性覆蓋,並且已從 PublishDate 屬性的值添加了“published”元素。當在 Internet Explorer 中查看此數據時,我們獲 得的信息更加符合我們的期望,如圖 7 所示。

圖 7 調整 XML 格式可正確顯示標題和內容

應該指出,這只用於支持我們關心的博客工具。Internet Explorer 不期望獲取客戶列表或發票;它期望獲取標題和發布日期以及 HTML 內容。對客戶列表和發票進行這種映射有時十分有用,Microsoft 在 WCF 數據服務中的一項名為“友好源”的功能就是一例(請參 見 blogs.msdn.com/astoriateam/archive/ 2008/09/28/making-feeds-friendly.aspx)。然而,該功能並不能執行所有操作(具體而言 ,它不會重新映射 Atom“content”元素),因為 WCF 數據服務團隊希望確保即使是“友好”源也仍然可以使用各種客戶端庫。這樣做的 目標是使 OData 源更加友好,而不是放棄 OData 以支持 Atom/AtomPub。

然而在本例中,我們放棄 OData,只使用 WCF 數據服務作為 AtomPub 終結點,這要求在 Atom 與 OData 之間進行映射,如圖 8 所示 。

圖 8 Atom 與 OData 之間的映射

奧妙在於:如何進行這種映射? 我們顯然已獲取了數據,但是需要將其重新映射到 Atom 屬性,以便 Atom 閱讀器(和編寫器)知道 存放數據的位置。這樣做的原因是使 WCF 數據服務仍可以對 .NET Framework 類型進行映射,或通過實體框架對數據庫進行映射。我們要 做的只是在 Atom/AtomPub 與 OData 之間進行一點簡單的映射。

本文附帶的代碼示例將一些代碼注入 WCF 管道,該管道恰好允許進行這種消息數據轉換。您可以將它讀入您的核心內容(請查看 ODataBlogging.cs),但我要介紹的是如何使用它。

首先,像以前一樣新建一個 WCF 數據服務終結點,但使用不同的名稱(我使用 atompub.svc)。掛接頂級對象上下文類並公開您需要 的所有實體集(像以前一樣),不過還要使用 ODataBloggingServiceBehavior 標記服務類,如下所示:

...
using ODataBlogging;

namespace ODataBloggingSample {
  [ODataBloggingServiceBehavior(typeof(MyBlogDBContainer))]
  [EntityAtomMapping("Posts", "PublishDate", "published")]
  public class atompub : DataService<MyBlogDBContainer> {
   public static void InitializeService(DataServiceConfiguration config) {
    config.SetEntitySetAccessRule("*", EntitySetRights.All);
    config.DataServiceBehavior.MaxProtocolVersion =
     DataServiceProtocolVersion.V2;
   }
  }
}

這會通過“content”元素內部的嵌套“properties”元素,從傳入的 Atom/AtomPub(例如“title”、“content”和“published” 元素)映射到對應的 OData 格式。默認情況下,如果實體上的名稱匹配(忽略大小寫),就會進行映射(以及類型強制)。例如,當公開 的某個實體包含 Title 屬性(如我們的 Post 實體)時,該屬性會映射到 Atom“title”元素。

另一方面,如果未進行自動映射,則您可以通過基於實體名稱提供顯式映射來重寫該行為,我們之前將“Posts”集合所含對象的 PublishDate 屬性映射到“published”atom 屬性時就是這樣做的。通過這兩個特性足以將我們的 OData 源轉換為 Atom 源,從而為我們 提供功能完整的數據視圖,如圖 7 所示。

此映射不是單向的;它支持所有 HTTP 方法,因此可以使用 AtomPub 協議在 Posts 集合中創建、更新和刪除項以及讀取這些項。這表 示您可以配置 Windows Live Writer (WLW) 之類的工具(該工具支持 AtomPub 作為博客 API),並使用該工具對文章進行豐富的文本編 輯。例如,如果提供了 atompub.svc 終結點,則在 WLW 中,您可以選擇“日志”|“添加日志帳戶”,然後在隨後出現的對話框中填寫以 下選項:

您使用哪種日志服務? 其他日志服務

您的日志網址:http://<<服務器>>:<<端口>>/atompub.svc

用戶名:<<用戶名>>(必填項,應使用標准 HTTP 方法在 AtomPub 終結點上實現)

密碼:<<密碼>>

要使用的日志類型:Atom 發布協議

服務文檔 URL:http://<<服務器>>:<<端口>>/­atompub.svc

博客昵稱:<<您喜歡的任何名稱>>

單擊“完成”,會向您顯示一個用於管理博客文章的 RTF 編輯器,如圖 9 所示。

圖 9 Atom/OData 映射有助於構建 RTF 編輯器以管理您的博客文章

我們在這裡采用了數據服務引擎(該引擎通過將屬性打包到 Atom“content”元素中來支持完整的 CRUD 功能),並進行了一些映射, 以使其也支持傳統的 Atom 和 AtomPub。

我用於撰寫本文的小型示例庫(是我同 Phani Raj 一起創作的,他是 Microsoft WCF 數據服務團隊的一名軟件工程師)只具備最簡單 的功能,構建真正的博客所需的工作遠不止這麼簡單。下面列出了我首先想到的、在實際支持 Atom 和 AtomPub 時仍需要進行的一些事項 :

映射 Atom 作者元素子元素,如姓名、URI 和電子郵件。

處理圖像(不過 WLW 允許使用 FTP,這樣可能就足夠了)。

公開功能以使 WLW 可識別這些功能。

本文配套源碼

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