在RESTful 规范中,有关版本的问题,用restful规范作开放接口的时候,用户请求API,系统返回数据。可是不免在系统发展的过程当中,不可避免的须要添加新的资源,或者修改现有资源。所以,改动升级必不可少,可是,做为平台开发者,应该知道:一旦你的API开放出去,有人开始用了,平台的任何改动都须要考虑对当前用户的影响。所以,作开放平台,从第一个API的设计就须要开始API的版本控制策略问题,API的版本控制策略就像是开放平台和平台用户之间的长期协议,其设计的好坏将直接决定用户是否使用该平台,或者说用户在使用以后是否会由于某次版本升级直接弃用该平台。html
怎么定义版本协议,前端后端怎么协调。有如下几种方式:前端
GET /something/ HTTP/1.1 Host: example.com Accept: application/json; version=1.0 #版本为1.0
URL: example.com/v1.0/ # 版本为1.0 GET /1.0/something/ HTTP/1.1 Host: example.com Accept: application/json
GET /something/ HTTP/1.1 Host: v1.example.com # 版本为1.0 Accept: application/json
GET /something/?version=0.1 HTTP/1.1 # 版本为1.0 Host: example.com Accept: application/json
在django rest framewrok
中,若是没有在配置文件setting.py
中设置默认的VERSION_PARAM
,即版本参数,drf
会设置默认的参数为version
,并将获取到的version
的值封装到request.version
中python
django rest framework
的request
实际上是对原生的Django
的HttpRequest
作了一个封装,经过直接获取属性能够获取到请求头中的版本号
django rest framework的requestdjango
原生的Django
的HttpRequest
json
请求头的版本和其余请求头信息最终会放到META
中,所以想要获取版本号能够以下这样后端
version = request._request.META.get('version') # 获取版本号
一样的像请求头中定义同样,在请求头中也能够直接获取的域名,放到META中,所以想要获取版本号能够以下这样api
host = request._request.META.get('HTTP_HOST') # 先获取主机域名 version = host.split('.')[0] # 获取版本号
注:其实在django rest framework
内部也有关于以上两种定义版本的处理方法浏览器
以前分别在django rest framework中关于节流,认证,权限三个组件,这里新建一个Django
项目,命名为drf2。并进入当前目录下执行python manage.py startapp api
,将新建的app,和rest_framework
放入INSTALLED_APPS。restful
# setting.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'api' ]
from django.conf.urls import url from .views import VersionView urlpatterns = [ url(r'^version/$', VersionView.as_view()), ]
from django.http import JsonResponse from rest_framework.views import APIView from rest_framework.versioning import QueryParameterVersioning class VersionView(APIView): versioning_class = QueryParameterVersioning # 局部配置请求参数处理 def get(self, request, *args, **kwargs): version = request.version ret = { 'code': 1000, 'msg': '请求成功', ‘version': version } return JsonResponse(ret)
像以前在权限,节流那样,能够配置一个全局默认的版本解析类session
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning", # 默认是url处理版本 "DEFAULT_VERSION":'v1', # 默认版本 "ALLOWED_VERSIONS":['v1','v2'], # 容许版本 "VERSION_PARAM":'version', # 版本参数例如 ?version=v1,,则表示版本为v1 }
使用postman或者浏览器发送请求测试
提供正常版本号:http://127.0.0.1:8000/api/version/?version=v1
获取版本成功
发送错误版本号:http://127.0.0.1:8000/api/version/?version=v3
因为容许版本只有v1和v2,因此版本错误,返回错误信息
不提供版本号:假如在url请求中不添加参数,http://127.0.0.1:8000/api/version/?
,能获取到默认的版本号
在url中定义,例如http://127.0.0.1:8000/api/v1/
from django.conf.urls import url from .views import VersionView urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/$', VersionView.as_view()), # 可用版本为v1和v2 ]
from django.http import JsonResponse from django.http import HttpRequest from rest_framework.views import APIView, Request from rest_framework.versioning import URLPathVersioning class VersionView(APIView): versioning_class = URLPathVersioning # 局部配置版本类 def get(self, request, *args, **kwargs): version = request.version ret = { 'code': 1000, 'msg': '请求成功', 'version': version } return JsonResponse(ret)
或者也能够全局配置, 不过使用URL解析的时候,须要在路由系统中正则匹配设置可用的版本,
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning", "VERSION_PARAM":'version', # 参数 }
使用postman或者浏览器发送请求测试
http://127.0.0.1:8000/api/v1/
,正确的获取版本号
在django中也提供了一个url解析的函数reverse
,不过在django rest framework
中也有一个将reverse
函数封装一层的接口能够进行url反向解析。
路由系统:加入namespace参数
from django.contrib import admin from django.urls import path, include from django.conf.urls import url urlpatterns = [ url(r'^api/', include('api.urls', namespace='api') ), ]
子路由系统:加入name参数
from django.conf.urls import url from .views import VersionView app_name = 'api' urlpatterns = [ url(r'^version/$', VersionView.as_view(), name='version'), url(r'^(?P<version>[v1|v2]+)/$', VersionView.as_view(), name='version'), ]
http://127.0.0.1:8000/api/version/?version=v1
,发送请求
class VersionView(APIView): versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): version = request.version url1 = request.versioning_scheme.reverse(viewname='api:version', request=request) url2 = reverse(viewname='api:version', kwargs=kwargs) ret = { 'code': 1000, 'msg': '请求成功', 'version': version, 'drf_url': url1, 'django_url': url2 } return JsonResponse(ret)
使用postman返送请求
http://127.0.0.1:8000/api/v1/
,发送请求
class VersionView(APIView): versioning_class = URLPathVersioning def get(self, request, *args, **kwargs): version = request.version url1 = request.versioning_scheme.reverse(viewname='api:version', request=request) url2 = reverse(viewname='api:version', kwargs=kwargs) ret = { 'code': 1000, 'msg': '请求成功', 'version': version, 'drf_url': url1, 'django_url': url2 } return JsonResponse(ret)
使用postman返送请求
这里有与drf的reverse
在对django中的reverse
函数进行封装的时候,获取了request.get_full_url()
,并作了一个拼接,因此才会出现所有的url
依旧从dispath
方法进入源码,找到initial
方法
这里调用了determine_version()
方法,并拿到两个返回值并封装到request中。这时候request.version_scheme
就是一个版本对象了
能够在setting.py
中配置以后,全局使用
在url反向解析中,调用了request.versioning_scheme.reverse()
中的reverse()
方法,说明request.versioning_scheme
返回的是一个版本对象,能够调用他的方法
BaseVersioning基类定义了三个接口
而上面示例使用的两个超类URLPathVersioning,QueryParameterVersioning
其实也就是,重写了determine_version,和reverse
两个方法。
版本的获取方式有多种,在django rest framewok
中也提供了一一对应的处理版本对象,能够根据本身的须要配置,或者继承重写接口使用。
配置也支持全局配置,和局部配置,在全局配置的时候,须要定义默认的版本号,以防万一。
在进行url反向解析的时候django rest framewok
提供了一个更好的方式。