关于本文
这篇文章的目的就是向大家阐述如何在.net framework 4.0中创建RestFul Service并且使用它。
什么是web Services,什么是WCF
首先讲到的是web Service, 它是一种能够让客户端程序在web页面上通过HTTP协议请求需要数据的部件。我们可以用Asp.net创建普通的Web Services并且让这些Services能够被客户端程序所调用。
其次说到的是Web Services,它是一个编程平台,它能够通过遵循Simple Object Access Protocol (SOAP)方式来接收或者是发送数据。
然后就是WCF了,它是能够编写基于service-oriented architecture (SOA)服务的编程模型。通过它,开发人员可以编写出跨平台的,安全的,可靠的解决方案出来。
WCF可以为各种各样的客户提供集中式的运算服务,客户可以调用多个服务,同时,相同的服务也能够被多个用户所调用。
在创建我们的WCF项目之前,我们最好能够看一下这篇文章:Introduction of Window Communication Foundation
下面是WebServices和WCF Services的数据比对:
Web Services | WCF Services |
Web Services能够用来开发基于SOAP的消息发送和接收的应用,这些消息是XML格式的,这些xml格式的消息可以通过.net Framework 提供的工具类来进行序列化。这种可以能够自动生成元数据(metadata)来描述Web Services的技术,我们称为: The Web Services Description Language ( WSDL )(注意:WSDL是一种描述WebService服务以及说明如何与Web服务进行通信的XML语言。) | WCF Services能够用来开发基于多种协议格式的应用,SOAP只是其默认的格式。它的消息格式也是使用XML,但是序列化这些xml数据的方式有很多种。它可以通过WSDL自动的生成能够描述其应用的元数据,同时也能够利用工具来生成。 对于WCF来说,发送消息不局限于HTTP方式,TCP或者是其他的网络协议也可以。并且在这些协议中进行切换,也是轻而易举的事儿。它除了能够放到服务器上运行之外,还能够支持宿主寄存,它同时能够非常容易的支持最新的Web Services标准(SOAP 1.2 and WS-*), 它除了能够以SOAP的方式发送数据,也能够用其他的方式来进行。 |
XmlSerializer | DataContractSerializer |
当Asp.net从服务器发送数据或者是接收客户端传来的数据的时候,它是利用XmlSerializer来进行数据的转换的。 | DataContractSerializer表明,有0个或者是多个属性或者是字段需要被序列化;而DataMemberAttribute 则表明一个指定的属性或者字段需要被序列化。DataContractAttribute可以被应用到具有Public 或者是Private的属性或者字段中,所有被标记上DataContractAttribute的属性或者字段,在WCF中被称作是数据契约(DataContracts),他们将会被DataContractSerializer序列化成XML结构。 |
在System.Xml.Serialization下的XmlSerializer类及其各种特性,可以让各种.net framework 类型转换成xml结构,它对XML提供了非常好的控制。 | DataContractSerializer,DataContractAttribute,DataMemberAttribute对XML提供了很有限的控制,所以,你只能够通过命名空间去甄别。你可以利用DataContractSerializer去操控除了xml之外的各种数据结构,这种没有添加过多限制的操作,能够让DataContractSerializer更易于操控。 |
和DataContractSerializer相比,它的性能差一些 | DataContractSerializer性能好一些,性能一般会提升10%左右 |
XmlSerializer不会预知应该或者是不应该包含什么字段或者是属性到XML中。 | 利用DataMemberAttribute,在DataContractSerializer进行序列化的时候,可以非常明确地知道数据结构。 |
XmlSerializer只能够将Public类型的成员进行序列化。 | DataContractSerializer不会类型成员的属性进行检测。 |
只有继承自IEnumerable或者是ICollection接口的集合类型才能够被序列化成XML。 | DataContractSerializer可以将任何.net类型转换成xml结构. |
继承自IDictionary接口的类,比如说Hashtable,是不能够被序列化成XML的。 | 继承自IDictionary接口的类,比如说Hashtable,能够被序列化成XML。 |
XmlSerializer不支持版本控制 | DataContractSerializer支持版本控制。 |
XmlSerializer对序列化的xml语义结构基本相同。 | DataContractSerializer序列化为xml的时候,需提供明确的命名空间。 |
什么是REST和 RESTFul?
Representational State Transfer(REST)在2000年被Roy Fielding提出。它是利用World Wide Web的相关技术以及协议来构建大规模的网络软件的构架方式。它意在说明,数据资源是可以被定义,被发布的,尤其是在消息交换的简易性和可扩展性方面。
早在2000年的时候, 制定HTTP规范的首席作者之一,Roy Fielding,写了一篇名为“Architectural Styles and the Design of Network-based Software Architectures.”的博士论文,在文中,作者如是说。
REST,一种能够构建分布式超媒体驱动程序的构架方式,它包括通过利用标准的HTTP Verbs(GET
, POST
, PUT
, and DELETE
)来定义资源,从而构建Resource-Oriented Architecture (ROA)程序。这种构建可以通过Uniform Resource Identifier(URI)发布给使用者。
REST 是不绑定任何技术或者是平台的。它只是用来把工作设计成能像Web那样。人们经常把这种方式定义为“RESTFul Services”,但是事实上,利用REST 构架方式开发的WCF Services,也能够被称作是RESTFul Services。
WCF Services | RESTFul Services |
各自的网络协议之间,需要创建端点。 | 可以通过HTTP Web方式来进行数据的收发操作。 |
需要Remote Procedural Call(RPC)的支持 | 需要定义基于HTTP的统一接口 |
客户端需要添加对服务端的引用 | 不需要客户端添加对服务端的引用 |
关于案例代码?
在WCF 中,REST,其实就是一系列的.net framework 类和visual studio的一些特性和模板,通过这些东西,用户可以创建并且使用基于REST方式的WCF服务。这些服务是需要.net 3.5 SP1中的WCF Web编程模型支持的。在后面的附件中,已经包含了所有的源代码,示例,以及单元测试。
创建基础的RESTFul Service
步骤一:
利用Visual Studio 2010创建一个新的WCF 项目:
步骤二:
删掉自带的IService1.cs以及Service1.svc文件,同时创建以下两个文件:IRESTService.cs &RESTService.cs.
步骤三:
在IService.cs文件中创建Person 实体类以及一些简单的属性,同时加上DataContract & DataMember特性以便于DataContractSerialization进行序列化:
[DataContract]
public class Person
{
[DataMember(order=1)]
public string ID;
[DataMember(order=2)]
public string Name;
[DataMember(order=3)]
public string Age;
[DataMember(order=4)]
public string Address;
} 注意,一定要加上Order=*,否则,客户端向服务端添加数据的时候,会出现某些数据字段没有值的情况。
步骤四:
在IRESTService接口文件中创建方法,利用ServiceContract和OperationContrat进行修饰:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Web; namespace RESTFulDaemon { [ServiceContract] public interface IRestSerivce { //POST operation [OperationContract] [WebInvoke( UriTemplate = "" ,ResponseFormat = WebMessageFormat.Xml ,RequestFormat = WebMessageFormat.Xml ,Method = "POST" )] Person CreatePerson(Person createPerson); //Get Operation [OperationContract] [WebGet( UriTemplate = "" , ResponseFormat = WebMessageFormat.Xml , RequestFormat = WebMessageFormat.Xml )] List<Person> GetAllPerson(); [OperationContract] [WebGet( UriTemplate = "{id}" , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json )] Person GetAPerson(string id); //PUT Operation [OperationContract] [WebInvoke( UriTemplate = "" , ResponseFormat = WebMessageFormat.Json , RequestFormat = WebMessageFormat.Json , Method = "PUT" )] string UpdatePerson(Person updatePerson); //DELETE Operation [OperationContract] [WebInvoke( UriTemplate = "" , ResponseFormat = WebMessageFormat.Json , Method = "DELETE" )] string DeletePerson(string id); } }
代码中,可以利用JSON来组织数据,也可以利用XML来组织数据
步骤五:
WebGet 操作只是从服务端接收数据的操作,它可以被REST编程模型所调用。它会和OperationContact一起被应用到方法上,并且会带有UriTemplate属性和HTTP中的Get操作。
WebInvoke 可以引发服务端操作,它可以被REST编程模型所调用。当它具有UriTemplate定义并且拥有传输方式(比如, HTTPPOST
,PUT
, orDELETE
),同时和OperationContact放在一块的时候,就表明当前方法支持的是不同类型的HTTP传输方式。默认是POST方式。
下面 是具体说明:
Person CreatePerson(Person createPerson);
//It is basically insert operation, so WebInvoke HTTP POST is used
List<person> GetAllPerson();
Person GetAPerson(string id);
//These two methods retrieve the information, so WebGet (HTTP Get) is used
Person UpdatePerson(Person updatePerson);
//This method updates the available information, so WebInvoke HTTP PUT is used
void DeletePerson(string id);
//This method deletes the available information,
//so WebInvoke HTTP DELETE is used
public class RESTService:IRestSerivce { List<Person> persons = new List<Person>(); private static int tCount = 0; public Person CreatePerson(Person person) { tCount++; person.ID = tCount.ToString(); persons.Add(person); return person; } public List<Person> GetAllPerson() { return persons.ToList(); } public Person GetAPerson(string id) { return persons.FirstOrDefault(e => e.ID.Equals(id)); } public string UpdatePerson(Person updatePerson) { Person p = persons.FirstOrDefault(e => e.ID.Equals(updatePerson.ID)); p.Name = updatePerson.Name+"[updated]"; p.Age = updatePerson.Age+"[updated]"; p.Address = updatePerson.Address+"[updated]"; return "Updated success"; } public string DeletePerson(string id) { persons.RemoveAll(e => e.ID.Equals(id)); return "success"; } }
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class RESTService:IRestSerivce { //Code here }
using System.Collections.Generic; using System.Linq; using System.ServiceModel.Activation; using System.ServiceModel; using System; namespace RESTFulDaemon { [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public class RESTService:IRestSerivce { List<Person> persons = new List<Person>(); private static int tCount = 0; public Person CreatePerson(Person person) { tCount++; person.ID = tCount.ToString(); persons.Add(person); return person; } public List<Person> GetAllPerson() { return persons.ToList(); } public Person GetAPerson(string id) { return persons.FirstOrDefault(e => e.ID.Equals(id)); } public string UpdatePerson(Person updatePerson) { Person p = persons.FirstOrDefault(e => e.ID.Equals(updatePerson.ID)); p.Name = updatePerson.Name+"[updated]"; p.Age = updatePerson.Age+"[updated]"; p.Address = updatePerson.Address+"[updated]"; return "Updated success"; } public string DeletePerson(string id) { persons.RemoveAll(e => e.ID.Equals(id)); return "success"; } } }
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true">
</serviceHostingEnvironment>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"></standardEndpoint>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
其中,<serviceHostingEnvironment>标记用于制定当前应用将运行在Asp.net兼容模式下.
<standardEndpoints>
标记用于为RESTFul应用获取WebHelp.
然后,在Global.asax文件中,我们需要在Application_Start事件中添加如下代码:
RouteTable.Routes.Add(new ServiceRoute("RestService", new WebServiceHostFactory(), typeof(RESTSerivce)));
客户端编程
这里我先贴出客户端代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
using System.Text; using System.Windows; using System.Net; using System.Xml.Serialization; using System.IO; using System.Text.RegularExpressions; using System.Threading; using System.Diagnostics; using System.Runtime.Serialization.Json; using System.Collections.Generic; namespace ConsumingDaemon { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void btnCreate_Click(object sender, RoutedEventArgs e) { //Client entity Person p = new Person(); p.ID = "1"; p.Name = "scy"; p.Age = "25"; p.Address = "China HeNan"; HttpWebRequest request = WebRequest.Create("http://localhost:11126/RESTService/") as HttpWebRequest; request.KeepAlive = false; request.Method = "POST"; StringWriter sw = new StringWriter(); XmlSerializer xs = new XmlSerializer(p.GetType()); xs.Serialize(sw,(object)p); string result = sw.ToString(); //generate xml that required result = result.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n",string.Empty); Regex regex = new Regex("<Person.*?>"); Match m = regex.Match(result); if (m.Success) { result = result.Replace(m.Value,"<Person xmlns=\"http://schemas.datacontract.org/2004/07/RESTFulDaemon\">"); } AutoResetEvent autoResetEvent = new AutoResetEvent(false); byte[] buffer = Encoding.Default.GetBytes(result); request.ContentLength = buffer.Length; request.ContentType = "text/xml"; request.BeginGetRequestStream((iar) => { Stream PostData = request.EndGetRequestStream(iar); PostData.Write(buffer, 0, buffer.Length); PostData.Close(); autoResetEvent.Set(); },null); autoResetEvent.WaitOne(); HttpWebResponse resp = request.GetResponse() as HttpWebResponse; StreamReader loResponseStream = new StreamReader(resp.GetResponseStream(), Encoding.Default); string Response = loResponseStream.ReadToEnd(); MessageBox.Show(Response); loResponseStream.Close(); resp.Close(); } private void btnGetAll_Click(object sender, RoutedEventArgs e) { WebClient client = new WebClient(); client.Headers["Content-type"] = "application/json"; // invoke the REST method byte[] data = client.DownloadData("http://localhost:11126/RESTService"); // put the downloaded data in a memory stream MemoryStream ms = new MemoryStream(); ms = new MemoryStream(data); // deserialize from json DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(List<Person>)); List<Person> result = ser.ReadObject(ms) as List<Person>; string resultEx = string.Empty; foreach (var r in result) { resultEx += r.ID + "--"; } MessageBox.Show(resultEx); } private void btnGetOne_Click(object sender, RoutedEventArgs e) { string id = txtGet.Text; Person result = GetOneByID(id); MessageBox.Show(result.ID + "|" + result.Name + "|" + result.Age + "|" + result.Address); } private Person GetOneByID(string id) { WebClient client = new WebClient(); client.Headers["Content-type"] = "application/json"; byte[] data = client.DownloadData("http://localhost:11126/RESTService/" + id); MemoryStream ms = new MemoryStream(); ms = new MemoryStream(data); DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person)); Person result = ser.ReadObject(ms) as Person; return result; } private void btnUpdateOne_Click(object sender, RoutedEventArgs e) { #region The XML way //string id = txtUpdate.Text; ////Client entity //Person p = new Person(); //p.ID = "3"; //p.Name = "scy1"; //p.Age = "28"; //p.Address = "Amearican Person"; //HttpWebRequest request = WebRequest.Create("http://localhost:11126/RESTService/") as HttpWebRequest; //request.KeepAlive = false; //request.Method = "PUT"; //StringWriter sw = new StringWriter(); //XmlSerializer xs = new XmlSerializer(p.GetType()); //xs.Serialize(sw, (object)p); //string result = sw.ToString(); ////generate xml that required //result = result.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n", string.Empty); //Regex regex = new Regex("<Person.*?>"); //Match m = regex.Match(result); //if (m.Success) //{ // result = result.Replace(m.Value, "<Person xmlns=\"http://schemas.datacontract.org/2004/07/RESTFulDaemon\">"); //} //AutoResetEvent autoResetEvent = new AutoResetEvent(false); //byte[] buffer = Encoding.Default.GetBytes(result); //request.ContentLength = buffer.Length; //request.ContentType = "text/xml"; //request.BeginGetRequestStream((iar) => //{ // Stream PostData = request.EndGetRequestStream(iar); // PostData.Write(buffer, 0, buffer.Length); // PostData.Close(); // autoResetEvent.Set(); //}, null); //autoResetEvent.WaitOne(); //HttpWebResponse resp = request.GetResponse() as HttpWebResponse; //StreamReader loResponseStream = new StreamReader(resp.GetResponseStream(), Encoding.Default); //string Response = loResponseStream.ReadToEnd(); //MessageBox.Show(Response); //loResponseStream.Close(); //resp.Close(); #endregion #region The JSON way string id = txtUpdate.Text; Person p = GetOneByID(id); WebClient client = new WebClient(); client.Headers["Content-type"] = "application/json"; // serialize the object data in json format MemoryStream ms = new MemoryStream(); DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person)); ser.WriteObject(ms, p); byte[] resultData = client.UploadData("http://localhost:11126/RESTService/", "PUT", ms.ToArray()); MessageBox.Show(Encoding.Default.GetString(resultData)); #endregion } private void btnDeleteOne_Click(object sender, RoutedEventArgs e) { string id = txtDelete.Text; WebClient client = new WebClient(); client.Headers["Content-type"] = "application/json"; // serialize the object data in json format MemoryStream ms = new MemoryStream(); DataContractJsonSerializer ser =new DataContractJsonSerializer(typeof(string)); ser.WriteObject(ms, id); // invoke the REST method byte[] data = client.UploadData("http://localhost:11126/RESTService/", "DELETE", ms.ToArray()); MessageBox.Show(Encoding.Default.GetString(data)); } } }
下面是利用WPF写的Client程序,当点击“创建用户”按钮的时候,显示的结果:
![](https://i-blog.csdnimg.cn/blog_migrate/b8872c4ca16a6640f4a5d29757a9093e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/53ef274d5967df52981a638a2922e81f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/25d68fae2894987bcc99f78d13b0b204.png)
![](https://i-blog.csdnimg.cn/blog_migrate/755f347b7eb39531df18cb85a8800c23.png)
当点击“删除某个用户”按钮时候的显示结果(4号用户被删除):
Client端的显示效果如下:
源码下载