exceptions.py

异常

异常…允许将错误处理组织在一个程序结构的中心或高级位置。

— 道格·赫尔曼,Python 异常处理技术

REST 框架视图中的异常处理

REST 框架的视图处理各种异常,并处理返回适当的错误响应。

处理的异常为

  • 在 REST 框架内部引发的 APIException 子类。
  • Django 的 Http404 异常。
  • Django 的 PermissionDenied 异常。

在每种情况下,REST 框架都会返回具有适当状态代码和内容类型的响应。响应正文将包括有关错误性质的任何其他详细信息。

大多数错误响应将在响应正文中包含一个键 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 使用的错误响应样式。

该函数必须采用一对参数,第一个是要处理的异常,第二个是包含任何额外上下文(例如当前正在处理的视图)的字典。异常处理函数应返回一个 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 框架提供的标准异常处理程序

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 服务不可用”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 未经身份验证”,但它也可能导致“403 禁止”响应,具体取决于正在使用的身份验证方案。有关更多详细信息,请参阅身份验证文档

NotAuthenticated

签名:NotAuthenticated(detail=None, code=None)

当未经身份验证的请求未通过权限检查时引发。

默认情况下,此异常会导致响应带有 HTTP 状态代码“401 未经身份验证”,但它也可能导致“403 禁止”响应,具体取决于正在使用的身份验证方案。有关更多详细信息,请参阅身份验证文档

PermissionDenied

签名:PermissionDenied(detail=None, code=None)

当经过身份验证的请求未通过权限检查时引发。

默认情况下,此异常会导致响应带有 HTTP 状态代码“403 禁止”。

NotFound

签名:NotFound(detail=None, code=None)

当给定 URL 处不存在资源时引发。此异常等效于标准的 Django 异常Http404

默认情况下,此异常会导致响应带有 HTTP 状态代码“404 未找到”。

MethodNotAllowed

签名:MethodNotAllowed(method, detail=None, code=None)

当传入的请求发生且未映射到视图上的处理程序方法时引发。

默认情况下,此异常会导致响应带有 HTTP 状态代码“405 方法不允许”。

NotAcceptable

签名:NotAcceptable(detail=None, code=None)

当传入的请求带有任何可用渲染器都无法满足的Accept标头时引发。

默认情况下,此异常会导致响应带有 HTTP 状态代码“406 不可接受”。

UnsupportedMediaType

签名:UnsupportedMediaType(media_type, detail=None, code=None)

当访问request.data时,如果没有解析器可以处理请求数据的内容类型时引发。

默认情况下,此异常会导致响应带有 HTTP 状态代码“415 不支持的媒体类型”。

Throttled

签名:Throttled(wait=None, detail=None, code=None)

当传入的请求未通过节流检查时引发。

默认情况下,此异常会导致响应带有 HTTP 状态代码“429 请求过多”。

ValidationError

签名:ValidationError(detail=None, code=None)

ValidationError异常与其他APIException类略有不同

  • detail参数可以是错误详细信息的列表或字典,也可以是嵌套数据结构。通过使用字典,您可以在序列化器的validate()方法中执行对象级验证时指定字段级错误。例如。raise serializers.ValidationError({'name': 'Please enter a valid name.'})
  • 根据惯例,你应该导入序列化器模块并使用完全限定的ValidationError样式,以便将其与 Django 的内置验证错误区分开来。例如。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错误请求响应的错误视图。(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 消费者处理。