1. 程式人生 > >DRF之版本控制、認證和許可權元件

DRF之版本控制、認證和許可權元件

一、版本控制組件

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("登陸後傳送的資料")