WCF实现REST

  • REST

          表述性状态转移(Representational State Transfer,REST),不是一种标准,而是一种软件架构风格。

          基于REST的服务与基于SOAP的服务相比,性能、效率和易用性上都更高,而SOAP协议非常的复杂和不透明。REST受到越来越多的Web服务供应商欢迎。目前大部分供应商,如yahoogoogleAmazon等都提供REST风格的服务。

     

          REST的主要原则是:

     1.网络上的所有事物都可被抽象为资源;

     2.每个资源都有一个唯一的资源标识符URI;

     3.使用标准方法操作资源;

     4.所有的操作都是无状态的;

     5.通过缓存来提高性能。

      

        REST是基于Http协议的,任何对资源的操作行为都是通过Http协议来实现。Http把对一个资源的操作限制在4个方法以内:GET、POST、PUTDELETE,这正是对资源CRUD操作的实现。

        REST的资源表述形式可以是XMLHTMLJSON,或者其他任意的形式,这取决于服务提供商和消费服务的用户。

         

        但是REST不是万能的。操作无状态也会带来巨大的安全问题,如何授权和验证用户?如果要求每次请求都包含完整的身份和验证信息,又如何避免信息泄漏?复杂的功能挑战架构的易用性,这就需要在性能与功能间权衡,究竟该用REST还是SOAP。

      

  • WebHttpBinding

          WebHttpBinding允许开发人员通过 HTTP 请求(这些请求使用“Plain old XML”(POX) 样式消息,而不是使用基于 SOAP 的消息)来公开 Web 服务,可以很便利的实现REST

          与其他绑定不同的是:必须使用WebHttpBehavior对服务的终结点进行配置。还要求使用WebGetAttributeWebInvokeAttribute属性将各个服务操作映射到 URI,同时定义调用和返回结果的消息格式。

           

  • 服务契约

          先定义服务契约。

          这里提供两个方法,分别采用GETPOST方式访问。

          我们可以看到,与普通WCF服务契约不同的是,需要额外用WebGet或者WebInvoke指定REST访问的方式。另外还要指定消息包装样式和消息格式,默认的消息请求和响应格式为XML,若选择JSON需要显式声明。 
           UriTemplate用来将方法映射到具体的Uri上,但如果不指定映射,将映射到默认的Uri。比如采用Get访问的GetUser方法,默认映射是:/GetUser?Name={Name}&Position={Position}。
     
    IContract
    复制代码
           
           
    1 namespace Rest.Contract
    2 {
    3 [DataContractFormat]
    4 [ServiceContract]
    5 public interface ITest
    6 {
    7 // [WebGet(UriTemplate = "/User/Get/{Name}/{Position}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
    8   [WebGet(UriTemplate = " /User/Get/{Name}/{Position} " , BodyStyle = WebMessageBodyStyle.Bare)]
    9 [OperationContract]
    10 List < User > GetUser( string Name, string Position);
    11
    12 // [WebInvoke(Method = "POST", UriTemplate = "/User/Create", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
    13   [WebInvoke(Method = " POST " , UriTemplate = "/User /Create " , BodyStyle = WebMessageBodyStyle.Bare)]
    14 [OperationContract]
    15 Result CreateUser(User u);
    16 }
    17
    18 [DataContract(Namespace = " http://rest-server/datacontract/user " )]
    19 public class User
    20 {
    21 [DataMember]
    22 public long ID { get ; set ; }
    23
    24 [DataMember]
    25 public string Name { get ; set ; }
    26
    27 [DataMember]
    28 public int Sex { get ; set ; }
    29
    30 [DataMember]
    31 public string Position { get ; set ; }
    32
    33 [DataMember]
    34 public string Email { get ; set ; }
    35 }
    36
    37 [DataContract(Namespace = " http://rest-server/datacontract/result " )]
    38 public class Result
    39 {
    40 [DataMember]
    41 public string Value { get ; set ; }
    42 }
    43
    44 }
    45  
    复制代码

         

  • 服务端

          这里最简单的实现GetUserCreateUser两个方法的逻辑,关注点不在这里。

     

    Service类
    复制代码
          
          
    1 namespace Rest.Service
    2 {
    3 public class Test : ITest
    4 {
    5 /// <summary>
    6 /// GET
    7 /// </summary>
    8 /// <param name="Name"></param>
    9 /// <param name="Position"></param>
    10 /// <returns></returns>
    11   public List < User > GetUser( string Name, string Position)
    12 {
    13 List < User > userList = List.Where(u => u.Name == Name && u.Position == Position).ToList();
    14
    15 return userList;
    16 }
    17
    18 /// <summary>
    19 /// POST
    20 /// </summary>
    21 /// <param name="u"></param>
    22 /// <returns></returns>
    23   public Result CreateUser(User u)
    24 {
    25 Result result = new Result();
    26
    27 if ( ! List.Any(user => user.ID == u.ID))
    28 List.Add(u);
    29
    30 result.Value = u.ID.ToString();
    31
    32 return result;
    33 }
    34
    35 /// <summary>
    36 /// 测试数据
    37 /// </summary>
    38   private static List < User > List
    39 {
    40 get
    41 {
    42 List < User > list = new List < User > {
    43 new User{
    44 ID = 19735 ,
    45 Name = " wuhong " ,
    46 Sex = 1 ,
    47 Position = " engineer " ,
    48 Email = " star_2345@qq.com "
    49 }
    50 };
    51
    52 return list;
    53 }
    54 }
    55 }
    56 }
    57  
    复制代码

       

         服务端的配置文件中只有一个特别处,必须使用WebHttpBehavior对服务的终结点进行配置。

       

    Web.Config
    复制代码
          
          
    1 < system.serviceModel >
    2 < bindings >
    3 < webHttpBinding >
    4 < binding name ="webBinding" >
    5 </ binding >
    6 </ webHttpBinding >
    7 </ bindings >
    8 < services >
    9 < service name ="Rest.Service.Test" behaviorConfiguration ="testServiceBehavior" >
    10 < endpoint address ="" behaviorConfiguration ="webBehavior"
    11 binding ="webHttpBinding" bindingConfiguration ="webBinding" contract ="Wuhong.Rest.Contract.ITest" >
    12 </ endpoint >
    13 </ service >
    14 </ services >
    15 < behaviors >
    16 < endpointBehaviors >
    17 < behavior name ="webBehavior" >
    18 <!-- 这里必须设置 -->
    19 < webHttp />
    20 </ behavior >
    21 </ endpointBehaviors >
    22 < serviceBehaviors >
    23 < behavior name ="testServiceBehavior" >
    24 </ behavior >
    25 </ serviceBehaviors >
    26 </ behaviors >
    27 </ system.serviceModel >
    28  
    复制代码

 

  • 客户端

         为了强调REST的通用性,客户端不用WCF的形式调用服务,而是用另外两种通用的方式:

         一是用C#编程直接HTTP访问,消息格式我们选XML

           二是用jquery实现GETPOST访问,消息格式我们选JSON。

     

           先实现C#方式,我们封装一个Client类,实现HTTP的GET和POST方式。

     

    Client类
    复制代码
          
          
    1 namespace Rest.Client
    2 {
    3 public class RestClient
    4 {
    5 /// <summary>
    6 /// 构造函数
    7 /// </summary>
    8 /// <param name="baseUrl"></param>
    9   public RestClient( string baseUri)
    10 {
    11 this .BaseUri = baseUri;
    12 }
    13
    14 /// <summary>
    15 /// 基地址
    16 /// </summary>
    17   private string BaseUri;
    18
    19 /// <summary>
    20 /// Post调用
    21 /// </summary>
    22 /// <param name="data"></param>
    23 /// <param name="uri"></param>
    24 /// <returns></returns>
    25   public string Post( string data, string uri)
    26 {
    27 // Web访问对象
    28   string serviceUrl = string .Format( " {0}/{1} " , this .BaseUri, uri);
    29 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl);
    30
    31 // 转成网络流
    32   byte [] buf = UnicodeEncoding.UTF8.GetBytes(data);
    33
    34 // 设置
    35   myRequest.Method = " POST " ;
    36 myRequest.ContentLength = buf.Length;
    37 myRequest.ContentType = " text/html " ;
    38
    39 // 发送请求
    40   Stream newStream = myRequest.GetRequestStream();
    41 newStream.Write(buf, 0 , buf.Length);
    42 newStream.Close();
    43
    44 // 获得接口返回值
    45   HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
    46 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8);
    47
    48 string ReturnXml = HttpUtility.HtmlDecode(reader.ReadToEnd());
    49
    50 reader.Close();
    51 myResponse.Close();
    52
    53 return ReturnXml;
    54 }
    55
    56 /// <summary>
    57 /// Get调用
    58 /// </summary>
    59 /// <param name="uri"></param>
    60 /// <returns></returns>
    61   public string Get( string uri)
    62 {
    63 // Web访问对象
    64   string serviceUrl = string .Format( " {0}/{1} " , this .BaseUri, uri);
    65 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(serviceUrl);
    66
    67 // 获得接口返回值
    68   HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
    69 StreamReader reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8);
    70
    71 string ReturnXml = HttpUtility.UrlDecode(reader.ReadToEnd());
    72
    73 reader.Close();
    74 myResponse.Close();
    75
    76 return ReturnXml;
    77 }
    78
    79 }
    80 }
    81  
    复制代码
  •  

          下面是主函数,按顺序调用两个接口,并显示返回值。需要注意XML约定的命名空间:

     

    Client Main函数
    复制代码
         
         
    1 namespace Rest.Client
    2 {
    3 class Program
    4 {
    5 static void Main( string [] args)
    6 {
    7 // 初始化
    8   RestClient client = new RestClient(ClientConfiguration.ServiceUrl);
    9
    10 // Get
    11   string uriGet = string .Format( " User/Get/{0}/{1} " , " wuhong " , " engineer " );
    12 string retGet = client.Get(uriGet);
    13
    14 Console.WriteLine(retGet);
    15
    16 // Post
    17   string uriPost = "User/ Create " ;
    18 string data = " <User xmlns=\ " http: // rest-server/datacontract/user\"><ID>19735</ID><Name>wuhong</Name><Sex>1</Sex><Position>engineer</Position><Email>star_2345@qq.com</Email></User>";
    19  
    20 string retPost = client.Post(data, uriPost);
    21
    22 Console.WriteLine(retPost);
    23
    24 Console.ReadLine();
    25 }
    26 }
    27
    28 }
    29  
    复制代码

         

          结果:

         

          接下来实现javascript方式。

          这里采用jquery访问REST服务,为了javascript操作数据的便利,消息格式选择JSON,可以忽略数据契约的命名空间。不过需要服务契约做一点修改,显式指定请求或响应消息的格式,例如: 

        
        
    1 [WebInvoke(Method = " POST " , UriTemplate = "User /Create " , BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]

       

          Html代码: 

    js client代码
    复制代码
         
         
    1 < html >
    2 < head >
    3 < script src ="jquery-1.3.2.min.js" type ="text/javascript" ></ script >
    4 < script type ="text/javascript" >
    5 function HttpGet() {
    6 $.get(   http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/User/Get/wuhong/engineer   ,
    7 function (data) {
    8 $( " #TextGet " ).val(data);
    9 });
    10 }
    11 function HttpPost() {
    12 var str = " { \ " Email\ " : \ " star_2345@qq.com\ " , \ " ID\ " : 19735, \ " Name\ " : \ " wuhong\ " , \ " Position\ " : \ " engineer\ " , \ " Sex\ " : 1 } " ;
    13 $.ajax({
    14 type: " POST " ,
    15 contentType: " application/json " ,
    16 url:   http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/User/Create ,
    17 data: str,
    18 success: function (data) {
    19 $( " #TextPost " ).val(data);
    20 }
    21 });
    22 }
    23 </ script >
    24 < style type ="text/css" >
    25 #TextGet
    26 {
    27 width : 700px ;
    28 }
    29 #TextPost
    30 {
    31 width : 700px ;
    32 }
    33 </ style >
    34 </ head >
    35 < body >
    36 < input id ="ButtonGet" type ="button" value ="GET" onclick ="HttpGet()" />
    37 < input id ="TextGet" type ="text" />
    38 < p />
    39 < input id ="ButtonPost" type ="button" value ="POST" onclick ="HttpPost()" />
    40 < input id ="TextPost" type ="text" />
    41 </ body >
    42   </ html >
    43  
    复制代码

         

           结果:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值