restful设计规范

什么是restful?

  • REST与技术无关,表明的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源经过URL进行标识,客户端应用经过URL来获取资源的表征,得到这些表征导致这些应用转变状态
  • 全部的数据,不过是经过网络获取的仍是操做(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其余架构风格的最本质属性
  • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

理解restful架构

愈来愈多的人开始意识到,网站即软件,并且是一种新型的软件。css

这种"互联网软件"采用客户端/服务器模式,创建在分布式体系上,经过互联网通讯,具备高延时(high latency)、高并发等特色。html

网站开发,彻底能够采用软件开发的模式。可是传统上,软件和网络是两个不一样的领域,不多有交集;软件开发主要针对单机环境,网络则主要研究系统之间的通讯。互联网的兴起,使得这两个领域开始融合,如今咱们必须考虑,如何开发在互联网环境中使用的软件。python

 

RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,因此正获得愈来愈多网站的采用。git

可是,到底什么是RESTful架构,并非一个容易说清楚的问题。下面,我就谈谈我理解的RESTful架构。github

1、起源

REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。数据库

Fielding是一个很是重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的做者之1、Apache基金会的第一任主席。因此,他的这篇论文一经发表,就引发了关注,而且当即对互联网开发产生了深远的影响。django

他这样介绍论文的写做目的:编程

"本文研究计算机科学两大前沿----软件和网络----的交叉点。长期以来,软件研究主要关注软件设计的分类、设计方法的演化,不多客观地评估不一样的设计选择对系统行为的影响。而相反地,网络研究主要关注系统之间通讯行为的细节、如何改进特定通讯机制的表现,经常忽视了一个事实,那就是改变应用程序的互动风格比改变互动协议,对总体表现有更大的影响。我这篇文章的写做目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,获得一个功能强、性能好、适宜通讯的架构。"json

(This dissertation explores a junction on the frontiers of two research disciplines in computer science: software and networking. Software research has long been concerned with the categorization of software designs and the development of design methodologies, but has rarely been able to objectively evaluate the impact of various design choices on system behavior. Networking research, in contrast, is focused on the details of generic communication behavior between systems and improving the performance of particular communication techniques, often ignoring the fact that changing the interaction style of an application can have more impact on performance than the communication protocols used for that interaction. My work is motivated by the desire to understand and evaluate the architectural design of network-based application software through principled use of architectural constraints, thereby obtaining the functional, performance, and social properties desired of an architecture. )api

 

2、名称

Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。

若是一个架构符合REST原则,就称它为RESTful架构。

要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组究竟是什么意思,它的每个词表明了什么涵义。若是你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。

3、资源(Resources)

REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它能够是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你能够用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就能够,所以URI就成了每个资源的地址或独一无二的识别符。

所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

4、表现层(Representation)

"资源"是一种信息实体,它能够有多种外在表现形式。咱们把"资源"具体呈现出来的形式,叫作它的"表现层"(Representation)。

好比,文本能够用txt格式表现,也能够用HTML格式、XML格式、JSON格式表现,甚至能够采用二进制格式;图片能够用JPG格式表现,也能够用PNG格式表现。

URI只表明资源的实体,不表明它的形式。严格地说,有些网址最后的".html"后缀名是没必要要的,由于这个后缀名表示格式,属于"表现层"范畴,而URI应该只表明"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。

5、状态转化(State Transfer)

访问一个网站,就表明了客户端和服务器的一个互动过程。在这个过程当中,势必涉及到数据和状态的变化。

互联网通讯协议HTTP协议,是一个无状态协议。这意味着,全部的状态都保存在服务器端。所以,若是客户端想要操做服务器,必须经过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是创建在表现层之上的,因此就是"表现层状态转化"。

客户端用到的手段,只能是HTTP协议。具体来讲,就是HTTP协议里面,四个表示操做方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操做:GET用来获取资源,POST用来新建资源(也能够用于更新资源),PUT用来更新资源,DELETE用来删除资源。

6、综述

综合上面的解释,咱们总结一下什么是RESTful架构:

  (1)每个URI表明一种资源;

  (2)客户端和服务器之间,传递这种资源的某种表现层;

  (3)客户端经过四个HTTP动词,对服务器端资源进行操做,实现"表现层状态转化"。

7、误区

RESTful架构有一些典型的设计误区。

最多见的一种设计错误,就是URI包含动词。由于"资源"表示一种实体,因此应该是名词,URI不该该有动词,动词应该放在HTTP协议中。

举例来讲,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,而后用GET方法表示show。

若是某些动做是HTTP动词表示不了的,你就应该把动做作成一种资源。好比网上汇款,从帐户1向帐户2汇款500元,错误的URI是:

POST /accounts/1/transfer/500/to/2

正确的写法是把动词transfer改为名词transaction,资源不能是动词,可是能够是一种服务:

POST /transaction HTTP/1.1
Host: 127.0.0.1
 
from=1&to=2&amount=500.00

另外一个设计误区,就是在URI中加入版本号:

http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo

由于不一样的版本,能够理解成同一种资源的不一样表现形式,因此应该采用同一个URI。版本号能够在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):

Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0

*注,虽然说restfull规范建议版本号放在请求头而不是url里,但事实上为了使用方便,大多数开发者仍是喜欢把版本号放在url上,这样容易直观区分

Restful API设计指南

接下来我将介绍RESTful API的设计细节,探讨如何设计一套合理、好用的API

1、协议:尽可能使用https

API与用户的通讯协议,老是使用HTTPs协议

2、体现API

应该尽可能将API部署在专用域名之下。

https://api.example.com

若是肯定API很简单,不会有进一步扩展,能够考虑放在主域名下。  

https://example.org/api/

3、体现版本(Versioning)

应该将API的版本号放入URL。

https://api.example.com/v1/

另外一种作法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种作法。

4、面向资源编程(Endpoint)

路径又称"终点"(endpoint),表示API的具体网址。

在RESTful架构中,每一个网址表明一种资源(resource),因此网址中不能有动词,只能有名词,并且所用的名词每每与数据库的表格名对应。通常来讲,数据库中的表都是同种记录的"集合"(collection),因此API中的名词也应该使用复数。

举例来讲,有一个API提供动物园(zoo)的信息,还包括各类动物和雇员的信息,则它的路径应该设计成下面这样。

https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees

5、根据接口的不一样,进行不一样的操做

对于资源的具体操做类型,由HTTP动词表示。

经常使用的HTTP动词有下面五个(括号里是对应的SQL命令)。

GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。

还有两个不经常使用的HTTP动词。

HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端能够改变的。

下面是一些例子。

GET /zoos:列出全部动物园
POST /zoos:新建一个动物园
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的所有信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的全部动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

6、条件:过滤信息(Filtering)

若是记录数量不少,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。

?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪一个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件

参数的设计容许存在冗余,即容许API路径和URL参数偶尔有重复。好比,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

7、状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有如下一些(方括号中是该状态码对应的HTTP动词)。

200 OK - [GET]:服务器成功返回用户请求的数据,该操做是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操做,该操做是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户获得受权(与401错误相对),可是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操做,该操做是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(好比用户请求JSON格式,可是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再获得的。
422 Unprocesable entity - [POST/PUT/PATCH] 当建立一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将没法判断发出的请求是否成功。

状态码的彻底列表参见这里

8、错误处理(Error handling)

若是状态码是4xx,就应该向用户返回出错信息。通常来讲,返回的信息中将error做为键名,出错信息做为键值便可。

{  code: 10001
    error: "Invalid API key"
}

9、返回结果

针对不一样操做,服务器向用户返回的结果应该符合如下规范。

GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

10、Hypermedia API

RESTful API最好作到Hypermedia,即返回结果中提供连接,连向其余API方法,使得用户不查文档,也知道下一步应该作什么。

好比,当用户向api.example.com的根目录发出请求,会获得这样一个文档。

{"link": {
  "rel":   "collection https://www.example.com/zoos",
  "href":  "https://api.example.com/zoos",
  "title": "List of zoos",
  "type":  "application/vnd.yourformat+json"
}}

上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会获得一个全部可用API的网址列表。

{
  "current_user_url": "https://api.github.com/user",
  "authorizations_url": "https://api.github.com/authorizations",
  // ...
}

从上面能够看到,若是想获取当前用户的信息,应该去访问api.github.com/user,而后就获得了下面结果。

{
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。  

11、其余

(1)API的身份认证应该使用OAuth 2.0框架。

(2)服务器返回的数据格式,应该尽可能使用JSON,避免使用XML。

 resetful设计规范快速总结

		(1) 根据接口的不一样,进行不一样的操做
			GET/POST/PUT/DELETE/PATCH
		(2) 面向资源编程
			http://www.luffycity.com/salary
			
		(3). 体现版本
			http://www.luffycity.com/v1/salary
			http://www.luffycity.com/v2/salary
			
			https://v4.bootcss.com/
			https://v3.bootcss.com/
		(4). 体现是API
			http://www.luffycity.com/api/v1/salary
			http://www.luffycity.com/api/v2/salary	
			
			http://api.luffycity.com/v1/salary	
			http://api.luffycity.com/v2/salary	
		(5). https
			https://www.luffycity.com/api/v1/salary
			https://www.luffycity.com/api/v2/salary	
			
		(6). 响应式设置状态码
			200
			300
			400
			500
			return HttpResponse('adfasdf',status=300)
		
		(7). 条件 
			https://www.luffycity.com/api/v2/salary?page=1&size=10
		
		(8). 返回值
			https://www.luffycity.com/api/v2/salary
			GET: 全部列表
			{
				code: 10000,
				data: [    
					{'id':1,'title':'高亮'},
					{'id':1,'title':'龙泰'},
					{'id':1,'title':'小东北'},
				]
			}
				
			POST: 返回新增的数据
				{'id':1,'title':'高亮'}
				
			https://www.luffycity.com/api/v2/salary/1/
			GET: 获取单条数据
					{'id':1,'title':'高亮'}
			PUT:更新
					{'id':1,'title':'高亮'}
			PATCH: 局部更新
					{'id':1,'title':'高亮'}
			DELETE:删除
				
		(9). 返回错误信息
			{
				code: 100001,
				error: 'xxx错误'
			}
		
		(10). Hypermedia API
			ret = {
				code: 1000,
				data:{
					id:1,
					name:'小强',
					depart_id:http://www.luffycity.com/api/v1/depart/8/
				}
			}
	
		建议你们使用restful规范

基于Django实现

    路由系统

urlpatterns = [
    url(r'^users', Users.as_view()),
]

 CBV视图

from django.views import View
from django.http import JsonResponse
 
class Users(View):
    def get(self, request, *args, **kwargs):
        result = {
            'status': True,
            'data': 'response data'
        }
        return JsonResponse(result, status=200)
 
    def post(self, request, *args, **kwargs):
        result = {
            'status': True,
            'data': 'response data'
        }
        return JsonResponse(result, status=200)