五、django rest_framework源碼之版本控制剖析
1.緒論
Djangorest_framework的版本控制允許用戶更改不同客戶端之間的行為,且提供了許多不同的版本控制方案。版本控制由傳入的客戶端請求確定,可以基於請求URL,也可以基於請求標頭。
版本控制入口在在dispatch方法中調用的initial方法中,如下所示:
def initial(self, request, *args, **kwargs): …… #版本控制 version, scheme = self.determine_version(request, *args, **kwargs) #將獲得的版本號和版本類對象放入request中request.version, request.versioning_scheme = version, scheme self.perform_authentication(request)#認證 self.check_permissions(request)#權限 self.check_throttles(request)#頻率控制
可以看出,版本控制而是認證、權限、頻率控制等操作之前。這一篇,我們來分析一下django rest_framework的版本控制源碼。
2. 源碼分析
先來看看determine_version方法,源碼如下:
def determine_version(self, request, *args, **kwargs): if self.versioning_class is None:#讀取配置好的版本控制類,如果沒有配置就返回(None, None) return (None, None) scheme = self.versioning_class()#實例化版本控制類對象 #調用封裝在版本控制類中的determine_version方法 #返回:(版本號 , 版本控制實例對象) return (scheme.determine_version(request, *args, **kwargs), scheme)
獲得版本號的關鍵在return語句中調用的determine_version方法中,我們以django rest_framework自帶的版本控制類QueryParameterVersioning中的determine_version為例進行分析:
def determine_version(self, request, *args, **kwargs): #獲得request傳入的版本號version_param , 項目配置的默認版本號default_version 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
判斷是否是允許的版本,是在is_allowed_version方法中進行,具體是怎麽一個過程呢?如下所示:
def is_allowed_version(self, version): #allowed_versions是BaseVersioning類中的屬性,讀取項目配置的允許的版本號,是一個列表 # allowed_versions = api_settings.ALLOWED_VERSIONS if not self.allowed_versions:#如果沒有設置允許版本號,則默認允許所有版本 return True return ( #如果request中的版本號不為空,且等於默認版本號 (version is not None and version == self.default_version) or (version in self.allowed_versions))#或者request的版本號在允許的版本號列表中
is_allowed_version返回的是布爾型值,如果允許則返回True,如果拒絕則返回False。回到上面的determine_version方法中,如果is_allowed_version返回的是True,即是允許的版本號,那麽版本控制對象中的determine_version方法就會繼續向上返回request中的版本號,最初的initial方法調用的determine_version方法(兩個determine_version可不一樣)獲得版本號之後會返回一個包含版本號和版本控制實例的元組,最後在initial方法中將版本號和版本實例信息添加到request中,整個版本控制過程就結束了。
3. 幾種版本控制的方式:
Django rest_framework提供了4種版本控制方法,分別是:基於url的get傳參方式獲取版本(QueryParameterVersioning)、基於url的正則方式(URLPathVersioning)、基於 accept 請求頭方式(AcceptHeaderVersioning)、基於主機名方法(HostNameVersioning),分別對應rest_framework。
(註:本部分內容來源於聽風。的博客https://www.cnblogs.com/huchong/p/8450355.html,感謝博主)
- 基於url的get傳參方式獲取版本:
如:/users?version=v1
版本配置:
REST_FRAMEWORK = { ‘DEFAULT_VERSION‘: ‘v1‘, # 默認版本 ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘], # 允許的版本 ‘VERSION_PARAM‘: ‘version‘ # URL中獲取值的key }
路由配置:
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r‘^test/‘, TestView.as_view(),name=‘test‘), ] from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import QueryParameterVersioning class TestView(APIView): versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 獲取版本 print(request.version) # 獲取版本管理的類 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse(‘test‘, request=request) print(reverse_url) return Response(‘GET請求,響應內容‘) def post(self, request, *args, **kwargs): return Response(‘POST請求,響應內容‘) def put(self, request, *args, **kwargs): return Response(‘PUT請求,響應內容‘)
- 基於url的正則方式
如:/v1/users/
版本配置:
REST_FRAMEWORK = { ‘DEFAULT_VERSION‘: ‘v1‘, # 默認版本 ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘], # 允許的版本 ‘VERSION_PARAM‘: ‘version‘ # URL中獲取值的key }
路由配置:
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r‘^(?P<version>[v1|v2]+)/test/‘, TestView.as_view(), name=‘test‘), ]
視圖類:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import URLPathVersioning class TestView(APIView): versioning_class = URLPathVersioning def get(self, request, *args, **kwargs): # 獲取版本 print(request.version) # 獲取版本管理的類 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse(‘test‘, request=request) print(reverse_url) return Response(‘GET請求,響應內容‘) def post(self, request, *args, **kwargs): return Response(‘POST請求,響應內容‘) def put(self, request, *args, **kwargs): return Response(‘PUT請求,響應內容‘)
- 基於 accept 請求頭方式:
如:Accept: application/json; version=1.0
版本配置:
REST_FRAMEWORK = { ‘DEFAULT_VERSION‘: ‘v1‘, # 默認版本 ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘], # 允許的版本 ‘VERSION_PARAM‘: ‘version‘ # URL中獲取值的key }
路由配置:
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r‘^test/‘, TestView.as_view(), name=‘test‘), ]
視圖:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import AcceptHeaderVersioning class TestView(APIView): versioning_class = AcceptHeaderVersioning def get(self, request, *args, **kwargs): # 獲取版本 HTTP_ACCEPT頭 print(request.version) # 獲取版本管理的類 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse(‘test‘, request=request) print(reverse_url) return Response(‘GET請求,響應內容‘) def post(self, request, *args, **kwargs): return Response(‘POST請求,響應內容‘) def put(self, request, *args, **kwargs): return Response(‘PUT請求,響應內容‘)
- 基於主機名方法
如:v1.example.com
版本配置:
ALLOWED_HOSTS = [‘*‘] REST_FRAMEWORK = { ‘DEFAULT_VERSION‘: ‘v1‘, # 默認版本 ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘], # 允許的版本 ‘VERSION_PARAM‘: ‘version‘ # URL中獲取值的key }
路由配置:
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r‘^test/‘, TestView.as_view(), name=‘test‘), ]
視圖:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.versioning import HostNameVersioning class TestView(APIView): versioning_class = HostNameVersioning def get(self, request, *args, **kwargs): # 獲取版本 print(request.version) # 獲取版本管理的類 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse(‘test‘, request=request) print(reverse_url) return Response(‘GET請求,響應內容‘) def post(self, request, *args, **kwargs): return Response(‘POST請求,響應內容‘) def put(self, request, *args, **kwargs): return Response(‘PUT請求,響應內容‘)
上面用的所有方法都是局部配置,如果是全局配置,是在項目的settings.py文件中配置:
REST_FRAMEWORK = { ‘DEFAULT_VERSIONING_CLASS‘:"rest_framework.versioning.URLPathVersioning", ‘DEFAULT_VERSION‘: ‘v1‘, ‘ALLOWED_VERSIONS‘: [‘v1‘, ‘v2‘], ‘VERSION_PARAM‘: ‘version‘ }
五、django rest_framework源碼之版本控制剖析