创建一个简单的restful wcf, 并且用silverlight做为客户端实现get, post, put,delete
Posted on 2011-09-12 00:50 Object.prototype 阅读(1797) 评论(1) 编辑 收藏 举报 先给大家转载一段对于REST的简单介绍
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.定义服务契约
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 { get; set; }
37
38 [DataMember]
39 public string Name { get; set; }
40
41 [DataMember]
42 public int Sex { get; set; }
43
44 [DataMember]
45 public string Position { get; set; }
46
47 [DataMember]
48 public string Email { get; set; }
49 }
50 }
Interface "IRestService"是wcf的服务映射借口, Class "User" 是保存数据的实体类
这里一共提供了4种访问方法GET,POST,PUT,DELETE.
如: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.服务端代码实现
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 服务契约中定义的方法.
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中需要注意
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
下面是客户端代码
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[1] as 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[1] as 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[1] as 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
<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.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,所以如果博客中有错误欢迎大家指出
祝大家过个快乐的端午节.