對(duì)接口進(jìn)行版本控制只是一種殺死已部署客戶端的“禮貌”方式?!?nbsp;Roy Fielding.
API 版本控制允許你在不同的客戶端之間更改行為. REST framework 提供了許多不同的版本控制方案。
版本控制由傳入的客戶端請(qǐng)求決定,可以基于請(qǐng)求的URL,也可以基于請(qǐng)求頭。
有許多有效的方法可以進(jìn)行版本控制。無(wú)版本控制的系統(tǒng)也可能是合適的,特別是如果你正在為一個(gè)長(zhǎng)期系統(tǒng)進(jìn)行工程設(shè)計(jì),而這個(gè)系統(tǒng)有多個(gè)超出你控制范圍的客戶端。
當(dāng)啟用API版本控制,request.version 屬性將包含與傳入客戶端請(qǐng)求中請(qǐng)求的版本相對(duì)應(yīng)的字符串。
默認(rèn)情況下,版本控制沒(méi)有啟用,request.version 將總是返回 None。
如何改變API的行為取決于你自己,但是一個(gè)你通常想要的例子是在新版本中切換到不同的序列化樣式。例如:
def get_serializer_class(self):
if self.request.version == 'v1':
return AccountSerializerVersion1
return AccountSerializer
REST framework 包含的 reverse 函數(shù)與版本控制方案相關(guān)聯(lián)。你需要確保將當(dāng)前 request 作為關(guān)鍵字參數(shù)傳遞進(jìn)去,如下所示。
from rest_framework.reverse import reverse
reverse('bookings-list', request=request)
上述函數(shù)將應(yīng)用任何適用于請(qǐng)求版本的URL轉(zhuǎn)換。例如:
當(dāng)將超鏈接序列化樣式與基于URL的版本控制方案一起使用時(shí),請(qǐng)確保將請(qǐng)求作為上下文包含在序列化程序中。
def get(self, request):
queryset = Booking.objects.all()
serializer = BookingsSerializer(queryset, many=True, context={'request': request})
return Response({'all_bookings': serializer.data})
這樣做將會(huì)在任何返回的URL中包含適當(dāng)?shù)陌姹究刂啤?/p>
版本控制方案由settings中的DEFAULT_VERSIONING_CLASS為key來(lái)配置。
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
}
除非顯式設(shè)置,否則DEFAULT_VERSIONING_CLASS將會(huì)是None。在這種情況下request.version屬性將總是返回None。
還可以在單個(gè)視圖上設(shè)置版本控制方案。通常不需要這樣做,因?yàn)槿质褂脝我话姹究刂品桨父幸饬x。如果確實(shí)需要這樣做,請(qǐng)?jiān)谝晥D類中使用versioning_class屬性。
class ProfileList(APIView):
versioning_class = versioning.QueryParameterVersioning
以下設(shè)置的key也用于控制版本控制:
你還可以通過(guò)自定義版本控制方案并使用default_version,allowed_versions和version_param類變量,在每個(gè)視圖或每個(gè)視圖集的基礎(chǔ)上設(shè)置版本類加上這三個(gè)值。例如,如果你想使用URLPathVersioning:
from rest_framework.versioning import URLPathVersioning
from rest_framework.views import APIView
class ExampleVersioning(URLPathVersioning):
default_version = ...
allowed_versions = ...
version_param = ...
class ExampleView(APIVIew):
versioning_class = ExampleVersioning
此方案要求客戶端在Accept header 中將版本指定為媒體類型的一部分。該版本作為媒體類型參數(shù)包含在內(nèi),它補(bǔ)充了主要媒體類型。
下面是一個(gè)使用accept header版本化樣式的HTTP請(qǐng)求示例。
GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/json; version=1.0
在上面的示例請(qǐng)求中request.version屬性將返回字符串'1.0'。 基于 accept headers 的版本控制通常被認(rèn)為是最佳實(shí)踐,盡管其他版本控制方式可能適合你的客戶端需求。
嚴(yán)格地說(shuō)json媒體類型未指定為包含其他參數(shù)。如果要構(gòu)建明確指定的公共API,則可以考慮使用vendor media type。為此,請(qǐng)將渲染器配置為使用自定義媒體類型的基于JSON的渲染器:
class BookingsAPIRenderer(JSONRenderer):
media_type = 'application/vnd.megacorp.bookings+json'
你的客戶端請(qǐng)求現(xiàn)在會(huì)是這樣的:
GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/vnd.megacorp.bookings+json; version=1.0
此方案要求客戶端將版本指定為URL路徑的一部分。
GET /v1/bookings/ HTTP/1.1
Host: example.com
Accept: application/json
你的URL conf中必須包含一個(gè)使用'version'關(guān)鍵字參數(shù)的匹配模式,,以便版本控制方案可以使用此版本信息。
urlpatterns = [
url(
r'^(?P<version>(v1|v2))/bookings/$',
bookings_list,
name='bookings-list'
),
url(
r'^(?P<version>(v1|v2))/bookings/(?P<pk>[0-9]+)/$',
bookings_detail,
name='bookings-detail'
)
]
對(duì)于客戶端,此方案與URLPathVersioning相同。唯一的區(qū)別是,它是如何在 Django 應(yīng)用程序中配置的,因?yàn)樗褂肬RL conf中的命名空間而不是URL conf中的關(guān)鍵字參數(shù)。
GET /v1/something/ HTTP/1.1
Host: example.com
Accept: application/json
使用此方案,request.version 屬性是根據(jù)與傳入請(qǐng)求的路徑匹配的 namespace 確定的。
在下面的示例中,我們給一組視圖提供了兩個(gè)可能出現(xiàn)的不同URL前綴,每個(gè)前綴在不同的命名空間下:
# bookings/urls.py
urlpatterns = [
url(r'^$', bookings_list, name='bookings-list'),
url(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]
# urls.py
urlpatterns = [
url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]
如果你只需要一個(gè)簡(jiǎn)單的版本控制方案URLPathVersioning和NamespaceVersioning都是合適的。URLPathVersioning 這種方法可能更適合小型項(xiàng)目,對(duì)于更大的項(xiàng)目來(lái)說(shuō)NamespaceVersioning可能更容易管理。
主機(jī)名版本控制方案要求客戶端將請(qǐng)求的版本指定為URL中主機(jī)名的一部分。 例如,以下是對(duì)http://v1.example.com/bookings/的HTTP請(qǐng)求:
GET /bookings/ HTTP/1.1
Host: v1.example.com
Accept: application/json
默認(rèn)情況下,此實(shí)現(xiàn)期望主機(jī)名與以下簡(jiǎn)單正則表達(dá)式匹配:
^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$
注意,第一組用括號(hào)括起來(lái),表示這是主機(jī)名的匹配部分。
HostNameVersioning這種方案在調(diào)試模式下使用方案可能會(huì)很尷尬,因?yàn)槟阃ǔ?huì)訪問(wèn)原始IP地址,例如127.0.0.1。有各種在線教程,介紹使用自定義子域名訪問(wèn)本地主機(jī),這種情況下你可能會(huì)發(fā)現(xiàn)這很有幫助。
如果你有基于版本將傳入請(qǐng)求路由到不同服務(wù)器的需求,那么基于主機(jī)名的版本控制會(huì)特別有用,因?yàn)槟憧梢詾椴煌?API 版本配置不同的 DNS 記錄。
此方案是一種在 URL 中包含版本信息作為查詢參數(shù)的簡(jiǎn)單方案。例如: This scheme is a simple style that includes the version as a query parameter in the URL. For example:
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json
要實(shí)現(xiàn)自定義版本控制方案,請(qǐng)繼承 BaseVersioning 并重寫 .determine_version 方法。
下面的例子中使用一個(gè)自定義的 X-API-Version header來(lái)確定所請(qǐng)求的版本。
class XAPIVersionScheme(versioning.BaseVersioning):
def determine_version(self, request, *args, **kwargs):
return request.META.get('HTTP_X_API_VERSION', None)
如果你的版本化方案基于請(qǐng)求URL,你還需要改變版本化URL的確定方式。為此,你還應(yīng)該重寫類中的.reverse()方法。有關(guān)示例,請(qǐng)參見(jiàn)源代碼。
更多建議: