异常
异常…允许将错误处理组织在一个程序结构的中心或高级位置。
— 道格·赫尔曼,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
返回状态代码为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 消费者处理。