Django----djagorest-framwork源碼剖析
阿新 • • 發佈:2018-08-11
fin 所有 term () 提示 otherwise pypi associate 風格
restful(表者征狀態轉移,面向資源編程)------------------------------------------->約定 從資源的角度審視整個網絡,將分布在網絡中某個節點的資源通過url進行標識,客戶端通過url獲取資源的表征, 獲得這些表征使應用轉變狀態。-----------------------------------------------------------是一種軟件架構風格。 所有數據是通過網絡獲取的是操作的數據(增刪改查),都是資源-------------------------------互聯網上的一切東西都視為資源。 restf規則: API與用戶的通信協議,使用的是http協議 1:域名盡量部署在專有域名之下,若API很簡單,不會進一步擴展,可以考慮放在主域名下。 2:應將api的版本號放入url,還可以將版本號放入Http請求頭信息中,但不如放在url中方便。 3:在RESTful架構中,每個網址代表一種資源(resource),所以網址中應該有動詞,應該使用名詞, 而且所用的名詞往往與數據庫的表格名對應。一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數。 4:用於區別url接口應將API加入到url. 5: 如果記錄數量很多,服務器不可能都將它們返回給用戶。API應該提供參數,過濾返回結果。 6: 服務器向用戶返回的狀態碼和提示信息。 8: 如果狀態碼是4xx,就應該向用戶返回出錯信息。一般來說,返回的信息中將error作為鍵名,出錯信息作為鍵值即可。 9: 請求方式的不同,進行不同的操作。post----get----put----patch----delete 10:返回錯誤信息 restful-api: API與用戶的通行協議,總是使用HTTPs協議 api:---------------------------------------------------------------------------------------接口 用途: 1:為別人提供服務----------發送短信 2:前後端分離--------------前後端分離 規範: 1:url+api https://api.example.com------------------------------------------------------------盡量將API部署在專用域名(會存在跨域問題) https://example.org/api/-----------------------------------------------------------API很簡單 2:名詞 資源名必須是名詞,不能是動詞....... 3:版本 URL,-------------------------------------------------------------------------------如:https://api.example.com/v1/ 請求頭------------------------------------------------------------------------------跨域時,引發發送多次請求 4:提交方式------------------------------------------------------------method GET:-------------------------------------------------------------------------------從服務器取出資源(一項或多項) POST:------------------------------------------------------------------------------在服務器新建一個資源 PUT:-------------------------------------------------------------------------------在服務器更新資源(客戶端提供改變後的完整資源) PATCH :----------------------------------------------------------------------------在服務器更新資源(客戶端提供改變的屬性) DELETE:----------------------------------------------------------------------------從服務器刪除資源 5:json數據------------------------------------------------------------返回json數據 6:status--------------------------------------------------------------狀態碼 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 7:aypermedia link-----------------------------------------------------返回鏈接 Hypermedia API,RESTful API最好做到Hypermedia,即返回結果中提供鏈接,連向其他API方法,使得用戶不查文檔,也知道下一步應該做什麽。 {"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }} 8:錯誤處理 錯誤處理,狀態碼是4xx時,應返回錯誤信息,error當做key。 { error: "Invalid API key" } 為什麽做前後端分離? 數據的解耦,提高開發效率。 安裝: pip3 install djangorestframework -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com 繼承關系: class View(object):-------------------------------------------------view class APIView(View):------------------------------------------------APIview class GenericAPIView(views.APIView):--------------------------------GenericAPIView class GenericViewSet(ViewSetMixin, generics.GenericAPIView)---------GenericViewSet class ModelViewSet(mixins.CreateModelMixin,-------------------------ModelViewSet(增刪改查,genericViewset) mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): Django Rest Framework 的的請求生命周期: hTTP請求 —> wsgi —> 中間件 —> 路由分發 —> 執行對應類的dispatch方法 —> 視圖函數 —>返回 采用CBV的請求方式。 源碼剖析 接收HTTP請求---->wsgi----->中間件------->路由分發---------->執行對應類的dispatch方法------->視圖函數------>返回 首先執行 as_view 我們可以知道,as_view調用了dispatch. 執行:------------------------>執行對應類的dispatch方法:---------------------dispatch 一:第一步對------------------------------------------------------------------request二次封裝 1:--查看initialize_request方法,可以知道這個方法接收客戶端的request請求,再重新封裝成新的request。 def initialize_request(self, request, *args, **kwargs): parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) 點擊進入Request 2:----再查看Request方法的源碼,可以知道這個Request類是rest framework中定義的一個類 ----Rquest類,這來類是經過Request處理過的request已經不再是客戶端發送過來的那個request了 class Request(object): def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): self._request = request self.parsers = parsers or () self.authenticators = authenticators or () self.negotiator = negotiator or self._default_negotiator() self.parser_context = parser_context self._data = Empty self._files = Empty self._full_data = Empty self._content_type = Empty self._stream = Empty if self.parser_context is None: self.parser_context = {} self.parser_context[‘request‘] = self self.parser_context[‘encoding‘] = request.encoding or settings.DEFAULT_CHARSET force_user = getattr(request, ‘_force_auth_user‘, None) force_token = getattr(request, ‘_force_auth_token‘, None) if force_user is not None or force_token is not None: forced_auth = ForcedAuthentication(force_user, force_token) self.authenticators = (forced_auth,) 3:----在initialize_request方法中,有一個方法處理過request,來看看get_parser_context方法的源碼 在這裏,view的值是self,代指的是UsersView這個對象,所以get_parser_context方法把UsersView這個類封裝進來然後返回 所以get_parser_context方法最後返回的當前對象以及當前對象所傳的參數,經過initialize_request函數處理之後的request,現在就變成了 Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) def get_parser_context(self, http_request): return { ‘view‘: self, #代指的是UsersView這個對象 ‘args‘: getattr(self, ‘args‘, ()), ‘kwargs‘: getattr(self, ‘kwargs‘, {}) } 4:----現在再來看看Request的其他參數代指的是什麽 get_parsers------------------根據字面意思,是解析get請求的意思 def get_parsers(self): return [parser() for parser in self.parser_classes] get_content_negotiator-------選擇相關 def get_content_negotiator(self): if not getattr(self, ‘_negotiator‘, None): self._negotiator = self.content_negotiation_class() return self._negotiator parser_context---------------封閉self和self的參數 parser_context = self.get_parser_context(request) *get_authenticators----------------------認證相關,在get_authenticators這個方法中循環了self.authentication_classes返回了一個列表對象, def get_authenticators(self): return [auth() for auth in self.authentication_classes] ----------他是self.找的,所有它先去我們寫的那個類中去找authentication_classes,我們寫的類中沒有才去父類中找,因此我們就可以自定義這個列表類了。 進入APIview類找------------------authentication_classes class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES #認證 throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES #權限 permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES #分流 content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS metadata_class = api_settings.DEFAULT_METADATA_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS .............. ------------authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES由此可以看出: 它默認的authentication_classes是從它的配置文件中讀出來的。現在就可以去看看它配置文件中的類了。 -------------進入api_settings可以看到api_settings=APISettings api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) -----------進入APISettings,默認的authentication_classes是從它的配置文件中讀出來的。現在就可以去看看它配置文件中的類了 DEFAULTS = { # Base API policies ‘DEFAULT_RENDERER_CLASSES‘: ( ‘rest_framework.renderers.JSONRenderer‘, ‘rest_framework.renderers.BrowsableAPIRenderer‘, ), ‘DEFAULT_PARSER_CLASSES‘: ( ‘rest_framework.parsers.JSONParser‘, ‘rest_framework.parsers.FormParser‘, ‘rest_framework.parsers.MultiPartParser‘ ), ‘DEFAULT_AUTHENTICATION_CLASSES‘: ( ‘rest_framework.authentication.SessionAuthentication‘, ‘rest_framework.authentication.BasicAuthentication‘ ), ‘DEFAULT_PERMISSION_CLASSES‘: ( ‘rest_framework.permissions.AllowAny‘, ), ‘DEFAULT_THROTTLE_CLASSES‘: (), ‘DEFAULT_CONTENT_NEGOTIATION_CLASS‘: ‘rest_framework.negotiation.DefaultContentNegotiation‘, ‘DEFAULT_METADATA_CLASS‘: ‘rest_framework.metadata.SimpleMetadata‘, ‘DEFAULT_VERSIONING_CLASS‘: None, # Generic view behavior ‘DEFAULT_PAGINATION_CLASS‘: None, ‘DEFAULT_FILTER_BACKENDS‘: (), # Schema ‘DEFAULT_SCHEMA_CLASS‘: ‘rest_framework.schemas.AutoSchema‘, # Throttling ‘DEFAULT_THROTTLE_RATES‘: { ‘user‘: None, ‘anon‘: None, }, ‘NUM_PROXIES‘: None, # Pagination ‘PAGE_SIZE‘: None, # Filtering ‘SEARCH_PARAM‘: ‘search‘, ‘ORDERING_PARAM‘: ‘ordering‘, # Versioning ‘DEFAULT_VERSION‘: None, ‘ALLOWED_VERSIONS‘: None, ‘VERSION_PARAM‘: ‘version‘, # Authentication ‘UNAUTHENTICATED_USER‘: ‘django.contrib.auth.models.AnonymousUser‘, ‘UNAUTHENTICATED_TOKEN‘: None, # View configuration ‘VIEW_NAME_FUNCTION‘: ‘rest_framework.views.get_view_name‘, ‘VIEW_DESCRIPTION_FUNCTION‘: ‘rest_framework.views.get_view_description‘, # Exception handling ‘EXCEPTION_HANDLER‘: ‘rest_framework.views.exception_handler‘, ‘NON_FIELD_ERRORS_KEY‘: ‘non_field_errors‘, # Testing ‘TEST_REQUEST_RENDERER_CLASSES‘: ( ‘rest_framework.renderers.MultiPartRenderer‘, ‘rest_framework.renderers.JSONRenderer‘ ), ‘TEST_REQUEST_DEFAULT_FORMAT‘: ‘multipart‘, # Hyperlink settings ‘URL_FORMAT_OVERRIDE‘: ‘format‘, ‘FORMAT_SUFFIX_KWARG‘: ‘format‘, ‘URL_FIELD_NAME‘: ‘url‘, # Input and output formats ‘DATE_FORMAT‘: ISO_8601, ‘DATE_INPUT_FORMATS‘: (ISO_8601,), ‘DATETIME_FORMAT‘: ISO_8601, ‘DATETIME_INPUT_FORMATS‘: (ISO_8601,), ‘TIME_FORMAT‘: ISO_8601, ‘TIME_INPUT_FORMATS‘: (ISO_8601,), # Encoding ‘UNICODE_JSON‘: True, ‘COMPACT_JSON‘: True, ‘STRICT_JSON‘: True, ‘COERCE_DECIMAL_TO_STRING‘: True, ‘UPLOADED_FILES_USE_URL‘: True, # Browseable API ‘HTML_SELECT_CUTOFF‘: 1000, ‘HTML_SELECT_CUTOFF_TEXT‘: "More than {count} items...", # Schemas ‘SCHEMA_COERCE_PATH_PK‘: True, ‘SCHEMA_COERCE_METHOD_NAMES‘: { ‘retrieve‘: ‘read‘, ‘destroy‘: ‘delete‘ }, } -----------找到DEFAULT_AUTHENTICATION_CLASSES可以看出他有兩個類是在authentication中:SessionAuthentication,BasicAuthentication, ‘DEFAULT_AUTHENTICATION_CLASSES‘: ( ‘rest_framework.authentication.SessionAuthentication‘, ‘rest_framework.authentication.BasicAuthentication‘ ), --------------from rest_framework import authentication進入 --------------SessionAuthentication class SessionAuthentication(BaseAuthentication): """ Use Django‘s session framework for authentication. """ def authenticate(self, request): """ Returns a `User` if the request session currently has a logged in user. Otherwise returns `None`. """ # Get the session-based user from the underlying HttpRequest object user = getattr(request._request, ‘user‘, None) # Unauthenticated, CSRF validation not required if not user or not user.is_active: return None self.enforce_csrf(request) # CSRF passed with authenticated user return (user, None) def enforce_csrf(self, request): """ Enforce CSRF validation for session based authentication. """ reason = CSRFCheck().process_view(request, None, (), {}) if reason: # CSRF failed, bail with explicit error message raise exceptions.PermissionDenied(‘CSRF Failed: %s‘ % reason) --------------BasicAuthentication class BasicAuthentication(BaseAuthentication): """ HTTP Basic authentication against username/password. """ www_authenticate_realm = ‘api‘ def authenticate(self, request): """ Returns a `User` if a correct username and password have been supplied using HTTP Basic authentication. Otherwise returns `None`. """ auth = get_authorization_header(request).split() if not auth or auth[0].lower() != b‘basic‘: return None if len(auth) == 1: msg = _(‘Invalid basic header. No credentials provided.‘) raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: msg = _(‘Invalid basic header. Credentials string should not contain spaces.‘) raise exceptions.AuthenticationFailed(msg) try: auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(‘:‘) except (TypeError, UnicodeDecodeError, binascii.Error): msg = _(‘Invalid basic header. Credentials not correctly base64 encoded.‘) raise exceptions.AuthenticationFailed(msg) userid, password = auth_parts[0], auth_parts[2] return self.authenticate_credentials(userid, password, request) def authenticate_credentials(self, userid, password, request=None): """ Authenticate the userid and password against username and password with optional request for context. """ credentials = { get_user_model().USERNAME_FIELD: userid, ‘password‘: password } user = authenticate(request=request, **credentials) if user is None: raise exceptions.AuthenticationFailed(_(‘Invalid username/password.‘)) if not user.is_active: raise exceptions.AuthenticationFailed(_(‘User inactive or deleted.‘)) return (user, None) def authenticate_header(self, request): return ‘Basic realm="%s"‘ % self.www_authenticate_realm --------------可以看出他們都繼承了一個BaseAuthentication的類並且都實現了authenticate方法。 class BaseAuthentication(object): """ All authentication classes should extend BaseAuthentication. """ def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ pass --------------進入authenticate,由此可以看出BaseAuthentication類其實是一個接口類,讓繼承它的類必須實現authenticate方法。 最後就是在request對象中的authenticatotes中封裝了一些關於認證的對。 def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ raise NotImplementedError(".authenticate() must be overridden.") -----------自己添加配置文件-------settings:若將自己定義的認證類添加的返回的列表,就通過seettings的配置走自己的定義的認證類 EST_FRAMEWORK = { ‘UNAUTHENTICATED_USER‘: None, ‘UNAUTHENTICATED_TOKEN‘: None, "DEFAULT_AUTHENTICATION_CLASSES": [ # "app01.utils.MyAuthentication", ], ‘DEFAULT_PERMISSION_CLASSES‘:[ ], ‘DEFAULT_THROTTLE_RATES‘:{ ‘wdp_anon‘:‘5/minute‘, ‘wdp_user‘:‘10/minute‘, } } 5:---再來看看UsersView這個類中的get方法和post方法----------------------------------------------------------------UserView 可以看到get方法的參數中有一個request,通過前面可以知道這個request已經不是最開始時到達服務端的request了 這個request方法中已經被REST framework封裝了解析,認證和選擇等相關的方法 def get(self,request,*args,**kwargs): pass def post(self,request,*args,**kwargs): pass 6:---efault_response_headers這個方法從它的註釋可以看出已經被丟棄了. 二:初始化--------------------------------------------------------------------獲取版本-----認證-----權限-----分流 7:---再來看initial這個方法 def initial(self, request, *args, **kwargs): self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request #執行內容協商並將接受的信息存儲在請求上 neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. #如果正在使用版本控制,請確定API版本。 version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #確保傳入的請求是允許的。 self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request) ----先執行get_format_suffix-------------------------------------------------------------來獲取客戶端所發送的url的後綴 ----然後執行perform_content_negotiation方法,--------------------------------------------這個方法的主要作用是執行內容選擇,並把服務端接收到的信息保存在request中 獲取版本----然後再執行determine_version方法---------------------------------------------如果url中有版本信息,就獲取發送到服務端的版本,返回一個元組 -------version,schemas是執行determine_version獲得的,versioning_scheme是設置類的對象也就是QueryParameterVersioning。 request.version就是QueryParameterVersioning執行etermine_version version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme --------執行determine_version,如果versioning是空的,就返回兩個空。,不為空走versioning設置值。 def determine_version(self, request, *args, **kwargs): if self.versioning_class is None: return (None, None) scheme = self.versioning_class() return (scheme.determine_version(request, *args, **kwargs), scheme) ---------走versiong_class設置值,走api_settings配置找DEFAULT_VERSIONING_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS ----------走api_settings尋找配置 api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) ---------api_settings = APISettings,走APISettings找DEFAULT_VERSIONING_CLASS。默認為空,需自己設置。 ‘DEFAULT_VERSIONING_CLASS‘: None, ---------進入QueryParameterVersioning class QueryParameterVersioning(BaseVersioning): """ GET /something/?version=0.1 HTTP/1.1 Host: example.com Accept: application/json """ invalid_version_message = _(‘Invalid version in query parameter.‘) def determine_version(self, request, *args, **kwargs): version = request.query_params.get(self.version_param, self.default_version) if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): url = super(QueryParameterVersioning, self).reverse( viewname, args, kwargs, request, format, **extra ) if request.version is not None: return replace_query_param(url, self.version_param, request.version) return url ---------獲取version,version_param就是配置 def determine_version(self, request, *args, **kwargs): version = request.query_params.get(self.version_param, self.default_version) if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version ---------進入version_param,在配置中找VERSION_PARAM version_param = api_settings.VERSION_PARAM ---------由配置可知VERSION_PARAM等於version. ‘VERSION_PARAM‘: ‘version‘, ---------default_version=配置中的DEFAULT_VERSION default_version = api_settings.DEFAULT_VERSION ---------進入配置找DEFAULT_VERSION,可以知道我們可以在setting中自己配置 ---------is_allowed_version是允許的版本,也可自己在seettings中配置。流程相似 -------from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning ---------QueryParameterVersioning class QueryParameterVersioning(BaseVersioning): """ GET /something/?version=0.1 HTTP/1.1 Host: example.com Accept: application/json """ invalid_version_message = _(‘Invalid version in query parameter.‘) def determine_version(self, request, *args, **kwargs): version = request.query_params.get(self.version_param, self.default_version) if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): url = super(QueryParameterVersioning, self).reverse( viewname, args, kwargs, request, format, **extra ) if request.version is not None: return replace_query_param(url, self.version_param, request.version) return url --------執行determine_version獲取版本 def determine_version(self, request, *args, **kwargs): version = request.query_params.get(self.version_param, self.default_version) if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version --------執行reverse反向生成url def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): url = super(QueryParameterVersioning, self).reverse( viewname, args, kwargs, request, format, **extra ) if request.version is not None: return replace_query_param(url, self.version_param, request.version) return url ---------URLPathVersioning class URLPathVersioning(BaseVersioning): """ To the client this is the same style as `NamespaceVersioning`. The difference is in the backend - this implementation uses Django‘s URL keyword arguments to determine the version. An example URL conf for two views that accept two different versions. 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‘) ] GET /1.0/something/ HTTP/1.1 Host: example.com Accept: application/json """ invalid_version_message = _(‘Invalid version in URL path.‘) def determine_version(self, request, *args, **kwargs): version = kwargs.get(self.version_param, self.default_version) if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): if request.version is not None: kwargs = {} if (kwargs is None) else kwargs kwargs[self.version_param] = request.version return super(URLPathVersioning, self).reverse( viewname, args, kwargs, request, format, **extra ) ---------自己在settings中配置 REST_FRAMEWORK = { ‘VERSION_PARAM‘:‘version‘, ‘DEFAULT_VERSION‘:‘v1‘, ‘ALLOWED_VERSIONS‘:[‘v1‘,‘v2‘], # ‘DEFAULT_VERSIONING_CLASS‘:"rest_framework.versioning.HostNameVersioning" ‘DEFAULT_VERSIONING_CLASS‘:"rest_framework.versioning.URLPathVersioning" } --------執行determine_version獲取版本 def determine_version(self, request, *args, **kwargs): version = kwargs.get(self.version_param, self.default_version) if not self.is_allowed_version(version): raise exceptions.NotFound(self.invalid_version_message) return version ---------執行reverse反向生成url def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): if request.version is not None: kwargs = {} if (kwargs is None) else kwargs kwargs[self.version_param] = request.version return super(URLPathVersioning, self).reverse( viewname, args, kwargs, request, format, **extra ) -------自定制settings REST_FRAMEWORK = { ‘VERSION_PARAM‘:‘version‘, ‘DEFAULT_VERSION‘:‘v1‘, ‘ALLOWED_VERSIONS‘:[‘v1‘,‘v2‘], # ‘DEFAULT_VERSIONING_CLASS‘:"rest_framework.versioning.HostNameVersioning" # ‘DEFAULT_VERSIONING_CLASS‘:"rest_framework.versioning.URLPathVersioning" } 認證-----執行完上面的方法,再執行perform_authentication方法來進行認證操作----------------執行認證功能,確認進行後續操作的用戶是被允許的, perform_authentication方法返回經過認證的用戶對象, 傳入的request是重新封裝過的。 def perform_authentication(self, request): request.user ------然後就在request.user中執行authenticate這個方法進行認證 def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ if not hasattr(self, ‘_user‘): with wrap_attributeerrors(): self._authenticate() return self._user 檢查權限-----執行check_permissions方法--------------------------------------------------如果用戶通過認證,檢查用戶是否有權限訪問url中所傳的路徑, 如用用戶訪問的是沒有沒有權限的路徑,則會拋出異常. def check_permissions(self, request): for permission in self.get_permissions(): if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, ‘message‘, None) ) ------循環,執行get_permissions返回一個列表對象。 def get_permissions(self): return [permission() for permission in self.permission_classes] --------執行permission_classes,self是當前類,所以是去當前類中找,當前類中沒有去父類(APIView)中找,所以可以自己定制。 permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES ----------可以看出是從api_settings中找到,我們執行api_settings api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) -------------進入APISettings,在DEFAULTS配置文件中找DEFAULT_PERMISSION_CLASSES,若自定制,自己在setting中配置。 ‘DEFAULT_PERMISSION_CLASSES‘: ( ‘rest_framework.permissions.AllowAny‘, -------------根據配置文件可知:我們走permissions的AllowAny類。 from rest_framework.permissions import AllowAny進入 -------------AllowAny執行了has_permission,返回True. class AllowAny(BasePermission): """ Allow any access. This isn‘t strictly required, since you could use an empty permission_classes list, but it‘s useful because it makes the intention more explicit. """ def has_permission(self, request, view): return True ), -------------可以看出AllowAny繼承了BasePermission,由此我們可以知道必須執行一個AllowAny 自己有執行自己的,自己沒有沒有執行父類的,都返回True 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 檢查限制訪問(分流)-----就會執行check_throttles方法--------------------------------------作用是檢查用戶是否被限制了訪問主機的次數 self.check_throttles(request) 如果用戶訪問服務器的次數超出設定值,則會拋出一個異常 ---------例如,如果想限制一個ip地址每秒鐘只能訪問幾次,一個小時之內最多可以訪問多少次,就可以在settings.py文件中進行配置 def check_throttles(self, request): for throttle in self.get_throttles(): if not throttle.allow_request(request, self): self.throttled(request, throttle.wait()) ----------循環執行,執行get_throttles,返回一個列表對象 def get_throttles(self): """ Instantiates and returns the list of throttles that this view uses. """ return [throttle() for throttle in self.throttle_classes] ----------執行throttle_classes,self是當前類,請求過來首先在自己的類中找,沒有去父類中找(APIView) throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES ----------可以看出是從api_settings中找到,我們執行api_settings api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) ----------進入APISettings,在DEFAULTS配置文件中找DEFAULT_PERMISSION_CLASSES,若自定制,自己在setting中配置 ‘DEFAULT_THROTTLE_CLASSES‘: (), ‘DEFAULT_CONTENT_NEGOTIATION_CLASS‘: ‘rest_framework.negotiation.DefaultContentNegotiation‘, ‘DEFAULT_METADATA_CLASS‘: ‘rest_framework.metadata.SimpleMetadata‘, ‘DEFAULT_VERSIONING_CLASS‘: None, ----------根據配置文件可知: from rest_framework.permissions import AllowAny進入 三:執行對應的視圖函數 8:---initial這個方法執行完成後,request.method.lower把請求的方法轉換成小寫 Get the appropriate handler method 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 response = handler(request, *args, **kwargs) 9:---再通過通過反射的方式來執行UsersView類中的get或post等自定義方法要註意的是,在執行initial方法之前,使用了try/except方法來進行異常處理 如果執行initial方法的時候出現錯誤,就調用handle_exception來處理initial方法拋出的異常,返回正確的響應信息 def handle_exception(self, exc): if isinstance(exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed)): # WWW-Authenticate header for 401 responses, else coerce to 403 auth_header = self.get_authenticate_header(self.request) if auth_header: exc.auth_header = auth_header else: exc.status_code = status.HTTP_403_FORBIDDEN exception_handler = self.get_exception_handler() context = self.get_exception_handler_context() response = exception_handler(exc, context) if response is None: self.raise_uncaught_exception(exc) response.exception = True return response 10:---在前面,如果initial方法執行完成沒有拋出異常,則根據反射執行自定義的請求方法,然後返回響應信息如果initial方法拋出異常則執行handle_exception 方法處理拋出的異常,也返回響應信息等到上面的過程執行完成後,再執行finalize_response方法把最終的響應信息返回給客戶端的瀏覽器 def finalize_response(self, request, response, *args, **kwargs): # Make the error obvious if a proper response is not returned assert isinstance(response, HttpResponseBase), ( ‘Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` ‘ ‘to be returned from the view, but received a `%s`‘ % type(response) ) if isinstance(response, Response): if not getattr(request, ‘accepted_renderer‘, None): neg = self.perform_content_negotiation(request, force=True) request.accepted_renderer, request.accepted_media_type = neg response.accepted_renderer = request.accepted_renderer response.accepted_media_type = request.accepted_media_type response.renderer_context = self.get_renderer_context() # Add new vary headers to the response instead of overwriting. vary_headers = self.headers.pop(‘Vary‘, None) if vary_headers is not None: patch_vary_headers(response, cc_delim_re.split(vary_headers)) for key, value in self.headers.items(): response[key] = value return response def dispatch(self, request, *args, **kwargs): self.args = args self.kwargs = kwargs # ####################### 第一步 request二次封裝 ####################### """ return Request( request, parsers=self.get_parsers(), 解析相關 對象列表 authenticators=self.get_authenticators(), 認證相關 對象列表 negotiator=self.get_content_negotiator(), 選擇相關 選擇對象 parser_context=parser_context 解析內容 ) """ request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? # ####################### 第二步 初始化 ####################### """ 2.1 獲取版本 返回(scheme.determine_version(request, *args, **kwargs), scheme) request.version, request.versioning_scheme =版本號,檢查版本的對象 2.2 認證 self.perform_authentication(request) 調用request.user方法 2.3 檢查權限 self.check_permissions(request) 獲取權限的對象列表 執行對象.has_permission方法 返回True有權限,返回False沒有權限,拋出異常,message定制錯誤信息。 2.4 檢查限制訪問 self.check_throttles(request) 獲取限制類的對象列表 執行對象.allow_request(request, self) 返回True可以訪問,返回False限制訪問。 """ try: self.initial(request, *args, **kwargs) # ####################### 第三步 執行對應的視圖函數 ####################### # Get the appropriate handler method 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 response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response 基本流程 1:請求到來之後,都要執行dispatch方法,dispatch方法根據請求方式不同觸發。 2:重要的功能是在APIView的dispatch中觸發。 url.py from django.conf.urls import url, include from web.views.s1_api import TestView urlpatterns = [ url(r‘^test/‘, TestView.as_view()), ] views.py from rest_framework.views import APIView from rest_framework.response import Response class TestView(APIView): def dispatch(self, request, *args, **kwargs): """ 請求到來之後,都要執行dispatch方法,dispatch方法根據請求方式不同觸發 get/post/put等方法 註意:APIView中的dispatch方法有好多好多的功能 """ return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): return Response(‘GET請求,響應內容‘) def post(self, request, *args, **kwargs): return Response(‘POST請求,響應內容‘) def put(self, request, *args, **kwargs): return Response(‘PUT請求,響應內容‘)
Django----djagorest-framwork源碼剖析