WebService就是一個應用程序向外界暴露出一個能經過Web進行調用的API,也就是說能用編程的方法經過 Web 來調用這個應用程序。咱們把調用這個WebService的應用程序叫作客戶端,而把提供這個WebService的應用程序叫作服務端python
RPC 全稱 Remote Procedure Call
—— 遠程過程調用。在學校學編程,咱們寫一個函數都是在本地調用就好了。可是在互聯網公司,服務都是部署在不一樣服務器上的分佈式系統,如何調用呢? RPC 技術簡單說就是爲了解決遠程調用服務的一種技術,使得調用者像調用本地服務同樣方便透明。 下圖是客戶端調用遠端服務的過程:web
client
發起服務調用請求。client stub
能夠理解成一個代理,會將調用方法、參數按照必定格式進行封裝,經過服務提供的地址,發起網絡請求。server stub
接受來自socket
的消息 。server stub
將消息進行解包、告訴服務端調用的哪一個服務,參數是什麼 。server stub
。sever stub
把結果進行打包交給socket
。socket
經過網絡傳輸消息 。client slub
從socket
拿到消息。client stub
解 包消息將結果返回給client
。一個RPC框架就是把步驟2到9都封裝起來。一種軟件架構風格、設計風格,而不是標準,只是提供了一組設計原則和約束條件,規定如何編寫以及如何設置返回值、狀態碼等信息數據庫
# -–-–API與用戶的通訊協議: https -–-–-– # -–-–-–-–-– 域名 -–-–-–-–-–-–-–-–-–-– # 應該儘可能將API部署在專用域名之下。 https://api.example.com #若是肯定API很簡單,不會有進一步擴展,能夠考慮放在主域名下 https://example.org/api #----------- 版 本 ------------------ # 應該將API的版本號放入URL。 https://api.example.com/v1/ #另外一種作法是,將版本號放在HTTP頭信息中,但不如放入URL方便和直觀 # ------------ 路徑 ------------------ #視網絡上任何東西都是資源,均使用名詞表示(可複數) https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees # --------- method ------------------- GET :從服務器取出資源(一項或多項) POST:在服務器新建一個資源 PUT:在服務器更新資源(客戶端提供改變後的完整資源) PATCH:在服務器更新資源(客戶端提供改變的屬性) DELETE :從服務器刪除資源 # ---------- 過濾 ---------------------- # 經過在url上傳參的形式傳遞搜索條件 https://api.example.com/v1/zoos?limit=10:指定返回記錄的數量 https://api.example.com/v1/zoos?offset=10:指定返回記錄的開始位置 https://api.example.com/v1/zoos?page=2&per_page=100:指定第幾頁,以及每頁的記錄 https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回結果按照哪一個屬性排序,以及排序順序 https://api.example.com/v1/zoos?animal_type_id=1:指定篩選條 #---------- 狀態碼 ------------ 200 OK:客戶端請求成功,通常用於GET和POST請求 400 Bad Request:客戶端請求有語法錯誤,不能被服務器所理解。 301 Moved Permanently:永久移動,請求的資源已被永久移動到新url,返回信息會包含新的url,瀏覽器會自動 定向到新url 401 Unauthorized:請求未經受權,這個狀態代碼必須和WWW-Authenticate報頭域一塊兒使用。 403 Forbidden:服務器收到請求,可是拒絕提供服務。 404 Not Found:請求資源不存在,舉個例子:輸入了錯誤的URL。 500 Internal Server Error:服務器發生不可預期的錯誤。 502 Bad Gateway: 充當網關或代理的服務器,從遠端接收到一個無效的請求 503 Server Unavailable:服務器當前不能處理客戶端的請求,一段時間後可能恢復正常 # ------錯誤處理 --------------- 狀態碼是4xx時,應返回錯誤信息,error當作key。 { error: "Invalid API key" } #------ 返回結果 -------------- GET /collection:返回資源對象的列表(數組) GET /collection/resource:返回單個資源對象 POST /collection:返回新生成的資源對象 PUT /collection/resource:返回完整的資源對象 PATCH /collection/resource:返回完整的資源對象 DELETE /collection/resource:返回一個空文檔
一個接口經過1次相同的訪問,再對該接口進行N次相同的訪問時,對資源不造影響就認爲接口具備冪等性 GET, # 第一次獲取結果、第二次也是獲取結果對資源都不會形成影響,冪等。 POST, # 第一次新增數據,第二次也會再次新增,非冪等。 PUT, # 第一次更新數據,第二次不會再次更新,冪等。 PATCH,# 第一次更新數據,第二次不會再次更新,非冪等。 DELTE,# 第一次刪除數據,第二次不在再刪除,冪等。
# 在編寫接口時能夠不使用django rest framework框架, # 不使用:也能夠作,能夠用django的CBV來實現,開發者編寫的代碼會更多一些。 # 使用:內部幫助咱們提供了不少方便的組件,咱們經過配置就能夠完成相應操做 如:'序列化'能夠作用戶請求數據校驗 + queryset對象的序列化稱爲json '解析器'獲取用戶請求數據request.data,會自動根據content-type請求頭的不能對數據進行解析 '分頁'將從數據庫獲取到的數據在頁面進行分頁顯示。 # 還有其餘組件: '認證'、'權限'、'訪問頻率控制
序列化、視圖、認證、權限、限制django
分頁、版本控制、過濾器、解析器、渲染器編程
# ---- 繼承APIView -------- # APIView繼承於View ,可是重寫了父類View中的dispatch(),將get、post、put的數據放入request。data中, 將請求的參數放入request.query_params
# ----繼承GenericAPIView---- # 每個接口都是生成一個序列化對象,實例化,調用data方法,對其進行封裝 class GenericAPIView(views.APIView): queryset = None serializer_class = None # python mixin(混合類):不能單獨使用,和其它類搭配起來使用(利用了Python支持多繼承) class PublisherList(GenericView,ListMixin,CreateMixin): queryset = models.Publisher.objects.all() serializer_class = PublisherSerializer # -----繼承GenericViewSet ---------- # GenericViewSet(ViewSetMixin, generics.GenericAPIView): # ViewSetMixin重寫了as.view()方法,實現了根據請求的方法執行具體的類方法 # 路由註冊的時候,利用actions參數,實現路由的定向分發 而不是簡單的 反射 url(r'authors/$', views.AuthorViewSet.as_view(actions={ 'get': 'list', 'post': 'create' })), # 做者列表 url(r'authors/(?P<pk>\d+)/$', views.AuthorViewSet.as_view( actions={ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })), # -----繼承ModelViewSet ------ class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet) #將不一樣的請求鏈接到不一樣的方法
序列化過程:ORM對象-->JSON格式數據 反序列化: JSON格式數據-->ORM對象
serializers.serializerjson
序列化過程: # (get請求)定義一個serializer類,繼承serializers.Serializer,定義字段告訴REST框架,哪些字段(field),須要被序列化/反序列化 # 使用serializer類,將查詢結果集QuerySet傳入,並標明 many=True,表示序列化多個 獲得序列化的結果對象ser_obj,ser_obj.data即爲獲得的json格式的數據 反序列化過程: 1.如果post請求提交數據 request.data即爲提交的json格式的數據, 使用serializer類對 數據進行反序列化 -->ser_obj對象 對ser_obj對象進行is_vaild()校驗,此處能夠自定義校驗規則,具體參照上述表格 ser_obj.save()須要重寫serializer.create方法 2.如果put請求: 根據pk去查詢具體的那本書籍對象obj 獲取用戶發送過來的數據而且更新對象, 賦值給instance的obj對象, partial=True的意識容許作局部更新 ser_obj = Serializer(instance=obj, data=request.data, partial=True) 對ser_obj對象進行is_vaild()校驗,此處能夠自定義校驗規則,具體參照上述表格 ser_obj.save() 須要重寫serializer.update方法
serializers.ModelSerializerapi
#序列化過程 - 不用定義字段 class Meta: model = models.Book fields = "__all__" # depth = 1 # 全部有關係的字段都變成 read_only # exclude = [] # 排除某個字段 extra_kwargs = { # 每一個字段的一些額外參數 'publisher': {'write_only': True}, 'authors': {'write_only': True}, 'category': {'write_only': True}, } #反序列化的過程: #.save() 直接一鍵更新或建立,已經封裝了這兩個方法 #另外:SerializerMethodField 會自動去找 get_字段名 的方法執行 class BookModelSerializer(serializers.ModelSerializer): # SerializerMethodField 會自動去找 get_字段名 的方法執行 category_info = serializers.SerializerMethodField(read_only=True) publisher_info = serializers.SerializerMethodField(read_only=True) authors_info = serializers.SerializerMethodField(read_only=True) def get_category_info(self, book_obj): return book_obj.get_category_display() def get_publisher_info(self, book_obj): return PublisherSerializer(book_obj.publisher).data def get_authors_info(self, book_obj): return AuthorSerializer(book_obj.authors.all(), many=True).data class Meta: model = models.Book fields = "__all__" # depth = 1 # 全部有關係的字段都變成 read_only # exclude = [] # 排除某個字段 extra_kwargs = { # 每一個字段的一些額外參數 'publisher': {'write_only': True}, 'authors': {'write_only': True}, 'category': {'write_only': True}, }
# 1.認證、權限和限制是在執行請求以前作的,路由-->as.view()-->APIView中的dispatch()方法中的 initial 方法 self.initial(request, *args, **kwargs) # 2.在initial函數中 # Ensure that the incoming request is permitted self.perform_authentication(request) # 認證 self.check_permissions(request) # 權限 self.check_throttles(request) # 限制 # 3.執行perform_authentication方法,其中request.user是一個@property的函數 # 4.在user方法中經過 self._authenticate()函數,去執行_authenticate()方法,將當前請求的用戶交給定義的在 authentication_classes=[]中的類的 authenticate進行身份驗證 # 5.實現 BaseAuthentication中的authenticate方法,返回元組,元組的第一個元素賦值給 request.user 第二個元素複製給了request.auth(token) # 6.如果authenticate方法 拋錯後,捕獲到報錯(raise),此時執行的 _not_authenticated 方法的,return 結果:user &auth 都賦值爲None
# 1.認證、權限和限制是在執行請求以前作的,路由-->as.view()-->APIView中的dispatch()方法中的initial方法 self.initial(request, *args, **kwargs) # 2.在initial函數中 # Ensure that the incoming request is permitted self.perform_authentication(request) # 認證 self.check_permissions(request) # 權限 self.check_throttles(request) # 限制 # 3.執行 check_permissions方法,從當前 permission_classes 列表中,執行 has_permission()方法,判斷有沒有權限 # 4.實現BasePermission中的has_permission()方法 class MyPermission(BasePermission): message = '只有VIP才能訪問' def has_permission(self, request, view): #經過上面的認證源碼得知:當不輸入token參數或者未登陸,則 user ,auth 均爲None,當auth存在則此時 的user不爲None if not request.auth: return False #當有Vip纔有權限訪問 #if request.user當前通過認證的用戶對象 if request.user.vip: return True else: #若是不是Vip就拒絕的範圍 return False # 5.若是存在驗證不經過,那麼就執行self.permission_denied,而後這個異常在dispatch函數中被捕捉,當作結果傳遞給response
# 1.如果未註冊用戶,因此不可能通過認證,則此時user,auth 均爲None # 2.自定義allow_request方法 #拿到當前的請求的ip做爲訪問記錄的key #把當前的請求的訪問記錄拿出來保存到一個變量中 #循環訪問歷史,把超過10 秒鐘的請求事件去掉 #在視圖或者全局中進行配置
throttle.py數組
# 使用內置限制類 from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = "xxx" def get_cache_key(self, request, view): return self.get_ident(request)
settings.py瀏覽器
# 在settings文件中進行配置 "DEFAULT_THROTTLE_CLASSES": {["BAR.XXX.VisitThrottle", ], "DEFAULT_THROTTLE_RATES": { "xxx": "1/s", }