zoukankan      html  css  js  c++  java
  • 【REST WCF】30分钟理论到实践

    【REST WCF】30分钟理论到实践

    http://blog.vsharing.com/agiledo/

    先来点理论知识,来自 http://www.cnblogs.com/simonchen/articles/2220838.html

    一.什么是Rest


      REST软件架构是由Roy Thomas Fielding博士2000年在他的论文《Architectural Styles and the Design of Network- based Software Architectures》首次提出的。他提出的理论对后来的Web技术的发展产生了巨大的影响,他是许多重要Web架构标准的设计者,这些标准就是 HTTP、URI等。

    • Rest的英文全称是“Representational State Transfer”。中文翻译为“表述性状态转移”。REST本身只是为分布式超媒体系统设计的一种架构风格,而不是标准。
    • 那么如何理解“Representational State Transfer”这句话呢?下面我们来解释一下:
      1.     Representational :中文直译:代表的,表像的。如果把WEB 服务器端中所有的东西(数据)都看作是资源(Resource),那么呈现在用户面前(客户端)的就是资源的表像(Representation)。每一个资源都有自己的唯一标识(URI)。
      2.     State :中文直译:状态。首先这个状态是客户端的状态,而不是服务器端的状态(在REST 中,服务器端应该是无状态的)。那么,把State和Representation联系在一起(Representational State),可以理解成:每一个资源(Resource)在客户端的表像(Representation)就是客户端的一个状态(State)。
      3.     Transfer:中文直译:转移。当用户通过不同的URI访问不同的资源时,客户端的表像(Representation)也会随着变化,也就意味着客户端的状态变更(Transfer)了,连起来就是:Representational State Transfer。
    • REST=老的Web规范+3个新的规范:REST实际上也是基于已有的Web规范集合产生的。传统的Web应用大都是BS系统,这些系统共同遵循一些老的Web规范,这些规范主要包含 3条:
      1.   客户-服务器:这种规范的提出,改善了用户接口跨多个平台的可移植性,并且通过简化服务器组件,改善了系统的可伸缩性。最为关键的是通过分离用户接口和数据存储这两个关注点,使得不同用户终端享受相同数据成为了可能。
      2.   无状态性:无状态性是在客户-服务器约束的基础上添加的又一层规范。他要求通信必须在本质上是无状态的,即从客户到服务器的每个request都 必须包含理解该request所必须的所有信息。这个规范改善了系统的可见性(无状态性使得客户端和服务器端不必保存对方的详细信息,服务器只需要处理当 前request,而不必了解所有的request历史),可靠性(无状态性减少了服务器从局部错误中恢复的任务量),可伸缩性(无状态性使得服务器端可 以很容易的释放资源,因为服务器端不必在多个request中保存状态)。同时,这种规范的缺点也是显而易见得,由于不能将状态数据保存在服务器上的共享 上下文中,因此增加了在一系列request中发送重复数据的开销,严重的降低了效率。
      3.       缓存:为了改善无状态性带来的网络的低效性,我们填加了缓存约束。缓存约束允许隐式或显式地标记一个response中的数据,这样就赋予了客户 端缓存response数据的功能,这样就可以为以后的request共用缓存的数据,部分或全部的消除一部分交互,增加了网络的效率。但是用于客户端缓存了信息,也就同时增加了客户端与服务器数据不一致的可能,从而降低了可靠性。
    • REST在原有的架构上增加了3个新规范:统一接口、分层系统和按需代码:
      1.       统一接口:REST架构风格的核心特征就是强调组件之间有一个统一的接口,这表现在REST世界里,网络上所有的事物都被抽象为资源,而REST 就是通过通用的链接器接口对资源进行操作。这样设计的好处是保证系统提供的服务都是解耦的,极大的简化了系统,从而改善了系统的交互性和可重用性。并且 REST针对Web的常见情况做了优化,使得REST接口被设计为可以高效的转移大粒度的超媒体数据,这也就导致了REST接口对其它的架构并不是最优的。
      2.       分层系统:分层系统规则的加入提高了各种层次之间的独立性,为整个系统的复杂性设置了边界,通过封装遗留的服务,使新的服务器免受遗留客户端的影响,这也就提高了系统的可伸缩性。
      3.       按需代码:REST允许对客户端功能进行扩展。比如,通过下载并执行 applet或脚本形式的代码,来扩展客户端功能。但这在改善系统可扩展性的同时,也降低了可见性。所以它只是REST的一个可选的约束。

    二.Rest的特点


         由于Rest遵守的这些规范,因此Rest架构的特点也非常的明显:

    •   REST是一种架构,而不是一个规范。
    •   REST是一种典型的Client-Server架构,但是强调瘦服务器端,服务器端只应该处理跟数据有关的操作,所有有关显示的工作都应该放在客户端。
    •   在REST架构中,服务器是无状态的,也就是说服务器不会保存任何与客户端的会话状态信息。所有的状态信息只能放在双方沟通的 Message(消息)中。
    •   REST架构是幂等的,对于相同的请求,服务器返回的结果也是相同的,因此服务器端返回的结果是可以缓存的,既可以存在客户端也可以存在代理服务器端。
    •   在REST架构中,所有的操作都是基于统一的方式进行的:
      1.     每个Resource都有一个唯一的ID。
      2.     通过Representation(客户端)来处理Resource(服务器端)。也就是说,客户端不能直接操作服务器端的Resource,只能通过对相应的Representation的操作,并发送相应的请求,最后由服务器端来处理Resource并返回结果。
      3.     客户端和服务器端传送的任何一个Message(消息),都应该是自描述的。也就是说处理这个 Message所需要的上下文环境都应该包含在这个Message当中。
      4.     多媒体的交互系统,客户端和服务器端传送的内容可以是文档,图片,声音等等多媒体数据,这也是一个Resource能够对应不同的Representation(例如文档,图片等)的基础。 
    •   分层结构,像TCP/IP的分层结构一样,第n层使用第n-1层提供的服务并为第n+1层提供服务。在REST中,Client- Server之间加入了Proxy层和Gateway层。在这些中间层可以加入一些业务处理以外的功能,譬如:负载均衡,安全控制等等。
    •   Code-On-Demand,客户端可以访问服务器端的Resource,但并不知道如何处理服务器端返回的结果,这个处理过程的代码应该是从服务器端发送过来,然后在客户端执行,也就是说客户端的功能是根据需要动态从服务器端获得的。一个很简单的例子,Applet就是从服务器端下载然后在客户端执行的。注意,这个特性是可选的(Optional),也就是说在你的REST实现当中,可以不考虑这个特性。

    三.Rest的优点


          既然Rest风格有这些特点,那么也就具备了许多优点:

    •   缓存使用 HTTP 向 RESTful 端点申请数据时,用到的 HTTP 动词是 GET。对于 GET 请求响应中返回的资源,可以用多种不同的方式进行缓存。Conditional GET 就是可供选择的一种实现细节,客户端可以向服务验证他的数据是否为最新版本;RESTful 端点可以通过它进一步提高速度和可伸缩性。
    •   扩展 REST 鼓励每项资源包含处理特殊请求所需的所有必要状态。满足这一约束时,RESTful 服务更易于扩展且可以没有状态。
    •   副作用如您使用 GET 请求资源,RESTful 服务应该没有副作用(遗憾的是,与其他一些 REST 约束相比,这一约束更容易被打破)。
    •   幂等统一接口另外两个常用到的主要 HTTP 动词是 PUT 和 DELETE。用户代理想要修改资源时最常使用 PUT,DELETE 可以自我描述。要点(也就是“幂等”一词所强调的)是您可以对特殊资源多次使用这两个动词,效果与首次使用一样——至少不会有任何其他影响。构建可靠的分布式系统时(即错误、网络故障或延迟可能导致多次执行代码),这一优点可提供保障。
    •   互操作性许多人将 SOAP 捧为建立客户端-服务器程序最具互操作性的方法。但一些语言和环境至今仍没有 SOAP 工具包。有一些虽然有工具包,但采用的是旧标准,不能保证与使用更新标准的工具包可靠沟通。对于大多数操作,REST 仅要求有 HTTP 库(当然,XML 库通常也很有帮助),它的互操作性肯定强过任何 RCP 技术(包括 SOAP)。
    •   简易性与其他优点相比,这一优点更主观一些,不同的人可能有不同的感受。对我而言,使用 REST 的简易性涉及到代表资源的 URI 和统一接口。作为一名 Web 冲浪高手,我理解在浏览器中输入不同的 URI 可以得到不同的资源(有时也被称为 URI 或 URL 黑客,但绝无恶意)。由于有多年使用 URI 的经验,所以为资源设计 URI 对我来说得心应手。使用统一接口简化了开发过程,因为我不必为每个需要建立的服务构建接口、约定或 API。接口(客户端与我的服务交互的方式)由体系结构约束设置。

    四.Rest的设计原则


      REST架构是针对Web应用而设计的,其目的是为了降低开发的复杂性,提高系统的可伸缩性。REST提出了如下设计准则:

    1. 网络上的所有事物都被抽象为资源(resource),比如图片、音乐、视频、文字、以及服务等等;
    2. 每个资源有唯一的资源标识符(resource identifier),URI定位资源;
    3. 通过通用的连接器接口(generic connector interface)对资源进行操作,比如使用 HTTP 标准动词(GET、POST、PUT 和 DELETE)的统一接口完成操作;
    4. 对资源的各种操作不会改变资源标识符,URI不变;
    5. 所有的操作都是无状态的(stateless)。

    五.wcf3.5到wcf4.0 Rest的新增特性

    1. 增加对路由的支持
    2. 对缓存的支持。
    3. 帮助(help)页面的改进。
    4. 消息错误处理
    5. 消息格式的多样性如(XMLJSONATOMTEXTBINARY)
    6. 简化操作。

    甩过一遍理论,那么就趁热实践一番吧!

    六.实践出真知

    按正常步骤新建一个WCF应用,常见的CRUD操作

    复制代码
     [ServiceContract]
        public interface IExampleService
        {
    
            [OperationContract]
            string GetData(string value);
    
            [OperationContract]
            string AddData(string value);
    
            [OperationContract ]
            string UpdateData(string value);
    
            [OperationContract ]
            string DeleteData(string value);
    
        }
    复制代码

    那么rest模式该是如何呢?

    复制代码
     [ServiceContract]
        public interface IExampleService
        {
    
            [OperationContract]
            [WebGet(UriTemplate = "/Rest/Get/{value}", ResponseFormat = WebMessageFormat.Json)]
            string GetData(string value);
    
            [OperationContract]
            [WebInvoke(UriTemplate = "/Rest/Add/{value}", Method = "POST")]
            string AddData(string value);
    
            [OperationContract ]
            [WebInvoke(UriTemplate = "/Rest/Update/{value}", Method = "PUT")]
            string UpdateData(string value);
    
            [OperationContract ]
            [WebInvoke (UriTemplate="/Rest/Delete/{value}",Method="DELETE")]
            string DeleteData(string value); 
           
        }
    复制代码

    比较下就很容易看出多加了些标签,并且也从方法的使用上可以对应出GET、POST、PUT、DELETE的使用。

    wcf可以看元数据,那么rest也有对应的方式,在web.config中添加如下配置就可以查看help页面

    复制代码
     <services>
          <service name="RestWCFTest.ExampleService">
            <endpoint address="" behaviorConfiguration="HelpBehavior" binding="webHttpBinding"
              bindingConfiguration="" contract="RestWCFTest.IExampleService" />
          </service>
        </services>
    
      <behaviors>
          <endpointBehaviors>
            <behavior name="HelpBehavior">
              <webHttp helpEnabled="true" />
            </behavior>
          </endpointBehaviors>
    </behaviors>
    复制代码

    help页面如下

    点击方法进去可以看见调用方式

    我们的接口实现

    复制代码
       public string GetData(string value)
            {
                return string.Format("You entered: {0}", value);
            }
    
            public string AddData(string value)
            {
                return string.Format("You added: {0}", value);
            }
    
            public string UpdateData(string value)
            {
                return string.Format("You updated: {0}", value);
            }
    
            public string DeleteData(string value)
            {
                return string.Format("You deleted: {0}", value);
            }
    复制代码

    现在我们用fiddler来模拟请求测试下

    在composer选项里有模拟请求的功能,very good!我们先来调用GetData操作,根据参数获取数据,根据设置的URI模板,“123456”为匹配

    的参数,执行它!

    看请求的信息

    GET http://localhost/REST4/ExampleService.svc/Rest/Get/123456 HTTP/1.1
    User-Agent: Fiddler
    Content-type: application/json
    Host: localhost
    Content-Length: 0

    看响应的数据

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 21
    Content-Type: application/json; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 05:16:52 GMT
    
    "You entered: 123456"
    复制代码

    200 OK 调用正常,content-type是json,因为我们指定的,IIS是7.5,对,我的确是部署在7.5上。。。看结果也是和预期一模一样,so easy~

    可能有同学会问,这是返回的json数据么?我也觉得不是,如果在方法标签上修改为如下

     [OperationContract]
            [WebGet(UriTemplate = "/Rest/Get/{value}",BodyStyle=WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]
            string GetData(string value);

    多加了个修饰bodystyle,它的功能是对结果进行包装,包装后再看返回的结果

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 39
    Content-Type: application/json; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 06:34:24 GMT
    
    {"GetDataResult":"You entered: 123456"}
    复制代码

    果然,被包装了,它是一个json格式的数据了。

    POST

    请求

    POST http://localhost/REST4/ExampleService.svc/Rest/Add/1234 HTTP/1.1
    User-Agent: Fiddler
    Content-type: application/json
    Host: localhost
    Content-Length: 0

    响应

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 92
    Content-Type: application/xml; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 05:06:41 GMT
    
    <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You added: 1234</string>
    复制代码

    这个时候我们没有指定返回的格式,默认为XML。

    PUT

    请求

    PUT http://localhost/REST4/ExampleService.svc/Rest/Update/123 HTTP/1.1
    User-Agent: Fiddler
    Content-type: application/json
    Host: localhost
    Content-Length: 0

    响应

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 93
    Content-Type: application/xml; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 05:23:04 GMT
    
    <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You updated: 123</string>
    复制代码

    DELETE

    请求

    DELETE http://localhost/REST4/ExampleService.svc/Rest/Delete/123 HTTP/1.1
    User-Agent: Fiddler
    Content-type: application/json
    Host: localhost
    Content-Length: 0

    响应

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 93
    Content-Type: application/xml; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 05:14:56 GMT
    
    <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">You deleted: 123</string>
    复制代码

    有同学可能DELETE请求发出去没反应(IIS 7.5),请在web.config里加上以下节点

    复制代码
     <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
          <remove name="WebDAVModule" />
        </modules>
    <handlers>
          <remove name="WebDAV" />
        </handlers>
     </system.webServer>
    复制代码

    至此一般的传参情况就是如此了,下面列举一些其它传参情况

    复制代码
       [OperationContract]
            [WebGet(UriTemplate = "/Rest/GetList2/", ResponseFormat = WebMessageFormat.Json)]
            List<ExampleData> GetDataLs2();
    
       [OperationContract]
            [WebInvoke(UriTemplate = "/Rest/AddLs3", BodyStyle = WebMessageBodyStyle.Wrapped,
                ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, Method = "POST")]
            List<ExampleData> AddDataLs3(List<ExampleData> datas);
    
            [OperationContract]
            [WebInvoke(UriTemplate = "/Rest/AddLs4", BodyStyle = WebMessageBodyStyle.Wrapped,
                ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, Method = "POST")]
            List<ExampleData> AddDataLs4(List<ExampleData> datas1, List<ExampleData> datas2);
    复制代码

    实体

    复制代码
      public class ExampleData
        {
            public string Name
            {
                get;
                set;
            }
    
            public string Age
            {
                get;
                set;
            }
        }
    复制代码

    接口实现

    复制代码
      public List<ExampleData> GetDataLs2()
            {
                List<ExampleData> result = new List<ExampleData> { 
                    new ExampleData{ Name="张三", Age="20"}
                    ,new ExampleData {Name="李四",Age="21"}
                     ,new ExampleData {Name="王五",Age="30"}
                };
    
                return result;
            }
    
      public List<ExampleData> AddDataLs3(List<ExampleData> datas)
            {
                return datas;
            }
    
      public List<ExampleData> AddDataLs4(List<ExampleData> datas1, List<ExampleData> datas2)
            {
                List<ExampleData> result = new List<ExampleData>();
                result.AddRange(datas1);
                result.AddRange(datas2);
    
                return result;
            }
    复制代码

    我们看到有获取实体集合了,还有传参的时候也是实体集合了

    首先看看获取集合的情况

    请求

    GET http://localhost/REST4/ExampleService.svc/Rest/GetList2/ HTTP/1.1
    User-Agent: Fiddler
    Content-type: application/json
    Host: localhost
    Content-Length: 0

    响应

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 88
    Content-Type: application/json; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 05:21:52 GMT
    
    [{"Age":"20","Name":"张三"},{"Age":"21","Name":"李四"},{"Age":"30","Name":"王五"}]
    复制代码

    嗯,返回的格式不错。

    看看怎样做新增操作的

    AddDataLs3

    请求

    复制代码
    POST http://localhost/REST4/ExampleService.svc/Rest/AddLs3 HTTP/1.1
    User-Agent: Fiddler
    Content-type: application/json
    Host: localhost
    Content-Length: 41
    
    {"datas":[{"Name":"xiaohua","Age":"13"}]}
    复制代码

    这时候我们会注意到,多了request body了,并且datas就是参数名

    响应

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 52
    Content-Type: application/json; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 06:59:55 GMT
    
    {"AddDataLs3Result":[{"Age":"13","Name":"xiaohua"}]}
    复制代码

    被包装了的数据。

    AddDataLs4

    请求

    复制代码
    POST http://localhost/REST4/ExampleService.svc/Rest/AddLs4 HTTP/1.1
    User-Agent: Fiddler
    Content-type: application/json
    Host: localhost
    Content-Length: 78
    
    {"datas1":[{"Name":"xiaohua","Age":"13"}],"datas2":[{"Name":"li","Age":"13"}]}
    复制代码

    响应

    复制代码
    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Length: 77
    Content-Type: application/json; charset=utf-8
    Server: Microsoft-IIS/7.5
    X-AspNet-Version: 4.0.30319
    X-Powered-By: ASP.NET
    Date: Fri, 27 Sep 2013 07:02:58 GMT
    
    {"AddDataLs4Result":[{"Age":"13","Name":"xiaohua"},{"Age":"13","Name":"li"}]}
    复制代码

    面对茫茫多的CRUD的时候,我们也许会显得不耐烦,因为每个操作都去写方法,真是烦躁,不妨可以试下如下的方式

       [OperationContract]
            [WebInvoke(UriTemplate = "/Rest/*", Method = "*", ResponseFormat = WebMessageFormat.Json)]
            string ExecuteData();

    用星号来匹配所有的请求,让程序区识别请求到底是GET、POST、PUT还是DELETE

    实现

    复制代码
       public string ExecuteData()
            {
                var request = WebOperationContext.Current.IncomingRequest;
    
                var method = request.Method;
                var args = request.UriTemplateMatch.WildcardPathSegments;
    
                switch (method)
                {
                    case "POST":
                        return "POST...";
                    case "DELETE":
                        return "DELETE...";
                    case "PUT":
                        return "UPDATE...";
                    default:
                        return "GET...";
                }
            }
    复制代码

    嗯,不知不觉就贴了这么多代码了,其实我字没写多少,今天就到这吧,算是入门了吧。

    以上例子所有源码下载

     
    分类: WCF
    标签: RESTWCFREST WCF
  • 相关阅读:
    hdu 4027 Can you answer these queries? 线段树
    ZOJ1610 Count the Colors 线段树
    poj 2528 Mayor's posters 离散化 线段树
    hdu 1599 find the mincost route floyd求最小环
    POJ 2686 Traveling by Stagecoach 状压DP
    POJ 1990 MooFest 树状数组
    POJ 2955 Brackets 区间DP
    lightoj 1422 Halloween Costumes 区间DP
    模板 有源汇上下界最小流 loj117
    模板 有源汇上下界最大流 loj116
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3343233.html
Copyright © 2011-2022 走看看