博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

先给大家转载一段对于REST的简单介绍

   REST

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

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

 

      REST的主要原则是:

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

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

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

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

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

  

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

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

     

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

以上这段介绍转载自:

 

1.REST   WCF 端

1.定义服务契约

 

IRestService
 1 namespace RestWCF.RestDefiniction
 2 {
 3     // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
 4     [ServiceContract]
 5     [DataContractFormat]
 6     public interface IRestService
 7     {
 8         [WebGet(UriTemplate = "/User/Get/{Name}", BodyStyle = WebMessageBodyStyle.Bare)]
 9         [OperationContract]
10         User GetUser(string Name);
11 
12         [WebGet(UriTemplate = "/User/All", BodyStyle = WebMessageBodyStyle.Bare)]
13         [OperationContract]
14         List<User> GetAllUsers();
15 
16         [WebInvoke(Method = "POST", UriTemplate = "/User/Create", BodyStyle = WebMessageBodyStyle.Bare)]
17         [OperationContract]
18         void CreateUser(User u);
19 
20         [WebInvoke(Method = "PUT", UriTemplate = "/User/Update/{Name}", BodyStyle = WebMessageBodyStyle.Bare)]
21         [OperationContract]
22         void ModifyUser(string name, User u);
23 
24         [WebInvoke(Method = "DELETE", UriTemplate = "/User/Delete/{Name}", BodyStyle = WebMessageBodyStyle.Bare)]
25         [OperationContract]
26         void DeleteUser(string name);
27         // TODO: 在此添加您的服务操作
28     }
29 
30 
31     // 使用下面示例中说明的数据约定将复合类型添加到服务操作。
32     [DataContract(Namespace = "http://rest-server/datacontract/user")]
33     public class User
34     {
35         [DataMember]
36         public long ID { getset; }
37 
38         [DataMember]
39         public string Name { getset; }
40 
41         [DataMember]
42         public int Sex { getset; }
43 
44         [DataMember]
45         public string Position { getset; }
46 
47         [DataMember]
48         public string Email { getset; }
49     }
50 }

 

 Interface  "IRestService"是wcf的服务映射借口, Class "User" 是保存数据的实体类

这里一共提供了4种访问方法GET,POST,PUT,DELETE.

普通WCF服务契约不同的是,需要额外用WebGet或者WebInvoke指定REST访问的方式。另外还要指定消息包装样式和消息格式

如:WebGet: 用于声明GET请求

   WebInvoke:通过指定Method来声明POST,PUT,DELETE请求

    RequestFormat 和 ResponseFormat 指定发送Request和接收Response所用的格式,这里只有2种格式XML和JSON,默认都是XML

    UriTemplate:用来设定方法映射到的具体URL上,如GetAllUsers就映射到了User/All目录下,这样在客户端就需要将HttpWebRequest的地址设置为http://localhost:2899/Service1.svc/User/All. 而对于GetUser的影射路径/User/Get/{Name},客户端HttpWebRequest的地址就是http://localhost:2899/Service1.svc/User/Get/tom

 

下图就是http://localhost:2899/Service1.svc/User/All/

 

 而http://localhost:2899/Service1.svc/User/Get/da 就是只查询name=da的人

 

 注意:

http://localhost:2899/Service1.svc/User/Get/{Name} 中的{Name}是一个输入参数,类型为string

同样这个参数也可以这么写 http://localhost:2899/Service1.svc?name={Name}

但是这种参数方法不可以用在post方法上,因为post是上传一个对象,而不是一个简单的数据类型 .

 

2.服务端代码实现

 

 

Service1
 1 namespace RestWCF
 2 {
 3     // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“Service1”。
 4     public class Service1 : RestDefiniction.IRestService
 5     {
 6         private List<RestDefiniction.User> users = new List<RestDefiniction.User>();
 7 
 8         public Service1()
 9         {
10             users.Add(new RestDefiniction.User
11             {
12                 ID = 3,
13                 Email = "sasa",
14                 Name = "tom",
15                 Position = "4dwdqrq",
16                 Sex = 4
17             }
18             );
19             users.Add(new RestDefiniction.User
20             {
21                 Email = "gfgfj@sina.com",
22                 ID = 32,
23                 Name = "jason",
24                 Sex = 1,
25                 Position = "dada"
26             });
27         }
28 
29         public RestDefiniction.User GetUser(string Name)
30         {
31             return users.Find(e => e.Name == Name);
32         }
33 
34         public void CreateUser(RestDefiniction.User u)
35         {
36             this.users.Add(u);
37         }
38 
39         public List<RestDefiniction.User> GetAllUsers()
40         {
41             return this.users;
42         }
43 
44         public void ModifyUser(string name, RestDefiniction.User u)
45         {
46             RestDefiniction.User m = this.users.Find(e => e.Name == name);
47             m.Email = u.Email;
48             m.Position = u.Position;
49             m.Sex = u.Sex;
50         }
51 
52         public void DeleteUser(string name)
53         {
54             this.users.Remove(this.users.Find(e => e.Name == name));
55         }
56     }
57 }

 这里就具体实现了IRestService 服务契约中定义的方法.

 

 

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <configuration>
 3   <system.web>
 4     <compilation debug="true" targetFramework="4.0" />
 5   </system.web>
 6   <system.serviceModel>
 7     <bindings>
 8       <webHttpBinding>
 9         <binding name="webBinding">
10         </binding>
11       </webHttpBinding>
12     </bindings>
13     <services>
14       <service name="RestWCF.Service1" behaviorConfiguration="testServiceBehavior">
15         <endpoint kind="webHttpEndpoint" address="" behaviorConfiguration="webBehavior"
16    binding="webHttpBinding" bindingConfiguration="webBinding" contract="RestWCF.RestDefiniction.IRestService">
17         </endpoint>
18       </service>
19     </services>
20     <behaviors>
21       <endpointBehaviors>
22         <behavior name="webBehavior">
23           <!--这里必须设置-->
24           <webHttp helpEnabled="true"/>
25         </behavior>
26       </endpointBehaviors>
27       <serviceBehaviors>
28         <behavior name="testServiceBehavior">
29           <serviceMetadata httpGetEnabled="true"/>
30         </behavior>
31       </serviceBehaviors>
32     </behaviors>
33   </system.serviceModel>
34   <system.webServer>
35     <modules runAllManagedModulesForAllRequests="true"/>
36   </system.webServer>
37 
38 </configuration>

 web.config中需要注意

 <endpoint kind="webHttpEndpoint" address="" behaviorConfiguration="webBehavior"
   binding
="webHttpBinding" bindingConfiguration="webBinding" contract="RestWCF.RestDefiniction.IRestService">
        
</endpoint>

 

这里的binding默认是basicHttpBinding,这里必须改成webHttpBinding, 因为basicHttpBinding是用SOAP方式来访问,而webHttpBinding才是Rest方式访问

 

2. 客户端

 在客户端我们可以用c# 直接发送httprequest来调用服务,或者jQuery来异步调用.

这里我选择用silverlight app来作为客户端,传输格式XML  此处的silverlight版本是silverlight4

下面是客户端代码

 

MainPage
  1 namespace SilverlightApplication1
  2 {
  3     public partial class MainPage : UserControl
  4     {
  5         private const string basicUrl = "http://localhost:2899/Service1.svc/user/";
  6 
  7         public MainPage()
  8         {
  9             InitializeComponent();
 10         }
 11 
 12         private void btnGet_Click(object sender, RoutedEventArgs e)
 13         {
 14             string getUrl = basicUrl + "all";
 15             HttpWebRequest req = WebRequest.Create(getUrl) as HttpWebRequest;
 16             req.Method = "get";
 17             req.BeginGetResponse(RequestGetUserCallBack, req);
 18         }
 19 
 20         private void RequestGetUserCallBack(IAsyncResult ia)
 21         {
 22             if (ia.IsCompleted)
 23             {
 24                 HttpWebResponse response = ((HttpWebRequest)ia.AsyncState).EndGetResponse(ia) as HttpWebResponse;
 25                 Stream webStream = response.GetResponseStream();
 26                 StreamReader sr = new StreamReader(webStream);
 27                 string xml = sr.ReadToEnd();
 28                 sr.Close();
 29                 webStream.Close();
 30                 response.Close();
 31             }
 32         }
 33 
 34         private void btnPost_Click(object sender, RoutedEventArgs e)
 35         {
 36             //    string xmlFile = "post.xml";
 37             //    StreamReader sr = new StreamReader(xmlFile, System.Text.Encoding.UTF8);
 38             //    string newUser = sr.ReadToEnd();
 39             //    sr.Close();
 40             string newUser = "<User xmlns=\"http://rest-server/datacontract/user\">" +
 41                              "<Email>da@fjijo</Email><ID>43</ID><Name>js</Name><Position>4dwdqrq</Position><Sex>4</Sex></User>";
 42             byte[] data = System.Text.UnicodeEncoding.UTF8.GetBytes(newUser);
 43 
 44             string post = basicUrl + "create";
 45             HttpWebRequest req = WebRequest.Create(post) as HttpWebRequest;
 46             req.Method = "post";
 47             req.ContentType = "text/xml";
 48             req.ContentLength = data.Length;
 49             req.BeginGetRequestStream(RequestCreateUserCallBack, new object[] { req, data });
 50         }
 51 
 52         private void RequestCreateUserCallBack(IAsyncResult ia)
 53         {
 54             if (ia.IsCompleted)
 55             {
 56                 object[] objlist = ia.AsyncState as object[];
 57                 byte[] data = objlist[1as byte[];
 58 
 59                 Stream stream = ((HttpWebRequest)objlist[0]).EndGetRequestStream(ia);
 60                 stream.Write(data, 0, data.Length);
 61                 stream.Close();
 62                 ((HttpWebRequest)objlist[0]).BeginGetResponse(NullCallBack, ia.AsyncState);
 63             }
 64         }
 65 
 66         private void btnPut_Click(object sender, RoutedEventArgs e)
 67         {
 68             string newUser = "<User xmlns=\"http://rest-server/datacontract/user\">" +
 69                              "<Email>da@fjijo</Email><ID>43</ID><Name>js</Name><Position>4dwdqrq</Position><Sex>4</Sex></User>";
 70             byte[] data = System.Text.UnicodeEncoding.UTF8.GetBytes(newUser);
 71 
 72             string put = basicUrl + "Update/" + "v-lk";
 73             HttpWebRequest req = WebRequest.CreateHttp(put);
 74             req.Method = "put";
 75             req.ContentType = "text/xml";
 76             req.ContentLength = data.Length;
 77             req.BeginGetRequestStream(RequestModifyUserCallBack, new object[] { req, data });
 78         }
 79 
 80         private void RequestModifyUserCallBack(IAsyncResult ia)
 81         {
 82             if (ia.IsCompleted)
 83             {
 84                 object[] objlist = ia.AsyncState as object[];
 85                 byte[] data = objlist[1as byte[];
 86 
 87                 Stream stream = ((HttpWebRequest)objlist[0]).EndGetRequestStream(ia);
 88                 stream.Write(data, 0, data.Length);
 89                 stream.Close();
 90                 ((HttpWebRequest)objlist[0]).BeginGetResponse(NullCallBack, ia.AsyncState);
 91             }
 92         }
 93 
 94         private void NullCallBack(IAsyncResult ia)
 95         {
 96 
 97         }
 98 
 99         private void btnDel_Click(object sender, RoutedEventArgs e)
100         {
101             string newUser = "<User xmlns=\"http://rest-server/datacontract/user\">" +
102                              "<Email>da@fjijo</Email><ID>43</ID><Name>js</Name><Position>4dwdqrq</Position><Sex>4</Sex></User>";
103             byte[] data = System.Text.UnicodeEncoding.UTF8.GetBytes(newUser);
104 
105             string delete = basicUrl + "Delete/" + "v-lk";
106             HttpWebRequest req = WebRequest.CreateHttp(delete);
107             req.Method = "delete";
108             req.ContentType = "text/xml";
109             req.ContentLength = data.Length;
110             req.BeginGetRequestStream(RequestDeleteUserCallBack, new object[] { req, data });
111         }
112 
113         private void RequestDeleteUserCallBack(IAsyncResult ia)
114         {
115             if (ia.IsCompleted)
116             {
117                 object[] objlist = ia.AsyncState as object[];
118                 byte[] data = objlist[1as byte[];
119 
120                 Stream stream = ((HttpWebRequest)objlist[0]).EndGetRequestStream(ia);
121                 stream.Write(data, 0, data.Length);
122                 stream.Close();
123                 ((HttpWebRequest)objlist[0]).BeginGetResponse(NullCallBack, ia.AsyncState);
124             }
125         }
126     }
127 }

 这里的btnget,btnpost...分别对应rest里的get,post,put,delete请求.

先简单说下Get:

在GET请求中,请求发送顺序是create WebRequest-->GetResponse--->GetStream--->ReadStream--->Display XML

由于silverlight sdk比较小所以在getresponse和getstream的时候需要用异步来解决.

在GET请求中,注意URL一定要正确,如果不知道URL是什么的可以访问http://localhost:2899/service1.svc/help来查看请求URL和发送数据的格式.

当然要看到help必须在web,config中把helpEnable设为true

helpEnable
<endpointBehaviors>
         
<behavior name="webBehavior">
           
<!--这里必须设置helpEnable=true才能看到请求-->
           
<webHttp helpEnabled="true"/>
         
</behavior>
      
</endpointBehaviors>

 

这就是help, 里面列出了rest service提供了哪些请求,具体路径是什么,点击method链接后可以看到请求发送的正确格式.

 

说完GET就来说说另外3种请求, 那三种请求实现方法一样就是request的method分别设置为post,put,delete,就拿多的最多的post讲下吧

POST请求的发送顺序为create httpwebrequest-->serilaztion xml to byte[]-->get stream-->write xml byte[] to stream-->getresponse

 

第一步:将你要添加的实体类实例序列化为符合要求的xml格式

第二步:通过System.Text.UnicodeEncoding.UTF8.GetBytes将那段xml文本转为byte[]

第三步:设置HttpWebRequest.

            HttpWebRequest req = WebRequest.Create(post) as HttpWebRequest;   //实例化httpwebrequest

            req.Method = "post";    //这是访问方式为post

            req.ContentType = "text/xml";   //由于我们的数据传输方式为xml,所以设置contentype为text/xml

            req.ContentLength = data.Length;   //设置contentlength

第四步:发送getstream

第五步:将xml转化为的byte[] 写入到获取的stream里面

第六步:GetResponse

 

其中有几个要注意的地方

1.在第2步, request.conntenttype一定要显式的设置清楚,无论你的传输方式是XML还是JSON都是在这写上去,一定不能不写.

   如果不写的话会发生什么问题了?
   嘿嘿,不写得话当你在getstream的callback函数里begingetresponse的时候会报出4004 no find或者405 method not allow 的错误

   大家看到这2个错误是不是很熟悉,很郁闷了,至少我就卡在这了.

2. 有些做过socket的朋友可能会觉得第6步感觉没什么用啊,因为在socket发送数据的时候在得到network stream后直接stream.wite就可以发送出去了.

    我先也这么认为,后来发现如果仅仅只是write进Stream的话,服务器端根本就没捕捉到你的请求.只有当你getresponse之后,数据才会发送到服务器端.

 

 由于本人也是刚用到restful web service,所以如果博客中有错误欢迎大家指出

祝大家过个快乐的端午节.