rest_framework.views.APIView -- Request 以及 使用者認證 原始碼分析
在講解原始碼之前,先介紹一下 APIView和django中的View有什麼不同
- APIView是REST framework提供的所有檢視的基類,繼承自Django的View父類。
APIView與View的不同之處在於:
- 傳入到檢視方法中的是REST framework的Request物件,而不是Django的HttpRequeset物件;REST framework 提供了Parser解析器,在接收到請求後會自動根據Content-Type指明的請求資料型別(如JSON、表單等)將請求資料進行parse解析,解析為類字典物件儲存到Request物件中;
- 檢視方法可以返回REST framework的Response物件,檢視會為響應資料設定(render)符合前端要求的格式;
- 任何APIException異常都會被捕獲到,並且處理成合適的響應資訊;
在進行dispatch()分發前,會對請求進行身份認證、許可權檢查、流量控制。
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 = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? 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
這個函式首先是給了我們一個註釋:
.dispatch()`與Django的常規排程幾乎相同,
但有額外的鉤子,用於啟動,結束和異常的處理
在dispatch類方法中,在request執行get\post\put\delete\等方法時候,會先對request請求進行使用者驗證,也就是如下2行程式碼:
1:request = self.initialize_request(request, *args, **kwargs)
2:self.initial(request, *args, **kwargs)
request = self.initialize_request(request, *args, **kwargs)
關於第一行程式碼initialize_request
,從字面意思來看,就是對請求來的request,進行再次初始化封裝,得到初始化後request,那就看看是如何進行初始化的:
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
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
)
嗯,這個函式裡面的註釋是驗證了剛才的說法,這個是用來初始化請求物件的:
先分析這一段程式碼parser_context = self.get_parser_context(request)
,我們可以通過這個函式的返回值看到,它是返回的一個字典型別的資料
def get_parser_context(self, http_request):
"""
Returns a dict that is passed through to Parser.parse(),
as the `parser_context` keyword argument.
"""
# Note: Additionally `request` and `encoding` will also be added
# to the context by the Request object.
return {
'view': self,
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {})
}
在進行字典格式轉換之後,initialize_request
方法最終返回一個django自己封裝的Request:
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
來細看一下它這個Request在返回的時候,做了哪些事情:
1:parsers=self.get_parsers()
是獲取解析器(用來解析通過get_parser_context構造的字典資料的)
2:parser_context=parser_context是構造好的原生request封裝的字典格式資料
3:negotiator=self.get_content_negotiator()
一個內容協商類,它決定如何為響應選擇一個呈現器,給定一個傳入請求 ;
4:authenticators=self.get_authenticators()
是獲取 [auth() for auth in self.authentication_classes]
認證列表,裡面是存放的一個個認證類物件;
get_authenticators方法如下,最後跟程式碼定位到如下鍵值對
我們可以先看一下父類BaseAuthentication
的程式碼,如下,裡面有2個方法,也就是進行認證,我們可以利用此來擴充校驗認證規則
get_authenticators
方法就是去遍歷繼承類SessionAuthentication、BasicAuthentication的子類的列表,裡面是一個一個子類物件,因為在列表推導是中[auth() for auth in self.authentication_classes]
通過auth()獲取類的例項物件;(其實這裡面還有關於區域性配置和全域性配置,簡單理解就是:單純在某個類視圖裡面的配置,還是在settings.py中配置的,下一次會有進一步說明,get_authenticators會從自身找尋,查詢不到,在去全域性配置中查詢)。
好的request = self.initialize_request(request, *args, **kwargs)
在此告一段落,現在開始說self.initial(request, *args, **kwargs)
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)
# 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.
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)
這個註釋挺好玩的:
在呼叫方法處理程式之前執行需要發生的任何事情。
我們來著重看一下我們需要了解的資訊,也就是最後三段程式碼的使用者認證、許可權認證、訪問頻率控制(本篇不做說明):
1.self.perform_authentication(request) 認證
哈哈,真是驚訝,就一段程式碼 request.user
, 其實看到這裡,我們應該知道,這個user肯定是被@property裝飾過的,不然是不可能被直接呼叫而且不加任何括號;可以在DRF中找一下這個Request中帶有@property的user:
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
#判斷當前類中是否有已經認證過的user
if not hasattr(self, '_user'):
#沒有認證則去認證
self._authenticate()
#認證過了直接返回
return self._user
沒有認證的話,就需要呼叫Request類中的_authenticate()方法進行認證
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
#遍歷request物件中封裝的Authentication物件
for authenticator in self.authenticators:
try:
#呼叫Authentication物件中的authenticate方法,必須要有這個方法不然丟擲異常
#當然Authentication類一般有我們自己定義,實現這個方法就可以了
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()
2. self.check_permissions(request) 許可權
#檢查許可權
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
#self.get_permissions()得到的是一個許可權物件列表
for permission in self.get_permissions():
#在自定義的Permission中has_permission方法是必須要有的
#判斷當前has_permission返回的是True,False,還是丟擲異常
#如果是True則表示許可權通過,False執行下面程式碼
if not permission.has_permission(request, self):
#為False的話則丟擲異常,當然這個異常返回的提示資訊是英文的,如果我們想讓他顯示我們自定義的提示資訊
#我們重寫permission_denied方法即可
self.permission_denied(
#從自定義的Permission類中獲取message(許可權錯誤提示資訊),一般自定義的話都建議寫上,如果沒有則為預設的(英文提示)
request, message=getattr(permission, 'message', None)
)
#self.get_permissions()得到的是一個許可權物件列表,如下圖
如果has_permission位False執行下面的程式碼
def permission_denied(self, request, message=None):
"""
If request is not permitted, determine what kind of exception to raise.
"""
if request.authenticators and not request.successful_authenticator:
#沒有登入提示的錯誤資訊
raise exceptions.NotAuthenticated()
#一般是登陸了但是沒有許可權提示
raise exceptions.PermissionDenied(detail=message)