異常
REST framework 檢視中的異常處理
REST framework 的檢視處理各種異常,並返回適當的錯誤響應。
需要處理的異常情況有:
- 在 REST framework 內引發的
APIException
的子類。 - Django 的
Http404
異常。 - Django 的
PermissionDenied
異常。
在每種情況下,REST framework 都會返回一個帶有適當狀態碼和內容型別的響應。響應的主體將包含有關錯誤性質的其他細節。
大多數錯誤響應將包含響應正文中的關鍵 detail
。
例如,以下請求:
DELETE http://api.example.com/foo/bar HTTP/1.1
Accept: application/json
複製程式碼
可能會收到錯誤響應,指出在該資源上不允許使用 DELETE
方法:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 42
{"detail": "Method 'DELETE' not allowed."}
複製程式碼
驗證錯誤的處理方式稍有不同,但都是將欄位名稱作為響應中的關鍵字。如果驗證錯誤不是特定於某個欄位的,那麼它將使用 “non_field_errors” 鍵,或者為 NON_FIELD_ERRORS_KEY
setting 設定的字串值。
示例驗證錯誤可能如下所示:
HTTP/1.1 400 Bad Request
Content-Type: application/json
Content-Length: 94
{"amount": ["A valid integer is required."], "description": ["This field may not be blank."]}
複製程式碼
自定義異常處理
你可以通過建立處理函式來實現自定義異常處理,該函式將 API 檢視中引發的異常轉換為響應物件。這使你可以控制 API 錯誤響應的樣式。
該函式必須帶有一對引數,第一個是要處理的異常,第二個是包含任何額外上下文(例如當前正在處理的檢視)的字典。異常處理函式應該返回一個 Response
物件,或者如果無法處理異常,則返回 None
。如果處理程式返回 None
,那麼異常將被重新丟擲,Django 將返回一個標準的 HTTP 500 'server error' 響應。
例如,你可能希望確保所有錯誤響應都包含響應正文中的 HTTP 狀態碼,如下所示:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 62
{"status_code": 405, "detail": "Method 'DELETE' not allowed."}
複製程式碼
為了改變響應的風格,你可以編寫下面的自定義異常處理程式:
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response is not None:
response.data['status_code'] = response.status_code
return response
複製程式碼
context 引數不被預設處理程式使用,但是如果異常處理程式需要更多資訊,例如當前正在處理的檢視(可以作為 context['view']
訪問),則該引數可能很有用。
異常處理程式還必須使用 EXCEPTION_HANDLER
setting key 在你的設定中進行配置。例如:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
複製程式碼
如果未指定,則 'EXCEPTION_HANDLER'
setting 預設為由 REST framework 提供的標準異常處理程式:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}
複製程式碼
請注意,異常處理程式只會根據由異常產生的響應呼叫。它不會用於檢視直接返回的任何響應,例如在序列化驗證失敗時通用檢視返回的 HTTP_400_BAD_REQUEST
響應。
API 參考
APIException
簽名: APIException()
在 APIView
類或 @api_view
中引發的所有異常的基類。
要自定義異常,請繼承 APIException
,並在該類上設定 .status_code
,.default_detail
和 default_code
屬性。
例如,如果你的 API 依賴於可能無法訪問的第三方服務,則可能需要為 "503 Service Unavailable" HTTP 響應碼封裝異常。你可以這樣做:
from rest_framework.exceptions import APIException
class ServiceUnavailable(APIException):
status_code = 503
default_detail = 'Service temporarily unavailable, try again later.'
default_code = 'service_unavailable'
複製程式碼
檢查 API 異常
有許多不同的屬性可用於檢查 API 異常的狀態。你可以使用它們為你的專案構建自定義異常處理。
可用的屬性和方法有:
.detail
- 返回錯誤的文字描述。.get_codes()
- 返回錯誤的程式碼識別符號。.get_full_details()
- 返回文字描述和程式碼識別符號。
在大多數情況下,錯誤詳情將是一個簡單的 item:
>>> print(exc.detail)
You do not have permission to perform this action.
>>> print(exc.get_codes())
permission_denied
>>> print(exc.get_full_details())
{'message':'You do not have permission to perform this action.','code':'permission_denied'}
複製程式碼
在驗證錯誤的情況下,錯誤詳情將是 item 列表或字典:
>>> print(exc.detail)
{"name":"This field is required.","age":"A valid integer is required."}
>>> print(exc.get_codes())
{"name":"required","age":"invalid"}
>>> print(exc.get_full_details())
{"name":{"message":"This field is required.","code":"required"},"age":{"message":"A valid integer is required.","code":"invalid"}}
複製程式碼
ParseError
簽名: ParseError(detail=None, code=None)
在訪問 request.data
時包含格式錯誤的資料則會引發此異常。
預設情況下,此異常會導致 HTTP 狀態碼 "400 Bad Request" 的響應。
AuthenticationFailed
簽名: AuthenticationFailed(detail=None, code=None)
當傳入的請求包含不正確的身份驗證時引發。
預設情況下,此異常會導致 HTTP 狀態碼 "401 Unauthenticated" 的響應,但也可能會導致 "403 Forbidden" 響應,具體取決於所使用的身份驗證方案。
NotAuthenticated
簽名: NotAuthenticated(detail=None, code=None)
當未經身份驗證的請求未通過許可權檢查時引發。
預設情況下,此異常會導致 HTTP 狀態碼 "401 Unauthenticated" 的響應,但也可能會導致 "403 Forbidden" 響應,具體取決於所使用的身份驗證方案。
PermissionDenied
簽名: PermissionDenied(detail=None, code=None)
當經過身份驗證的請求未通過許可權檢查時引發。
預設情況下,此異常會導致 HTTP 狀態碼 "403 Forbidden" 的響應。
NotFound
簽名: NotFound(detail=None, code=None)
當資源不存在於給定的 URL 時引發。這個異常相當於標準的 Http404
Django 異常。
預設情況下,此異常會導致 HTTP 狀態碼為 "404 Not Found" 的響應。
MethodNotAllowed
簽名: MethodNotAllowed(method, detail=None, code=None)
當請求發生時,找不到檢視上對應的處理方法時引發。
預設情況下,此異常會導致 HTTP 狀態碼為 "405 Method Not Allowed" 的響應。
NotAcceptable
簽名: NotAcceptable(detail=None, code=None)
當請求發生時,任何可用渲染器都不符合 Accept
header 時引發。
預設情況下,此異常會導致 HTTP 狀態碼為 "406 Not Acceptable" 的響應。
UnsupportedMediaType
簽名: UnsupportedMediaType(media_type, detail=None, code=None)
如果在訪問 request.data
時沒有可以處理請求資料的內容型別的解析器,就會引發。
預設情況下,此異常會導致 HTTP 狀態碼 "415 Unsupported Media Type" 的響應。
Throttled
簽名: Throttled(wait=None, detail=None, code=None)
傳入的請求未通過限流檢查時引發。
預設情況下,此異常會導致 HTTP 狀態碼 "429 Too Many Requests" 的響應。
ValidationError
簽名: ValidationError(detail, code=None)
ValidationError
異常與其他 APIException
類略有不同:
detail
引數是必需的,不是可選的。detail
引數可以是錯誤詳情列表或字典,也可以是巢狀的資料結構。- 按照慣例,你應該匯入 serializers 模組並使用完全限定的
ValidationError
樣式,以區別於 Django 內建的驗證錯誤。例如:raise serializers.ValidationError('This field must be an integer value.')
ValidationError
類應該用於序列化類和欄位驗證以及驗證器類。使用 raise_exception
關鍵字引數呼叫 serializer.is_valid
時也會引發此問題:
serializer.is_valid(raise_exception=True)
複製程式碼
通用檢視使用 raise_exception=True
標誌,意味著你可以在 API 中全域性覆蓋驗證錯誤響應的樣式。為此,請使用自定義異常處理程式,如上所述。
預設情況下,此異常會導致 HTTP 狀態碼 "400 Bad Request" 的響應。