WCF实现REST服务

醉酒当歌 提交于 2020-01-02 02:12:53

 

  • 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}。

     

     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 { getset; }
    23  
    24          [DataMember] 
    25          public string Name { getset; }
    26  
    27          [DataMember]
    28          public int  Sex { getset; }
    29  
    30          [DataMember]
    31          public string Position { getset; }
    32  
    33          [DataMember]
    34          public string Email { getset; }
    35      }
    36  
    37      [DataContract(Namespace = "http://rest-server/datacontract/result")]
    38      public class Result
    39      {
    40          [DataMember]
    41          public string Value { getset; }
    42      }
    43  
    44  }

     

  • 服务端

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

     

     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  }

     

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

     

     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>

     

  • 客户端

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

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

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

     

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

     

     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  }

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

     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  }


    接下来实现javascript方式。

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

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

    Html代码:

     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>

      结果:

  • 易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
    该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!