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("登陆后发送的数据")

猜你喜欢

转载自www.cnblogs.com/Zzbj/p/10112019.html