rest-framework框架——版本

一、DRF版本控制介绍

  随着项目更新,版本会越来越多,不能新的版本出现,旧版本就不再使用维护了。因此不同的版本会有不同的处理,且接口会返回不同的信息。

  API版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。

  DRF提供了许多不同的版本控制方案。可能会有一些客户端因为某些原因不再维护了,但是我们后端的接口还要不断的更新迭代,这个时候通过版本控制返回不同的内容就是一种不错的解决方案。

  rest_framework.versioning里提供了五种版本控制方案如下所示:

from rest_framework import versioning           # view中引入版本控制
# 查看 rest_framework/versioning.py文件:

# 最基础的版本控制类,给其他版本控制类提供一些共用方法
class BaseVersioning:...

# 在accept请求头中配置版本信息
# accept代表希望返回的数据类型,可以携带版本信息
# Accept: application/json; version=1.0
class AcceptHeaderVersioning(BaseVersioning):   # 将版本信息放到请求头中
    """
    GET /something/ HTTP/1.1
    Host: example.com
    Accept: application/json; version=1.0
    """

# 在url上携带版本信息
# url(r‘^(?P<version>[v1|v2]+)/users/$‘, users_list, name=‘users-list‘),
class URLPathVersioning(BaseVersioning):        # 将版本信息放入URL中
    """
    To the client this is the same style as `NamespaceVersioning`.
    The difference is in the backend - this implementation uses
    Django‘s URL keyword arguments to determine the version.

    An example URL conf for two views that accept two different versions.

    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‘)
    ]

    GET /1.0/something/ HTTP/1.1
    Host: example.com
    Accept: application/json
    """

# 把版本信息放在路由分发里,并把路由的namespace配置成版本
# url(r‘^v1/‘, include(‘users.urls‘, namespace=‘v1‘))
class NamespaceVersioning(BaseVersioning):      # 通过namespace来区分版本
    """
    To the client this is the same style as `URLPathVersioning`.
    The difference is in the backend - this implementation uses
    Django‘s URL namespaces to determine the version.

    An example URL conf that is namespaced into two separate versions

    # 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‘))
    ]

    GET /1.0/something/ HTTP/1.1
    Host: example.com
    Accept: application/json
    """

# 在我们的host上配置版本信息
# Host:v1.example.com
class HostNameVersioning(BaseVersioning):        # 通过主机名来区分版本
    """
    GET /something/ HTTP/1.1
    Host: v1.example.com
    Accept: application/json
    """

# 在url过滤条件上配置版本信息
# GET /something/?version=0.1 HTTP/1.1
class QueryParameterVersioning(BaseVersioning):  # 通过url查询参数区分版本
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """

二、源码分析

1、查看APIView类中的dispatch方法

  APIView中重新定义了dispatch方法,重新封装了request。同时try中的代码一定会执行,因此会执行self.initial()方法。

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
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)

2、查看initial方法

  iniital方法中做了各种初始化操作。

  其中version和scheme是版本控制组件的入口,也是determine_version方法的返回值。

  determine_version()的返回值,传值给 request.version 和 request.versioning_scheme。

class APIView(View):

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
        ... 

  可以看到在这里是有version的,也就是版本。

3、查看determine_version方法

  可以看到首先判断 self.versioning_class 是否为None。如果为None,则返回None、None。

  scheme实际是自己配置的版本控制类的实例化对象。默认的版本控制类是null,我们必须要有一个自己配置的版本控制类。

  且配置的类里必须要有一个 determine_version 方法(返回值就是版本号)。

class APIView(View):

    def determine_version(self, request, *args, **kwargs):
        """
        If versioning is being used, then determine any API version for the
        incoming request. Returns a two-tuple of (version, versioning_scheme)
        """
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme)

(1)继续查看versioning_class

class APIView(View):
    ...
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

  由此可知就是在api_sttings中配置了一个类。

  因此在前面determine_version()中self.versioning_class()是做了一个实例化的动作。

  继续查看api_settings:在site-packages/rest_framework/settings.py文件中可以看到api_settings其实是APISettings的实例化:

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

  DEFAULTS默认读取的是 site-packages/rest_framework/settings.py中DEFAULTS字段:

DEFAULTS = {
    ...
    ‘DEFAULT_VERSIONING_CLASS‘: None,
    ...
}

  因此,默认情况下self.versioning_class的返回值为None。

(2)return (scheme.determine_version(request, *args, **kwargs), scheme)返回了一个元组

  如果在/views/course.py的视图类中定义versioning_class:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning

class CourseView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        self.dispatch
        return Response(‘...‘)

  则可以实例化得到scheme实例,并在函数返回语句中返回scheme。

(3)进一步查看QueryParameterVersioning中的determine_version

class QueryParameterVersioning(BaseVersioning):
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """
    invalid_version_message = _(‘Invalid version in query parameter.‘)

    def determine_version(self, request, *args, **kwargs):
        version = request.query_params.get(self.version_param, self.default_version)
        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version

  这里返回的是version,预计就是版本号。

(4)进一步查看request.query_params定义

class Request(object):

    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

  因此version = request.query_params.get(self.version_param, self.default_version)其实是去URL中获取GET传来的version对应的参数。

(5)查看version_param

class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

  由此可知这是一个全局配置,默认值就等于version,由此可知前面返回的version就是版本号。

4、version返回,分析inital方法

    def initial(self, request, *args, **kwargs):

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

  version是版本,scheme是对象(自己配置的版本控制类的实例化对象)并分别赋值给request.version和request.scheme。

5、在视图类中拿到版本

class CourseView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response(‘...‘)

6、页面访问测试

(1)直接访问

  

  此时python后台输出:none

(2)用url参数传递版本

  

  此时python后台输出:v1

二、版本控制类及源码解析

  在项目中要引入rest_framework框架提供的版本控制类:

from rest_framework import versioning

1、查看QueryParameterVersioning类

class QueryParameterVersioning(BaseVersioning):
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """
    invalid_version_message = _(‘Invalid version in query parameter.‘)

    def determine_version(self, request, *args, **kwargs):
        version = request.query_params.get(self.version_param, self.default_version)
        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version

  可以看到version拿到后,用self.is_allowed_version方法做了一个判断。

2、查看is_allowed_version方法

class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

    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))

  可以看到ALLOWED_VERSIONS也是存放在配置文件中。

3、在settings.py中添加允许的版本

  凡是关于restframework框架的配置都需要写 REST_FRAMEWORK,并让它等于一个字典

# 渲染器配置到全局
REST_FRAMEWORK = {
    ‘DEFAULT_RENDERER_CLASSES‘: [‘rest_framework.renderers.JSONRenderer‘,
                                 ‘rest_framework.renderers.BrowsableAPIRenderer‘],
    ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘]  # 允许的版本
}

4、访问验证

  

三、默认版本与版本参数

  settings.py做如下配置

# 渲染器配置到全局
REST_FRAMEWORK = {
    ‘DEFAULT_RENDERER_CLASSES‘: [‘rest_framework.renderers.JSONRenderer‘,
                                 ‘rest_framework.renderers.BrowsableAPIRenderer‘],
    ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘],  # 允许的版本
    ‘VERSION_PARAM‘: ‘version‘,     # 把默认的version修改为其他参数:http://127.0.0.1:8000/api/course/?versionsss=v1
    ‘DEFAULT_VERSION‘: ‘v1‘,        # 默认版本
}

1、修改参数为其他值访问效果

  

2、配置默认版本后不写版本参数也可获取默认版本

  

  python后台输出:v1。

四、配置版本控制

1、局部版本控制

  前面都是在单个类中配置了版本控制,如下所示:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning

class CourseView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        self.dispatch
        return Response(‘...‘)

2、全局版本控制

  源码查看到全局版本控制配置信息:

class APIView(View):

    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    # Allow dependency injection of other settings to make testing easier.
    settings = api_settings

  因此也可以在settings.py中配置全局版本控制:

# 渲染器配置到全局
REST_FRAMEWORK = {
    ‘DEFAULT_RENDERER_CLASSES‘: [‘rest_framework.renderers.JSONRenderer‘,
                                 ‘rest_framework.renderers.BrowsableAPIRenderer‘],
    ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘],  # 允许的版本
    ‘VERSION_PARAM‘: ‘version‘,     # 把默认的version修改为其他参数:http://127.0.0.1:8000/api/course/?versionsss=v1
    ‘DEFAULT_VERSION‘: ‘v1‘,        # 默认版本
    ‘DEFAULT_VERSIONING_CLASS‘: ‘rest_framework.versioning.QueryParameterVersioning‘,  # 全局版本控制
}

  显示效果如下:

  

五、写版本推荐方式——基于url的正则方式(如:/v1/users/)

  前面写的是基于url的get传参方式,如:/users?version=v1,但是这种方式显示版本不是最推荐的。一般需要把版本号写在前面。改写需要调整urls.py配置。

1、项目urls.py修改

from django.conf.urls import url, include

urlpatterns = [
    # path(‘admin/‘, admin.site.urls),
    url(r‘^api/‘, include(‘api.urls‘)),
]

2、应用目录下创建urls.py文件及配置

from django.conf.urls import url, include
from api.views import course

urlpatterns = [
    url(r‘^(?P<version>[v1|v2]+)/course/$‘, course.CourseView.as_view()),
]

3、修改/api/views/course.py类视图文件

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning

class CourseView(APIView):
    versioning_class = URLPathVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response(‘...‘)

4、访问显示效果

  

  以后都推荐用这种方式写版本,全局配置修改同上。

六、其他版本使用方式

  详见:http://www.cnblogs.com/wupeiqi/articles/7805382.html

  基于 accept 请求头方式,如:Accept: application/json; version=1.0

  基于主机名方法,如:v1.example.com

  基于django路由系统的namespace,如:example.com/v1/users/

七、项目示例

1、DRFDemo项目中引入版本控制应用

  引入versionDemo应用。配置url.py和view.py文件如下所示:

# versionDemo/urls.py
from django.urls import path, include
from .views import DemoView

urlpatterns = [
    path(r"", DemoView.as_view()),
]

# versionDemo/views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response

class DemoView(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("不存在的版本")

2、在settings.py文件中添加版本控制类

  创建定义自己的版本控制类,类中必须自定义 determine_version 方法:

# 创建文件/DRFDemo/utils/version.py

class MyVersion:
    def determine_version(self, request, *args, **kwargs):
        # 该方法返回值给了 request.version
        # 返回版本号
        # 版本号携带在过滤条件中  xxx?version=v1
        version = request.query_params.get("version", "v1")
        return version 

  再在settings.py中全局引入版本控制,覆盖默认值为None的 versioning_class:

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion"
}

3、测试获取version信息

  默认访问“127.0.0.1:8000/version/”,返回得到信息:“v1版本的数据”

  

  访问“127.0.0.1:8000/version/?version=v2”,返回得到信息:“v2版本的数据”

  

  访问"127.0.0.1:8000/version/?version=v3",返回得到信息:“不存在的版本”

  

4、使用内置的版本控制类

  引入内置的版本控制类:

from rest_framework.versioning import QueryParameterVersioning,AcceptHeaderVersioning,NamespaceVersioning,URLPathVersioning

#基于url的get传参方式:QueryParameterVersioning------>如:/users?version=v1
#基于url的正则方式:URLPathVersioning------>/v1/users/
#基于accept请求头方式:AcceptHeaderVersioning------>Accept: application/json; version=1.0
#基于主机名方法:HostNameVersioning------>v1.example.com
#基于django路由系统的namespace:NamespaceVersioning------>example.com/v1/users/

  上述五种版本控制类都默认继承 BaseVersioning:

class BaseVersioning:
    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))

  依据BaseVersioning配置settings.py中版本控制:

REST_FRAMEWORK = {
    # "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion",
    # 默认使用的版本控制类
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    # 默认使用的版本
    "DEFAULT_VERSION": "v1",
    # 允许的版本
    "ALLOWED_VERSIONS": "v1, v2",
    # 版本使用的参数名称
    "VERSION_PARAM": "ver"
}

  访问成功,显示如下所示:

  

  访问失败,会使用框架提示信息:

  

原文地址:https://www.cnblogs.com/xiugeng/p/11972119.html

时间: 2024-10-08 03:24:18

rest-framework框架——版本的相关文章

visual studio如何修改c++项目的.net framework框架版本

一. 修改项目文件 在 Visual Studio 的“解决方案资源管理器”中,打开项目的快捷菜单,然后选择“卸载项目”. 这将为你的项目卸载项目文件 (.vcxproj). 在菜单栏上,依次选择“文件”.“打开”.“文件”. 在“打开文件”对话框中,导航到项目文件夹,然后打开项目文件 (.vcxproj). 在项目文件中,找到目标 Framework 版本的条目. 例如,如果你的项目设计为使用 .NET Framework 4.5, <PropertyGroup Label="Globa

zend framework框架学习走起——从零开始,点击记录-安装

zend framework第一步,先来安装下zend framework框架. 先介绍下,我这边的php配置环境,为图省事,我是安装wampserver来搭载php环境的,,php+Apache+mysql一下子全部搞定.所以我们的zendframwok框架安装所在文件夹就是在wampserver的www文件夹下. 我们要先去下载下来zend framework框架. 进入zend framework官网,你可以直接下载zend framework框架,也可以和我一样,选择composer方式

net.sz.framework 框架 轻松搭建数据服务中心----读写分离数据一致性,滑动缓存

前言 前文讲述了net.sz.framework 框架的基础实现功能,本文主讲 net.sz.framework.db 和 net.sz.framework.szthread; net.sz.framework.db 是 net.sz.framework 底层框架下的orm框架,仿照翻译了hibernate实现功能,虽然不足hibernate强大:但在于其功能实现单一高效和高可控性: net.sz.framework.szthread 是 net.sz.framework 底层框架下的线程控制中

利用AddressBook.framework框架获取iOS系统通讯录数据

此方法是使用AddressBook.framework框架获取通讯录信息 第一步.在info.plist文件里面配置访问权限 第二步.导入头文件 1 #import <AddressBook/AddressBook.h> 2 #import <AddressBookUI/AddressBookUI.h> 第三步.获取通讯录数据 1 - (void)getSystemAddress{ 2 3 //新建一个通讯录类 4 ABAddressBookRef addressBooks = n

Framework 框架安装 学习篇(二)

一.Framework版本(1.0)(1.1)(2.0)(3.0)(3.5)(4.0)(4.5) FrameWork框架可以从微软官方网站下载免费的框架进行安装. 下载地址:http://rj.baidu.com/soft/detail/22370.html?ald (版本:4.5) 二.配置框架环境变量 1.右键点击“我的电脑”,选择“属性”,点击右侧的“高级系统设置”,弹出对话框,选择“环境变量”. 2.在系统变量中选择变量名“Path”进行编辑,在变量值最后加上FrameWork路径(切记

Hush Framework框架配置(续) 转自《Android和PHP最佳实践》官方站

图书资源下载 Xampp 开发环境下载:http://pan.baidu.com/share/link?shareid=531771&uk=773037279 微博实例完整源码包下载:http://pan.baidu.com/share/link?shareid=531769&uk=773037279 Hush Framework 框架源码及相关资源下载:http://code.google.com/p/hush-framework/downloads/list 其他 Android 客户

.net FrameWork各个版本之间的发展

上个星期看到了.NET 4.0框架退休日期逐渐临近文章,发现自己一直在使用NET  FrameWork,身为一个NET程序员,里面大概的区别自己还是知道的,但是自己要说出个所以然来了,发现还是有点力不从心,当时关于这几个版本之间的有很多,目前很多地方都只是写到了4.5,关于4.5.1,4.5.2这两个版本提及的还是非常少的,相对来说,自己写这篇文章也就没有回锅肉的嫌疑,正文开始吧: DoNet  FrameWork NET Framework由微软开发的一个致力于敏捷软件开发(Agile sof

.net FrameWork各个版本之间的发展[转]

上个星期看到了.NET 4.0框架退休日期逐渐临近文章,发现自己一直在使用NET  FrameWork,身为一个NET程序员,里面大概的区别自己还是知道的,但是自己要说出个所以然来了,发现还是有点力不从心,当时关于这几个版本之间的有很多,目前很多地方都只是写到了4.5,关于4.5.1,4.5.2这两个版本提及的还是非常少的,相对来说,自己写这篇文章也就没有回锅肉的嫌疑,正文开始吧: DoNet  FrameWork NET Framework由微软开发的一个致力于敏捷软件开发(Agile sof

net.sz.framework 框架 轻松搭建服务---让你更专注逻辑功能---初探

前言 在之前的文章中,讲解过 threadmodel,socket tcp ,socket http,log,astart ,scripts: 都是分片讲解,从今天开始,将带大家,一窥 net.sz.framework 框架: net.sz.framework 框架分为java版本和C#.net 版本,两种语言版本保持高一致性: net.sz.framework 能做什么呢? net.sz.framework 是经过多年实践和研究,包括在线项目验证的底层框架,致力于解决框架问题: 让你更轻松,愉

.NET Framework各版本汇总以及之间的关系

目录(?)[-] 原文链接:http://blog.csdn.net/kingmax54212008/article/details/25886345 NET Framework 版本关系 获取NET框架版本 参考资料 .NET Framework .NET版本 1.0 1.1 2.0 3.0 3.5 4.0 4.5 完整版本 1.0.3705.0 1.1.4322.573 2.0.50727.42 3.0.4506.30 3.5.21022.8 4.0.30319.1 4.5.40805 发布