python - 如何改进 python/django 中的异常处理
问题描述
这是我在 django 项目中处理异常的示例:
def boxinfo(request, url: str):
box = get_box(url)
try:
box.connect()
except requests.exceptions.ConnectionError as e:
context = {'error_message': 'Could not connect to your box because the host is unknown.'}
return render(request, 'box/error.html', context)
except requests.exceptions.RequestException as e:
context = {'error_message': 'Could not connect to your box because of an unknown error.'}
return render(request, 'box/error.html', context)
- 现在只有两个异常,但是对于几个请求异常应该更多。但是视图方法已经被这个膨胀了。有没有办法将异常处理转发到单独的错误方法?
- 还有一个问题,我需要在这里为每个调用渲染消息,我想避免这种情况。
- 在这里我也重复每个除了“无法连接到你的盒子,因为”,当出现任何异常时应该设置一次。
我可以通过以下方式解决它:
try:
box.connect()
except Exception as e:
return error_handling(request, e)
-
def error_handling(request, e):
if type(e).__name__ == requests.exceptions.ConnectionError.__name__:
context = {'error_message': 'Could not connect to your box because the host is unknown.'}
elif type(e).__name__ == requests.exceptions.RequestException.__name__:
context = {'error_message': 'Could not connect to your box because of an unknown error.'}
else:
context = {'error_message': 'There was an unkown error, sorry.'}
return render(request, 'box/error.html', context)
然后我当然可以改进错误消息。但总的来说,它是一种处理异常的pythonic方式if/else
吗?例如RequestException
,如果ConnectionError
被抛出,我无法在这里捕获,所以我需要捕获每个请求错误,这看起来更像是一个丑陋的摆弄......
解决方案
这是装饰器的一个用例。如果它是适用于所有视图的更通用的东西(例如,错误日志记录),您可以使用Django 异常中间件钩子,但这里似乎并非如此。
关于重复错误字符串问题,解决它的 Pythonic 方法是{replaceable_parts}
插入一个常量基字符串,以便稍后您可以使用.format()
它们。
有了这个,假设我们有以下文件decorators.py
:
import functools
from django.shortcuts import render
from requests.exceptions import ConnectionError, RequestException
BASE_ERROR_MESSAGE = 'Could not connect to your box because {error_reason}'
def handle_view_exception(func):
"""Decorator for handling exceptions."""
@functools.wraps(func)
def wrapper(request, *args, **kwargs):
try:
response = func(request, *args, **kwargs)
except RequestException as e:
error_reason = 'of an unknown error.'
if isinstance(e, ConnectionError):
error_reason = 'the host is unknown.'
context = {
'error_message': BASE_ERROR_MESSAGE.format(error_reason=error_reason),
}
response = render(request, 'box/error.html', context)
return response
return wrapper
我们使用ConnectionError 是请求库中 RequestException 的子类这一事实。我们也可以用异常类作为键来做一个字典,但这里的问题是它不能处理异常类继承,这是一种稍后会产生细微错误的遗漏。该isinstance
函数是一种更可靠的检查方式。
如果您的异常树不断增长,您可以不断添加if
语句。万一开始变得笨拙,我建议看这里,但我会说在错误处理中有这么多分支是一种代码味道。
那么在你看来:
from .decorators import handle_view_exception
@handle_view_exception
def boxinfo(request, url: str):
box = get_box(url)
box.connect()
...
这样,错误处理逻辑与您的视图完全分离,最重要的是,它是可重用的。
推荐阅读
- java - 我正在我的应用程序中构建选项卡布局,在调试时我不断收到此错误,阻止应用程序工作并不断崩溃,我该怎么办?,
- swift - 线程 1:“UICollectionView 必须使用非零布局参数初始化
- security - Delphi中的缓冲区溢出漏洞
- python - 放入 main() 定义时代码不起作用
- jquery - 当用户到达页面中的特定点时如何打印内容?(不依赖于任何类或 ID 元素)
- java - 使用 ObjectRocket 插件将带有 MongoDB 应用程序的 jHipster 部署到 Heroku
- java - 单击按钮时Android应用程序崩溃
- selenium - 如何管理手动打开的选项卡
- mysql - ORM 如何从数据库中获取数据
- vue.js - 自相矛盾的漂亮