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

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

一.什么是Rest


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

  • 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架构的特色也很是的明显:json

  •   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. 消息格式的多样性如(XML\JSON\ATOM\TEXT\BINARY)
  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...";
            }
        }

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

以上例子全部源码下载

相关文章
相关标签/搜索