【DRF框架之梅開二度】

染指未来發表於2024-04-17

DRF 框架

restful 規範

  • 定義
    • 軟體的一種架構風格,透過HTTP協議進行資料互動。核心思想:將系統中的資源(網頁,文字,資料等)表示為統一的資源識別符號(URL)
    • 使用:POST,GET,PUT,DELETE等HTTP請求方法,對資源操作
  • 特點
    • 使用HTTP方法表示資源操作型別。GET 獲取,POST建立,PUT更新,DELETE刪除
    • 使用URL表示資源:URL儘可能多的反應資源的層次結構和名稱
    • 使用狀態碼操作:2xx,4xx,5xx等
    • 使用JSON/XML作為資料傳輸格式。保證資料的可讀性和可擴充套件性
    • 無狀態:restful系統是無狀態的,每個請求是獨立的,不依賴之前的請求和會話
    • 快取友好性:restful支援快取,提高效能和減少服務資源負載

DRF 元件認證過程

1. 使用者傳送請求到 API端點(服務,伺服器上的服務)
2. API檢查請求頭的Authorization欄位,來確定請求是否經過認證
3. 如果請求未經過認證,則返回401,提示使用者進行認證操作
4. 如果請求經過認證,則要求API檢查請求的許可權,確定使用者是否具有執行該操作的許可權
5. 如果使用者執行該做操沒有許可權,則返回403,提示使用者沒有許可權執行該操作
6. 如果使用者具有該操作的許可權,則執行API邏輯,並返回響應結果
7. 認證方式:基本認證,令牌認證,JSON WEB Token認證等

DRF 元件中的許可權認證

- 檢視級許可權 ,檢視中新增 permission_classes 屬性指定許可權
    from rest_framework.permissions import IsAuthenticated
    class MyView(APIView):
        permission_classes = (IsAuthenticated,)
            
- 路由級許可權,路由中新增 permission_classes 屬性指定許可權
    from rest_framework.permissions import IsAuthenticated
    router = DefaultRouter()
    router.register(r'users', UserViewSet, base_name='user')
    router.route('list', permission_classes=(IsAuthenticated,))
- 自定義許可權。
    from rest_framework.permissions import BasePermission
    class MyPermission(BasePermission):
        def has_permission(self, request, view):
            # 自定義許可權邏輯
            return True
    
    class MyView(APIView):
        permission_classes = (MyPermission,)

DRF 元件中的節流實現方式

- 頻率限制
    - 使用者在一定時間可以傳送的請求數量。如:限制使用者每分鐘只能傳送10個請求。
    - 實現:透過cache模組,DRF提供 _throttled_cache 中介軟體,來實現頻率限制
    - 程式碼如下:    
        from django.core.cache import cache
        from django.middleware.cache import ThrottledCache
        
        # 建立一個 ThrottledCache 例項
        throttled_cache = ThrottledCache(timeout=60, key_prefix='my_cache')
        
        # 將 ThrottledCache 例項新增到 DRF 的中介軟體列表中
        MIDDLEWARE = [
            xxxxx,
            xxxxx,
            'throttled_cache.middleware.ThrottledCacheMiddleware',
        ]


- 請求限制
    - 指某個IP在一定時間內傳送的請求數量。如:限制43.10.10.22 ip 每小時只能傳送1000個請求
    - 實現:使用django的request模組,DRF 提供 _throttled_request 中介軟體來限制
    - 程式碼:
        # 在檢視中使用 ThrottledCache 中介軟體
        from rest_framework.response import JsonResponse 
        def my_view(request):
            # 檢查請求是否被快取
            if not throttled_cache.get_cache_key(request):
                return JsonResponse({'error': '請求太頻繁,請稍後再試。'}, status=429)
        
            # 處理請求
            #...
        
            # 快取請求
            throttled_cache.set_cache_key(request)
        
            # 返回響應
            return JsonResponse({'result': '請求處理成功。'})

什麼是JWT?

  • 一般用於前後端分離專案。 用於做使用者認證

  • jwt實現原理

    • 使用者登陸成功後,返回一段字串(token)
    • token 是由 三段組成
      • 第一段: 型別 和 演算法 資訊
      • 第二段: 使用者資訊 + 超時時間
      • 第三段: hs256加密 (拼接前兩段資訊)
    • 在使用 base64url 進行二次加密。(處理&和特殊字元)
    • 後端驗證token
      • token 超時驗證
      • token的合法性驗證
  • jwt的優勢

    • 服務端不儲存token資訊,只在客戶瀏覽器儲存。 後端只負責校驗
    • 內部整合超時時間,後端可以根據時間校驗token是否超時
    • 使用者不可修改token,修改後token驗證失敗

Django 編寫試圖方式

  • 第一種:APIView
    • 自己編寫CRUD檢視,編寫分頁等
  • 第二種:ListAPIView,CreateAPIView等
    • 實現認證,分頁,CRUD等
  • 第三種:GenericViewSet,ListModelMixin,CreateModelMixin等
    • 整合所有的功能,需要在路由指定方法
  • 第四種 viewsetes.ModelViewSet 實現上面所有功能

DRF 回顧

  • 裝飾器
def wrapper(func):
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        return ret
    return inner

# 裝飾器1
@wrapper
def index(f1):
    pass

# 裝飾器2
index = wrapper(index)

  • django csrf_exempt 免認證

    from django.views.decorators.csrf import csrf_exempt
    from django.shortcuts import HttpResponse
    
    @csrf_exempt
    def index(request):
     return HttpResponse('...')
    
    # index = csrf_exempt(index)
    
    urlpatterns = [
     url(r'^index/$',index),
    ]
    
    urlpatterns = [
     url(r'^login/$',account.LoginView.as_view()),
    ]
    
    class APIView(View):
     @classmethod
     def as_view(cls, **initkwargs):
         view = super().as_view(**initkwargs)
         view.cls = cls
         view.initkwargs = initkwargs
    
         # Note: session based authentication is explicitly CSRF validated,
         # all other authentication is CSRF exempt.
         return csrf_exempt(view)
    
  • 物件導向中 基於 異常+繼承 實現約束

    class BaseVersioning:
     def determine_version(self, request, *args, **kwargs):
         raise NotImplementedError("must be implemented")
         
    class URLPathVersioning(BaseVersioning):
    def determine_version(self, request, *args, **kwargs):
         version = kwargs.get(self.version_param, self.default_version)
         if version is None:
             version = self.default_version
    
         if not self.is_allowed_version(version):
             raise exceptions.NotFound(self.invalid_version_message)
         return version
    
  • 物件導向的封裝

       class Foo(object):
       	def __init__(self,name,age):
       		self.name = name
       		self.age = age 
       		
       obj = Foo('汪洋',18)
    
    class APIView(View):
     def dispatch(self, request, *args, **kwargs):
    
         self.args = args
         self.kwargs = kwargs
         request = self.initialize_request(request, *args, **kwargs)
         self.request = request
       ...
         
    def initialize_request(self, request, *args, **kwargs):
         """
         Returns the initial request object.
         """
         parser_context = self.get_parser_context(request)
    
         return Request(
             request,
             parsers=self.get_parsers(),
             authenticators=self.get_authenticators(), # [MyAuthentication(),]
             negotiator=self.get_content_negotiator(),
             parser_context=parser_context
         )
    
  • 物件導向的繼承

     class View(object):
        	pass
     
     class APIView(View):
         def dispatch(self):
             method = getattr(self,'get')
             method()
     
     class GenericAPIView(APIView):
         serilizer_class = None
         
         def get_seriliser_class(self):
             return self.serilizer_class
     
     class ListModelMixin(object):
         def get(self):
             ser_class = self.get_seriliser_class()
             print(ser_class)
     
     class ListAPIView(ListModelMixin,GenericAPIView):
         pass
     
     class UserInfoView(ListAPIView):
         pass
     
     
     view = UserInfoView()
     view.dispatch()
    
    class View(object):
    	pass
    
    class APIView(View):
     def dispatch(self):
         method = getattr(self,'get')
         method()
    
    class GenericAPIView(APIView):
     serilizer_class = None
     
     def get_seriliser_class(self):
         return self.serilizer_class
    
    class ListModelMixin(object):
     def get(self):
         ser_class = self.get_seriliser_class()
         print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
     pass
    
    class UserInfoView(ListAPIView):
     serilizer_class = "汪洋"
    
    
    view = UserInfoView()
    view.dispatch()
    
    class View(object):
    	pass
    
    class APIView(View):
     def dispatch(self):
         method = getattr(self,'get')
         method()
    
    class GenericAPIView(APIView):
     serilizer_class = None
     
     def get_seriliser_class(self):
         return self.serilizer_class
    
    class ListModelMixin(object):
     def get(self):
         ser_class = self.get_seriliser_class()
         print(ser_class)
    
    class ListAPIView(ListModelMixin,GenericAPIView):
     pass
    
    class UserInfoView(ListAPIView):
     
     def get_seriliser_class(self):
         return "咩咩"
    
    view = UserInfoView()
    view.dispatch()
    
  • 反射

    class View(object):
    def dispatch(self, request, *args, **kwargs):
         # Try to dispatch to the right method; if a method doesn't exist,
         # defer to the error handler. Also defer to the error handler if the
         # request method isn't on the approved list.
         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
         return handler(request, *args, **kwargs)
    
  • 傳送ajax請求

    $.ajax({
    	url:"地址",
    	type:"GET",
    	data:{},
    	success:function(e){
    		console.log(e)
    	}
    })
    
    
  • 瀏覽器具有 同源策略 跨域如何產生的? 導致 ajax請求+跨域 存在 無法獲取資料

    • 簡單請求,傳送一次請求
    • 複雜請求,先做 options 請求預檢,在傳送真正的請求
    IP
    埠
    協議
    
  • 如何解決 跨域

    CORS 本質:設定響應頭
    
  • 常見的HTTP請求方法

    Get,POST,PUT,OPTIONS,PATCH,DELETE ... 
    
  • HTTP 請求頭。Content-Type 請求頭

    - application/x-www-form-urlencoded
    	django 的 request.POST 和 request.body 中均有資料
    	
    - appliction/json
    	django 的 request.POST 沒有值,request.body 有資料
    
  • django 的 fbv 和 cbv 都能遵循 restful 規範介面

  • django rest framework 框架 實現 restful api 開發

    - 免除 csrf 認證
    - 分頁
    - 許可權
    - 檢視(APIView,ListApiView,ListModelMixin...) 類的繼承
    - 序列化 serializer
    - 版本 Version
    - 節流
    - 解析器
    - 篩選器 
    - 認證
    - 渲染器 
    
  • 簡述 drf 中的認證流程

  • 簡述drf 中的節流實現原理, 匿名使用者 和 非匿名使用者 實現頻率限制

  • GenericAPIView 檢視類

    - 提供了一些規則
    	- serilizer_class = None
    	- lookup_field = 'pk'
    	- queryset = None
    	- filter_backends = 
    	- get_queryset(self)
    	- get_object(self)
    	- get_serializer_class(self):
    		return self.
    
  • jwt的優勢

  • 序列化時,many=True 和 many = False 的區別

  • django 的 F 查詢

  • django 的 獲取空的queryset

    models.XXX.objects.all().none()
    
  • 應用 DRF 中的功能開發

    ***** 非常重要 
    	檢視: 
    	序列化: 
    	解析器 request.data / request.query_params
    	渲染器:Response
    
    **** 
    	request 物件封裝
    	版本處理
    	分頁處理
    
    ***
    	認證
    	許可權
    	節流
    	
    	
    - 基於 APIView 實現 呼啦圈 專案
    - 基於 GenericView,ListModelMixin 實現呼啦圈 專案
    
  • GenericAPIView檢視類的作用?

        他提供了一些規則,例如:
        
        class GenericAPIView(APIView):
            serializer_class = None
            queryset = None
            lookup_field = 'pk'
            
            filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
            pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
            
            def get_queryset(self):
                return self.queryset
            
            def get_serializer_class(self):
                return self.serializer_class
            
        	def filter_queryset(self, queryset):
                for backend in list(self.filter_backends):
                    queryset = backend().filter_queryset(self.request, queryset, self)
                return queryset
            
            @property
            def paginator(self):
                if not hasattr(self, '_paginator'):
                    if self.pagination_class is None:
                        self._paginator = None
                    else:
                        self._paginator = self.pagination_class()
                return self._paginator
            
        他相當於提供了一些規則,建議子類中使用固定的方式獲取資料,例如:
        class ArticleView(GenericAPIView):
            queryset = models.User.objects.all()
            
            def get(self,request,*args,**kwargs):
                query = self.get_queryset()
        
        我們可以自己繼承GenericAPIView來實現具體操作,但是一般不會,因為更加麻煩。
        而GenericAPIView主要是提供給drf內部的 ListAPIView、Create....
        class ListModelMixin:
            def list(self, request, *args, **kwargs):
                queryset = self.filter_queryset(self.get_queryset())
        
                page = self.paginate_queryset(queryset)
                if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    return self.get_paginated_response(serializer.data)
        
                serializer = self.get_serializer(queryset, many=True)
                return Response(serializer.data)
            
        class ListAPIView(mixins.ListModelMixin,GenericAPIView):
            def get(self, request, *args, **kwargs):
                return self.list(request, *args, **kwargs)
        
        class MyView(ListAPIView):
            queryset = xxxx 
            ser...
    
    總結:GenericAPIView主要為drf內部幫助我們提供增刪改查的類LIstAPIView、CreateAPIView、UpdateAPIView、提供了執行流程和功能,我們在使用drf內建類做CURD時,就可以透過自定義 靜態欄位(類變數)或重寫方法(get_queryset、get_serializer_class)來進行更高階的定製。
    
    
  • 序列化時many=True和many=False的區別?

    在 Django Rest Framework(DRF)中,序列化時 many=True 和 many=False 的區別在於它們指定了序列化的物件是單個例項還是一個集合。
      
      many=True 表示序列化的是一個集合(列表、可迭代物件等)。這意味著序列化器將處理多個物件,並返回一個包含多個序列化例項的陣列或列表。
      many=False 表示序列化的是單個例項。序列化器將處理單個物件,並返回該單個序列化例項。
    
    以下是一些主要的區別:
    	返回格式:many=True 返回列表,many=False 返回單個物件。
    	資料處理:在序列化多個物件時,many=True 會逐個處理每個物件。
    	檢視響應:根據 many 的設定,檢視可能會返回不同的 HTTP 狀態碼和響應結構。
    	序列化邏輯:可能會有不同的邏輯來處理單個例項或集合。
    	透過設定 many 引數,DRF 可以靈活地處理單個物件和物件集合的序列化需求,以便與客戶端進行有效的資料互動。
    

相關文章