zoukankan      html  css  js  c++  java
  • 基于.Net FrameWork的 RestFul Service

    关于本文

    这篇文章的目的就是向大家阐述如何在.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 项目:

    QQ截图20131022141221

    步骤二:

    删掉自带的IService1.cs以及Service1.svc文件,同时创建以下两个文件:IRESTService.cs &RESTService.cs.

    QQ截图20131022141505

    步骤三:

    在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定义并且拥有传输方式(比如, HTTP POST, PUT, or DELETE),同时和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
     
    步骤六:
    实现在RESTService类中实现IRESTService接口:
    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";
            }
        }
    步骤七:
    为了让这个服务能够在ASP.NET兼容模式下运行,我们需要利用AspNetCompatibilityRequirements特性来修饰RESTService类:
     [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";
            }
        }
    }
    在上述的类中,类前面被打上了ServiceBehavior特性,主要是用来说明这服务端只会存在这个类的一个实例.
     
    步骤八:
    如果要使用这个RESTFul Service应用,那么这个服务就必须得被寄宿.所以我们必须做如下的更改才能让RESTService跑起来.
    在Web.config文件中,移除掉<system.servicemodel>下面的所有代码,并且加入如下的新的代码:
    <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)));  
     
    步骤九:
    运行这个应用:
    我们可以看到如下的页面:
    QQ截图20131022144957
    当运行http://localhost:****/RestService/Help的时候,我们可以看到如下页面:
    QQ截图20131022145015
     
    如何使用RESTFul Service呢?

    客户端编程

    这里我先贴出客户端代码:

    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"?>
    ",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"?>
    ", 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));
            }
        }
    }
    View Code

    下面是利用WPF写的Client程序,当点击“创建用户”按钮的时候,显示的结果:

    当点击“获取所有用户”的时候,会把所有用户的ID号给获取到:
    当点击“获取某个用户”按钮时的结果(2号用户信息被获取):
    当点击“更新某个用户”按钮时的结果(3号用户信息被更新):

    当点击“删除某个用户”按钮时候的显示结果(4号用户被删除):

     

    Client端的显示效果如下:

    源码下载

     Click here to get source code

    特别说明:本文翻译自Create and Consume RESTFul Service in .NET Framework 4.0一文,但是文中的示例已经被我重写了一遍,能够充分展示在客户端的CRUD的过程。
     
     
     
  • 相关阅读:
    ## 序列化和反序列化
    C#小型资源管理器
    codeforces #310 div1 B
    codeforces #310 div1 A
    BZOJ 1030 文本生成器
    BZOJ 2806 cheat
    BZOJ 2553 禁忌
    BZOJ 4199 品酒大会
    codeforces #309 div1 D
    codeforces #309 div1 C
  • 原文地址:https://www.cnblogs.com/scy251147/p/3382436.html
Copyright © 2011-2022 走看看