1. 程式人生 > >DRF之版本控制、認證和權限組件

DRF之版本控制、認證和權限組件

object amp sed uuid int exception acc 獲取版本 版本信息

一、版本控制組件

1、為什麽要使用版本控制

首先我們開發項目是有多個版本的
當我們項目越來越更新,版本就越來越多,我們不可能新的版本出了,以前舊的版本就不進行維護了
像bootstrap有2、3、4版本的,每個版本都有它對應的url,https://v2.bootcss.com/ 、 https://v3.bootcss.com/
這就需要我們對版本進行控制,這個DRF也給我們提供了一些封裝好的版本控制方法

2、原理

1.
在DRF框架中,它默認幫我們設置了版本信息在request.versionrequest.versioning_scheme中,
只是DRF默認的版本信息是None,我們需要重寫一些配置,讓request.version攜帶上我自定義的版本信息

request.versioning_scheme是實現版本控制的類的實例化對象

2.

CBV視圖函數都是首先會去執行as_view方法的,as_view會找到路由的分發方法dispatch,在真正分發之前會執行initial方法
技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

3、使用

0. 在哪裏取版本信息
我們的版本類必須重寫determine_version這個方法,request.version就是拿它的返回值
在視圖中就可以使用request.version取到版本信息


1. 新建一個py文件
我建在utils文件夾下的version.py
在version.py下重寫一個MyVersion類
註意:必須重寫determine_version這個方法,因為request.version就是拿它的返回值
class MyVersion(object): def determine_version(self,request, *args, **kwargs): version = request.query_params.get("version", "v1") return version 2. 去項目的settings配置 # 覆蓋DRF原本的配置DEFAULT_VERSIONING_CLASS,讓它的值指向我們寫的那個類 REST_FRAMEWORK = { DEFAULT_VERSIONING_CLASS:
utils.version.MyVersion } 3. 在視圖中使用 class VersionView(APIView): def get(self, request): print(request.version) print(request.versioning_scheme) if request.version == v1: return Response("v1版本") elif request.version == v2: return Response(v2版本) return Response(不存在的版本)

4、DRF的versioning模塊

1. 在這裏模塊內給我們配置了各種獲取版本的類

2. versioning模塊
技術分享圖片
# 基礎類
class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

    def determine_version(self, request, *args, **kwargs):
        msg = {cls}.determine_version() must be implemented.
        raise NotImplementedError(msg.format(
            cls=self.__class__.__name__
        ))

    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
        return _reverse(viewname, args, kwargs, request, format, **extra)

    def is_allowed_version(self, version):
        if not self.allowed_versions:
            return True
        return ((version is not None and version == self.default_version) or
                (version in self.allowed_versions))


# 在請求頭中攜帶版本信息
class AcceptHeaderVersioning(BaseVersioning):
    """
    GET /something/ HTTP/1.1
    Host: example.com
    Accept: application/json; version=1.0
    """


# 在URL中攜帶版本信息
class URLPathVersioning(BaseVersioning):
    ‘‘‘
    urlpatterns = [
        url(r‘^(?P<version>[v1|v2]+)/users/$‘, users_list, name=‘users-list‘),
        url(r‘^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$‘, users_detail, name=‘users-detail‘)
    ]
    ‘‘‘


# 在namespace中攜帶版本信息
class NamespaceVersioning(BaseVersioning):
    ‘‘‘
    # users/urls.py
    urlpatterns = [
        url(r‘^/users/$‘, users_list, name=‘users-list‘),
        url(r‘^/users/(?P<pk>[0-9]+)/$‘, users_detail, name=‘users-detail‘)
    ]

    # urls.py
    urlpatterns = [
        url(r‘^v1/‘, include(‘users.urls‘, namespace=‘v1‘)),
        url(r‘^v2/‘, include(‘users.urls‘, namespace=‘v2‘))
    ]
    ‘‘‘


# 在域名中攜帶版本信息
class HostNameVersioning(BaseVersioning):
    """
    GET /something/ HTTP/1.1
    Host: v1.example.com
    Accept: application/json
    """


# 在參數中攜帶版本信息
class QueryParameterVersioning(BaseVersioning):
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """
version模塊的類

3. 基礎類的三個配置
REST_FRAMEWORK = {
    DEFAULT_VERSIONING_CLASS: utils.version.MyVersion,
    # 默認版本
    default_version: v1,
    # 可用版本
    allowed_versions: [v1, v2],
    # 參數
    version_param: version,
}
4. 使用DRF的版本控制
方法一
1. 在utils文件夾下的version.py繼承versioning的類

from rest_framework import versioning

class MyVersion(versioning.URLPathVersioning):
    pass


2. urls.py
urlpatterns = [
    # url(r‘^version_demo/‘, views.VersionView.as_view()),
    url(r^(?P<version>[v1|v2]+)/version_demo/, views.VersionView.as_view()),
]

3. settings.py
REST_FRAMEWORK = {
    DEFAULT_VERSIONING_CLASS: utils.version.MyVersion,
    # 默認版本
    default_version: v1,
    # 可用版本
    allowed_versions: [v1, v2],
    # 參數
    version_param: version,
}

4.在視圖中使用需要接收參數version
class VersionView(APIView):
    def get(self, request, version):
        print(request.version)
        print(request.versioning_scheme)
        if request.version == v1:
            return Response("v1版本")
        elif request.version == v2:
            return Response(v2版本)
        return Response(不存在的版本)

5. 在瀏覽器中
可以輸入:
    v1/version_demo/
    v2/version_demo/


方法二
其他步驟基本一樣,只是我們在配置的時候直接指定某個類
1. urls.py
urlpatterns = [
    # url(r‘^version_demo/‘, views.VersionView.as_view()),
    url(r^(?P<version>[v1|v2]+)/version_demo/, views.VersionView.as_view()),
]


2. settings.py
REST_FRAMEWORK = {
    DEFAULT_VERSIONING_CLASS: rest_framework.versioning.URLPathVersioning,  # 指定URLPathVersioning這個類
    # 默認版本
    default_version: v1,
    # 可用版本
    allowed_versions: [v1, v2],
    # 參數
    version_param: version,
}

二、認證

1、為什麽要認證

我們可以在網站上登錄,然後可以有個人中心,對自己信息就行修改
但是我們每次給服務器發請求,由於Http的無狀態,導致我們每次都是新的請求
那麽服務端需要對每次來的請求進行認證,看用戶是否登錄,以及登錄用戶是誰
那麽我們服務器對每個請求進行認證的時候,不可能在每個視圖函數中都寫認證
一定是把認證邏輯抽離出來,以前我們可能會加裝飾器,或者中間件,DRF框架也有它的認證

2、原理

在前端進行登錄的時候,用戶名和密碼輸入正確後,後端根據用戶名和密碼拿到這個用戶的一些信息(用戶名,性別等),
把這些信息加密後生成一個隨機字符串token返回給前端,
前端下次請求再來的時候,帶上這個隨機字符串,後端根據自己的解密算法再算出來,實現認證。

3、token認證

0. 在哪裏取認證的信息
我們的認證類必須重寫authenticate這個方法,返回值是元組
第一個值賦值給了request.user, 第二個賦值給request.auth
在視圖中就可以使用request.user和request.auth取到認證的信息


1. models.py
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    # 用UUID字符串存隨機字符串
    token = models.UUIDField(null=True, blank=True)
    CHOICES = ((1, "vip"), (2, "普通用戶"), (3, "vvip"))


2. settings配置
# 跟版本一樣,覆蓋原本的配置,使用自己的配置
REST_FRAMEWORK = {
    # 配置認證類
    # 這樣配是全局,所有路由都要認證
    DEFAULT_AUTHENTICATION_CLASSES:[utils.auth.MyAuth, ]
}


3. utils/auth.py
from rest_framework import authentication
from AuthDemo.models import User
from rest_framework.exceptions import AuthenticationFailed

class MyAuth(authentication.BaseAuthentication):
    # 必須重寫authenticate這個方法,返回值是元組
    # 第一個值賦值給了request.user, 第二個賦值給request.auth
    def authenticate(self, request):
        # 獲取前端攜帶的token,看token是否合法
        token = request.query_params.get("token", "")
        if not token:
            raise AuthenticationFailed("沒有攜帶token")
        user_obj = User.objects.filter(token=token).first()
        # 返回需要的信息
        if user_obj:
            return (user_obj, token)
        # 驗證不通過,拋出異常
        raise AuthenticationFailed("token不合法")


4. 視圖
class TestView(APIView):
    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response("登陸後發送的數據")

5. 局部配置
1. 如果只是某些路由需要認證,那麽可以局部設置認證,包括版本也可以局部認證

2. 把settings的配置註釋掉

3. 在需要認證的視圖中聲明
from utils.auth import MyAuth

class TestView(APIView):
    authentication_classes = [MyAuth, ]  # 局部配置認證組件
    # versioning_class = []  # 局部配置版本組件
    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response("登陸後發送的數據")

DRF之版本控制、認證和權限組件