API版本 在开发过程中可能会有多版本的API,因此需要对API进行管理。django drf中对于版本的管理也很方便。
http://www.example.com/api/v1/info
http://www.example.com/api/v2/info
上面这种形式就是很常见的版本管理
在restful规范中,后端的API需要体现出版本
在django drf中,共有三种形式的版本管理
通过GET参数传递
通过URL路由进行传递
通过请求头进行传递
下面将对这三种方法逐一介绍
1、通过get请求传递版本信息 视图函数代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework.versioning import QueryParameterVersioningclass HomeView (APIView ): versioning_class = QueryParameterVersioning def get (self, request ): print("api_version=" , request.version) print(request.versioning_scheme) url = request.versioning_scheme.reverse("home" , request=request) print("drf反向生成的URL为" , url) self.dispatch return Response({"code" : 123123 , "Home" : "Home" })
urls.py
1 2 3 4 5 6 from django.urls import pathfrom app01.views import HomeViewurlpatterns = [ path("home/" , HomeView.as_view(), name="home" ), ]
在项目的settings.py
文件中,我们还需要进行三个配置
1 2 3 4 5 6 7 8 REST_FRAMEWORK = { "VERSION_PARAM" : "version" , "DEFAULT_VERSION" : "v1" , "ALLOWED_VERSIONS" : ["v1" , "v2" , "v3" , "v111" ] }
其中,VERSION_PARAM
代表get请求的默认参数名,后面请求接口http://127.0.0.1:8000/home/?version=v1时,django会自动将参数中的version获取到,并赋值到request.version中。
具体的可以参考drf中通过get请求传递版本对应部分的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 def reverse (self, viewname, args=None , kwargs=None , request=None , format =None , **extra ): url = super ().reverse( viewname, args, kwargs, request, format , **extra ) if request.version is not None : return replace_query_param(url, self.version_param, request.version) return url
QueryParameterVersioning类继承自BaseVersioing类。在BaseVersioing类中,有三个默认的定义:
1 2 3 4 class BaseVersioning : default_version = api_settings.DEFAULT_VERSION allowed_versions = api_settings.ALLOWED_VERSIONS version_param = api_settings.VERSION_PARAM
default_version
代表默认的版本,会自动去全局配置中寻找,如果全局中没有配置则去局部进行寻找
allowed_versions
代表允许的版本号,会自动取settings.py
文件中去读取相应的配置
version_param
代表get请求参数中的关键字,例如http://127.0.0.1:8000/home/?version=v1 例如,若settings.py
文件中配置了此url中的version_param
值为version,那么version就是获取版本的关键字(本质上是字典的键)
接下来看效果演示,这里通过postman来模拟get和post请求。
首先,这里我没有在URL中携带版本信息,由于我在全局配置中写了默认是v1,并且关键字是version,因此会帮我按照这个配置信息反向生成一个url链接。
接下来的请求我会携带version参数,可以看到响应成功,输出api版本信息并反向生成了url
接下来我换一个关键词使用?xx=v3,drf会按照之前的配置,默认使用v1参数
接下来我会使用不在ALLOWED_VERSIONS
中的版本信息,v1000,程序报错,不合格的版本信息
2、通过url路由传递版本信息 URL路由中携带版本(*)公司中常用的方法 可以直接将其写到settings.py文件中,这样可以方便后续使用,直接去全局配置中找到即可。
视图类代码
1 2 3 4 5 6 7 8 9 10 class Home2View (APIView ): versioning_class = URLPathVersioning def get (self, request, *args, **kwargs ): print("api_version=" , request.version) print(request.versioning_scheme) url = request.versioning_scheme.reverse("home2" , request=request) print("drf_HOME2反向生成的URL为" , url) self.dispatch return Response({"code" : 222222 , "Home" : "Home2" })
这里注意,反向生成URL的时候需要加上一个name
urls.py
1 2 3 4 5 6 from django.urls import pathfrom app01.views import Home2Viewurlpatterns = [ path("api/<str:version>/home2/" , Home2View.as_view(), name="home2" ), ]
在类视图的代码中,我们将versioning_class
赋值为URLPathVersioning
在drf的源代码中,URLPathVersioning
是这样实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class URLPathVersioning (BaseVersioning ): """ 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 = [ re_path(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'), re_path(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 """ invalid_version_message = _('Invalid version in URL path.' ) def determine_version (self, request, *args, **kwargs ): version = kwargs.get(self.version_param, self.default_version) if version is None : version = self.default_version if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version def reverse (self, viewname, args=None , kwargs=None , request=None , format =None , **extra ): if request.version is not None : kwargs = {} if (kwargs is None ) else kwargs kwargs[self.version_param] = request.version return super ().reverse( viewname, args, kwargs, request, format , **extra )
本质上就是一个determin_version()函数,首先会从**kwargs
中获取有没有版本,如果没有版本信息则将版本赋值为全局配置中的默认版本。如果API是非法的,则抛出异常。
3、通过请求头传递 视图类:
1 2 3 4 5 6 7 8 9 10 class Home3View (APIView ): versioning_class = AcceptHeaderVersioning def get (self, request, *args, **kwargs ): print("api_version=" , request.version) print(request.versioning_scheme) url = request.versioning_scheme.reverse("home3" , request=request) print("drf_HOME2反向生成的URL为" , url) self.dispatch return Response({"code" : 333333 , "Home" : "Home3" })
urls.py
1 2 3 4 5 6 from django.urls import pathfrom app01.views import Home3Viewurlpatterns = [ path("api/home3/" , Home3View.as_view(), name="home3" ), ]
在类视图的代码中,我们将versioning_class
赋值为AcceptHeaderVersioning
在drf的源代码中,AcceptHeaderVersioning
是这样实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class AcceptHeaderVersioning (BaseVersioning ): """ GET /something/ HTTP/1.1 Host: example.com Accept: application/json; version=1.0 """ invalid_version_message = _('Invalid version in "Accept" header.' ) def determine_version (self, request, *args, **kwargs ): media_type = _MediaType(request.accepted_media_type) version = media_type.params.get(self.version_param, self.default_version) version = unicode_http_header(version) if not self.is_allowed_version(version): raise exceptions.NotAcceptable(self.invalid_version_message) return version
这个请求无法再浏览器中模拟,只能通过postman中去添加请求头进行模拟,
Headers中的KEY为Accept,VALUE为application/json; version=1.0
这里我先将version设置为1.0,可以看到显示信息为不合法的版本信息
然后我将version设置为v3,可以看到可以正常访问
如果我不在请求头中设置的话,默认还是会访问v1版本的API,这是由于我在全局中的配置所导致的。