一、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"
}

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

  

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

  

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

  1. ASP.NET在IIS7中如何更改网站的.net framework框架版本

    IIS7安装好以后使用了.net 2.0 framework框架,经过折腾发现如下方法可以更改框架版本,从而可以部署使用其他版本框架开发的网站 方法一:建立网站时设置.net框架版本 方法二:对于已经 ...

  2. django的rest framework框架——版本、解析器、序列化

    一.rest framework的版本使用 1.版本可以写在URL中,通过GET传参,如 http://127.0.0.1:8082/api/users/?version=v1 (1)自定义类获取版本 ...

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

    目录(?)[-] 原文链接:http://blog.csdn.net/kingmax54212008/article/details/25886345 NET Framework 版本关系 获取NET ...

  4. .NET Framework个版本说明

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

  5. 简单概述 .NET Framework 各版本区别

    目前已发行的版本有1.0.1.1.2.0.3.0.3.5.4.0.4.5(及4.5.1.4.5.2).4.6(及4.6.1). 1.0版本:最初的.net framework版本,作为一个独立的工具包 ...

  6. .net Framework各个版本之间的发展

    原文:.net Framework各个版本之间的发展 上个星期看到了.NET 4.0框架退休日期逐渐临近文章,发现自己一直在使用NET  FrameWork,身为一个NET程序员,里面大概的区别自己还 ...

  7. .NET Framework 各版本区别

    .NET Framework 各版本区别 .NET Framework 1.1 自1.0版本以来的改进:自带了对mobile asp .net控件的支持.这在1.0版本是以附加功能方式实现的,现在已经 ...

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

    前言 在之前的文章中,讲解过 threadmodel,socket tcp ,socket http,log,astart ,scripts: 都是分片讲解,从今天开始,将带大家,一窥 net.sz. ...

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

    前言 前文讲述了net.sz.framework 框架的基础实现功能,本文主讲 net.sz.framework.db 和 net.sz.framework.szthread; net.sz.fram ...

  10. Hush Framework框架配置

    在写这篇文章的时候,楼主已经饿的不行了,因为我从3点开始就在折腾Hush Framework,走了很多弯路,打铁要趁热,先把基本的过程记录下来,留待以后翻阅,同时记录其中容易走弯路的地方,特别是对于一 ...

随机推荐

  1. SpringBootTest MockMVC绑定session(需要登陆的接口)

    https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testing spring-te ...

  2. 第05组 Alpha冲刺(3/4)

    第05组 Alpha冲刺(3/4) 队名:天码行空 组长博客连接 作业博客连接 团队燃尽图(共享): GitHub当日代码/文档签入记录展示(共享): 组员情况: 组员1:卢欢(组长) 过去两天完成了 ...

  3. C缺陷与陷阱

    导读 词法"陷阱" 语法"陷阱" 语义"陷阱" 链接 库函数 预处理器 可移植性缺陷

  4. numpy中多维数组的绝对索引

    这涉及到吧多维数组映射为一维数组. 对于3维数组,有公式: def MAP(x,y,z): return y_s * z_s * x + z_s * y + z 此公式可以推广到N维 测试代码:(两个 ...

  5. BERT-wwm、BERT-wwm-ext、RoBERTa、SpanBERT、ERNIE2

    一.BERT-wwm wwm是Whole Word Masking(对全词进行Mask),它相比于Bert的改进是用Mask标签替换一个完整的词而不是子词,中文和英文不同,英文中最小的Token就是一 ...

  6. 手动创建分区以及软硬raid的问题

    本文章旨在介绍安装centos7时手动分区问题,及其所对应的软硬raid问题.在新建centos7系统时,需要手动分区时,提供给我们三个选项: 在上图中,默认选项为“Standard Partitio ...

  7. this泛指函数的上下文

    this泛指函数的上下文 当前函数运行的类型上下文.

  8. 死磕 java同步系列之AQS终篇(面试)

    问题 (1)AQS的定位? (2)AQS的重要组成部分? (3)AQS运用的设计模式? (4)AQS的总体流程? 简介 AQS的全称是AbstractQueuedSynchronizer,它的定位是为 ...

  9. wpf listview images

    <ListView x:Name="lv"> <ListView.ItemsPanel> <ItemsPanelTemplate> <St ...

  10. dictionary vs Hashtables

    DictionaryDictionary is generic type Dictionary<TKey,TValue>Dictionary class is a strong type ...