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"
}
访问成功,显示如下所示:
访问失败,会使用框架提示信息:
rest-framework框架——版本的更多相关文章
- ASP.NET在IIS7中如何更改网站的.net framework框架版本
IIS7安装好以后使用了.net 2.0 framework框架,经过折腾发现如下方法可以更改框架版本,从而可以部署使用其他版本框架开发的网站 方法一:建立网站时设置.net框架版本 方法二:对于已经 ...
- django的rest framework框架——版本、解析器、序列化
一.rest framework的版本使用 1.版本可以写在URL中,通过GET传参,如 http://127.0.0.1:8082/api/users/?version=v1 (1)自定义类获取版本 ...
- .NET Framework各版本汇总以及之间的关系
目录(?)[-] 原文链接:http://blog.csdn.net/kingmax54212008/article/details/25886345 NET Framework 版本关系 获取NET ...
- .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 ...
- 简单概述 .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版本,作为一个独立的工具包 ...
- .net Framework各个版本之间的发展
原文:.net Framework各个版本之间的发展 上个星期看到了.NET 4.0框架退休日期逐渐临近文章,发现自己一直在使用NET FrameWork,身为一个NET程序员,里面大概的区别自己还 ...
- .NET Framework 各版本区别
.NET Framework 各版本区别 .NET Framework 1.1 自1.0版本以来的改进:自带了对mobile asp .net控件的支持.这在1.0版本是以附加功能方式实现的,现在已经 ...
- net.sz.framework 框架 轻松搭建服务---让你更专注逻辑功能---初探
前言 在之前的文章中,讲解过 threadmodel,socket tcp ,socket http,log,astart ,scripts: 都是分片讲解,从今天开始,将带大家,一窥 net.sz. ...
- net.sz.framework 框架 轻松搭建数据服务中心----读写分离数据一致性,滑动缓存
前言 前文讲述了net.sz.framework 框架的基础实现功能,本文主讲 net.sz.framework.db 和 net.sz.framework.szthread; net.sz.fram ...
- Hush Framework框架配置
在写这篇文章的时候,楼主已经饿的不行了,因为我从3点开始就在折腾Hush Framework,走了很多弯路,打铁要趁热,先把基本的过程记录下来,留待以后翻阅,同时记录其中容易走弯路的地方,特别是对于一 ...
随机推荐
- C语言验证哥德巴赫猜想
#include<stdio.h>int f(int x);int main(void){ int n,i; scanf("%d",&n); for( ...
- Noip2017Day1T3 逛公园
题目链接 problem 一个有向无重边自环图,设\(D\)为从\(1\)号点走到\(n\)号点的最短距离.问有多少条从\(1\)到\(n\)的路径长度不超过\(D+K\).\(K\)为给定的值,且\ ...
- 第04组 Beta冲刺(5/5)
队名:new game 组长博客 作业博客 组员情况 鲍子涵(队长) 过去两天完成了哪些任务 动画优化 接下来的计划 等待答辩 还剩下哪些任务 让游戏本体运行 遇到了哪些困难 时间太少了 有哪些收获和 ...
- 黄聪:mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高
mysql的SQL_CALC_FOUND_ROWS 使用 类似count(*) 使用性能更高 在很多分页的程序中都这样写: SELECT COUNT(*) from `table` WHERE ... ...
- Java电商项目-3.使用VSFTPD_Nginx完成商品新增
目录 到Github获取源码请点击此处 一. 商品类目查询 二. FTP图片服务器的搭建 图片上传思路介绍 Linux中安装vsftpd 接着配置ftp服务, 让外网可以访问 Http服务器搭建 Ng ...
- Asp.Net Mvc自定义控件之树形结构数据生成表格 - WPF特工队内部资料
最近项目中有一个需求,将树形结构的数据,以表格的形式展示在页面中,下图是最终呈现效果: 源码: @{ Layout = null; } <!DOCTYPE html> <html&g ...
- Ubuntu 安装最新版nodejs
转自:ubuntu快速安装最新版nodejs,只需2步 第一步,去 nodejs 官网 https://nodejs.org 看最新的版本号: 也就是说此时此刻,12.6.0 是最新的版本,不过你求稳 ...
- Java生鲜电商平台-电商会员体系系统的架构设计与源码解析
Java生鲜电商平台-电商会员体系系统的架构设计与源码解析 说明:Java生鲜电商平台中会员体系作为电商平台的基础设施,重要性不容忽视.我去年整理过生鲜电商中的会员系统,但是比较粗,现在做一个最好的整 ...
- Dynamics CRM定制子网格添加按钮实例之一
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复221或者20160430可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- 【jQuery】jQuery基础
jQuery介绍 jQuery是一个轻量级JS库,使用十分简单: jQuery的核心是选择器,用于获取页面元素: jQuery提供了大量高效的方法,开发速度大幅提升: jQuery选择器 jQuery ...