DRF之頻率限制、分頁、解析器和渲染器
一、頻率限制
1、頻率限制是做什麼的
開放平臺的API介面呼叫需要限制其頻率,以節約伺服器資源和避免惡意的頻繁呼叫。
2、頻率元件原理
DRF中的頻率控制基本原理是基於訪問次數和時間的,當然我們可以通過自己定義的方法來實現。
當我們請求進來,走到我們頻率元件的時候,DRF內部會有一個字典來記錄訪問者的IP,
以這個訪問者的IP為key,value為一個列表,存放訪問者每次訪問的時間,
{
IP1: [第三次訪問時間,第二次訪問時間,第一次訪問時間],
IP2: [第三次訪問時間,第二次訪問時間,第一次訪問時間],
}
把每次訪問最新時間放入列表的最前面,記錄成這樣的一個數據結構
如果我們設定的是10秒內只能訪問5次,
-- 1,判斷訪問者的IP是否在這個請求IP的字典裡
-- 2,保證這個列表裡都是最近10秒內的訪問的時間
判斷當前請求時間和列表裡最早的(也就是最後的)請求時間的差值
如果差大於10秒,說明請求以及不是最近10秒內的,刪除(最早的)最後一個時間,
繼續判斷倒數第二個,直到差值小於10秒
-- 3,判斷列表的長度(即訪問次數),是否大於我們設定的5次,
如果大於就限流,否則放行,並把時間放入列表的最前面。
3、自定義頻率元件的使用
ps:原始碼自己看吧,相信你看得懂的,跟認證、許可權流程差不多的
1. 自定義頻率限制類 from rest_framework import throttling import time # 10秒內不能超過5次訪問 class MyThrottle(throttling.BaseThrottle): VISIT_RECORD = {} def __init__(self): self.history = None def allow_request(self, request, view): # 頻率限制 # 獲取使用者ip ip = request.META.get('REMOTE_ADDR') now = time.time() # 判斷使用者是否第一次訪問 if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip] = [now] return True # 拿到使用者的訪問記錄列表 history = self.VISIT_RECORD[ip] # 用於返回還有多久才能再次訪問 self.history = history # 把訪問的時間插入訪問記錄列表 history.insert(0, now) # 如果當前訪問時間和最早的時間間隔超過10秒,刪除最早的時間 while history and now - history[-1] > 10: history.pop() # 如果訪問列表長度大於5,限制訪問 if len(history) > 5: return False else: return True def wait(self): # 還有等多久才能訪問 now = time.time() return self.history[-1] + 10 - now 2. 檢視 class TestView(APIView): throttle_classes = [MyThrottle, ] def get(self, request): return Response("頻率測試介面")
4、DRF的頻率限制模組
1. DRF的頻率限制模組有5個類(即5種限制方式) 2. SimpleRateThrottle # 從原始碼中可以指定頻率限制類必須要有allow_request和wait方法 # 注意在SimpleRateThrottle類裡面已經有allow_request和wait方法 # 但是allow_request方法,還需要你重寫一些額外的方法和屬性 from rest_framework import throttling # 1分鐘內不能超過5次 class MyVisit(throttling.SimpleRateThrottle): scope = "MV" def get_cache_key(self, request, view): # 這個方法的返回值應該是ip地址 return request.META.get('REMOTE_ADDR') 還必須要配置一些設定settings REST_FRAMEWORK = { # 頻率限制的配置 "DEFAULT_THROTTLE_RATES": { 'MV': '5/m', # 速率配置每分鐘不能超過5次訪問,MV是scope定義的值, } } 3. 其他 限制匿名使用者 AnonRateThrottle VISIT_RECORD那個大字典的鍵:登入使用者用主鍵,沒有登入的使用者用IP地址 UserRateThrottle 自己看。。 ScopedRateThrottle
二、DRF之分頁
1、介紹
DRF的自帶的分頁器幾乎能滿足我們對分頁的要求了,所以這裡分頁我們就直接使用DRF的分頁,
用自定義的類繼承DRF的分頁類,然後重寫一些我們需要的引數就好了。
2、PageNumberPagination分頁類
0. 簡介 看第n頁,每頁顯示預設設定的資料數量 --> http://127.0.0.1:8000/book/?page=1 看第n頁,每頁顯示n條資料 --> http://127.0.0.1:8000/book/?page=2&size=4 1. 分頁類 class MyPagination(pagination.PageNumberPagination): # 每頁顯示的數量 page_size = 2 # 每頁顯示的最大數量 max_page_size = 5 # 搜尋的引數關鍵字,即 ? page_query_param = 'page' # 控制每頁顯示數量的關鍵字 page_size_query_param = 'size' 2. 在自定義檢視中使用分頁 class BookView(APIView): def get(self, request): queryset = Book.objects.all() # 1.例項化分頁器物件 paginator = MyPagination() # 2.呼叫這個分頁器類的分頁方法,拿到分頁後的資料 page_queryset = paginator.paginate_queryset(queryset, request) # 3.把分頁好的資料拿去序列化 ser_obj = BookSerializer(page_queryset, many=True) # 這樣返回資料,可以在瀏覽器輸入size引數設定每頁顯示的資料 # return Response(ser_obj.data) # 呼叫分頁器的get_paginated_response方法 返回帶上一頁下一頁的資料 # 使用這個方法後不能在瀏覽器輸入size引數設定每頁顯示的資料了 return paginator.get_paginated_response(ser_obj.data) 3. 在DRF的提供的檢視中使用分頁 class BookView(generics.GenericAPIView, mixins.ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer # 配置分頁器類 pagination_class = MyPagination def get(self, request): return self.list(request)
3、LimitOffsetPagination分頁類
0. 簡介 從0開始取兩條資料(1、2) --> http://127.0.0.1:8000/api/pagination/book/?limit=2 從第二條資料開始取兩條資料(3、4) --> http://127.0.0.1:8000/api/pagination/book/?limit=2&offset=2 1. 分頁類 class MyPagination(pagination.LimitOffsetPagination): # 從哪裡開始拿資料 offset_query_param = 'offset' # 拿多少條資料 limit_query_param = 'limit' # 預設拿多少條資料 default_limit = 2 # 最多拿多少條 max_limit = 5 2. 在自定義檢視或者DRF提供的檢視中使用分頁 跟上面使用方法一致。
4、CursorPagination分頁類
0. 簡介 按xx的順序(倒序)顯示xx條資料 --> http://127.0.0.1:8000/api/pagination/book/ 上一頁下一頁是的值是隨機字串,每頁顯示3條資料 --> http://127.0.0.1:8000/api/pagination/book/?cursor=cD01&size=3 1. 分頁類 class MyPagination(pagination.CursorPagination): cursor_query_param = 'cursor' # 每頁顯示的數量的搜尋關鍵字 page_size_query_param = 'size' # 每頁顯示的資料數量 page_size = 3 # 每頁最大顯示的資料數量 max_page_size = 5 # 按id的倒序顯示 ordering = '-id' 2. 在自定義檢視或者DRF提供的檢視中使用分頁 跟上面使用方法一致。
三、解析器
1、介紹
解析器的作用就是服務端接收客戶端傳過來的資料,把資料解析成自己想要的資料型別的過程。
本質就是對請求體中的資料進行解析。
請求頭中的Content-Type告訴我們傳過來的是什麼型別的資料,我們就找自己的解析器,看有沒有對應的解析器。
2、Django的解析器
請求進來的時候,請求體中的資料在request.body中,說明,解析器會把解析好的資料放入request.body,
我們在檢視中可以列印request的型別,就能夠發現request是WSGIRequest這個類。
那我們是怎麼拿到request.POST資料的?
application/x-www-form-urlencoded只能上傳文字格式的檔案,
multipart/form-data是將檔案以二進位制的形式上傳,這樣可以實現多種型別的檔案上傳
一個解析到request.POST,一個解析到request.FILES中。
也就是說我們之前能在request中能到的各種資料是因為用了不同格式的資料解析器
3、DRF的解析器
在DRF框架中,解析器在request.data拿資料的時候會被呼叫,
也就是說DRF框架,請求資料都在request.data中,那我們看下這個Request類裡的data
小結:
DRF解析器
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
)
-- 原理:
拿到我們配置的所有的解析器類的例項化物件
通過ContentType跟解析器的media_type進行匹配
匹配成功把解析器類例項化物件返回
呼叫解析器類的parse方法去解析資料
把解析好的資料返回
可以在我們的檢視中配置檢視級別的解析器
from rest_framework.parsers import JSONParser from rest_framework.parsers import FormParser from rest_framework.parsers import MultiPartParser class TestView(generics.GenericAPIView, mixins.ListModelMixin): # 如果我只配置了兩個解析器,那麼這個檢視就只能解析這兩種格式 # 不在檢視配置,就預設使用全域性的三種解析器 parser_classes = [JSONParser, FormParser,] def get(self, request): return self.list(request)
四、渲染器
渲染器就是把資料有格式的、友好地展示出來
DRF給我們提供的渲染器有
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
我們在瀏覽器中展示的DRF測試的頁面,就是通過瀏覽器的渲染器來做的
當然我們可以展示Json資料型別,渲染器比較簡單