Require a present and correct csrfmiddlewaretoken for POST requests that have a CSRF cookie, and set an outgoing CSRF cookie.html
This middleware should be used in conjunction with the {% csrf_token %}
template tag.django
django 的中間件是基於 post 的請求,當post 請求到來的時候 在csrftoken 中間件中執行 process_view 驗證 token的正確性,若是驗證失敗,則返回forbidden 提示給用戶。編程
其次咱們能夠在咱們不須要驗證csrftoken的視圖view中加裝飾器來使咱們的視圖函數免予csrftoken的校驗,使用以下:json
from django.shortcuts import render, HttpResponse from django.views import View from django.views.decorators.csrf import csrf_exempt, csrf_protect # csrf_token 單獨示例 from django.utils.decorators import method_decorator # 基於函數的使用 @csrf_exempt def index(request): # what you want you view do in this return HttpResponse("index") # 基於類的方式 2-1 @method_decorator(csrf_exempt, name="dispatch") class StudentView(View): #方式 2-1 @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): # do something before diaspatch ret = super(StudentView, self).dispatch(request, *args, **kwargs) #do somethig after dispatch return ret def get(self, request, *args, **kwargs): return HttpResponse("GET") def post(self, request, *args, **kwargs): return HttpResponse("POST")
django 中cbv模式中,已類來接受一個請求的到來,經過類繼承 view 類
經過父類的dispatch 方法經過反射 進行請求方法的分發。咱們也能夠在繼承父類的dispatch 方法 來自定製本身的一些操做,rest-framework 也是基於這個實現的,所以咱們先看看下他的源碼部分:api
首先是url中的配置:跨域
urlpatterns = [ re_path('^student/$', views.StudentView.as_view()), re_path('^dog/$', views.DogView.as_view()), ]
當請求到來的時候,執行as_view() 方法讓咱們來看下 as_view()作了啥:安全
def as_view(cls, **initkwargs): """Main entry point for a request-response process.""" ....... def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) # 看這裏執行了View的dispatch 方法 view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view
再來看看 view類的dispatch 方法:注意可定製的操做前面已經給出服務器
def dispatch(self, request, *args, **kwargs): # 這裏的self. http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] if request.method.lower() in self.http_method_names: # 經過反射找到對應的方法 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
首先 rest_framework 是爲了遵循RESTful 規範而誕生的框架;
那咱們首先須要知道什麼是RESTful 規範restful
REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」cookie
REST從資源的角度類審視整個網絡,它將分佈在網絡中某個節點的資源經過URL進行標識,客戶端應用經過URL來獲取資源的表徵,得到這些表徵導致這些應用轉變狀態
REST與技術無關,表明的是一種軟件架構風格,REST是Representational State Transfer的簡稱,中文翻譯爲「表徵狀態轉移」
全部的數據,不過是經過網絡獲取的仍是操做(增刪改查)的數據,都是資源,將一切數據視爲資源是REST區別與其餘架構風格的最本質屬性
對於REST這種面向資源的架構風格,有人提出一種全新的結構理念,即:面向資源架構(ROA:Resource Oriented Architecture)
接下來讓咱們瞭解下他的API設計規範
一、在url接口中推薦使用Https協議,讓網絡接口更加安全(Https是Http的安全版,即HTTP下加入 SSL層,HTTPS的安全基礎是SSL,所以加密的詳細內容就須要SSL(安全套接層協議))
二、url中能夠體現這是個API接口 域名規範
https://api.example.com 儘可能將API部署在專用域名(會存在跨域問題)
https://example.org/api/ API很簡單
三、url中還能夠體現版本號,不一樣的版本能夠有不一樣的接口,使其更加簡潔,清晰
URL,如:https://api.example.com/v1/
請求頭跨域時, 引起發送屢次請求
四、restful 提倡面向資源編程,因此在url接口中儘可能要使用名詞,不要使用動詞
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees
五、此外url中還能夠添加條件去篩選匹配
過濾,經過在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:指定篩選條件
六、能夠根據Http不一樣的method,進行不一樣的資源操做(5種方法:GET / POST / PUT / DELETE
/ PATCH)
GET :從服務器取出資源(一項或多項)
POST :在服務器新建一個資源
PUT :在服務器更新資源(客戶端提供改變後的完整資源)
PATCH :在服務器更新資源(客戶端提供改變的屬性)
DELETE :從服務器刪除資源
200 OK - [GET]:服務器成功返回用戶請求的數據,該操做是冪等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。 202 Accepted - [*]:表示一個請求已經進入後臺排隊(異步任務) 204 NO CONTENT - [DELETE]:用戶刪除數據成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操做,該操做是冪等的。 401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。 403 Forbidden - [*] 表示用戶獲得受權(與401錯誤相對),可是訪問是被禁止的。 404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操做,該操做是冪等的。 406 Not Acceptable - [GET]:用戶請求的格式不可得(好比用戶請求JSON格式,可是隻有XML格式)。 410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再獲得的。 422 Unprocesable entity - [POST/PUT/PATCH] 當建立一個對象時,發生一個驗證錯誤。 500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將沒法判斷髮出的請求是否成功。 更多看這裏:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
1.首先咱們的視圖須要時候基於cbv的模式,咱們的類須要繼承rest_framework的view的 APIView 父類
以下:
class DogView(APIView): def get(self, request, *args, **kwargs): return HttpResponse("GET") def post(self, request, *args, **kwargs): return HttpResponse("POST")
接下來讓咱們研究下APIView的dispath 方法:
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs # 對原生的request 加工(豐富) """" Request( request,parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context) 原生的request = request._request """ # 首先是對原生的request進行了加個處理 request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate?
咱們來看下 self.initialize_request 這個方法實現方法
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) # self.get_authenticators() seteings配置的一個列表 # authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 對原生的request第二次加工 return Request( request, # 封裝瞭解析器類 parsers=self.get_parsers(), # 封裝了用戶用戶認證的類 authenticators=self.get_authenticators(), # url後綴名的顯示方式 ex--> .json negotiator=self.get_content_negotiator(), parser_context=parser_context )
在討論源碼以前我先rest_framework配置放在這裏(後面須要修改的會加上去):
# 能夠看這裏 from rest_framework import settings # List of settings that may be in string import notation. IMPORT_STRINGS = ( 'DEFAULT_RENDERER_CLASSES', 'DEFAULT_PARSER_CLASSES', 'DEFAULT_AUTHENTICATION_CLASSES', 'DEFAULT_PERMISSION_CLASSES', 'DEFAULT_THROTTLE_CLASSES', 'DEFAULT_CONTENT_NEGOTIATION_CLASS', 'DEFAULT_METADATA_CLASS', 'DEFAULT_VERSIONING_CLASS', 'DEFAULT_PAGINATION_CLASS', 'DEFAULT_FILTER_BACKENDS', 'DEFAULT_SCHEMA_CLASS', 'EXCEPTION_HANDLER', 'TEST_REQUEST_RENDERER_CLASSES', 'UNAUTHENTICATED_USER', 'UNAUTHENTICATED_TOKEN', 'VIEW_NAME_FUNCTION', 'VIEW_DESCRIPTION_FUNCTION' )
封住完request以後接下來咱們看看後面的源碼
# 進行一系列的認證 #初始化進入驗證,下面來看看initial 這個函數作了啥 self.initial(request, *args, **kwargs) #-------------------------------------------------- def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) ....... ## 版本的控制的 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # 認證 self.perform_authentication(request) # 權限驗證 self.check_permissions(request) # 訪問頻率 self.check_throttles(request)
接下來討論 認證方法
self.perform_authentication(request)源碼:
View 類 def perform_authentication(self, request): """ Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """ request.user Request 類 def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ # 遍歷 setting defaults 的列表 的 """ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication' """ # 執行每一個self.authenticators類中的認證類方法: for authenticator in self.authenticators: try: # 返回值有三種 """ 1.返回user 與 token 對象(認證成功) 2.返回None(不作處理,交個下一個) 3.拋出認證異常(認證失敗) """ user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()
認證類的寫法:
# Lower 寫法 class MyBasicAuthentication(object): def authenticate(self, request): token = request._request.GET.get("token") if token: return ("alex",None) raise exceptions.AuthenticationFailed("failed") def authenticate_header(self, val): pass """咱們能夠繼承他的類""" # 基於用戶token的認證 from rest_framework.authentication import BasicAuthentication 默認的幾種用戶認證類 本身寫的能夠繼承BasicAuthentication 1.一中是本身定義類而後在view視圖中這要表達: authentication_classes=[ yourauthclass, ] 2.或者全局配置 REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":[.......] }
接下來是用戶權限的控制 這個比較簡單直接上rest的自帶的幾個權限驗證的源碼類本身研究:
from rest_framework.permissions import BasePermission class BasePermission(object): """ A base class from which all permission classes should inherit. """ def has_permission(self, request, view): """ Return `True` if permission is granted, `False` otherwise. """ return True # 這個力度更加細緻對於每一個用戶的管理,後面會將 def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return True