一、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. C语言验证哥德巴赫猜想

    #include<stdio.h>int f(int x);int main(void){    int n,i;  scanf("%d",&n);  for( ...

  2. Noip2017Day1T3 逛公园

    题目链接 problem 一个有向无重边自环图,设\(D\)为从\(1\)号点走到\(n\)号点的最短距离.问有多少条从\(1\)到\(n\)的路径长度不超过\(D+K\).\(K\)为给定的值,且\ ...

  3. 第04组 Beta冲刺(5/5)

    队名:new game 组长博客 作业博客 组员情况 鲍子涵(队长) 过去两天完成了哪些任务 动画优化 接下来的计划 等待答辩 还剩下哪些任务 让游戏本体运行 遇到了哪些困难 时间太少了 有哪些收获和 ...

  4. 黄聪:mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高

    mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高 在很多分页的程序中都这样写: SELECT COUNT(*) from `table` WHERE ... ...

  5. Java电商项目-3.使用VSFTPD_Nginx完成商品新增

    目录 到Github获取源码请点击此处 一. 商品类目查询 二. FTP图片服务器的搭建 图片上传思路介绍 Linux中安装vsftpd 接着配置ftp服务, 让外网可以访问 Http服务器搭建 Ng ...

  6. Asp.Net Mvc自定义控件之树形结构数据生成表格 - WPF特工队内部资料

    最近项目中有一个需求,将树形结构的数据,以表格的形式展示在页面中,下图是最终呈现效果: 源码: @{ Layout = null; } <!DOCTYPE html> <html&g ...

  7. Ubuntu 安装最新版nodejs

    转自:ubuntu快速安装最新版nodejs,只需2步 第一步,去 nodejs 官网 https://nodejs.org 看最新的版本号: 也就是说此时此刻,12.6.0 是最新的版本,不过你求稳 ...

  8. Java生鲜电商平台-电商会员体系系统的架构设计与源码解析

    Java生鲜电商平台-电商会员体系系统的架构设计与源码解析 说明:Java生鲜电商平台中会员体系作为电商平台的基础设施,重要性不容忽视.我去年整理过生鲜电商中的会员系统,但是比较粗,现在做一个最好的整 ...

  9. Dynamics CRM定制子网格添加按钮实例之一

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复221或者20160430可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  10. 【jQuery】jQuery基础

    jQuery介绍 jQuery是一个轻量级JS库,使用十分简单: jQuery的核心是选择器,用于获取页面元素: jQuery提供了大量高效的方法,开发速度大幅提升: jQuery选择器 jQuery ...