目錄
一 什麼是restful架構
二 Django REST framework簡介
三 Django REST framework原理
四 Django REST framework源碼流程
五 Django REST framework實現用戶登陸json
一 什麼是restful架構
一、起源
REST即表述性狀態傳遞(英文:Representational State Transfer,簡稱REST)是Roy Fielding博士在2000年他的博士論文中提出來的一種軟件架構風格。它是一種針對網絡應用的設計和開發方式,能夠下降開發的複雜性,提升系統的可伸縮性。
目前在三種主流的Web服務實現方案中,由於REST模式的Web服務與複雜的SOAP和XML-RPC對比來說明顯的更加簡潔,愈來愈多的web服務開始採用REST風格設計和實現。例如,Amazon.com提供接近REST風格的Web服務進行圖書查找;雅虎提供的Web服務也是REST風格的
二、框架組成
根據rest的論文,咱們大體能夠把restful架構分紅如下幾個部分
1. 資源
資源就是網絡中的具體信息。好比說招聘信息、美女圖片、NBA賽事、股票信息、歌曲等等。每一種具體資源均可以用一個URI(統一資源定位符)指向它。若是想要獲取這些資源,則直接訪問它的URI就能夠。
有不少公共的URI,例如阿里雲的api市場就提供了不少種的api,每種api代替了具體的資源,每種資源你均可以經過訪問api獲取到。
例如訪問下面圖片中的api地址,就能夠獲取到滬深港股票歷史行情數據
2. 表現方式
在資源裏咱們提到了阿里雲提供了不少api,api獲取了是一些靜態資源,而表現方式就會爲了處理獲取到的這些資源以什麼樣的形式展現給訪問者。
如今經常使用的方式有好比,txt格式、HTML格式、XML格式、JSON格式表現,甚至能夠採用二進制格式;圖片能夠用JPG格式表現,也能夠用PNG格式表現。
從上圖能夠看到,api的表現形式(也就是返回數據格式)爲json格式。
3. 狀態
狀態定義了作資源的操做方式。這些操做方式所有定義在http協議裏面,而再也不api上表現。客戶端經過四個HTTP動詞,對服務器端資源進行操做
具體操做:
- GET(SELECT):從服務器取出資源(一項或多項)。
- POST(CREATE):在服務器新建一個資源。
- PUT(UPDATE):在服務器更新資源(客戶端提供完整資源數據)。
- PATCH(UPDATE):在服務器更新資源(客戶端提供須要修改的資源數據)。
- DELETE(DELETE):從服務器刪除資源。
三、認證機制
咱們知道,客戶端經過api能夠訪問到資源,但咱們還須要對訪問者進行驗證,以用來判斷該用戶的訪問權限。
經常使用的認證機制包括 session auth(即經過用戶名密碼登陸),basic auth,token auth和OAuth,服務開發中經常使用的認證機制爲後三者。
Basic Auth
Basic Auth是配合RESTful API 使用的最簡單的認證方式,只需提供用戶名密碼便可,但因爲有把用戶名密碼暴露給第三方客戶端的風險,在生產環境下被使用的愈來愈少。
Token Auth
Token Auth並不經常使用,它與Basic Auth的區別是,不將用戶名和密碼發送給服務器作用戶認證,而是向服務器發送一個事先在服務器端生成的token來作認證。所以Token Auth要求服務器端要具有一套完整的Token建立和管理機制,該機制的實現會增長大量且非必須的服務器端開發工做,也不見得這套機制足夠安全和通用,所以Token Auth用的並很少。
1. OAuth介紹
OAuth(開放受權)是一個開放的受權標準,容許用戶讓第三方應用訪問該用戶在某一web服務上存儲的私密的資源(如照片,視頻,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。
OAuth容許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每個令牌受權一個特定的第三方系統(例如,視頻編輯網站)在特定的時段(例如,接下來的2小時內)內訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth讓用戶能夠受權第三方網站訪問他們存儲在另外服務提供者的某些特定信息,而非全部內容。
正是因爲OAUTH的嚴謹性和安全性,如今OAUTH已成爲RESTful架構風格中最經常使用的認證機制,和RESTful架構風格一塊兒,成爲企業級服務的標配。
目前OAuth已經從OAuth1.0發展到OAuth2.0,但這兩者並不是平滑過渡升級,OAuth2.0在保證安全性的前提下大大減小了客戶端開發的複雜性。
二 Django REST framework簡介
Django REST framework是一個基於Django實現的一個restful框架,一個十分強大切靈活的工具包,用以構建Web APIs。
Djando REST framework的優勢:
- 在線可視的API
- 驗證策略涵蓋了OAuth1和OAuth2
- 同時支持ORM和非ORM數據源的序列化
- 支持基於類的視圖
安裝與部署環境
使用pip安裝框架以及全部依賴包
pip install djangorestframework pip install markdown # 能夠更好的支持可瀏覽的API pip install django-filter # 支持過濾
還可使用clone安裝git clone git@github.com:encode/django-rest-framework.git
安裝完以後須要在項目的配置文件的app裏面註冊rest_framework,由於有些地方會用到rest_framework這個app裏面的數據,例如返回數據的模板
INSTALLED_APPS= (
... 'rest_framework', )
三 Django REST framework簡單流程
首先咱們看一個經過Django REST framework實現的一個api的簡單實例
# views.py 首先寫視圖函數 from rest_framework.views import APIView from rest_framework.response import Response class TestView(APIView): # CBV模式的視圖函數 def get(self, request, *args, **kwargs): # 定義get方法 # 在django-rest-framework中的request被從新封裝了,後續分析源碼的時候會有具體體現 return Response('測試api') # rest-framework的模板對數據進行渲染 # urls.py 定義路由 from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^user/', views.TestView.as_view()) ]
啓動項目,訪問api:
原理分析:
- rest_framework重寫了一個APIView的類,這個類是以前django中的view類的派生類,在寫CBV的視圖函數是,讓視圖函數繼承了rest-framework這個APIView類
- 在視圖函數中寫具體方法,例如get,port,put,delete,可使用rest_framework的Response對返回數據進行渲染
- 在url的寫法和Django是同樣的
- 經過對http://127.0.0.1:8000/user/ 的訪問,能夠獲得如圖顯示的數據
四 Django REST framework源碼流程
在第三節咱們分析了一個簡單的api實現過程,如今咱們主要去分析rest_framework內部對這個url的具體實現過程。
- 首先咱們訪問http://127.0.0.1:8000/user/ 根據urls.py中的配置,執行views.TestView.as_view()函數
-
as_view方法是被定義在rest_framework/views.py裏面的一個靜態方法,因此能夠經過類名直接調用。
-
父類的as_view方法是定義在django/views/generic/base.py裏面的View類中的方法。在這個方法中最終會執行cls.dispatch,在第一步中咱們知道cls是<class 'app01.views.TestView'>
-
dispatch是定義在TestView繼承的父類APIView(rest_framework/views.py)裏面的方法。在這個方法裏面,首先經過
request = self.initialize_request(request, *args, **kwargs)
這條語句從新封裝了request對象,這兩行代碼,重中之重,實現了對request方法的封裝和重寫。爲何要重寫request呢?由於在Django中,信息的解析與封裝是經過wsgiref這個模塊實現的,這個模塊有一個缺陷。封裝的request.POST方法不支持json格式的字符串,也就是說這個模塊中沒有提供json格式的解析器。而先後端的分離,就須要一種先後端都支持的數據格式,那就是json,而這剛好是wsgiref所沒法實現的,要實現先後端分離,必需要對request方法重寫。
rest_framework是如何實現request的重寫的。self.initialize_request(request, *args, **kwargs)裏面都實現了那些東西?
-
initialize_request是APIView類裏面的一個方法,從新封裝了request對象,增長了一些屬性信息這個方法return了一個類Request的實例對象,這個Request類傳入舊的request,對舊的equest進行了一系列的加工,返回新的request對象
- 這個方法return了一個類Request的實例對象,這個Request類傳入舊的request,對舊的equest進行了一系列的加工,返回新的request對象
-
初始化的過程當中,咱們能夠獲得一些對咱們有用的信息,咱們能夠經過這個類Request的實例對象,也就是新的request,經過
request._request 就能夠獲得咱們舊的request。
因此,咱們能夠經過這個新的request.query_params 獲得舊的request.GET的值(這個方法沒啥卵用),最關鍵的是request.data這個方式,能夠獲得序列化後的數據,這個方法補全了舊request的不足。
備註:在request.data中,提供了不少數據類型的解析器,包括json的,因此,對於提交的數據,咱們能夠直接經過這個方法獲取到。而不須要經過request.POST
-
認證信息。主要經過APIView類中的get_authenticators(rest_framework/views.py)方法獲取,這個方法會返回一個全部認證對象的列表
在全局定義的authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
-
默認的認證配置信息是在rest_framework/settings.py文件中定義的
-
在rest_framework/authentication.py中定義了幾種認證類型,通常狀況咱們須要自定義認證類,也可使用django-oauth-toolkit組件進行認證。
- dispatch中的initialize_request方法執行完成以後,還有執行一個重要方法是self.initial(request, *args, **kwargs),這個方法也是APIView類裏的。在這個方法裏面初始化
被從新封裝的request對象
實現功能:- 版本處理
- 用戶認證
- 權限
- 訪問頻率限制
-
執行APIView裏面的perform_authentication方法,該方法返回request.user,則會調用<rest_framework.request.Request object at 0x10e80deb8>裏面的user方法。在user方法裏面最終調用了Request類裏面的_authenticate方法
-
執行rest_framework.request.Request類中的_authenticate方法,這個方法會遍歷認證類,並根據認證結果給self.user, self.auth賦值。因爲user,和auth都有property屬性,
因此給賦值的時候先在先執行setter方法 -
dispatch中的initial方法執行完以後,會繼續判斷request.method並執行method相應的method.
目錄
限流實現流程
一 什麼是throttle
二 Django REST framework是如何實現throttle的
三 Django REST framework中throttle源碼流程一 什麼是throttle
節流也相似於權限,它用來決定一個請求是否被受權。節流表示着一種臨時的狀態,經常用來控制客戶端對一個
API的請求速率。例如,你能夠經過限流來限制一個用戶在每分鐘對一個API的最多訪問次數爲60次,天天的訪問次數爲1000次。
二 Django REST framework是如何實現throttle的
-
在Django REST framework中主要是經過throttling.py文件裏面的幾個類來實現限流功能的。
-
在整個流程上是在dispatch中的調用的initial方法中的self.check_throttles(request)調用到throttle類中的方法
- throttle策略的配置:
全局配置settings.py
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( # 定義限流類 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { # 定義限流速率,支持秒、分、時、天的限制 'anon': '100/day', 'user': '1000/day' } }
- 把限流策略設置在視圖函數上
CBV
from rest_framework.response import Response from rest_framework.throttling import UserRateThrottle from rest_framework.views import APIView class ExampleView(APIView): throttle_classes = (UserRateThrottle,) def get(self, request, format=None): content = { 'status': 'request was permitted' } return Response(content)
FBV
@api_view(['GET']) @throttle_classes([UserRateThrottle]) def example_view(request, format=None): content = { 'status': 'request was permitted' } return Response(content)
三 Django REST framework中throttle源碼流程
-
調用check_throttles方法,在這個方法中會遍歷經過self.get_throttles()獲取到的限流對象列表,默認列表裏面是空的。也就是說默認不會有限流的策略。
-
在視圖函數裏面配置參數,讓其應用上限流策略。咱們這裏以UserRateThrottle這個限流方法爲例。(配置如第二節中的settings.py配置和FBV配置),在這裏繼續第二步的操做,執行UserRateThrottle對象的allow_request方法。
因爲UserRateThrottle這個類自己沒有allow_request方法,因此在其父類SimpleRateThrottle中找這個方法. -
執行allow_request方法,會首先判斷是否認義了self.rate。根據self.rate執行,最終回去查找self.scope屬性,並且這個屬性是必須的。
-
在UserRateThrottle中查找到定義的scope="user", 接着執行self.key語句。這條語句最終調用了UserRateThrottle裏面的get_cache_key方法。
此例中咱們沒有配置authenticate,全部會執行get_cache_key裏面的get_indet方法,並最終返回了scope和ident被self.key接收(返回格式:throttle_user_127.0.0.1)。 -
返回self.key以後繼續執行self.history,self.history會返回客戶端的訪問記錄列表,並根據rate的配置去判斷是不是要pop某一條訪問記錄。並最終根據列表長度和容許的長度作對比,判斷客戶端當前是否具備訪問權限。
-
若最終check_throttles返回True,則繼續執行dispatch。dispatch以後的操做請參考以前寫的django rest framework流程。若是返回False,則會繼續執行self.throttled(request, throttle.wait())。
-
執行父類SimpleRateThrottle裏面的wait方法。這個方法主要用來向客戶端返回還須要多少時間能夠繼續訪問的提示。
組件2、serializers
開發咱們的Web API的第一件事是爲咱們的Web API提供一種將代碼片斷實例序列化和反序列化爲諸如
json
之類的表示形式的方式。咱們在使用json序列化時,有這麼幾種方式:
第一種,藉助Python內置的模塊j。手動構建一個字典,經過json.dumps(obj)獲得一個json格式的字符串,使用這種方式有必定的侷限性,那就是咱們沒法控制這張表中的字段,有多少個字段,咱們就須要添加多少個鍵值對,一旦後期表結構發生變化,就會很麻煩。
第二種方式:解決上述方式中字段的不可控性,就須要藉助Django中內置的一個方法model_to_dict,(from django.forms.models import model_to_dict),咱們能夠將取出的全部的字段循環,依次將每一個對象傳入model_to_dict中,這樣就解決了字段的問題。
還有一種方式,也是Django提供的一個序列化器:from django.core.serializers import serialize ,咱們能夠直接將queryset數據類型直接傳進去,而後指定咱們要轉化的格式便可,這兩種Django提供給咱們的方法雖然能夠解決這個序列化字段的問題,可是有一個缺點,那就是咱們能夠直接將一個數據轉化爲json字符串形式,可是卻沒法反序列化爲queryset類型的數據。
rest_framework提供了一個序列化組件---serializers,完美的幫助咱們解決了上述的問題:from rest_framework import serializers ,用法跟Django中forms組件的用法很是類似,也須要先自定製一個類,而後這個類必須繼承serializers.Serializer,而後咱們須要序列化那些字段就在這個類中配置那些字段,還能夠自定製字段的展現格式,很是的靈活。
models部分:
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
views部分:
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.Serializer): title=serializers.CharField(max_length=32) price=serializers.IntegerField() pub_date=serializers.DateField() publish=serializers.CharField(source="publish.name") #authors=serializers.CharField(source="authors.all") authors=serializers.SerializerMethodField() def get_authors(self,obj): temp=[] for author in obj.authors.all(): temp.append(author.name) return temp class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() # 序列化方式1: # from django.forms.models import model_to_dict # import json # data=[] # for obj in book_list: # data.append(model_to_dict(obj)) # print(data) # return HttpResponse("ok") # 序列化方式2: # data=serializers.serialize("json",book_list) # return HttpResponse(data) # 序列化方式3: bs=BookSerializers(book_list,many=True) return Response(bs.data)
值得注意的時,咱們使用這種方式序列化時,須要先實例化一個對象,而後在傳值時,若是值爲一個queryset對象時,須要指定一個參數many=True,若是值爲一個obj時,不須要指定many=False,默認爲False。
#咱們自定義一個類Bookserialize,繼承serializers.Serializer from framework.views import Bookserialize from framework import models book_list = models.Book.objects.all() bs= Bookserialize(book_list,many=True) bs.data [OrderedDict([('title', '神墓')]), OrderedDict([('title', '完美世界')])] # queryset對象 結果爲一個列表,裏面放着每個有序字典 book_obj = models.Book.objects.first() bs_ = Bookserialize(book_obj) bs_.data {'title': '神墓'} # 若是爲一個對象時,結果爲一個字典
特別須要注意的是:使用這種方式序列化時,對於特殊字段(一對多ForeignKey、多對多ManyToMany),serializers沒有提供對應的字段,須要指定特殊的方式,由於obj.這個字段時,獲得的是一個對象,因此咱們對於FK,須要使用一個CharField字段,而後在這個字段中指定一個source屬性,指定顯示這個對象的那個字段。一樣的,對於多對多的字段,咱們也要使用特殊的顯示方式:SerializerMethodField(),指定爲這種字段類型時,顯示的結果爲一個自定義的函數的返回值,這個自定義函數的名字必須是get_字段名,固定寫法,接收一個obj對象,返回值就是該字段在序列化時的顯示結果。
另外,咱們在取值時,直接經過這個對象.data的方式取值,這是rest_framework提供給咱們的序列化接口。
其實。咱們應該明白它內部的實現方式:若是值爲一個queryset對象,就建立一個list,循環這個queryset獲得每個數據對象,而後在循環配置類下面的每個字段,直接這個對象obj.字段 得出值,添加到一個字典中,在將這個字典添加到這個列表中。因此,對於這些特殊字段,咱們取值時,經過這種方式獲得的是一個對象。
經過這種方式就會出現一個問題,咱們每序列化一個表,就要將這個表中的字段所有寫一遍,這樣顯得很麻煩。在Djangoforms組件中,有一個ModelForm,能夠幫咱們將咱們模型表中的全部字段轉化爲forms組件中對應的字段,一樣的,在serializers中,一樣有一個,能夠幫咱們將咱們的模型類轉化爲serializers中對應的字段。這個組件就是ModelSerializer。
-