[WCF REST] 一个简单的REST服务实例
2013-04-01 17:06
281 查看
转:/article/1308240.html
微软在WCF 3.5中就通过提供基于Web HTTP的编程模式使我们很容易地创建基于REST的服务,WCF 4.0中对此进行了较大的改进。为了让读者对REST在WCF中的应用有一个大致的了解,我们先来进行一个简单的实例演示。 [源代码从这里下载]
[/code]
接下来我们定义了如下一个表示服务契约的接口IEmployeesService。和基于SOAP的服务契约定义不同,我们无需在相应的操作方法上面应用OperationContractAttribute特性,但是应用在接口/类上的ServiceContractAttribute特性仍是必需的。在这里替换OperationContractAttribute特性的分别是WebGetAttribute和WebInvokeAttribute,它们均定义在System.ServiceModel.Web程序集中。
[/code]
契约接口IEmployeesService中定义了5个操作,分别用于实现针对员工数据的获取、添加、修改和删除。按照REST设计原则,我们将被操作的员工信息体现为某种网络资源,而操作类型最好与相应的HTTP方法相匹配。在操作方法中针对资源的操作类型与HTTP方法之间的匹配是通过应用在它们上面的WebGetAttribute和WebInvokeAttribute特性来体现。
WebGetAttribute针对GET方法,而其他的HTTP方法则通过WebInvokeAttribute的Method属性来体现。在IEmployeesService中,两个用于获取员工信息GetAll和Get方法均应用了WebGetAttribute特性,而其他的Create、Update和Delete方法在应用了WebInvokeAttribute特性,并且其Method属性被分别设置为PUT、POST和DELETE。
WebGetAttribute和WebInvokeAttribute和均具有相同的属性UriTemplate,该属性用于定义作为最终操作URI的模板。我们不仅可以通过UriTemplate属性为操作指定一个相对于终结点地址的静态路径,还可以通过占位符实现路径中的动态部分与参数之间的映射。
同样以定义在契约接口IEmployeesService中的5个操作方法为例,如果终结点地址为http://127.0.0.1:3721/employees,由于用于返回所有员工列表的GetAll操作的UriTemplate被设置“All”,所以其地址为http://127.0.0.1:3721/employees。用于返回指定员工ID的Get操作的UriTemplate被设置成“{id}”,意味着我们直接在表示请求地址的URI中指定员工的ID,而它会自动映射为该操作方法的参数id。用于删除某个指定员工的Delete操作具有相同的UriTemplate设置,而用于创建添加新员工和修改现有员工信息的Create和Update操作,由于作为参数的Employee对象具有ID属性,所以直接采用终结点地址。
[/code]
值得一提的是,不论是用于获取某个指定ID的员工信息的Get方法,还是用于修改和删除员工记录的Update和Delete方法,当指定ID的员工不存在时都通过WebOperationContext表示当前Web操作上下文的对象将回复状态设置为NotFound(即404
Not Found),这体现了我们的服务是基于Web的。
接下来我们通过自我寄宿的方式对上面定义的EmployeesService服务进行寄宿,下面是相应的配置。我们为寄宿的服务添加了唯一一个终结点,并简单地指定了其ABC三要素。和我们之前配置的终结点不同的是,在这里我们采用的绑定类型为WebHttpBinding。
[/code]
最终我们通过如下的程序进行服务的寄宿。之前我们总是使用基于服务类型创建的ServiceHost进行服务寄宿,在这里我们使用的是ServiceHost它的子类WebServiceHost。
[/code]
我们也可以通过浏览器调用Get操作并直接通过在地址中指定员工的ID(http://127.0.0.1:3721/employees/001)并得到以XML表示的基于相应员工的信息。下图所示XML正式ID为001的Employee对象序列化后的结果。如果在请求地址中指定一个不存在的ID(比如http://127.0.0.1:3721/employees/003),由于Get方法中指定了回复状态为NotFound,我们会得到类似于访问资源不存在的错误信息,就像访问一个不存在的Web页面一样。
上面我们演示了通过浏览器以HTTP-GET方式请求操作地址的方式从而直接将返回结果呈现出来,现在我们来演示如何使用通过ChannelFactory<TChannel>创建的服务代理进行服务调用。我们首先在作为客户端应用程序的Client项目中创建一个App.config,并定义如下的配置。
[/code]
如上面的配置片断所示,我们定义了一个与服务端相匹配的客户端终结点,该终结点上应用了一个WebHttpBehavior终结点行为。WebHttpBehavior可以说是整个Web HTTP编程模型的核心,绝大部分针对Web的支持都是通过该行为实现的。实际上服务端终结点通过WebServiceHost应用了这个终结点行为。
[/code]
服务调用程序如上所示,我们模拟了员工的添加、修改和删除。程序之后会在客户端控制台产生如下的输出。
所有员工列表:
[/code]
从编程角度来看,我们采用与SOAP服务完全一样的服务调用方式,那么如何反映出服务调用基于Web的本质呢?首先,之前我们能够通过浏览器访问GetAll和Get两个操作可以证明这两个服务操作是基于HTTP-GET的,返回的数据直接以单纯的XML返回,并没有封装成SOAP。为了证明Create、Update和Delete也是完全基于Web的,我们可以通过Fiddler来分析HTTP请求的内容。
如下所示的三段XML片断分别对应着针对上述三个服务操作调用的HTTP请求消息,从这我们可以看出它们就是单纯的针对PUT、POST和DELETE方法的HTTP请求,而传输给服务端的数据直接作为消息的主体,并没有封装成SOAP消息。
[/code]
微软在WCF 3.5中就通过提供基于Web HTTP的编程模式使我们很容易地创建基于REST的服务,WCF 4.0中对此进行了较大的改进。为了让读者对REST在WCF中的应用有一个大致的了解,我们先来进行一个简单的实例演示。 [源代码从这里下载]
一、定义服务契约
在这个实例中,我们创建一个简单的服务来管理员工的基本信息。至于实例程序的结构,我们依然采用熟悉的包含三个项目(Service.Interface、Service和Client)的解决方案。如下所示的是定义在Service.Interface中用于表示员工的Employee类的定义,它是一个数据契约。[code] [DataContract(Namespace="http://www.artech.com/")]
public class Employee
{
[DataMember]
public string Id{ get; set;}
[DataMember]
public string Name{ get; set;}
[DataMember]
public string Department{ get; set;}
[DataMember]
public string Grade{ get; set;}
public override string ToString()
{
return string.Format("ID:{0,-5}姓名:{1, -5}级别:{2, -4} 部门:{3}",Id, Name, Grade, Department);
}
}
[/code]
接下来我们定义了如下一个表示服务契约的接口IEmployeesService。和基于SOAP的服务契约定义不同,我们无需在相应的操作方法上面应用OperationContractAttribute特性,但是应用在接口/类上的ServiceContractAttribute特性仍是必需的。在这里替换OperationContractAttribute特性的分别是WebGetAttribute和WebInvokeAttribute,它们均定义在System.ServiceModel.Web程序集中。
[code] [ServiceContract(Namespace="http://www.artech.com/")]
public interface IEmployees
{
[WebGet(UriTemplate = "all")]
IEnumerable<Employee> GetAll();
[WebGet(UriTemplate = "{id}")]
Employee Get(string id);
[WebInvoke(UriTemplate = "/", Method = "POST")]
void Create(Employee employee);
[WebInvoke(UriTemplate = "/", Method = "PUT")]
void Update(Employee employee);
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
void Delete(string id);
}
[/code]
契约接口IEmployeesService中定义了5个操作,分别用于实现针对员工数据的获取、添加、修改和删除。按照REST设计原则,我们将被操作的员工信息体现为某种网络资源,而操作类型最好与相应的HTTP方法相匹配。在操作方法中针对资源的操作类型与HTTP方法之间的匹配是通过应用在它们上面的WebGetAttribute和WebInvokeAttribute特性来体现。
WebGetAttribute针对GET方法,而其他的HTTP方法则通过WebInvokeAttribute的Method属性来体现。在IEmployeesService中,两个用于获取员工信息GetAll和Get方法均应用了WebGetAttribute特性,而其他的Create、Update和Delete方法在应用了WebInvokeAttribute特性,并且其Method属性被分别设置为PUT、POST和DELETE。
WebGetAttribute和WebInvokeAttribute和均具有相同的属性UriTemplate,该属性用于定义作为最终操作URI的模板。我们不仅可以通过UriTemplate属性为操作指定一个相对于终结点地址的静态路径,还可以通过占位符实现路径中的动态部分与参数之间的映射。
同样以定义在契约接口IEmployeesService中的5个操作方法为例,如果终结点地址为http://127.0.0.1:3721/employees,由于用于返回所有员工列表的GetAll操作的UriTemplate被设置“All”,所以其地址为http://127.0.0.1:3721/employees。用于返回指定员工ID的Get操作的UriTemplate被设置成“{id}”,意味着我们直接在表示请求地址的URI中指定员工的ID,而它会自动映射为该操作方法的参数id。用于删除某个指定员工的Delete操作具有相同的UriTemplate设置,而用于创建添加新员工和修改现有员工信息的Create和Update操作,由于作为参数的Employee对象具有ID属性,所以直接采用终结点地址。
二、创建/寄宿服务
在控制台程序Service中我们定义了如下一个实现了契约接口IEmployeesService的服务类型EmployeesService。简单 起见,我们直接通过一个静态字段employees表示存储的员工列表,该静态字段在初始化的工作中被添加了两个ID分别为001和002的Employee对象。针对员工信息的获取、添加、修改和删除的操作均在此列表中进行。
[code]public class EmployeesService : IEmployees
{
private static IList<Employee> employees = new List<Employee>
{
new Employee{ Id = "001", Name="张三", Department="开发部", Grade = "G7"},
new Employee{ Id = "002", Name="李四", Department="人事部", Grade = "G6"}
};
public Employee Get(string id)
{
Employee employee = employees.FirstOrDefault(e => e.Id == id);
if (null == employee)
{
WebOperationContext.Current.OutgoingResponse.StatusCode =
HttpStatusCode.NotFound;
}
return employee;
}
public void Create(Employee employee)
{
employees.Add(employee);
}
public void Update(Employee employee)
{
this.Delete(employee.Id);
employees.Add(employee);
}
public void Delete(string id)
{
Employee employee = this.Get(id);
if (null != employee)
{
employees.Remove(employee);
}
}
public IEnumerable<Employee> GetAll()
{
return employees;
}
}
[/code]
值得一提的是,不论是用于获取某个指定ID的员工信息的Get方法,还是用于修改和删除员工记录的Update和Delete方法,当指定ID的员工不存在时都通过WebOperationContext表示当前Web操作上下文的对象将回复状态设置为NotFound(即404
Not Found),这体现了我们的服务是基于Web的。
接下来我们通过自我寄宿的方式对上面定义的EmployeesService服务进行寄宿,下面是相应的配置。我们为寄宿的服务添加了唯一一个终结点,并简单地指定了其ABC三要素。和我们之前配置的终结点不同的是,在这里我们采用的绑定类型为WebHttpBinding。
[code] <configuration>
<system.serviceModel>
<services>
<service name="Artech.WcfServices.Service.EmployeesService">
<endpoint address="http://127.0.0.1:3721/employees"
binding="webHttpBinding"
contract="Artech.WcfServices.Service.Interface.IEmployees"/>
</service>
</services>
</system.serviceModel>
</configuration>
[/code]
最终我们通过如下的程序进行服务的寄宿。之前我们总是使用基于服务类型创建的ServiceHost进行服务寄宿,在这里我们使用的是ServiceHost它的子类WebServiceHost。
[code] using (WebServiceHost host = new WebServiceHost(typeof(EmployeesService)))
{
host.Open();
Console.Read();
}
[/code]
三、进行服务调用
由于我们寄宿的服务完全是基于Web的,所以和普通的Web站点没有本质的区别。由于EmployeesService服务的GetAll和Get操作支持HTTP-GET请求,所以我们完全可以在浏览器中针对操作的地址发起请求,而返回的数据可以直接显示在浏览器上。下图所示的是通过浏览器调用GetAll操作(http://127.0.0.1:3721/employees/all)得到的结果,我们可以看到所有员工的列表以XML的形式返回。我们也可以通过浏览器调用Get操作并直接通过在地址中指定员工的ID(http://127.0.0.1:3721/employees/001)并得到以XML表示的基于相应员工的信息。下图所示XML正式ID为001的Employee对象序列化后的结果。如果在请求地址中指定一个不存在的ID(比如http://127.0.0.1:3721/employees/003),由于Get方法中指定了回复状态为NotFound,我们会得到类似于访问资源不存在的错误信息,就像访问一个不存在的Web页面一样。
上面我们演示了通过浏览器以HTTP-GET方式请求操作地址的方式从而直接将返回结果呈现出来,现在我们来演示如何使用通过ChannelFactory<TChannel>创建的服务代理进行服务调用。我们首先在作为客户端应用程序的Client项目中创建一个App.config,并定义如下的配置。
[code] <configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint name="employeeService"
address="http://127.0.0.1:3721/employees"
behaviorConfiguration="webBehavior"
binding="webHttpBinding"
contract="Artech.WcfServices.Service.Interface.IEmployees"/>
</client>
</system.serviceModel>
</configuration>
[/code]
如上面的配置片断所示,我们定义了一个与服务端相匹配的客户端终结点,该终结点上应用了一个WebHttpBehavior终结点行为。WebHttpBehavior可以说是整个Web HTTP编程模型的核心,绝大部分针对Web的支持都是通过该行为实现的。实际上服务端终结点通过WebServiceHost应用了这个终结点行为。
[code] using(ChannelFactory<IEmployees> channelFactory = new ChannelFactory<IEmployees>("employeeService"))
{
IEmployees proxy = channelFactory.CreateChannel();
Console.WriteLine("所有员工列表:");
Array.ForEach<Employee>(proxy.GetAll().ToArray(),employee=>Console.WriteLine(employee));
Console.WriteLine("\n添加一个新员工(003):");
proxy.Create(new Employee
{
Id= "003",
Name= "王五",
Grade = "G9",
Department= "行政部"
});
Array.ForEach<Employee>(proxy.GetAll().ToArray(),employee => Console.WriteLine(employee));
Console.WriteLine("\n修改员工(003)信息:");
proxy.Update(new Employee
{
Id= "003",
Name= "王五",
Grade = "G11",
Department= "销售部"
});
Array.ForEach<Employee>(proxy.GetAll().ToArray(), employee => Console.WriteLine(employee));
Console.WriteLine("\n删除员工(003)信息:");
proxy.Delete("003");
Array.ForEach<Employee>(proxy.GetAll().ToArray(), employee => Console.WriteLine(employee));
}
[/code]
服务调用程序如上所示,我们模拟了员工的添加、修改和删除。程序之后会在客户端控制台产生如下的输出。
所有员工列表:
[code] 所有员工列表:
ID: 001姓名: 张三 级别: G7 部门: 开发部
ID: 002姓名: 李四 级别: G6 部门: 人事部
添加一个新员工(003):
ID: 001姓名: 张三 级别: G7 部门: 开发部
ID: 002姓名: 李四 级别: G6 部门: 人事部
ID: 003姓名: 王五 级别: G9 部门: 行政部
修改员工(003)信息:
ID: 001姓名: 张三 级别: G7 部门: 开发部
ID: 002姓名: 李四 级别: G6 部门: 人事部
ID: 003姓名: 王五 级别: G11部门: 销售部
删除员工(003)信息:
ID: 001姓名: 张三 级别: G7 部门: 开发部
ID: 002姓名: 李四 级别: G6 部门: 人事部
[/code]
从编程角度来看,我们采用与SOAP服务完全一样的服务调用方式,那么如何反映出服务调用基于Web的本质呢?首先,之前我们能够通过浏览器访问GetAll和Get两个操作可以证明这两个服务操作是基于HTTP-GET的,返回的数据直接以单纯的XML返回,并没有封装成SOAP。为了证明Create、Update和Delete也是完全基于Web的,我们可以通过Fiddler来分析HTTP请求的内容。
如下所示的三段XML片断分别对应着针对上述三个服务操作调用的HTTP请求消息,从这我们可以看出它们就是单纯的针对PUT、POST和DELETE方法的HTTP请求,而传输给服务端的数据直接作为消息的主体,并没有封装成SOAP消息。
[code] Create:
PUT http://jinnan-pc:3721/employees/ HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: jinnan-pc:3721
Content-Length: 187
Expect: 100-continue
Accept-Encoding: gzip, deflate
<Employee xmlns="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Department>销售部</Department><Grade>G11</Grade><Id>003</Id><Name>王五</Name></Employee>
Update:
POST http://jinnan-pc:3721/employees/ HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: jinnan-pc:3721
Content-Length: 186
Expect: 100-continue
Accept-Encoding: gzip, deflate
<Employee xmlns="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Department>行政部</Department><Grade>G9</Grade><Id>003</Id><Name>王五</Name></Employee>
Delete:
DELETE http://jinnan-pc:3721/employees/003 HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: jinnan-pc:3721
Content-Length: 80
Expect: 100-continue
Accept-Encoding: gzip, deflate
[/code]
相关文章推荐
- [WCF REST] 一个简单的REST服务实例
- 【转载】[WCF REST] 一个简单的REST服务实例
- [WCF REST] 一个简单的REST服务实例
- [WCF REST] 一个简单的REST服务实例
- [WCF REST] 一个简单的REST服务实例
- 一个简单的REST服务实例
- C# 一个WCF简单实例
- Silverlight实用窍门系列:30.Silverlight中创建一个最简单的WCF RIA Services访问数据库实例【实例源码+数据库下载】
- 一个通过JSONP跨域调用WCF REST服务的例子(以jQuery为例)
- (C#)一个WCF简单实例
- 30.Silverlight中创建一个最简单的WCF RIA Services访问数据库实例
- 一个简单的WCF实例
- 一个通过JSONP跨域调用WCF REST服务的例子(以jQuery为例)
- 简单的实例来理解WCF 数据服务
- WCF并发(Concurrency)的本质:同一个服务实例上下文(InstanceContext)同时处理多个服务调用请求
- WCF热门问题编程示例(2)多个实例调用一个WCF服务操作,需要等待服务响应吗
- 一步一个脚印学习WCF系列之WCF概要—WCF服务的创建与调用HelloWorld实例,通过配置文件方式(六)
- 热门话题:使用WCF实现SOA面向服务编程(二)——实现简单的WCF开发实例
- 一个简单AJAX请求WCF实例
- (C#)一个WCF简单实例