# 跟我一起學WCF(12)——WCF中Rest服務入門
## 一、引言
要將Rest與.NET Framework 3.0配合使用,還需要構建基礎架構的一些部件。在.NET Framework 3.5中,WCF在System.ServiceModel.Web組件中新增了編程模型和這些基礎架構部件。
新編程模型有兩個主要的新屬性:[WebGetAttribute](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.web.webgetattribute(v=vs.110).aspx)和[WebInvokeAttribute](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.web.webinvokeattribute(v=vs.110).aspx),還有一個URI模板機制,幫助你聲明每種方法響應使用的URI和動詞。.NET Framework還提供了一個新的綁定(WebHttpBinding)和新的行為(WebHttpBehavior),此外,還提供了WebServiceHost和WebServiceHostFactory類來對Rest服務進行支持。下面讓我們具體看看WCF目前對Rest服務的支持和實現。
## 二、REST服務是什么
對于這個問題,百度下有很多答案,這里給出百度百科中一個詳細介紹的鏈接:[Rest服務](http://baike.baidu.com/view/1077487.htm?fr=aladdin)。我的理解的Rest服務是:以前我們都是把WCF服務抽象為操作的概念,而Rest最早是由Roy Thomas Fielding 在他的博士論文([“體系結構風格和基于網絡軟件體系的設計](http://ics.uci.edu/~fielding/pubs/dissertation/top.htm)”)中提出的。Rest服務是將服務抽象為資源,每個資源都有一個唯一的統一資源標識符(URI),我們不再是通過調用操作的方式與服務進行交互了,而是通過HTTP標準動詞(GET、POST、PUT和DELETE)的統一接口來完成。總之一句話概括,Rest服務換了一種思維方式,把服務也當成一種資源,通過Get、Post、Put和Delete這些HTTP動詞來進行交互。這樣,問題就來了,Rest服務具有什么好處呢?即我們為什么要去關注Rest和實現它呢?
Rest優勢就在于其使用極其簡單,Rest服務要求很少的編碼工作量,可以減少很多不必要的工作。Rest服務主要有以下優點:
* 無需引入SOAP消息傳輸層,輕量級和高效率的HTTP格式可直接被應用。
* 可以輕易地在任何編程語言中實現,尤其是在JS中。使用SOAP的服務與JS交互非常繁瑣,而使用Rest服務與JS交互非常簡單。
* 可以不使用任何編程語言就能訪問服務,而只需要使用Web瀏覽器即可。
* 更好的性能和緩存支持。使用Rest服務可以改善響應時間和改善用戶體驗。
* 可擴展性和無狀態性。Rest服務基于HTTP協議,每個請求都是獨立的,一旦被調用,服務器不保留任何會話,這樣可以更具響應性,通過減少通訊狀態的維護工作來提供服務的可擴展性。
## 三、WXF和Asp.net Web API的比較
WCF是微軟為生成面向服務的應用程序而提供的統一編程模型。[Asp.net Web API](http://www.asp.net/web-api) 是一個用來方便生成HTTP服務的框架,這些服務可供廣泛的客戶端訪問,包括瀏覽器和移動設備。Asp.net Web API用于在.NET平臺上生成Restful應用程序的理想平臺。到這里問題又來了,Rest服務與SOAP服務的區別又是什么呢?
Rest相對于SOAP服務使用更加簡單,對于開發者來說,學習成本較低,而SOAP作為一種古老的Web服務技術,近期內還不回退出歷史舞臺,而且隨著SOAP 1.2的出現,SOAP 1.1中的一些缺點已得到改進。
REST目前只基于HTTP和HTTPS協議,而SOAP可支持任何傳輸協議,包括HTTP/HTTPS、TCP、SMTP等協議。另外Rest服務除了能使用XML作為數據承載外,還有JSON,RSS和[ATOM](http://baike.baidu.com/subview/21936/7031123.htm?fr=aladdin)形式。
Rest與SOAP對應的比較如下所示:
1. SOAP是一種工業標準,面對的應用需求時RPC(遠程過程調用),而Rest面對的應用需求是分布式Web系統。
2. Rest服務強調數據,請求和響應消息都是數據的封裝,而SOAP服務更強調接口,SOAP消息封裝的是過程調用。Rest是面向資源的,而SOAP是面向接口的。
3. Rest架構下,HTTP是承載協議,也是應用協議,而SOAP架構下,HTTP只是承載協議,SOAP才是應用協議。
那在什么情況下使用Rest,什么情況下使用SOAP呢?這要看具體的實際情況。具體應用場景如下所示:
* 遠程調用用SOAP。如果服務是作為一種功能提供,客戶端調用服務是為了執行一個功能,用SOAP,比如常見的需求是認證授權。而數據服務用Rest。
* 要更多的考慮非功能需求時使用SOAP,如需考慮安全、傳輸和協議等需求的情況下。
* 低帶寬、客戶端的處理能力受限的場合可以考慮使用Rest。如在PDA或手機上消費服務。
介紹了Rest與SOAP的區別之后,讓我們回到WCF與Asp.net Web API的比較上來,具體兩者功能之間的對比如下圖所示:

使用 WCF 可創建可靠、安全的 Web 服務,這些服務可通過各種傳輸方式來訪問。 使用 ASP.NET Web API 可創建基于 HTTP 的服務,這些服務可從各種客戶端來訪問。 如果要創建和設計新的 REST 樣式服務,請使用 ASP.NET Web API。 雖然 WCF 針對編寫 REST 樣式服務提供了一些支持,但 ASP.NET Web API 中的 REST 支持更加完整,并且,所有將來的 REST 功能改進都將在 ASP.NET Web API 中進行。
## 四、WCF中實現Rest服務
WCF 3.5中對Rest服務也做了支持,主要提供了[WebHttpBinding](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.webhttpbinding(v=vs.110).aspx)來對Rest進行支持,下面我們通過一個具體的實例來看看如何在WCF中實現一個Rest服務。我們還是按照之前三個步驟來創建該實例。
第一步:創建Rest服務接口和實現。具體的實現代碼如下所示。
服務契約代碼如下所示:
```
1 [ServiceContract(Namespace ="http://www.cnblogs.com/zhili/")]
2 public interface IEmployees
3 {
4 // 契約操作不再使用操作契約的方式來標識,而是使用WebGetAttribute特性來標識,從而表明該服務是Rest服務
5 [WebGet(UriTemplate = "all")]
6 IEnumerable<Employee> GetAll();
7
8 [WebGet(UriTemplate = "{id}")]
9 Employee Get(string id);
10
11 [WebInvoke(UriTemplate="/", Method="PUT")]
12 void Create(Employee employee);
13
14 [WebInvoke(UriTemplate = "/", Method = "POST")]
15 void Update(Employee employee);
16
17 [WebInvoke(UriTemplate = "/", Method = "DELETE")]
18 void Delete(string id);
19 }
20
21 [DataContract(Namespace = "http://www.cnblogs.com.zhili/")]
22 public class Employee
23 {
24 [DataMember]
25 public string Id { get; set; }
26
27 [DataMember]
28 public string Name { get; set; }
29
30 [DataMember]
31 public string Department { get; set; }
32
33 [DataMember]
34 public string Grade { get; set; }
35
36 public override string ToString()
37 {
38 return string.Format("ID: {0,-5}姓名:{1,-5}部門:{2,-5}級別:{3}",Id, Name, Department, Grade);
39 }
40 }
```
從上面代碼可以看出,Rest服務不再使用OperactionContract的方式來標識操作了,此時在Rest架構下,每個操作都被當做一種資源對待,所以這里的操作使用了WebGetAttribute特性和WebInvokeAttribute來標識。下面具體看看契約的具體實現,即Rest服務的實現代碼。
```
1 namespace WCFContractAndService
2 {
3 public class EmployeesService : IEmployees
4 {
5 private static IList<Employee> employees = new List<Employee>
6 {
7 new Employee{ Id = "0001", Name = "LearningHard", Department = "開發部",Grade = "G6"},
8 new Employee{Id = "0002", Name = "張三", Department = "QA", Grade = "G5"}
9 };
10
11 public Employee Get(string id)
12 {
13 Employee employee = employees.FirstOrDefault(e => e.Id == id);
14 if (null == employee)
15 {
16 WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.NotFound;
17 }
18 return employee;
19 }
20
21 public IEnumerable<Employee> GetAll()
22 {
23 return employees;
24 }
25
26 public void Create(Employee employee)
27 {
28 employees.Add(employee);
29 }
30
31
32 public void Update(Employee emp)
33 {
34 this.Delete(emp.Id);
35 employees.Add(emp);
36 }
37
38 public void Delete(string id)
39 {
40 Employee employee = this.Get(id);
41 if (null != employee)
42 {
43 employees.Remove(employee);
44 }
45 }
46 }
47 }
```
第二步:實現Rest服務宿主。這里還是使用控制臺程序來作為宿主程序,主要的實現代碼如下所示:
```
namespace RestServiceHost
{
class Program
{
static void Main(string[] args)
{
// Rest服務使用WebServiceHost類來為服務提供宿主
using (WebServiceHost webHost = new WebServiceHost(typeof(EmployeesService)))
{
webHost.Opened += delegate
{
Console.WriteLine("Rest Employee Service 開啟成功!");
};
webHost.Open();
Console.Read();
}
}
}
}
```
對應的配置文件內容如下所示:
```
<configuration>
<system.serviceModel>
<services>
<service name="WCFContractAndService.EmployeesService">
<!--這里Rest服務只能使用WebHttpBinding來作為綁定-->
<endpoint address="http://localhost:9003/employeeService"
binding="webHttpBinding" contract="RestContract.IEmployees"/>
</service>
</services>
</system.serviceModel>
</configuration>
```
第三步:實現Rest服務調用客戶端。這里通過通道工廠的方式來創建代理對象的,具體的實現代碼如下所示:
```
1 namespace WCFClient
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 using (ChannelFactory<IEmployees> channelFactory = new ChannelFactory<IEmployees>("employeeService"))
8 {
9 // 創建代理類
10 IEmployees proxy = channelFactory.CreateChannel();
11
12 Console.WriteLine("所有員工信息:");
13
14 // 通過代理類來對Rest服務進行操作
15 Array.ForEach<Employee>(proxy.GetAll().ToArray(), emp => Console.WriteLine(emp.ToString()));
16
17 Console.WriteLine("\n添加一個新員工{0003}:");
18 proxy.Create(new Employee
19 {
20 Id = "0003", Name="李四", Department="財務部", Grade="G7"
21 });
22
23 Array.ForEach<Employee>(proxy.GetAll().ToArray(), emp => Console.WriteLine(emp.ToString()));
24
25 Console.WriteLine("\n修改員工(0003)信息:");
26 proxy.Update(new Employee
27 {
28 Id = "0003", Name="李四", Department = "銷售部", Grade ="G8"
29 });
30 Array.ForEach<Employee>(proxy.GetAll().ToArray(), emp => Console.WriteLine(emp.ToString()));
31 Console.WriteLine("\n刪除員工(0002)信息:");
32 proxy.Delete("0002");
33 Array.ForEach<Employee>(proxy.GetAll().ToArray(), emp => Console.WriteLine(emp.ToString()));
34
35 Console.Read();
36 }
37 }
38 }
39 }
```
客戶端對應的配置文件內容如下所示:
```
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
** <webHttp/>**
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint name="employeeService" address="http://localhost:9003/employeeService" **binding="webHttpBinding"** behaviorConfiguration="webBehavior" contract="RestContract.IEmployees">
</endpoint>
</client>
</system.serviceModel>
</configuration>
```
經過上面的三步,Rest服務的構建工作就完成了,下面看看該程序的運行結果。
首先以管理員權限運行服務宿主程序,運行成功后的結果如下圖所示:

然后再運行客戶端程序,運行成功后的結果如下圖所示:

## 五、總結
到這里,本文要分享的內容就結束了,同時這也是WCF系列的最后一篇博文。WCF主要通過提供幾個新的API來對Rest服務的實現,這里包括WebHttpBinding類、WebGetAttribute、WebInvokeAttribute特性和WebServiceHost類等。接下來一篇博文將對本系列做一個總結。
本文所有源碼:[WCFRestService.zip](http://files.cnblogs.com/zhili/WCFRestService.zip)
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結