REST(Representational State Transfer 一般被翻译为“表述性状态传输”或者“表述性状态转移”)是RoyFielding提出的一个描述互联系统架构风格的名词。为何称为REST?Web本质上由各类各样的资源组成,资源由URI 惟一标识。浏览器(或者任何其它相似于浏览器的应用程序)将展现出该资源的一种表现方式,或者一种表现状态。若是用户在该页面中定向到指向其它资源的连接,则将访问该资源,并表现出它的状态。这意味着客户端应用程序随着每一个资源表现状态的不一样而发生状态转移,也即所谓REST。html
简单地来讲REST它是一种使用URL来定位资源,使用HTTP请求描述操做的Web服务规范。REST主要包括如下几方面:git
(1) REST是一组架构约束条件和原则,而知足这些约束条件和原则的应用程序就是RESTful。
(2)REST的目标是构建可扩展的Web Service,它是一种更简单的SOAP(Simple Object Access Protocol)协议以及以WSDL为基础的WebService的替代。
(3)REST采用的是HTTP协议并经过HTTP中的GET、POST、PUT、DELETE等动词收发数据。
(4) REST但愿经过HTTP来完成对数据的元操做,即传统的CRUD(Create、Read、Update、Delete)分别对应GET、POST、PUT、DELETE,这样就统一了数据操做的接口,实如今不一样平台上提供一套相同的服务。
(5) REST是一种面向服务的、分布式的API设计风格。github
RESTful API的开发和使用,无非是客户端向服务器发请求(request),以及服务器对客户端请求的响应(response)。因此RESTful架构风格具备统一接口的特色,即:使用不一样的http方法表达不一样的行为:web
REST本质上是Web服务的一种规范,一种思想。它主要包括如下特性:json
在REST中资源是整个架构或者说整个网络处理的核心,那么什么又是资源呢?在咱们传统的观念中,资源是指服务器上的一个文件,而在REST里资源则是指一个URL。URL即统一资源定位,而咱们都知道经过URL能够访问互联网上的资源,因此在REST里这种对资源的指向性更增强烈,而且在这里资源的范畴会被无限放大而并不是局限在文件自己,例如如下实例:api
1 http://api.cnblogs.com/info/source 表示获取某人的成绩 2 http://api.cnblogs.com/info/friends 表示获取某人的好友列表 3 http://api.cnblogs.com/info/profile 表示获取某人的详细信息
由此咱们注意到REST在形式上更加趋向API设计,而咱们获取的资源则经过必定的形式进行统一而规范化的表达,所以REST实现了让不一样的平台共享一套API这样的愿望,这是一件很是美好的事情,这个世界上的技术阵营举不胜数,而它们为了各自的利益创建一套封闭、臃肿的体系框架,不少时候当咱们不须要这样的“全家桶”而且但愿“跨平台”的时候,REST将会是一个不错的选择。浏览器
在REST中表现形式做为咱们对资源请求的一个结果的呈现,经过对HTTP协议的学习咱们已经知道,服务器会给客户端返回什么形式的信息,这一点取决于服务器响应报文中相关头部字段,而对REST来说,它一般会采用XML或者JSON来告诉请求者请求的结果,由于JSON相比XML所含的冗余信息较少,因此目前更加倾向于或者说流行使用JSON做为请求结果的表现形式。安全
虽然咱们一再强调HTTP协议是无状态,这主要体如今HTTP请求与请求、HTTP响应与响应的上下文无关性上。在REST中,咱们所说状态变化更可能是指HTTP中的GET、POST、PUT、DELETE等动词实现。具体来说,看下面的简单示例:服务器
1 GET http://url/info 表示获取所有的info 2 POST http://url/info 表示建立一个新的info 3 GET http://url/info/{id} 表示获取一个指定id的info 4 PUT http://url/info/{id} 表示更新一个指定id的info 5 DELETE http://url/info/{id} 表示删除一个指定id的info
除此以外,咱们注意到REST基于HTTP协议,因此HTTP协议中的状态码对它来说一样适用,例如最经常使用的200表示成功、500表示服务器内部错误、404表示没法找到请求资源、400表示请求错误等等。网络
如何构建REST风格的API?咱们能够经过如下实例说明
1 http://example.com/api/v1/ 2 http://api.example.com/v1/
1 http://example.com/api/v1/getProducts 这是一个糟糕的设计 2 GET http://example.com/api/v1/products 这是一个优雅的设计
GET http://example.com/api/v1/deleteProduct?id=1 这是一个危险的信号
GET http://example.com/api/v1/friends/123456789/profile
下面主要讲解如何用C#实现一个Rest 风格的web服务供外部调用,主要包括如下4点:
先定义服务契约,这里我介绍最多见的两种方式,分别采用GET和POST方式访问,使用VS2015建立一个新的控制台工程,命名为RestFulService。如图所示:
并为该工程添加引用System.ServiceModel 和System.ServiceModel.Web
建立一个接口类文件,命名为IPersonInfoQuery。为了让.Net FrameWork识别这是一个service接口,咱们须要给接口添加上ServiceContract特性,而且给接口定义的方法添加OperationContract特性。这里我介绍最多见的两种方式,分别采用GET和POST方式访问。咱们能够看到,与普通WCF服务契约不一样的是,须要额外用WebGet或者WebInvoke指定REST访问的方式。另外还要指定消息包装样式和消息格式,默认的消息请求和响应格式为XML,若选择JSON须要显式声明。 UriTemplate用来将方法映射到具体的Uri上,但若是不指定映射,将映射到默认的Uri。好比采用Get访问的GetUser方法,默认映射是:/GetSource?Name={Name}
咱们定义两种方法,一、GetScore方法:经过GET请求传入name,返回对应的成绩;二、GetInfo方法:经过POST请求,传入Info对象,查找对应的User并返回给客户端,代码以下
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.ServiceModel; 5 using System.ServiceModel.Web; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace RestFulService 10 { 11 /// <summary> 12 /// 简单定义两种方法,一、GetScore方法:经过GET请求传入name,返回对应的成绩;二、GetInfo方法:经过POST请求,传入Info对象,查找对应的User并返回给客户端 13 /// </summary> 14 [ServiceContract(Name = "PersonInfoQueryServices")] 15 public interface IPersonInfoQuery 16 { 17 /// <summary> 18 /// 说明:GET请求 19 /// WebGet默认请求是GET方式 20 /// UriTemplate(URL Routing)的参数名name必需要方法的参数名必须一致(不区分大小写) 21 /// RequestFormat规定客户端必须是什么数据格式请求的(JSon或者XML),不设置默认为XML 22 /// ResponseFormat规定服务端返回给客户端是以是什么数据格返回的(JSon或者XML) 23 /// </summary> 24 /// <param name="name"></param> 25 /// <returns></returns> 26 [OperationContract] 27 [WebGet(UriTemplate = "PersonInfoQuery/{name}", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 28 User GetScore(string name); 29 30 /// <summary> 31 /// 说明:POS请求 32 /// WebInvoke请求方式有POST、PUT、DELETE等,因此须要明确指定Method是哪一种请求的,这里咱们设置POST请求。 33 /// 注意:POST状况下,UriTemplate(URL Routing)通常是没有参数(和上面GET的UriTemplate不同,由于POST参数都经过消息体传送) 34 /// RequestFormat规定客户端必须是什么数据格式请求的(JSon或者XML),不设置默认为XML 35 /// ResponseFormat规定服务端返回给客户端是以是什么数据格返回的(JSon或者XML) 36 /// </summary> 37 /// <param name="info"></param> 38 /// <returns></returns> 39 [OperationContract] 40 [WebInvoke(Method = "POST", UriTemplate = "PersonInfoQuery/Info", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 41 User GetInfo(Info info); 42 } 43 }
定义对象User
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Runtime.Serialization; 7 8 namespace RestFulService 9 { 10 [DataContract] 11 public class User 12 { 13 [DataMember] 14 public int ID { get; set; } 15 16 [DataMember] 17 public string Name { get; set; } 18 19 [DataMember] 20 public int Age { get; set; } 21 22 [DataMember] 23 public int Score { get; set; } 24 } 25 }
定义对象Info
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.Serialization; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace RestFulService 9 { 10 [DataContract] 11 public class Info 12 { 13 [DataMember] 14 public int ID { get; set; } 15 16 [DataMember] 17 public string Name { get; set; } 18 } 19 }
说明:
UriTemplate就是咱们以前提到的URL Routing(能够单首创建一个Routing进行管理)
WebGet默认请求是GET方式。
WebInvoke请求方式有POST、PUT、DELETE等,因此须要明确指定Method是哪一种请求的。
UriTemplate(URL Routing)的参数名name必需要方法的参数名必须一致(不区分大小写)
POST状况下,UriTemplate(URL Routing)通常是没有参数(和GET的UriTemplate不同,由于POST参数都经过消息体传送)
RequestFormat规定客户端必须是什么数据格式请求的(JSon或者XML),不设置默认为XML
ResponseFormat规定服务端返回给客户端是以是什么数据格返回的(JSon或者XML)
上面咱们定义了接口,那么咱们须要建立一个服务去实现这个接口的方法,咱们建立一个类名为PersonInfoQueryServices,咱们须要设置一些ServiceBehavior特征属性。代码以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.ServiceModel; 5 using System.ServiceModel.Activation; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace RestFulService 10 { 11 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, IncludeExceptionDetailInFaults = true)] 12 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 13 public class PersonInfoQueryServices : IPersonInfoQuery 14 { 15 private List<User> UserList = new List<User>(); 16 /// <summary> 17 /// 生成一些测试数据 18 /// </summary> 19 public PersonInfoQueryServices() 20 { 21 UserList.Add(new User() { ID = 1, Name = "张三", Age = 18, Score = 98 }); 22 UserList.Add(new User() { ID = 2, Name = "李四", Age = 20, Score = 80 }); 23 UserList.Add(new User() { ID = 3, Name = "王二麻子", Age = 25, Score = 59 }); 24 } 25 /// <summary> 26 /// 实现GetScore方法,返回某人的成绩 27 /// </summary> 28 /// <param name="name"></param> 29 /// <returns></returns> 30 public User GetScore(string name) 31 { 32 return UserList.FirstOrDefault(n => n.Name == name); 33 } 34 /// <summary> 35 /// 实现GetInfo方法,返回某人的User信息 36 /// </summary> 37 /// <param name="info"></param> 38 /// <returns></returns> 39 public User GetInfo(Info info) 40 { 41 return UserList.FirstOrDefault(n => n.ID == info.ID && n.Name == info.Name); 42 } 43 44 } 45 }
上面咱们定义了Service接口,并实现了Service方法,如今咱们须要将编写宿主程序,以便可以使客户端经过GET或者POST方法进行请求。代码以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.ServiceModel; 5 using System.ServiceModel.Description; 6 using System.ServiceModel.Web; 7 using System.Text; 8 using System.Threading.Tasks; 9 10 namespace RestFulService 11 { 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 try 17 { 18 PersonInfoQueryServices service = new PersonInfoQueryServices(); 19 Uri baseAddress = new Uri("http://127.0.0.1:7788/"); 20 using (ServiceHost _serviceHost = new ServiceHost(service, baseAddress))//或者:WebServiceHost _serviceHost = new WebServiceHost(typeof(PersonInfoQueryServices), baseAddress); 21 { 22 //若是不设置MaxBufferSize,当传输的数据特别大的时候,很容易出现“提示:413 Request Entity Too Large”错误信息,最大设置为20M 23 WebHttpBinding binding = new WebHttpBinding 24 { 25 TransferMode = TransferMode.Buffered, 26 MaxBufferSize = 2147483647, 27 MaxReceivedMessageSize = 2147483647, 28 MaxBufferPoolSize = 2147483647, 29 ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max, 30 Security = { Mode = WebHttpSecurityMode.None } 31 }; 32 _serviceHost.AddServiceEndpoint(typeof(IPersonInfoQuery), binding, baseAddress); 33 _serviceHost.Opened += delegate 34 { 35 Console.WriteLine("Web服务已开启..."); 36 }; 37 _serviceHost.Open(); 38 Console.WriteLine("输入任意键关闭程序!"); 39 Console.ReadKey(); 40 _serviceHost.Close(); 41 } 42 } 43 catch (Exception) 44 { 45 Console.WriteLine("Web服务开启失败:{0}\r\n{1}", ex.Message, ex.StackTrace); 46 } 47 } 48 } 49 }
开启效果以下:
最后,咱们经过浏览器简单的测试一下GET请求的效果是怎样的,如图所示:
PS:更多Restful的C#规范,能够参考微软的文档--->WebGet和WebInvoke传送门 API传送门
上面咱们已经简单的成功实现了Restful Service,下面咱们简单的讲解一下,如何实现Restful 客户端来校验上面的Restful 服务器的正确性。PS:如何定义高效便捷的Restful Client帮助类,咱们将在下篇文章进行讲解,本文就先介绍一种简单有效的Restful Client作一个Demo测试。
咱们定义一个Restful 客户端的帮助类RestClient,用于和Restful 服务端交互,如图所示:
、
帮助类代码以下:
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Net; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace RestFulClient 10 { 11 /// <summary> 12 /// 请求类型 13 /// </summary> 14 public enum EnumHttpVerb 15 { 16 GET, 17 POST, 18 PUT, 19 DELETE 20 } 21 22 public class RestClient 23 { 24 #region 属性 25 /// <summary> 26 /// 端点路径 27 /// </summary> 28 public string EndPoint { get; set; } 29 30 /// <summary> 31 /// 请求方式 32 /// </summary> 33 public EnumHttpVerb Method { get; set; } 34 35 /// <summary> 36 /// 文本类型(一、application/json 二、txt/html) 37 /// </summary> 38 public string ContentType { get; set; } 39 40 /// <summary> 41 /// 请求的数据(通常为JSon格式) 42 /// </summary> 43 public string PostData { get; set; } 44 #endregion 45 46 #region 初始化 47 public RestClient() 48 { 49 EndPoint = ""; 50 Method = EnumHttpVerb.GET; 51 ContentType = "application/json"; 52 PostData = ""; 53 } 54 55 public RestClient(string endpoint) 56 { 57 EndPoint = endpoint; 58 Method = EnumHttpVerb.GET; 59 ContentType = "application/json"; 60 PostData = ""; 61 } 62 63 public RestClient(string endpoint, EnumHttpVerb method) 64 { 65 EndPoint = endpoint; 66 Method = method; 67 ContentType = "application/json"; 68 PostData = ""; 69 } 70 71 public RestClient(string endpoint, EnumHttpVerb method, string postData) 72 { 73 EndPoint = endpoint; 74 Method = method; 75 ContentType = "application/json"; 76 PostData = postData; 77 } 78 #endregion 79 80 #region 方法 81 /// <summary> 82 /// http请求(不带参数请求) 83 /// </summary> 84 /// <returns></returns> 85 public string HttpRequest() 86 { 87 return HttpRequest(""); 88 } 89 90 /// <summary> 91 /// http请求(带参数) 92 /// </summary> 93 /// <param name="parameters">parameters例如:?name=LiLei</param> 94 /// <returns></returns> 95 public string HttpRequest(string parameters) 96 { 97 var request = (HttpWebRequest)WebRequest.Create(EndPoint + parameters); 98 99 request.Method = Method.ToString(); 100 request.ContentLength = 0; 101 request.ContentType = ContentType; 102 103 if (!string.IsNullOrEmpty(PostData) && Method == EnumHttpVerb.POST) 104 { 105 var bytes = Encoding.UTF8.GetBytes(PostData); 106 request.ContentLength = bytes.Length; 107 108 using (var writeStream = request.GetRequestStream()) 109 { 110 writeStream.Write(bytes, 0, bytes.Length); 111 } 112 } 113 114 using (var response = (HttpWebResponse)request.GetResponse()) 115 { 116 var responseValue = string.Empty; 117 118 if (response.StatusCode != HttpStatusCode.OK) 119 { 120 var message = string.Format("请求数据失败. 返回的 HTTP 状态码:{0}", response.StatusCode); 121 throw new ApplicationException(message); 122 } 123 124 using (var responseStream = response.GetResponseStream()) 125 { 126 if (responseStream != null) 127 using (var reader = new StreamReader(responseStream)) 128 { 129 responseValue = reader.ReadToEnd(); 130 } 131 } 132 return responseValue; 133 } 134 } 135 #endregion 136 } 137 }
接下来咱们验证上面resful 服务端当中的两个实例,代码以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Newtonsoft.Json; 7 8 namespace RestFulClient 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 Console.Title = "Restful客户端Demo测试"; 15 16 RestClient client = new RestClient(); 17 client.EndPoint = @"http://127.0.0.1:7788/"; 18 19 client.Method = EnumHttpVerb.GET; 20 string resultGet = client.HttpRequest("PersonInfoQuery/王二麻子"); 21 Console.WriteLine("GET方式获取结果:" + resultGet); 22 23 client.Method = EnumHttpVerb.POST; 24 Info info = new Info(); 25 info.ID = 1; 26 info.Name = "张三"; 27 client.PostData = JsonConvert.SerializeObject(info);//JSon序列化咱们用到第三方Newtonsoft.Json.dll 28 var resultPost = client.HttpRequest("PersonInfoQuery/Info"); 29 Console.WriteLine("POST方式获取结果:" + resultPost); 30 Console.Read(); 31 } 32 } 33 34 [Serializable] 35 public class Info 36 { 37 public int ID { get; set; } 38 public string Name { get; set; } 39 } 40 }
结果如图所示:
至此、一个简单的Restful 服务端和Restful 客户端的就这样实现了,下篇文章讲解若是设计一个高效可扩展的Restful完整帮助类,这样咱们就能够跨平台、跨语言访问其余平台提供的Restful Service了。
文章代码已上传Github--->下载地址
PS:若有疑问,请留言,未经容许,不得私自转载,转载请注明出处:http://www.cnblogs.com/xuliangxing/p/8735552.html