Django REST framework的請求生命週期
REST framework
是基於Django的API框架,REST framework採用的是CBV的請求模式.
所以在一個專案中,使用了REST framework的時候,
請求到達REST framework
後,也先執行REST framework
中的dispatch
方法
先來看看dispatch
def dispatch(self, request, *args, **kwargs):
self.args = args # 函式傳遞過來的引數
self.kwargs = kwargs # 函式傳遞過來的引數
# 封裝request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
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
檢視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方法的原始碼
可以知道這個Request
類是rest framework
中定義的一個類
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,)
先不看這個Request
到底執行了什麼操作
但是已經知道經過Request處理過的request已經不再是客戶端傳送過來的那個request了
在initialize_request
方法中,有一個方法處理過request,來看看get_parser_context
方法的原始碼
def get_parser_context(self, http_request):
return {
'view': self,
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {})
}
在這裡,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
)
現在再來看看Request的其他引數代指的是什麼
get_parsers 根據字面意思,是解析get請求的意思
get_authenticators 認證相關
get_content_negotiator 選擇相關
parser_context 封閉self和self的引數
def get_parsers(self):
return [parser() for parser in self.parser_classes]
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
def get_permissions(self):
return [permission() for permission in self.permission_classes]
def get_throttles(self):
return [throttle() for throttle in self.throttle_classes]
def get_content_negotiator(self):
if not getattr(self, '_negotiator', None):
self._negotiator = self.content_negotiation_class()
return self._negotiator
再來看看UsersView
這個類中的get方法和post方法
def get(self,request,*args,**kwargs):
pass
def post(self,request,*args,**kwargs):
pass
可以看到get方法的引數中有一個request,通過前面可以知道這個request已經不是最開始時到達服務端的request了
這個request方法中已經被REST framework
封裝瞭解析,認證和選擇等相關的方法
def dispatch(self, request, *args, **kwargs):
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)
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
default_response_headers
這個方法從它的註釋可以看出已經被丟棄了.
再來看initial
這個方法
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)
先執行get_format_suffix
來獲取客戶端所傳送的url的字尾
然後執行perform_content_negotiation
方法,從它的註釋可以知道這個方法的主要作用是執行內容選擇,並把服務端接收到的資訊儲存在request中
然後再執行determine_version
方法
def determine_version(self, request, *args, **kwargs):
"""
If versioning is being used, then determine any API version for the
incoming request. Returns a two-tuple of (version, versioning_scheme)
"""
if self.versioning_class is None:
return (None, None)
scheme = self.versioning_class()
return (scheme.determine_version(request, *args, **kwargs), scheme)
在determine_version
方法的官方註釋中可以知道,determine_version
方法的主要作用是
如果url中有版本資訊,就獲取傳送到服務端的版本,返回一個元組
執行完上面的方法,再執行perform_authentication
方法來進行認證操作
來看下perform_authentication
方法的原始碼
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
從上面有程式碼及註釋中可以看出,perform_authentication
方法的作用就是
執行認證功能,確認進行後續操作的使用者是被允許的.
perform_authentication方法返回經過認證的使用者物件
執行完perform_authentication
方法,就會執行check_permissions
方法
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
check_permissions
方法的作用是
如果使用者通過認證,檢查使用者是否有許可權訪問url中所傳的路徑.
如用使用者訪問的是沒有沒有許可權的路徑,則會丟擲異常.
check_permissions
方法執行完成後,就會執行check_throttles
方法
check_throttles
方法的作用是檢查使用者是否被限制了訪問主機的次數
如果使用者訪問伺服器的次數超出設定值,則會丟擲一個異常
例如,如果想限制一個ip地址每秒鐘只能訪問幾次,一個小時之內最多可以訪問多少次,就可以在settings.py
檔案中進行配置
def check_throttles(self, request):
"""
Check if request should be throttled.
Raises an appropriate exception if the request is throttled.
"""
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())
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)
再通過通過反射的方式來執行UsersView
類中的get或post等自定義方法
需要注意的是,在執行initial
方法之前,使用了try/except
方法來進行異常處理
如果執行initial
方法的時候出現錯誤,就呼叫handle_exception
來處理initial
方法丟擲的異常,返回正確的響應資訊
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
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
在前面,如果initial
方法執行完成沒有丟擲異常,則根據反射執行自定義的請求方法,然後返回響應資訊
如果initial
方法丟擲異常則執行handle_exception
方法處理丟擲的異常,也返回響應資訊
等到上面的過程執行完成後,再執行finalize_response
方法把最終的響應資訊返回給客戶端的瀏覽器
def finalize_response(self, request, response, *args, **kwargs):
"""
Returns the final response object.
"""
# 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
所以總結:
REST framework
請求的生命週期為:
1.請求到達服務端,經過WSGI和中介軟體到達路由系統
2.路由系統執行配置的CBV或者FBV中的dispatch方法
3.在dispatch方法中,request方法被封裝添加了解析器,認證方法及選擇器等方法
4.然後執行initial方法
5.再獲取版本,進行認證操作,許可權操作和節流操作
6.最後執行自定義的get,post,push,delete等自定義方法
7.在執行initial方法之前,通過try來捕獲可能出現的異常
8.如果出現異常,就執行handle_exception方法來處理捕獲到的異常
9.不管是否出現異常,最後的返回值都通過finalize_response方法來處理響應的內容
REST framework
是基於Django的API框架,REST framework採用的是CBV的請求模式.
所以在一個專案中,使用了REST framework的時候,
請求到達REST framework
後,也先執行REST framework
中的dispatch
方法
先來看看dispatch
方法的原始碼
def dispatch(self, request, *args, **kwargs):
self.args = args # 函式傳遞過來的引數
self.kwargs = kwargs # 函式傳遞過來的引數
# 封裝request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
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
檢視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方法的原始碼
可以知道這個Request
類是rest framework
中定義的一個類
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,)
先不看這個Request
到底執行了什麼操作
但是已經知道經過Request處理過的request已經不再是客戶端傳送過來的那個request了
在initialize_request
方法中,有一個方法處理過request,來看看get_parser_context
方法的原始碼
def get_parser_context(self, http_request):
return {
'view': self,
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {})
}
在這裡,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
)
現在再來看看Request的其他引數代指的是什麼
get_parsers 根據字面意思,是解析get請求的意思
get_authenticators 認證相關
get_content_negotiator 選擇相關
parser_context 封閉self和self的引數
def get_parsers(self):
return [parser() for parser in self.parser_classes]
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
def get_permissions(self):
return [permission() for permission in self.permission_classes]
def get_throttles(self):
return [throttle() for throttle in self.throttle_classes]
def get_content_negotiator(self):
if not getattr(self, '_negotiator', None):
self._negotiator = self.content_negotiation_class()
return self._negotiator
再來看看UsersView
這個類中的get方法和post方法
def get(self,request,*args,**kwargs):
pass
def post(self,request,*args,**kwargs):
pass
可以看到get方法的引數中有一個request,通過前面可以知道這個request已經不是最開始時到達服務端的request了
這個request方法中已經被REST framework
封裝瞭解析,認證和選擇等相關的方法
def dispatch(self, request, *args, **kwargs):
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)
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
default_response_headers
這個方法從它的註釋可以看出已經被丟棄了.
再來看initial
這個方法
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)
先執行get_format_suffix
來獲取客戶端所傳送的url的字尾
然後執行perform_content_negotiation
方法,從它的註釋可以知道這個方法的主要作用是執行內容選擇,並把服務端接收到的資訊儲存在request中
然後再執行determine_version
方法
def determine_version(self, request, *args, **kwargs):
"""
If versioning is being used, then determine any API version for the
incoming request. Returns a two-tuple of (version, versioning_scheme)
"""
if self.versioning_class is None:
return (None, None)
scheme = self.versioning_class()
return (scheme.determine_version(request, *args, **kwargs), scheme)
在determine_version
方法的官方註釋中可以知道,determine_version
方法的主要作用是
如果url中有版本資訊,就獲取傳送到服務端的版本,返回一個元組
執行完上面的方法,再執行perform_authentication
方法來進行認證操作
來看下perform_authentication
方法的原始碼
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
從上面有程式碼及註釋中可以看出,perform_authentication
方法的作用就是
執行認證功能,確認進行後續操作的使用者是被允許的.
perform_authentication方法返回經過認證的使用者物件
執行完perform_authentication
方法,就會執行check_permissions
方法
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
check_permissions
方法的作用是
如果使用者通過認證,檢查使用者是否有許可權訪問url中所傳的路徑.
如用使用者訪問的是沒有沒有許可權的路徑,則會丟擲異常.
check_permissions
方法執行完成後,就會執行check_throttles
方法
check_throttles
方法的作用是檢查使用者是否被限制了訪問主機的次數
如果使用者訪問伺服器的次數超出設定值,則會丟擲一個異常
例如,如果想限制一個ip地址每秒鐘只能訪問幾次,一個小時之內最多可以訪問多少次,就可以在settings.py
檔案中進行配置
def check_throttles(self, request):
"""
Check if request should be throttled.
Raises an appropriate exception if the request is throttled.
"""
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())
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)
再通過通過反射的方式來執行UsersView
類中的get或post等自定義方法
需要注意的是,在執行initial
方法之前,使用了try/except
方法來進行異常處理
如果執行initial
方法的時候出現錯誤,就呼叫handle_exception
來處理initial
方法丟擲的異常,返回正確的響應資訊
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
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
在前面,如果initial
方法執行完成沒有丟擲異常,則根據反射執行自定義的請求方法,然後返回響應資訊
如果initial
方法丟擲異常則執行handle_exception
方法處理丟擲的異常,也返回響應資訊
等到上面的過程執行完成後,再執行finalize_response
方法把最終的響應資訊返回給客戶端的瀏覽器
def finalize_response(self, request, response, *args, **kwargs):
"""
Returns the final response object.
"""
# 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
所以總結:
REST framework
請求的生命週期為:
1.請求到達服務端,經過WSGI和中介軟體到達路由系統
2.路由系統執行配置的CBV或者FBV中的dispatch方法
3.在dispatch方法中,request方法被封裝添加了解析器,認證方法及選擇器等方法
4.然後執行initial方法
5.再獲取版本,進行認證操作,許可權操作和節流操作
6.最後執行自定義的get,post,push,delete等自定義方法
7.在執行initial方法之前,通過try來捕獲可能出現的異常
8.如果出現異常,就執行handle_exception方法來處理捕獲到的異常
9.不管是否出現異常,最後的返回值都通過finalize_response方法來處理響應的內容