WCF实现REST服务

时间:2022-06-30 13:20:13

 

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不是万能的。操作无状态也会带来巨大的安全问题,如何授权和验证用户?如果要求每次请求都包含完整的身份和验证信息,又如何避免信息泄漏?复杂的功能挑战架构的易用性,需要在性能与功能间权衡。 

 

 

 

WebHttpBinding

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

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

 

服务契约

      先定义服务契约。

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

    我们可以看到,与普通WCF服务契约不同的是,这里需要额外用WebGet或者WebInvoke指定REST访问的方式,并且用UriTemplate将方法映射到具体的uri上,另外还要指定消息包装样式和消息格式。默认的消息请求和响应格式为XML,若选择JSON需要显式声明。  WCF实现REST服务WCF实现REST服务IContract 1 namespace Rest.Contract
2 {
3 [DataContractFormat]
4 [ServiceContract]
5 public interface ITest
6 {
7 //[WebGet(UriTemplate = "/GetUser/{Name}/{Position}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
8   [WebGet(UriTemplate = "/GetUser/{Name}/{Position}", BodyStyle = WebMessageBodyStyle.Bare)]
9 [OperationContract]
10 List<User> GetUser(string Name, string Position);
11
12 //[WebInvoke(Method = "POST", UriTemplate = "/CreateUser", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
13   [WebInvoke(Method = "POST", UriTemplate = "/CreateUser", 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两个方法的逻辑,关注点不在这里。

 

WCF实现REST服务WCF实现REST服务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对服务的终结点进行配置。  

   

WCF实现REST服务WCF实现REST服务Web.Config 1 <system.serviceModel>
2 <bindings>
3 <webHttpBinding>
4 <binding name="webBinding">
5 </binding>
6 </webHttpBinding>
7 </bindings>
8 <services>
9 <service name="Wuhong.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方式。

 

WCF实现REST服务WCF实现REST服务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  

 

       下面是主函数,按顺序调用两个接口,并显示返回值:

 

 

WCF实现REST服务WCF实现REST服务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("GetUser/{0}/{1}", "wuhong", "engineer");
12 string retGet = client.Get(uriGet);
13
14 Console.WriteLine(retGet);
15
16 //Post
17   string uriPost = "CreateUser";
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  

 

  

      结果:

 

 

WCF实现REST服务

 

 

      接下来是javascript方式。

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

 

 

 

 

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

 

 

    Html代码: 

 

WCF实现REST服务WCF实现REST服务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 var tmp = new Date().getTime();
7 $.get("http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/GetUser/wuhong/engineer",
8 function(data) {
9 $("#TextGet").val(data);
10 });
11 }
12 function HttpPost() {
13 var str = "{ /"Email/": /"star_2345@qq.com/", /"ID/": 19735, /"Name/": /"wuhong/", /"Position/": /"engineer/", /"Sex/": 1 }";
14 $.ajax({
15 type: "POST",
16 contentType: "application/json",
17 url: "http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/CreateUser",
18 data: str,
19 success: function(data) {
20 $("#TextPost").val(data);
21 }
22 });
23 }
24 </script>
25 <style type="text/css">
26 #TextGet
27 {
28 width: 700px;
29 }
30 #TextPost
31 {
32 width: 700px;
33 }
34 </style>
35 </head>
36 <body>
37 <input id="ButtonGet" type="button" value="GET" onclick="HttpGet()" />
38 <input id="TextGet" type="text" />
39 <p/>
40 <input id="ButtonPost" type="button" value="POST" onclick="HttpPost()" />
41 <input id="TextPost" type="text" />
42 </body>
43  </html>
44  

 

      结果:

 

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个方法以内:GETPOSTPUTDELETE,这正是对资源CRUD操作的实现。

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

     

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

 

WebHttpBinding

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

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

 

服务契约

      先定义服务契约。

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

    我们可以看到,与普通WCF服务契约不同的是,这里需要额外用WebGet或者WebInvoke指定REST访问的方式,并且用UriTemplate将方法映射到具体的uri上,另外还要指定消息包装样式和消息格式。默认的消息请求和响应格式为XML,若选择JSON需要显式声明。  IContract 1 namespace Rest.Contract
2 {
3 [DataContractFormat]
4 [ServiceContract]
5 public interface ITest
6 {
7 //[WebGet(UriTemplate = "/GetUser/{Name}/{Position}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
8   [WebGet(UriTemplate = "/GetUser/{Name}/{Position}", BodyStyle = WebMessageBodyStyle.Bare)]
9 [OperationContract]
10 List<User> GetUser(string Name, string Position);
11
12 //[WebInvoke(Method = "POST", UriTemplate = "/CreateUser", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
13   [WebInvoke(Method = "POST", UriTemplate = "/CreateUser", 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两个方法的逻辑,关注点不在这里。

 

Service1 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="Wuhong.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类,实现HTTPGETPOST方式。

 

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  

 

       下面是主函数,按顺序调用两个接口,并显示返回值:

 

 

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("GetUser/{0}/{1}", "wuhong", "engineer");
12 string retGet = client.Get(uriGet);
13
14 Console.WriteLine(retGet);
15
16 //Post
17   string uriPost = "CreateUser";
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 = "/CreateUser", 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 var tmp = new Date().getTime();
7 $.get("http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/GetUser/wuhong/engineer",
8 function(data) {
9 $("#TextGet").val(data);
10 });
11 }
12 function HttpPost() {
13 var str = "{ /"Email/": /"star_2345@qq.com/", /"ID/": 19735, /"Name/": /"wuhong/", /"Position/": /"engineer/", /"Sex/": 1 }";
14 $.ajax({
15 type: "POST",
16 contentType: "application/json",
17 url: "http://doxt-wuhong/Wuhong.Rest.Web/TestService.svc/CreateUser",
18 data: str,
19 success: function(data) {
20 $("#TextPost").val(data);
21 }
22 });
23 }
24 </script>
25 <style type="text/css">
26 #TextGet
27 {
28 width: 700px;
29 }
30 #TextPost
31 {
32 width: 700px;
33 }
34 </style>
35 </head>
36 <body>
37 <input id="ButtonGet" type="button" value="GET" onclick="HttpGet()" />
38 <input id="TextGet" type="text" />
39 <p/>
40 <input id="ButtonPost" type="button" value="POST" onclick="HttpPost()" />
41 <input id="TextPost" type="text" />
42 </body>
43  </html>
44  

 

      结果: