開放平臺的API接口調用須要限制其頻率,以節約服務器資源和避免惡意的頻繁調用。django
DRF中的頻率控制基本原理是基於訪問次數和時間的,固然咱們能夠經過本身定義的方法來實現。
當咱們請求進來,走到咱們頻率組件的時候,DRF內部會有一個字典來記錄訪問者的IP,
以這個訪問者的IP爲key,value爲一個列表,存放訪問者每次訪問的時間,api
{
IP1: [第三次訪問時間,第二次訪問時間,第一次訪問時間],
IP2: [第三次訪問時間,第二次訪問時間,第一次訪問時間],
}瀏覽器
把每次訪問最新時間放入列表的最前面,記錄成這樣的一個數據結構服務器
若是咱們設置的是10秒內只能訪問5次,數據結構
-- 1,判斷訪問者的IP是否在這個請求IP的字典裏app
-- 2,保證這個列表裏都是最近10秒內的訪問的時間
判斷當前請求時間和列表裏最先的(也就是最後的)請求時間的差值
若是差大於10秒,說明請求以及不是最近10秒內的,刪除(最先的)最後一個時間,
繼續判斷倒數第二個,直到差值小於10秒ide
-- 3,判斷列表的長度(即訪問次數),是否大於咱們設置的5次,
若是大於就限流,不然放行,並把時間放入列表的最前面。測試
頻率組件提供的幾大類:加密
咱們以繼承下面這個類爲例:url
from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^book/', include("SerDemo.urls")), url(r'^api/user/', include("AuthDemo.urls")), url(r'^api/throttle/', include("Throttle.urls")), url(r'^api/pagination/', include("Pagenation.urls")), url(r'^api/(?P<version>[v1|v2]+)/', include("VersionDemo.urls")), ]
from django.conf.urls import url, include from django.contrib import admin from .views import TestView urlpatterns = [ url(r'^test', TestView.as_view()), ]
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from utils.throttle import MyThrottle, MyVisit # Create your views here. class TestView(APIView): throttle_classes = [MyVisit, ] def get(self, request): return Response("頻率測試接口")
# 以IP地址作限流 # 1 獲取訪問者的IP地址 # 2 訪問列表 {IP: [time2, time1]} # 3 判斷IP是否在咱們的訪問列表裏 # 4 若是不在 第一次訪問 給訪問列表加入IP:[now] # 5 若是在 把now放入IP:[now, time2, time1] # 6 確保列表裏最近的訪問時間以及最遠的訪問時間差在限制時間範圍內 # 7 判斷列表長度是不是限制次數以內 from rest_framework import throttling import time # [now, 5, 4, 3, 2, old] # 自定義60秒訪問3次 class MyThrottle(throttling.BaseThrottle): VisitRecord = {} def __init__(self): self.history = "" def allow_request(self, request, view): # 作頻率限流 ip = request.META.get("REMOTE_ADDR") now = time.time() if ip not in self.VisitRecord: self.VisitRecord[ip] = [now,] return True history = self.VisitRecord.get(ip) # self.history = self.cache.get(self.key, []) history.insert(0, now) self.history = history while history and history[0] - history[-1] > 60: history.pop() if len(history) > 3: return False else: return True def wait(self): # 還要等多久才能訪問 # old + 60 - now return self.history[-1] + 60 - self.history[0] #繼承SimpleRateThrottle class MyVisit(throttling.SimpleRateThrottle): scope = "WD" def get_cache_key(self, request, view): # 返回值應該IP return self.get_ident(request)
從當前位置向後找幾個
看第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)
從當前位置向後找幾個
從0開始取兩條數據(一、2) --> http://127.0.0.1:8000/api/pagination/book/?limit=2
從第二條數據開始取兩條數據(三、4) --> http://127.0.0.1:8000/api/pagination/book/?limit=2&offset=2
class MyPagination(pagination.LimitOffsetPagination): # 從哪裏開始拿數據 offset_query_param = 'offset' # 拿多少條數據 limit_query_param = 'limit' # 默認拿多少條數據 default_limit = 2 # 最多拿多少條 max_limit = 5
-- 遊標加密
-- 只能讀上一頁以及下一頁
-- 記錄每次的最大id以及最小id
按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
class MyPagination(pagination.CursorPagination): cursor_query_param = 'cursor' # 每頁顯示的數量的搜索關鍵字 page_size_query_param = 'size' # 每頁顯示的數據數量 page_size = 3 # 每頁最大顯示的數據數量 max_page_size = 5 # 按id的倒序顯示 ordering = '-id'
解析器的做用就是服務端接收客戶端傳過來的數據,把數據解析成本身想要的數據類型的過程。
本質就是對請求體中的數據進行解析。
請求進來的時候,請求體中的數據在request.body中,說明,解析器會把解析好的數據放入request.body,
咱們在視圖中能夠打印request的類型,就可以發現request是WSGIRequest這個類。
那咱們是怎麼拿到request.POST數據的?
咱們請求進來請求體中的數據在request.body中,那也就證實,解析器會把解析好的數據放入request.body
咱們在視圖中能夠打印request的類型,可以知道request是WSGIRequest這個類。
咱們能夠看下這個類的源碼~~~咱們是怎麼拿到request.POST數據的~~
application/x-www-form-urlencoded不是不能上傳文件,是隻能上傳文本格式的文件,
multipart/form-data是將文件以二進制的形式上傳,這樣能夠實現多種類型的文件上傳
一個解析到request.POST, request.FILES中。
也就是說咱們以前能在request中能到的各類數據是由於用了不一樣格式的數據解析器~
那麼咱們的DRF可以解析什麼樣的數據類型呢~~~
咱們想一個問題~何時咱們的解析器會被調用呢~~ 是否是在request.data拿數據的時候~
咱們說請求數據都在request.data中,那咱們看下這個Request類裏的data~~
獲得解析器後,調用解析器裏的parse方法~~
那說到這裏~咱們看下DRF配置的默認的解析器的類都有哪些~~
也就是說咱們的DRF支持Json,Form表單的請求,包括多種文件類型的數據~~~~
能夠在咱們的視圖中配置視圖級別的解析器~~~
原理:
拿到咱們配置的全部的解析器類的實例化對象
經過ContentType跟解析器的media_type進行匹配
匹配成功把解析器類實例化對象返回
調用解析器類的parse方法去解析數據
把解析好的數據返回
渲染器就是友好的展現數據~~
DRF給咱們提供的渲染器有~~
咱們在瀏覽器中展現的DRF測試的那個頁面~就是經過瀏覽器的渲染器來作到的~~
固然咱們能夠展現Json數據類型~~~~渲染器比較簡單~~~~