DRF之版本控制、認證和許可權元件
阿新 • • 發佈:2018-12-13
一、版本控制組件
1、為什麼要使用版本控制
首先我們開發專案是有多個版本的
當我們專案越來越更新,版本就越來越多,我們不可能新的版本出了,以前舊的版本就不進行維護了
像bootstrap有2、3、4版本的,每個版本都有它對應的url,https://v2.bootcss.com/ 、 https://v3.bootcss.com/
這就需要我們對版本進行控制,這個DRF也給我們提供了一些封裝好的版本控制方法
2、原理
1.
在DRF框架中,它預設幫我們設定了版本資訊在request.version和request.versioning_scheme中,
只是DRF預設的版本資訊是None,我們需要重寫一些配置,讓request.version攜帶上我自定義的版本資訊
request.versioning_scheme是實現版本控制的類的例項化物件
2.
CBV檢視函式都是首先會去執行as_view方法的,as_view會找到路由的分發方法dispatch,在真正分發之前會執行initial方法
3、使用
0. 在哪裡取版本資訊 我們的版本類必須重寫determine_version這個方法,request.version就是拿它的返回值 在檢視中就可以使用request.version取到版本資訊 1. 新建一個py檔案 我建在utils資料夾下的version.py 在version.py下重寫一個MyVersion類 注意:必須重寫determine_version這個方法,因為request.version就是拿它的返回值class MyVersion(object): def determine_version(self,request, *args, **kwargs): version = request.query_params.get("version", "v1") return version 2. 去專案的settings配置 # 覆蓋DRF原本的配置DEFAULT_VERSIONING_CLASS,讓它的值指向我們寫的那個類 REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS': 'utils.version.MyVersion' } 3. 在檢視中使用 class VersionView(APIView): def get(self, request): print(request.version) print(request.versioning_scheme) if request.version == 'v1': return Response("v1版本") elif request.version == 'v2': return Response('v2版本') return Response('不存在的版本')
4、DRF的versioning模組
1. 在這裡模組內給我們配置了各種獲取版本的類 2. versioning模組
# 基礎類 class BaseVersioning(object): default_version = api_settings.DEFAULT_VERSION allowed_versions = api_settings.ALLOWED_VERSIONS version_param = api_settings.VERSION_PARAM def determine_version(self, request, *args, **kwargs): msg = '{cls}.determine_version() must be implemented.' raise NotImplementedError(msg.format( cls=self.__class__.__name__ )) def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): return _reverse(viewname, args, kwargs, request, format, **extra) def is_allowed_version(self, version): if not self.allowed_versions: return True return ((version is not None and version == self.default_version) or (version in self.allowed_versions)) # 在請求頭中攜帶版本資訊 class AcceptHeaderVersioning(BaseVersioning): """ GET /something/ HTTP/1.1 Host: example.com Accept: application/json; version=1.0 """ # 在URL中攜帶版本資訊 class URLPathVersioning(BaseVersioning): ''' urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'), url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail') ] ''' # 在namespace中攜帶版本資訊 class NamespaceVersioning(BaseVersioning): ''' # users/urls.py urlpatterns = [ url(r'^/users/$', users_list, name='users-list'), url(r'^/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail') ] # urls.py urlpatterns = [ url(r'^v1/', include('users.urls', namespace='v1')), url(r'^v2/', include('users.urls', namespace='v2')) ] ''' # 在域名中攜帶版本資訊 class HostNameVersioning(BaseVersioning): """ GET /something/ HTTP/1.1 Host: v1.example.com Accept: application/json """ # 在引數中攜帶版本資訊 class QueryParameterVersioning(BaseVersioning): """ GET /something/?version=0.1 HTTP/1.1 Host: example.com Accept: application/json """version模組的類
3. 基礎類的三個配置 REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS': 'utils.version.MyVersion', # 預設版本 'default_version': 'v1', # 可用版本 'allowed_versions': ['v1', 'v2'], # 引數 'version_param': 'version', }
4. 使用DRF的版本控制 方法一 1. 在utils資料夾下的version.py繼承versioning的類 from rest_framework import versioning class MyVersion(versioning.URLPathVersioning): pass 2. urls.py urlpatterns = [ # url(r'^version_demo/', views.VersionView.as_view()), url(r'^(?P<version>[v1|v2]+)/version_demo/', views.VersionView.as_view()), ] 3. settings.py REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS': 'utils.version.MyVersion', # 預設版本 'default_version': 'v1', # 可用版本 'allowed_versions': ['v1', 'v2'], # 引數 'version_param': 'version', } 4.在檢視中使用需要接收引數version class VersionView(APIView): def get(self, request, version): print(request.version) print(request.versioning_scheme) if request.version == 'v1': return Response("v1版本") elif request.version == 'v2': return Response('v2版本') return Response('不存在的版本') 5. 在瀏覽器中 可以輸入: v1/version_demo/ v2/version_demo/ 方法二 其他步驟基本一樣,只是我們在配置的時候直接指定某個類 1. urls.py urlpatterns = [ # url(r'^version_demo/', views.VersionView.as_view()), url(r'^(?P<version>[v1|v2]+)/version_demo/', views.VersionView.as_view()), ] 2. settings.py REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', # 指定URLPathVersioning這個類 # 預設版本 'default_version': 'v1', # 可用版本 'allowed_versions': ['v1', 'v2'], # 引數 'version_param': 'version', }
二、認證
1、為什麼要認證
我們可以在網站上登入,然後可以有個人中心,對自己資訊就行修改
但是我們每次給伺服器發請求,由於Http的無狀態,導致我們每次都是新的請求
那麼服務端需要對每次來的請求進行認證,看使用者是否登入,以及登入使用者是誰
那麼我們伺服器對每個請求進行認證的時候,不可能在每個檢視函式中都寫認證
一定是把認證邏輯抽離出來,以前我們可能會加裝飾器,或者中介軟體,DRF框架也有它的認證
2、原理
在前端進行登入的時候,使用者名稱和密碼輸入正確後,後端根據使用者名稱和密碼拿到這個使用者的一些資訊(使用者名稱,性別等),
把這些資訊加密後生成一個隨機字串token返回給前端,
前端下次請求再來的時候,帶上這個隨機字串,後端根據自己的解密演算法再算出來,實現認證。
3、token認證
0. 在哪裡取認證的資訊 我們的認證類必須重寫authenticate這個方法,返回值是元組 第一個值賦值給了request.user, 第二個賦值給request.auth 在檢視中就可以使用request.user和request.auth取到認證的資訊 1. models.py class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) # 用UUID字串存隨機字串 token = models.UUIDField(null=True, blank=True) CHOICES = ((1, "vip"), (2, "普通使用者"), (3, "vvip")) 2. settings配置 # 跟版本一樣,覆蓋原本的配置,使用自己的配置 REST_FRAMEWORK = { # 配置認證類 # 這樣配是全域性,所有路由都要認證 'DEFAULT_AUTHENTICATION_CLASSES':['utils.auth.MyAuth', ] } 3. utils/auth.py from rest_framework import authentication from AuthDemo.models import User from rest_framework.exceptions import AuthenticationFailed class MyAuth(authentication.BaseAuthentication): # 必須重寫authenticate這個方法,返回值是元組 # 第一個值賦值給了request.user, 第二個賦值給request.auth def authenticate(self, request): # 獲取前端攜帶的token,看token是否合法 token = request.query_params.get("token", "") if not token: raise AuthenticationFailed("沒有攜帶token") user_obj = User.objects.filter(token=token).first() # 返回需要的資訊 if user_obj: return (user_obj, token) # 驗證不通過,丟擲異常 raise AuthenticationFailed("token不合法") 4. 檢視 class TestView(APIView): def get(self, request): print(request.user) print(request.auth) return Response("登陸後傳送的資料") 5. 區域性配置 1. 如果只是某些路由需要認證,那麼可以區域性設定認證,包括版本也可以區域性認證 2. 把settings的配置註釋掉 3. 在需要認證的檢視中宣告 from utils.auth import MyAuth class TestView(APIView): authentication_classes = [MyAuth, ] # 區域性配置認證元件 # versioning_class = [] # 區域性配置版本元件 def get(self, request): print(request.user) print(request.auth) return Response("登陸後傳送的資料")