DRF 异常处理 #
一、异常概述 #
1.1 DRF异常层次 #
text
DRF异常层次
├── APIException (基类)
│ ├── ParseError
│ ├── AuthenticationFailed
│ ├── NotAuthenticated
│ ├── PermissionDenied
│ ├── NotFound
│ ├── MethodNotAllowed
│ ├── NotAcceptable
│ ├── UnsupportedMediaType
│ ├── Throttled
│ ├── ValidationError
│ └── 自定义异常
1.2 异常处理流程 #
text
视图抛出异常
│
▼
exception_handler()
│
▼
返回Response或None
二、内置异常 #
2.1 常用异常 #
python
from rest_framework.exceptions import (
APIException,
NotFound,
PermissionDenied,
ValidationError,
AuthenticationFailed,
NotAuthenticated,
Throttled,
)
2.2 使用示例 #
python
from rest_framework.exceptions import NotFound, PermissionDenied
class ArticleViewSet(viewsets.ModelViewSet):
def retrieve(self, request, pk=None):
try:
article = Article.objects.get(pk=pk)
except Article.DoesNotExist:
raise NotFound('文章不存在')
if not article.is_published and request.user != article.author:
raise PermissionDenied('无权访问')
serializer = self.get_serializer(article)
return Response(serializer.data)
2.3 ValidationError #
python
from rest_framework.exceptions import ValidationError
class ArticleViewSet(viewsets.ModelViewSet):
def create(self, request):
if not request.data.get('title'):
raise ValidationError({'title': ['标题不能为空']})
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response(serializer.data, status=201)
三、自定义异常 #
3.1 创建自定义异常 #
python
from rest_framework.exceptions import APIException
from rest_framework import status
class ArticleNotFound(APIException):
status_code = status.HTTP_404_NOT_FOUND
default_detail = '文章不存在'
default_code = 'article_not_found'
class ArticleAlreadyPublished(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = '文章已发布,无法重复操作'
default_code = 'article_already_published'
class QuotaExceeded(APIException):
status_code = status.HTTP_429_TOO_MANY_REQUESTS
default_detail = '已超出配额限制'
default_code = 'quota_exceeded'
3.2 使用自定义异常 #
python
class ArticleViewSet(viewsets.ModelViewSet):
@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
article = self.get_object()
if article.is_published:
raise ArticleAlreadyPublished()
article.is_published = True
article.save()
return Response({'status': 'published'})
四、全局异常处理 #
4.1 自定义异常处理器 #
python
from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
import logging
logger = logging.getLogger(__name__)
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None:
custom_response = {
'success': False,
'error': {
'code': response.status_code,
'message': get_error_message(response.data),
'details': response.data
}
}
response.data = custom_response
logger.error(f'Exception: {exc}, Context: {context}')
return response
def get_error_message(data):
if isinstance(data, str):
return data
if isinstance(data, list):
return data[0] if data else '未知错误'
if isinstance(data, dict):
for key, value in data.items():
if isinstance(value, list):
return f'{key}: {value[0]}'
return f'{key}: {value}'
return '未知错误'
4.2 配置异常处理器 #
python
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'core.exceptions.custom_exception_handler',
}
4.3 响应格式 #
json
{
"success": false,
"error": {
"code": 404,
"message": "文章不存在",
"details": {
"detail": "文章不存在"
}
}
}
五、异常处理最佳实践 #
5.1 业务异常 #
python
class BusinessException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
def __init__(self, message, code=None):
self.default_detail = message
self.default_code = code or 'business_error'
super().__init__()
class ArticleViewSet(viewsets.ModelViewSet):
def perform_create(self, serializer):
if self.request.user.article_count >= 10:
raise BusinessException('已达到文章数量上限', 'article_limit_exceeded')
serializer.save(author=self.request.user)
5.2 错误码系统 #
python
class ErrorCode:
VALIDATION_ERROR = 'VALIDATION_ERROR'
NOT_FOUND = 'NOT_FOUND'
PERMISSION_DENIED = 'PERMISSION_DENIED'
AUTHENTICATION_FAILED = 'AUTHENTICATION_FAILED'
RATE_LIMITED = 'RATE_LIMITED'
BUSINESS_ERROR = 'BUSINESS_ERROR'
class ErrorDetail:
def __init__(self, message, code):
self.message = message
self.code = code
class APIException(APIException):
def __init__(self, detail=None, code=None):
if isinstance(detail, ErrorDetail):
self.detail = {
'message': detail.message,
'code': detail.code
}
else:
super().__init__(detail)
六、总结 #
本章学习了DRF异常处理:
- 内置异常:APIException及其子类
- 自定义异常:创建业务异常
- 全局处理:统一异常处理器
- 最佳实践:错误码系统
掌握异常处理,构建健壮的API系统!
最后更新:2026-03-28