Django Rest framework 之 版本

1、前言

一、版本的重要性

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中定義
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
  • HttpReqeust參數傳遞
GET /something/?version=0.1 HTTP/1.1  # 版本爲1.0
    Host: example.com
    Accept: application/json

2、示例

django rest framewrok中,若是沒有在配置文件setting.py中設置默認的VERSION_PARAM,即版本參數,drf會設置默認的參數爲version,並將獲取到的version的值封裝到request.versionpython

一、請求頭中定義

django rest frameworkrequest實際上是對原生的DjangoHttpRequest作了一個封裝,經過直接獲取屬性能夠獲取到請求頭中的版本號
django rest framework的requestdjango

原生的DjangoHttpRequestjson

請求頭的版本和其餘請求頭信息最終會放到META中,所以想要獲取版本號能夠以下這樣後端

version = request._request.META.get('version')  # 獲取版本號

二、子域名中定義

一樣的像請求頭中定義同樣,在請求頭中也能夠直接獲取的域名,放到META中,所以想要獲取版本號能夠以下這樣api

host = request._request.META.get('HTTP_HOST')  # 先獲取主機域名
version = host.split('.')[0]  #  獲取版本號

注:其實在django rest framework內部也有關於以上兩種定義版本的處理方法瀏覽器

三、HttpReqeust參數傳遞

以前分別在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'
]

<1>、目錄結構

<2>、路由系統

from django.conf.urls import url

from .views import VersionView

urlpatterns = [
    url(r'^version/$', VersionView.as_view()),

]

<3>、視圖

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)

<4>、配置文件

像以前在權限,節流那樣,能夠配置一個全局默認的版本解析類session

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",  # 默認是url處理版本
    "DEFAULT_VERSION":'v1',  # 默認版本
    "ALLOWED_VERSIONS":['v1','v2'],  # 容許版本
    "VERSION_PARAM":'version',  # 版本參數例如  ?version=v1,,則表示版本爲v1 
}

<5>、測試

使用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中定義

在url中定義,例如http://127.0.0.1:8000/api/v1/

<1>、路由系統

from django.conf.urls import url

from .views import VersionView

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/$', VersionView.as_view()),  # 可用版本爲v1和v2
]

<2>、視圖

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',  # 參數
}

<3>、測試

使用postman或者瀏覽器發送請求測試
http://127.0.0.1:8000/api/v1/,正確的獲取版本號

五、反向解析URL

在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返送請求

示例二:URL攜帶版本

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

3、源碼分析

一、找到initial()方法

依舊從dispath方法進入源碼,找到initial方法

二、進入initial()方法

這裏調用了determine_version()方法,並拿到兩個返回值並封裝到request中。這時候request.version_scheme就是一個版本對象了

三、查看具體的determine_version()方法

四、默認的版本處理對象

能夠在setting.py中配置以後,全局使用

五、drf提供的版本類

在url反向解析中,調用了request.versioning_scheme.reverse()中的reverse()方法,說明request.versioning_scheme返回的是一個版本對象,能夠調用他的方法

BaseVersioning基類定義了三個接口

  • determine_version:返回版本
  • reverse:url反向解析使用
  • is_allowed_version:就是判斷版本號是否合法

而上面示例使用的兩個超類URLPathVersioning,QueryParameterVersioning其實也就是,重寫了determine_version,和reverse兩個方法。

4、總結

版本的獲取方式有多種,在django rest framewok中也提供了一一對應的處理版本對象,能夠根據本身的須要配置,或者繼承重寫接口使用。
配置也支持全局配置,和局部配置,在全局配置的時候,須要定義默認的版本號,以防萬一。
在進行url反向解析的時候django rest framewok提供了一個更好的方式。

相關文章
相關標籤/搜索