REST介紹
全稱Representational State Transfer,即表現層狀態轉換,如果一個架構符合REST原則,我們就稱他為Restfull架構,其主要包括如下方面:
- 資源Resources
REST的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。
所謂"資源",就是網絡上的一個實體,或者說是網絡上的一個具體信息。它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的實在。你可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的URI。要獲取這個資源,訪問它的URI就可以,因此URI就成了每一個資源的地址或獨一無二的識別符。
所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的URI。
- 表現層 Representation
"資源"是一種信息實體,它可以有多種外在表現形式,我們把"資源"具體呈現出來的形式,叫做它的”表現“
比如,文本可以用txt格式表現,也可以用HTML格式、XML格式、JSON格式表現,甚至可以采用二進制格式;圖片可以用JPG格式表現,也可以用PNG格式表現。
URI只代表資源的實體,不代表它的形式。嚴格地說,有些網址最后的".html"后綴名是不必要的,因為這個后綴名表示格式,屬於"表現層"范疇,而URI應該只代表"資源"的位置。它的具體表現形式,應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段才是對"表現層"的描述
-
狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,勢必涉及到數據和狀態的變化。
互聯網通信協議HTTP協議,是一個無狀態協議。這意味着,所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是建立在表現層之上的,所以就是"表現層狀態轉化"。
客戶端用到的手段,只能是HTTP協議。具體來說,就是HTTP協議里面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源。
綜上,其實RESTful架構就是:
(1)每一個URI代表一種資源;
(2)客戶端和服務器之間,傳遞這種資源的某種表現層;
(3)客戶端通過四個HTTP動詞,對服務器端資源進行操作,實現"表現層狀態轉化"。
RESTful架構設計規范
-
希望URI中不出現動詞
如 顯示文章
野生寫法:
/artical/show/1
正規寫法:
/artical/1
如果必須要動詞,此時動詞需要看作為一個服務,如銀行轉賬,從賬戶1向賬戶2匯款500元
野生寫法:
POST /accounts/1/transfer/500/to/2
正規寫法:
POST /transaction HTTP/1.1 Host: 127.0.0.1 from=1&to=2&amount=500.00
-
希望URL中不出現版本號
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字段中進行區分
-
協議 API最好使用https
-
域名 盡量使用專用API域名,如 api.baidu.com
-
路徑 又稱為endpoint,表示API的具體資源,在restfull架構中,每個URI代表一種資源,所以URI中的名詞需和實際功能對應
-
HTTP 動詞
-
常用動詞
GET(select) 獲取資源
POST (create)創建資源
PUT (update)更新資源(客戶端提供改變后的完整資源)
DELETE (delete)刪除資源
PATCH(update) 更新資源(客戶端提供改變的屬性)
-
不常用的HTTP動詞:
HEAD 獲取資源的元數據
OPTIONS 獲取信息,關於資源的那些屬性是客戶端可以改變的
-
-
過濾信息
如果記錄的數量很多,服務器需要分段返回給用戶,API應該提供參數,過濾返回結果,如下:
- ?limit=10 指定返回的記錄數量
- ?offset=10 指定反饋記錄的開始位置
- ?page=2&per_page=100 指定第幾頁,以及每頁的記錄數量
- ?sortby=name&order=asc 指定返回的結果按那個屬性進行排序,以及如何排序
- ?animal_type_id=1 指定篩選條件
-
狀態碼
- 200 (“OK”) 用於一般性的成功返回, 不可用於請求錯誤返回
- 201 (“Created”) 資源被創建
- 202 (“Accepted”) 用於Controller控制類資源異步處理的返回,僅表示請求已經收到。對於耗時比較久的處理,一般用異步處理來完成
- 204 (“No Content”) 此狀態可能會出現在PUT、POST、DELETE的請求中,一般表示資源存在,但消息體中不會返回任何資源相關的狀態或信息。
- 301 (“Moved Permanently”) 資源的URI被轉移,需要使用新的URI訪問
- 302 (“Found”) 不推薦使用,此代碼在HTTP1.1協議中被303/307替代。我們目前對302的使用和最初HTTP1.0定義的語意是有出入的,應該只有在GET/HEAD方法下,客戶端才能根據Location執行自動跳轉,而我們目前的客戶端基本上是不會判斷原請求方法的,無條件的執行臨時重定向
- 303 (“See Other”) 返回一個資源地址URI的引用,但不強制要求客戶端獲取該地址的狀態(訪問該地址)
- 304 (“Not Modified”) 有一些類似於204狀態,服務器端的資源與客戶端最近訪問的資源版本一致,並無修改,不返回資源消息體。可以用來降低服務端的壓力
- 307 (“Temporary Redirect”) 目前URI不能提供當前請求的服務,臨時性重定向到另外一個URI。在HTTP1.1中307是用來替代早期HTTP1.0中使用不當的302
- 400 (“Bad Request”) 用於客戶端一般性錯誤返回, 在其它4xx錯誤以外的錯誤,也可以使用400,具體錯誤信息可以放在body中
- 401 (“Unauthorized”) 在訪問一個需要驗證的資源時,驗證錯誤
- 403 (“Forbidden”) 一般用於非驗證性資源訪問被禁止,例如對於某些客戶端只開放部分API的訪問權限,而另外一些API可能無法訪問時,可以給予403狀態
- 404 (“Not Found”) 找不到URI對應的資源
- 405 (“Method Not Allowed”) HTTP的方法不支持,例如某些只讀資源,可能不支持POST/DELETE。但405的響應header中必須聲明該URI所支持的方法
- 406 (“Not Acceptable”) 客戶端所請求的資源數據格式類型不被支持,例如客戶端請求數據格式為application/xml,但服務器端只支持application/json
- 409 (“Conflict”) 資源狀態沖突,例如客戶端嘗試刪除一個非空的Store資源
- 412 (“Precondition Failed”) 用於有條件的操作不被滿足時
- 415 (“Unsupported Media Type”) 客戶所支持的數據類型,服務端無法滿足
- 500 (“Internal Server Error”) 服務器端的接口錯誤,此錯誤於客戶端無關
-
返回結果
針對不同的操作,server向用戶返回的結果應該符合如下規范
- GET 返回資源對象的列表或單個資源
- POST 返回新生的資源對象
- PUT 返回完整的資源對象
- PATCH 返回完整的資源對象
- DELETE 返回空文檔,並告知結果
-
Django + RESTful安裝配置
RESTful api 關於django的插件名為 djangorestframework,官方網站為:http://www.django-rest-framework.org/
- 安裝
pip install djangorestframework
- setting配置
在project的settings.py中進行注冊app,並添加驗證配置
INSTALLED_APPS = [
...
'rest_framework',
]
#對於匿名只讀
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
- models配置
class IDC(models.Model):
'''機房'''
name = models.CharField(max_length=64, unique=True)
def __str__(self):
return self.name
class Host(models.Model):
hostname = models.CharField(max_length=64,unique=True)
ip_addr = models.GenericIPAddressField()
port = models.SmallIntegerField(default=22)
idc = models.ForeignKey('IDC',blank=True,null=True)
system_type_choices = ((0,'Linux'),(1,'Windows'))
system_type = models.SmallIntegerField(choices=system_type_choices,default=0)
memo = models.CharField(max_length=128,blank=True,null=True)
enabled = models.BooleanField(default=1,verbose_name="啟用本機")
class Meta:
unique_together = ('ip_addr','port')
verbose_name = "主機表"
def __str__(self):
return "%s(%s)"%( self.hostname,self.ip_addr)
- Serializers配置
rest_serializer.py
from app01 import models
from rest_framework import serializers
class IDCSerializer(serializers.ModelSerializer):
class Meta:
model = models.IDC
fields = ('name',)
class HostSerializer(serializers.ModelSerializer):
class Meta:
model = models.Host
fields = ('id','hostname','ip_addr','port','idc','system_type','memo','enabled')
- views配置
rest_views.py
from rest_framework import viewsets
from app01 import models
from app01 import rest_searializer
class UserViewSet(viewsets.ModelViewSet):
queryset = models.UserProfile.objects.all()
serializer_class = rest_searializer.UserSerializer
class IDCViewSet(viewsets.ModelViewSet):
queryset = models.IDC.objects.all()
serializer_class = rest_searializer.IDCSerializer
class HostViewSet(viewsets.ModelViewSet):
queryset = models.Host.objects.all()
serializer_class = rest_searializer.HostSerializer
- URL 配置
from rest_framework import routers
from app01 import rest_viewset
from django.conf.urls import url,include
router = routers.DefaultRouter()
router.register(r'idc',rest_viewset.IDCViewSet)
router.register(r'host',rest_viewset.HostViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
- 啟動Django 測試
后續會對上述的所有點進行詳細說明。