1.REST framework版本控制的流程分析
1.1 determine_version方法的執行流程
首先,請求到達
REST framework
的CBV,執行CBV中的
dispatch
方法再次封裝完成request後,執行
initial
方法.
在
REST framework
中的版本控制就是在
initial
函數中調用
determine_version
方法完成的
來看看源碼
initial
方法的源碼:
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
# Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
determine_version
方法的源碼
def determine_version(self, request, *args, **kwargs):
if self.versioning_class is None: # 如果versioning_class為空則傳回一個None的元組
return (None, None)
scheme = self.versioning_class()
return (scheme.determine_version(request, *args, **kwargs), scheme)
determine_version
方法中的
versioning_class
方法又是從哪裡來的呢
1.2 versioning_class的由來
在UserView視圖函數中沒有定義versioning_class,那就要到UserView的父類APIView中去找
在APIView類中定義了versioning_class的資訊
class APIView(View):
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
在視圖函數中列印versioning_class
None
可以看到預設設定的
versioning_class的值是None
,這說明我們
可以在視圖函數中為versioning_class設定一個值
detemine_version
函數的源碼中,可以看到versioning_class後面加了一個括号,是以
versioning_class是一個函數或一個類
如果versioning_class是一個函數,那麼執行versioning_class後會有一個傳回值
如果versioning_class是一個類,那麼versioning_class加括号就執行個體化一個類
從rest_framework中導入versioning子產品
from rest_framework import versioning
然後進入versioning子產品,可以看到這個versioning中定義了6個類
這6個類是
BaseVersioning
,
AcceptHeaderVersioning
URLPathVersioning
NamespaceVersioning
HostNameVersioning
QueryParameterVersioning
而且還可以看到
BaseVersioning類
是其餘5個類的父類.
并且這其餘的5個類,每個類中都有一個
determine_version
方法
在項目的視圖函數中導入其中任意一個類,列印
versioning_class
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from django.views import View
from rest_framework.versioning import QueryParameterVersioning
class UsersView(APIView):
versioning_class=QueryParameterVersioning
def get(self,request,*args,**kwargs):
print(self.versioning_class) #列印versioning_class
return HttpResponse("aaaa")
列印結果
<class 'rest_framework.versioning.QueryParameterVersioning'>
是以versioning_class是一個類,并且
versioning_class類中有一個determine_version方法
1.3 REST framework版本控制的流程總結
initial
方法中,執行完
determine_version
後的傳回值被指派給version, scheme這兩個變量
def initial(self, request, *args, **kwargs):
self.format_kwarg = self.get_format_suffix(**kwargs)
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
這兩個變量又把
determine_version
方法的傳回值指派給
request.version
request.versioning_scheme
這兩個變量
在視圖函數中列印這兩個變量
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import QueryParameterVersioning
class UsersView(APIView):
versioning_class=QueryParameterVersioning
def get(self,request,*args,**kwargs):
print(self.versioning_class)
print("request.version:",request.version)
print("request.versioning_scheme:",request.versioning_scheme)
return HttpResponse("aaaa")
<class 'rest_framework.versioning.QueryParameterVersioning'>
request.version: None
request.versioning_scheme: <rest_framework.versioning.QueryParameterVersioning object at 0x00000000057722B0>
2. REST framework擷取版本的方法
在上面的流程分析中,versioning子產品中定義了6個類
BaseVersioning
AcceptHeaderVersioning
URLPathVersioning
NamespaceVersioning
HostNameVersioning
QueryParameterVersioning
BaseVersioning類
是其餘5個類的父類,
REST framework
擷取版本調用的就是這5個類
2.1 QueryParameterVersioning:基于url的get傳參方式
在settings.py檔案的
INSTALLED_APPS
配置項中引入rest-framework
INSTALLED_APPS = [
...
'rest_framework',
]
配置路由表
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/$',views.UsersView.as_view()),
]
視圖函數配置擷取版本方式為
QueryParameterVersioning
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import QueryParameterVersioning
class UsersView(APIView):
versioning_class=QueryParameterVersioning
def get(self,request,*args,**kwargs):
# print("request:",request.__dict__)
print("request.version:",request.version) # 列印版本
# print(request.version.scheme)
# print(request.versioning_scheme.reverse("test1",request=request))
# print(request.versioning_scheme.reverse(viewname="test1",request=request))
return HttpResponse("aaaa")
在浏覽器中輸入
http://127.0.0.1:8000/users/?version=v1
位址,服務端列印結果
request.version: v1
再把浏覽器中的url位址更換為
http://127.0.0.1:8000/users/?version=v5
,重新整理浏覽器,服務端列印結果
request.version: v5
2.2 URLPathVersioning:基于url的正則方式
配置url路由資訊
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^(?P<version>\w+)/users/$',views.UsersView.as_view()),
]
URLPathVersioning
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import URLPathVersioning
class UsersView(APIView):
versioning_class=URLPathVersioning
def get(self,request,*args,**kwargs):
print("request.version:",request.version) # 列印版本
return HttpResponse("aaaa")
http://127.0.0.1:8000/v1/users/
request.version: v1
http://127.0.0.1:8000/v10/users/
request.version: v10
2.3 AcceptHeaderVersioning:基于accept請求頭方式擷取版本資訊
在settings.py檔案中添加如下配置
REST_FRAMEWORK = {
'VERSION_PARAM': "version", # 版本的參數,在url中可以展現
'DEFAULT_VERSION': 'V1', # 預設的版本
'ALLOWED_VERSIONS': ['v1', 'v2','v3'] # 允許的版本
}
urls.py設定為
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/$',views.UsersView.as_view()),
]
視圖函數定義
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import AcceptHeaderVersioning
class UsersView(APIView):
versioning_class=AcceptHeaderVersioning
def get(self,request,*args,**kwargs):
print("request.version:",request.version) # 擷取版本資訊
return HttpResponse("aaaa")
用浏覽器打開
http://127.0.0.1:8000/users/
的url位址
request.version: V1
由于在settings.py檔案中已經設定了預設的版本是v1,是以在服務端背景擷取到的版本是v1
把settings.py中定義的預設版本更改變v2或者v3,再次重新整理浏覽器,背景列印的版本資訊又會跟着改變
request.version: V2
2.4 NamespaceVersioning:基于django路由系統的namespace
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^v1/users/',([url(r'test/',views.UsersView.as_view(),name='test1')],None,'v1')),
url(r'^v2/users/',([url(r'test/',views.UsersView.as_view(),name='test2')],None,'v2')),
]
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import NamespaceVersioning
class UsersView(APIView):
versioning_class=NamespaceVersioning
def get(self,request,*args,**kwargs):
print("request.version:",request.version) # 擷取版本資訊
return HttpResponse("aaaa")
http://127.0.0.1:8000/v1/users/test/
request.version: V1
把url的位址更換為
http://127.0.0.1:8000/v2/users/test/
,重新整理浏覽器,背景列印資訊如下
request.version: V2
2.5 HostNameVersioning由于要更換電腦的主機名稱,是以這裡不再進行測試
3. versioning_class的全局配置
在視圖函數中定義
versioning_class
,隻能作用于單個類,
如果想整個項目都使用同一種方法來進行版本控制,就可以在settings.py檔案中定義全局的
versioning_class
settings.py
中配置預設的
versioning_class
為
URLPathVersioning
REST_FRAMEWORK={
'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
}
在前面檢視到
BaseVersioning
的源碼時,可以看到還有幾個參數可以在settings.py檔案中定義的
class BaseVersioning(object):
default_version = api_settings.DEFAULT_VERSION
allowed_versions = api_settings.ALLOWED_VERSIONS
version_param = api_settings.VERSION_PARAM
再來看看這幾個參數配置項的作用
REST_FRAMEWORK={ # 預設使用URLPathVersioning類來擷取版本資訊
'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
'VERSION_PARAM':"version", # 版本的參數,在url中可以展現
'DEFAULT_VERSION':'V1', # 預設的版本
'ALLOWED_VERSIONS':['v1','v2'] # 允許的版本
}
修改urls.py檔案,使url可以比對任意長度的字元url
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^(?P<version>\w+)/users/$',views.UsersView.as_view()),
]
在浏覽器中分别輸入
http://127.0.0.1:8000/v1/users/
和
http://127.0.0.1:8000/v2/users/
都可以擷取到正确的響應資訊
再在浏覽器中輸入
http://127.0.0.1:8000/v3/users/
時,浏覽器中出現了報錯
從這裡可以知道,在settings.py檔案中設定的url中允許的版本隻能是v1或v2,在浏覽器中輸入的版本是v3,是以就會出現錯誤了