一、Restful規範
Restful規範是一種web API介面的設計風格,在前後端分離的應用模式中適用較多。
這種風格的理念認為後端開發任務就是提供資料的,對外提供的是資料資源的訪問介面,所以在定義介面時,客戶端訪問的URL路徑就表示這種要操作的資料資源。
十條規範
1、是資料的安全保障:url連結一般都採用HTTPS協議進行傳輸
2、介面特徵表現,一看就知道是個api介面
用api關鍵字標識介面url:
https://api.baidu.com
https://www.baidu.com/api
3、多資料版本共存
在url連結中標識資料版本:url連結中的v1、v2就是不同資料版本的體現(只有在一種資料資源有多版本情況下)
https://api.baidu.com/v1
https://api.baidu.com/v2
4、資料即是資源,均使用名詞(可複數) **
介面一般都是完成前後臺資料的互動,互動的資料我們稱為資源
https://api.baidu.com/users
https://api.baidu.com/books
https://api.baidu.com/book
**5、資源操作由請求方式決定(method) ** **
操作資源一般都會涉及到增刪改查,使用提供請求方式來標識增刪改查動作
https://api.baidu.com/books - get請求:獲取所有書
https://api.baidu.com/books/1 - get請求:獲取主鍵為1的書
https://api.baidu.com/books - post請求:新增一本書書
https://api.baidu.com/books/1 - put請求:整體修改主鍵為1的書
https://api.baidu.com/books/1 - delete請求:刪除主鍵為1的書
6、過濾,通過在url上傳參的形式傳遞搜尋條件
https://api.example.com/v1/zoos?limit=10: 指定返回記錄的數量
https://api.example.com/v1/zoos?offset=10: 指定返回記錄的開始位置
https://api.example.com/v1/zoos?page=2&per_page=100:指定第幾頁,以及每頁的記錄數
https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回結果按照哪個屬性排序,以及排序順序
https://api.example.com/v1/zoos?animal_type_id=1: 指定篩選條件
7、響應狀態碼
正常響應
響應狀態碼2xx
200:常規請求
201:建立成功
重定向響應
響應狀態碼3xx
301:永久重定向
302:暫時重定向
客戶端異常
響應狀態碼4xx
403:請求無許可權
404:請求路徑不存在
405:請求方法不存在
伺服器異常
響應狀態碼5xx
500:伺服器異常
8、錯誤處理、應返回錯誤資訊,error當做key
{
"error": "無許可權操作"
}
9、返回結果,針對不同操作,伺服器向返回的結果應該符合以下規範
GET /collection: 返回資源物件的列表(陣列)
GET /collection/resource: 返回單個資源物件
POST /collection: 返回新生成的資源物件
PUT /collection/resource: 返回完整的資源物件
PATCH /collection/resource: 返回完整的資源物件
DELETE /collection/resource:返回一個空文件
10、需要url請求的資源需要訪問資源的請求連結
{
"status": 0,
"msg": "ok",
"results":[
{
"name":"肯德基(羅餐廳)",
"img": "https://image.baidu.com/kfc/001.png"
}
]
}
二、drf的簡單使用
1、在setting.py 的app中註冊
INSTALLED_APPS = [
'rest_framework'
]
2、在models.py中寫表模型
class Book(models.Model):
nid=models.AutoField(primary_key=True)
name=models.CharField(max_length=32)
price=models.DecimalField(max_digits=5,decimal_places=2)
author=models.CharField(max_length=32)
3、新建一個序列化類
from rest_framework.serializers import ModelSerializer
from app01.models import Book
class BookModelSerializer(ModelSerializer):
class Meta:
model = Book
fields = "__all__"
4、在檢視中寫檢視類
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .ser import BookModelSerializer
class BooksViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookModelSerializer
5、寫路由關係
from app01 import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter() # 可以處理檢視的路由器
router.register('book', views.BooksViewSet) # 向路由器中註冊檢視集
# 將路由器中的所以路由資訊追到到django的路由列表中
urlpatterns = [
path('admin/', admin.site.urls),
]
# 兩個列表相加
urlpatterns += router.urls
三、APIView原始碼分析
ModelViewSet繼承View(django原生View)
APIView繼承了View
先讀View的原始碼
CBV原始碼分析
from django.views import View
# urls.py
# views.Books.as_view()是一個函式記憶體地址,as_view是一個類方法,Books類直接呼叫,會把類自動傳入
path('books/', views.Books.as_view()),
# as_view返回了一個內層函式view,相當於放了一個view的記憶體地址(View——>as_view——>內層函式)
# 請求來了,如果路徑匹配,會執行————> view(request)
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
# 然後返回一個self.dispatch方法,self是誰呼叫就是誰,由於是Books.as_view(),所以self是Books物件,而Books類繼承了View類,所以執行在View類中的dispatch方法
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names: # 先判斷請求方式是否存在
# 存在則反射self(Books)中的請求方法的記憶體地址————>handler=getattr(self,'get')
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 執行get(request)
APIView原始碼分析
from rest_framework.views import APIView
# urls.py
# 這裡同樣寫的是個函式的記憶體地址,不過是用的APIView裡的as_view方法
path('booksapiview/', views.BooksAPIView.as_view()),
# APIView的as_view方法(類的繫結方法)
@classmethod
def as_view(cls, **initkwargs):
......
# 主要的如下
# 先呼叫了父類(View)的as_view方法
view = super().as_view(**initkwargs)
# 當呼叫了父類(View)的as_view方法會執行self.dispatch(),此時的self是BooksAPIView,並且繼承了APIView類,所以此時的dispatch()方法是APIView的dispatch()方法(原dispatch方法被重寫了)
view.cls = cls
view.initkwargs = initkwargs
# 以後的所有請求都沒有csrf認證了,只要繼承了APIView,就沒有CSRF認證
return csrf_exempt(view)
# 區域性禁用csrf,在檢視上加裝飾器@csrf_exempt,和csrf_exempt(view)是一樣的
# 請求來了——》路由匹配上——》view(request)——》呼叫了self.dispatch()——》執行APIView的dispatch方法
# APIView的dispatch方法
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 將原生的request包裝成一個新的request,以後在用request,就是新的request物件了
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 三大認證模組
self.initial(request, *args, **kwargs)
# 這裡就相當於原生的dispatch方法的反射,先判斷請求方法是否存在,在去反射獲取請求方法的記憶體地址
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 響應模組
response = handler(request, *args, **kwargs)
except Exception as exc:
# 異常模組
response = self.handle_exception(exc)
# 渲染模組,
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
# APIView的initial方法(三大認證模組)
def initial(self, request, *args, **kwargs):
...
# 認證元件:校驗使用者 - 遊客、合法使用者、非法使用者
# 遊客:代表校驗通過,直接進入下一步校驗(許可權校驗)
# 合法使用者:代表校驗通過,將使用者儲存在request.user中,再進入下一步校驗(許可權校驗)
# 非法使用者:代表校驗失敗,丟擲異常,返回403許可權異常結果
self.perform_authentication(request)
# 許可權元件:校驗使用者許可權 - 必須登入、所有使用者、登入讀寫遊客只讀、自定義使用者角色
# 認證通過:可以進入下一步校驗(頻率認證)
# 認證失敗:丟擲異常,返回403許可權異常結果
self.check_permissions(request)
# 頻率元件:限制檢視介面被訪問的頻率次數 - 限制的條件(IP、id、唯一鍵)、頻率週期時間(s、m、h)、頻率的次數(3/s)
# 沒有達到限次:正常訪問介面
# 達到限次:限制時間內不能訪問,限制時間達到後,可以重新訪問
self.check_throttles(request)
只要繼承了APIView,檢視類中的request物件都是新的,也就是上面那個request物件,
原生的request在新的request._request中
以後使用request物件,就像使用之前的request是一樣的(因為重寫了__getattr__
方法)
from rest_framework.request import Request
# 點攔截,當新request.的時候觸發
def __getattr__(self, attr):
try:
return getattr(self._request, attr) # 通過反射,獲取原生request物件,取出屬性或方法
except AttributeError:
return self.__getattribute__(attr)
# request.data 是一個方法,不過是被@property裝飾了
# request.data 是一個字典,post請求不管使用什麼編碼,傳過來的資料都在request.data中
# get請求的資料還可以在request.query_params中取
@property
def query_params(self):
return self._request.GET
# 檢視類中
print(request.query_params) # get請求,地址中的引數
# 原來在
print(request.GET)