異常模組原始碼入口
APIView
類中dispatch
方法中的:response = self.handle_exception(exc)
原始碼分析
我們點選handle_exception
跳轉,檢視該方法原始碼
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
# 判斷異常型別是否是沒有認證的型別,最後返回403狀態碼
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
以上原始碼最為關鍵的一句就在於exception_handler = self.get_exception_handler()
,我們可以點選檢視該方法原始碼
def get_exception_handler(self):
"""
Returns the exception handler that this view uses.
"""
return self.settings.EXCEPTION_HANDLER
該方法返回該檢視的異常處理方法,從返回的內容,我們可以知道,該方法在settings
檔案中有個預設值,進入settings
可檢視到
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
異常處理的預設方法就是views
下的exception_handler
方法,我們再進入檢視該方法原始碼
def exception_handler(exc, context):
"""
Returns the response that should be used for any given exception.
By default we handle the REST framework `APIException`, and also
Django's built-in `Http404` and `PermissionDenied` exceptions.
Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised.
"""
# 判斷異常是否是404
if isinstance(exc, Http404):
exc = exceptions.NotFound()
# 判斷異常是否是沒有許可權
elif isinstance(exc, PermissionDenied):
exc = exceptions.PermissionDenied()
# 判斷異常是否是drf的基類異常,該異常提供了狀態碼和異常欄位detail
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
headers['WWW-Authenticate'] = exc.auth_header
if getattr(exc, 'wait', None):
headers['Retry-After'] = '%d' % exc.wait
# 判斷detail是否是list型別或dict型別
if isinstance(exc.detail, (list, dict)):
data = exc.detail
else:
data = {'detail': exc.detail}
set_rollback()
return Response(data, status=exc.status_code, headers=headers)
return None
從上述程式碼我們可以知道,當response
返回為None
時,是不會返回異常資訊,而是直接丟擲異常,所以我們可以自定義異常類
自定義異常
在我們的app
目錄下,建立utils
包,並建立exceptions
檔案,並寫入如下原始碼:
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
if response is None:
print(f"{context['view']} - {context['request'].method} - {exc}")
return Response(status=500, data="伺服器錯誤")
return response
最後我們將預設異常資訊配置改為自己的配置即可,在settings
檔案中寫入如下配置
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'drf_app.utils.exceptions.exception_handler',
}
以後碰到response
響應為None
的時候,我們就會丟擲伺服器錯誤的異常資訊
總結
為什麼要自定義異常模組?
- 所有經過
drf
的APIView
檢視類產生的異常,都可以提供異常處理方案 drf
預設提供了異常處理方案(rest_framework.views.exception_handler)
,但是處理範圍有限drf
提供的處理方案兩種,處理了返回異常現象,沒處理返回None
(後續就是伺服器拋異常給前臺)- 自定義異常的目的就是解決
drf
沒有處理的異常,讓前臺得到合理的異常資訊返回,後臺記錄異常具體資訊