例外
例外は、プログラム構造の中央または上位レベルでエラー処理を整理して、きれいにすることを可能にします。
— ダグ・ヘルマン, Pythonの例外処理テクニック
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
設定に設定された文字列値を使用します。
バリデーションエラーの例は次のようになります。
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で使用されるエラーレスポンスのスタイルを制御できます。
関数は引数のペアを受け取る必要があります。最初の引数は処理される例外であり、2番目の引数は現在処理されているビューなどの追加のコンテキストを含むディクショナリです。例外ハンドラー関数はResponse
オブジェクトを返すか、例外を処理できない場合はNone
を返す必要があります。ハンドラーがNone
を返した場合、例外が再発生し、Djangoは標準のHTTP 500 'サーバーエラー'レスポンスを返します。
たとえば、すべてのエラーレスポンスにレスポンスの本文に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
設定キーを使用して設定に構成する必要もあります。例:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
指定されていない場合、'EXCEPTION_HANDLER'
設定は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()
- テキストによる説明とコード識別子の両方を返します。
ほとんどの場合、エラーの詳細は単純な項目になります。
>>> 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'}
バリデーションエラーの場合、エラーの詳細は項目のリストまたはディクショナリになります。
>>> 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
ヘッダーを持つ着信リクエストが発生した場合に発生します。
デフォルトでは、この例外は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=None, code=None)
ValidationError
例外は、他のAPIException
クラスとは少し異なります。
detail
引数は、エラー詳細のリストまたはディクショナリにすることができ、ネストされたデータ構造にすることもできます。ディクショナリを使用することで、シリアライザーのvalidate()
メソッドでオブジェクトレベルのバリデーションを実行しながら、フィールドレベルのエラーを指定できます。例えば。raise serializers.ValidationError({'name': '有効な名前を入力してください。'})
- 慣例として、serializersモジュールをインポートし、Djangoの組み込みのバリデーションエラーと区別するために、完全修飾された
ValidationError
スタイルを使用する必要があります。例えば。raise serializers.ValidationError('このフィールドは整数値である必要があります。')
ValidationError
クラスは、シリアライザーとフィールドのバリデーション、およびバリデータークラスで使用する必要があります。また、raise_exception
キーワード引数を使用してserializer.is_valid
を呼び出すときにも発生します。
serializer.is_valid(raise_exception=True)
ジェネリックビューはraise_exception=True
フラグを使用します。つまり、APIでバリデーションエラーレスポンスのスタイルをグローバルにオーバーライドできます。これを行うには、前述のように、カスタム例外ハンドラーを使用します。
デフォルトでは、この例外はHTTPステータスコード「400 Bad Request」のレスポンスになります。
ジェネリックエラービュー
Django REST Frameworkは、ジェネリックJSON 500
サーバーエラーと400
不正なリクエストのレスポンスを提供するために適切な2つのエラービューを提供します。(DjangoのデフォルトのエラービューはHTMLレスポンスを提供しますが、APIのみのアプリケーションには適切ではない場合があります。)
これらは、Djangoのエラービューのカスタマイズに関するドキュメントに従って使用してください。
rest_framework.exceptions.server_error
ステータスコード500
とapplication/json
のコンテンツタイプを持つレスポンスを返します。
handler500
として設定します。
handler500 = 'rest_framework.exceptions.server_error'
rest_framework.exceptions.bad_request
ステータスコード400
とapplication/json
のコンテンツタイプを持つレスポンスを返します。
handler400
として設定します。
handler400 = 'rest_framework.exceptions.bad_request'
サードパーティパッケージ
以下のサードパーティパッケージも利用可能です。
DRF標準化エラー
drf-standardized-errorsパッケージは、すべての4xxおよび5xxレスポンスに対して同じフォーマットを生成する例外ハンドラを提供します。これは、デフォルトの例外ハンドラのドロップイン置換であり、例外ハンドラ全体を書き直すことなく、エラーレスポンスのフォーマットをカスタマイズできます。標準化されたエラーレスポンスフォーマットは、ドキュメント化が容易で、APIコンシューマによる処理が容易です。