最可靠方式是使用中间件捕获异常:在__call__中try/except包裹get_response,显式处理Exception、Http404、PermissionDenied等异常,返回匹配状态码的JsonResponse,并统一添加request_id便于追踪。

用 middleware 拦截所有未处理异常最可靠
直接在视图里 try/except 无法覆盖中间件、认证、路由解析等环节抛出的异常,而 middleware 是唯一能稳定捕获整个请求生命周期中任意位置异常的机制。Django 的异常中间件必须继承自 BaseHandler 流程,推荐实现为一个类中间件,在 __call__ 中包裹 self.get_response(request)。
常见错误是只捕获 Exception 却漏掉 Http404、PermissionDenied 等 Django 内置异常子类——它们不继承 Exception(而是继承 django.core.exceptions.PermissionDenied 等),必须显式列出或统一用 BaseException(但慎用,会吞掉 SystemExit 等)。
实操建议:
- 在
middlware.py中定义类,__init__不做重载,__call__包裹调用并try/except - 捕获列表至少包括:
Exception、Http404、PermissionDenied、ValidationError - 不要在中间件里调用
logger.exception()后直接return HttpResponse,应构造标准响应对象,保持状态码准确 - 避免在异常处理分支中再抛新异常(比如 JSON 序列化失败),否则会触发默认 500 页面
JsonResponse 和 status 必须匹配异常语义
返回 JsonResponse 时,status 字段不能全写 200 或硬编码 500。例如 Http404 应返回 404,PermissionDenied 对应 403,ValidationError 推荐 400。手动拼接 JSON 字符串再用 HttpResponse 返回,容易遗漏 content_type 或编码问题。
立即学习“Python免费学习笔记(深入)”;
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
实操建议:
- 用
JsonResponse({'code': 404, 'msg': 'Not found'}, status=404),而非HttpResponse(json.dumps(...), content_type='application/json') - 对
ValidationError,提取error_dict或messages字段,不要直接 str(e) - 生产环境禁用
DEBUG=True下的详细 traceback 输出,settings.DEBUG为 False 时只返回通用提示 - 统一加一个
'request_id'字段,从request.META.get('HTTP_X_REQUEST_ID')或 uuid4 生成,便于日志追踪
别动 handler404 和 handler500 这些全局钩子
很多人试图重写 handler404 视图来统一格式,但该函数只处理 URL 路由未匹配的场景,对视图内抛出的 Http404、中间件异常、数据库连接失败等完全无效。同理,handler500 只在视图层崩溃且未被任何中间件捕获时才触发,可靠性远低于自定义中间件。
实操建议:
- 删掉
urls.py里自定义的handler404 = 'myapp.views.custom_404'配置,它和你想要的“全局异常”不是一回事 - 如果真要用
handler404,仅限静态页面兜底,不要在里面做 JSON 响应逻辑 - Django 4.0+ 支持在中间件中通过
response.status_code == 404判断是否为 404 响应,可在此处二次加工,但优先级低于异常捕获中间件
测试时用 Client 模拟真实异常路径
光跑单元测试验证中间件语法正确没用。必须构造真实会触发异常的请求路径:比如访问一个不存在的 API endpoint(触发 404)、带错误 token 访问需鉴权接口(触发 AuthenticationFailed)、POST 一个字段缺失的表单(触发 ValidationError)。
实操建议:
- 用
from django.test import Client,调用c.get('/api/xxx/')后检查response.status_code和response.json()结构 - 在测试中主动 raise 异常:
with self.assertRaises(ValidationError): view_func(...),再验证中间件是否拦截 - 注意测试数据库事务回滚可能掩盖 DB 异常,可在测试设置中关闭
TransactionTestCase的自动回滚,或用TestCase+setUpTestData - 用
pytest-django的settings_overridefixture 临时关闭DEBUG,确认生产行为一致
ConnectionError、甚至 WSGI 层的 socket.timeout,这些都可能绕过 Django 异常体系。要么在 ASGI/WSGI 入口再包一层,要么接受它们最终落到 500 handler,然后确保你的中间件至少覆盖了 Django 自己抛的所有异常类型。

















