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。
WebHttpBinding WebHttpBinding允许开发人员通过 HTTP 请求(这些请求使用“Plain old XML”(POX) 样式消息,而不是使用基于 SOAP 的消息)来公开 Web 服务,可以很便利的实现REST。
与其他绑定不同的是:必须使用WebHttpBehavior对服务的终结点进行配置。还要求使用WebGetAttribute或WebInvokeAttribute属性将各个服务操作映射到 URI,同时定义调用和返回结果的消息格式。
服务契约 先定义服务契约。
这里提供两个方法,分别采用GET和POST方式访问。
我们可以看到,与普通
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 {
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 }
服务端 这里最简单的实现GetUser和CreateUser两个方法的逻辑,关注点不在这里。
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实现GET和POST访问,消息格式我们选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 > 结果: