1. 程式人生 > >DRF之頻率限制、分頁、解析器和渲染器

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資料型別,渲染器比較簡單