exceptions.py

例外

例外は、プログラム構造の中央または上位レベルでエラー処理を整理して、きれいにすることを可能にします。

— ダグ・ヘルマン, 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

ステータスコード500application/jsonのコンテンツタイプを持つレスポンスを返します。

handler500として設定します。

handler500 = 'rest_framework.exceptions.server_error'

rest_framework.exceptions.bad_request

ステータスコード400application/jsonのコンテンツタイプを持つレスポンスを返します。

handler400として設定します。

handler400 = 'rest_framework.exceptions.bad_request'

サードパーティパッケージ

以下のサードパーティパッケージも利用可能です。

DRF標準化エラー

drf-standardized-errorsパッケージは、すべての4xxおよび5xxレスポンスに対して同じフォーマットを生成する例外ハンドラを提供します。これは、デフォルトの例外ハンドラのドロップイン置換であり、例外ハンドラ全体を書き直すことなく、エラーレスポンスのフォーマットをカスタマイズできます。標準化されたエラーレスポンスフォーマットは、ドキュメント化が容易で、APIコンシューマによる処理が容易です。