Django REST framework API 指南(24):異常

wcode發表於2018-03-22

官方原文連結
本系列文章 github 地址
轉載請註明出處

異常

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_detaildefault_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" 的響應。

相關文章