# 跟我一起学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的比较上来,具体两者功能之间的对比如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4a0059a.png)
使用 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服务的构建工作就完成了,下面看看该程序的运行结果。
首先以管理员权限运行服务宿主程序,运行成功后的结果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4a15e8d.png)
然后再运行客户端程序,运行成功后的结果如下图所示:
![](https://box.kancloud.cn/2016-01-23_56a2eb4a26c54.png)
## 五、总结
到这里,本文要分享的内容就结束了,同时这也是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 领域驱动设计实战系列总结