django rest framework 的 exception_handler 是专为 api 异常标准化设计的中心化钩子,它能统一处理 drf 视图内抛出的语义化异常(如 validationerror、permissiondenied),返回结构化 json 响应,避免视图层重复 try/except;但不捕获视图外异常(如 url 解析失败、中间件错误或数据库连接中断),此类需由中间件 process_exception 兜底。

为什么直接在视图里写 try/except 不够用
Django 的 APIView 或 ViewSet 中手动捕获异常,会导致重复逻辑:每个接口都要写类似的 try...except ValidationError as e:、return Response(..., status=400)。更麻烦的是,第三方库抛的异常(比如 django.core.exceptions.ObjectDoesNotExist)或自定义业务异常(如 InsufficientBalanceError)无法被统一拦截,前端收到的错误格式也不一致。
真正需要的是一个中心化入口,在请求进入视图前/后、响应返回前做异常翻译和标准化封装 —— 这就是 Django 的 EXCEPTION_HANDLER 钩子。
怎么配置全局异常处理器
Django REST Framework 提供了 EXCEPTION_HANDLER 设置项,它指向一个函数,接收 exc 和 context,返回 Response 对象。
在 settings.py 中添加:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'myapp.exceptions.custom_exception_handler',
}
然后在 myapp/exceptions.py 中定义处理函数:
from rest_framework.views import exception_handler
from rest_framework.response import Response
<p>def custom_exception_handler(exc, context):</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill3219" title="python全能编程助手"><img
src="https://img.php.cn/upload/skill/000/000/081/178952049933674.jpg" alt="python全能编程助手" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill3219" title="python全能编程助手" class="overflowclass">python全能编程助手</a>
<p class="overflowclass">SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、</p>
</div>
<a rel="nofollow" href="/xiazai/skill3219" title="python全能编程助手" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h1>先让 DRF 默认处理一部分(如 ParseError、AuthenticationFailed)</h1><pre class="brush:python;toolbar:false;">response = exception_handler(exc, context)
if response is not None:
# DRF 已处理的,只统一调整结构
response.data = {'code': response.status_code, 'message': response.data}
return response
# 处理 DRF 未覆盖的异常,例如 ObjectDoesNotExist、ValueError、自定义异常
if isinstance(exc, ValueError):
return Response({'code': 400, 'message': str(exc)}, status=400)
if isinstance(exc, ZeroDivisionError):
return Response({'code': 500, 'message': '计算错误'}, status=500)
# 兜底:未识别异常一律 500
return Response({'code': 500, 'message': '服务器内部错误'}, status=500)
如何让自定义异常自动映射 HTTP 状态码
硬编码 isinstance(exc, MyCustomError) 在大型项目里难维护。推荐用异常类自带状态码属性:
定义异常基类:
class APIException(Exception):
status_code = 500
default_message = '服务器内部错误'
<pre class="brush:python;toolbar:false;">def __init__(self, message=None, status_code=None):
self.message = message or self.default_message
self.status_code = status_code or self.status_code
super().__init__(self.message)
子类直接声明状态码:
class PermissionDeniedError(APIException):
status_code = 403
default_message = '权限不足'
<p>class NotFoundError(APIException):
status_code = 404
default_message = '资源不存在'</p>
在 custom_exception_handler 中补充支持:
if isinstance(exc, APIException):
return Response({'code': exc.status_code, 'message': exc.message}, status=exc.status_code)
容易忽略的边界情况
- exception_handler 不处理视图外的异常(如中间件抛错、URL 解析失败、数据库连接中断),这类错误会落到 Django 默认 500 页面,需配合 LOGGING 和 handler500 视图兜底
- context 参数里有 context['request'] 和 context['view'],可用于记录日志时带上请求 ID 或视图名,但别在里面做耗时操作(如发邮件)
- 如果用了 django-filter 或 drf-spectacular,某些校验异常(如 ValidationError)可能已被 DRF 自动转成 400,此时 response 不为 None,直接改 response.data 即可,无需重复判断
- 返回的 Response 必须是 JSON 可序列化的数据,避免传入 datetime、Decimal 等类型,否则会触发二次异常
钩子本身不复杂,关键在于异常分类是否清晰、状态码映射是否符合 REST 语义、以及和日志、监控链路是否对齐 —— 这些才是后期排查问题时最费时间的地方。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










