我开始考虑在我的Django应用程序中进行适当的异常处理,我的目标是尽可能使用它。根据用户友好性,我暗示用户必须始终详细说明究竟出了什么问题。 关注this post,最佳做法是
使用状态为200的JSON响应作为正常响应和 返回(适当的!)4xx / 5xx错误响应。这些可以携带 JSON有效负载也是如此,因此您的服务器端可以添加其他详细信息 关于错误。
我试着用这个答案中的关键词来谷歌,但仍然有更多的问题而不是我脑子里的答案。
让我们在一个简单的视图中考虑它
def test_view (request):
try:
# Some code ....
if my_business_logic_is_violated():
# How do I raise the error
error_msg = "You violated bussiness logic because..."
# How do I pass error_msg
my_response = {'my_field' : value}
except ExpectedError as e:
# what is the most appropriate way to pass both error status and custom message
# How do I list all possible error types here (instead of ExpectedError to make the exception handling block as DRY and reusable as possible
return JsonResponse({'status':'false','message':message}, status=500)
答案 0 :(得分:16)
首先,您应该考虑要公开的错误:
通常会泄露4xx错误(归因于客户端的错误),以便用户更正请求。
另一方面,5xx错误(归因于服务器端的错误)通常仅在没有信息的情况下显示。在我看来,你应该使用像Sentry这样的工具来监控和解决这些错误,这可能会在其中嵌入安全问题。
在我看来,对于正确的Ajax请求,请注意,您应该返回状态代码,然后返回一些json,以帮助理解发生的事情,如消息和解释(如果适用)。
如果您的目标是使用ajax提交信息,我建议您根据需要设置form。这样您就可以轻松地通过一些验证过程。我将在示例中假设情况如此。
首先 - 请求是否正确?
def test_view(request):
message = None
explanation = None
status_code = 500
# First, is the request correct?
if request.is_ajax() and request.method == "POST":
....
else:
status_code = 400
message = "The request is not valid."
# You should log this error because this usually means your front end has a bug.
# do you whant to explain anything?
explanation = "The server could not accept your request because it was not valid. Please try again and if the error keeps happening get in contact with us."
return JsonResponse({'message':message,'explanation':explanation}, status=status_code)
第二次 - 表单中是否有错误?
form = TestForm(request.POST)
if form.is_valid():
...
else:
message = "The form has errors"
explanation = form.errors.as_data()
# Also incorrect request but this time the only flag for you should be that maybe JavaScript validation can be used.
status_code = 400
您甚至可能逐字段获取错误,因此您可以在表单中以更好的方式呈现。
第三次 - 让我们处理请求
try:
test_method(form.cleaned_data)
except `PermissionError` as e:
status_code= 403
message= "Your account doesn't have permissions to go so far!"
except `Conflict` as e:
status_code= 409
message= "Other user is working in the same information, he got there first"
....
else:
status_code= 201
message= "Object created with success!"
根据您定义的例外情况,可能需要不同的代码。转到Wikipedia并查看列表。
不要忘记响应也会因代码而异。如果您向数据库添加内容,则应返回201
。如果您刚收到信息,那么您正在寻找GET请求。
回答问题
如果没有处理,Django异常将返回500个错误,因为如果你不知道会发生异常那么它就是服务器中的错误。除了404和登录要求之外,我会为所有事情try catch
阻止。 (对于404,您可以提高它,如果您执行@login_required
或需要许可,django将使用相应的代码回复,而无需您做任何事情。)
我不完全同意这种做法。正如你所说的那样,错误应该是明确的,所以你应该知道假设发生什么以及如何解释,并使其可靠地执行。
我会说400错误就可以了。这是一个糟糕的请求,您只需要解释原因,错误代码适合您和您的js代码,所以只需保持一致。
(提供的示例) - 在text_view
中,您应该拥有第三个示例中的test_method
。
测试方法应具有以下结构:
def test_method(validated_data):
try:
my_business_logic_is_violated():
catch BusinessLogicViolation:
raise
else:
... #your code
在我的例子中:
try:
test_method(form.cleaned_data)
except `BusinessLogicViolation` as e:
status_code= 400
message= "You violated the business logic"
explanation = e.explanation
...
我认为业务逻辑违规是客户端错误,因为如果在该请求之前需要某些东西,客户端应该知道这一点并要求用户先执行此操作。 (来自Error Definition):
400(错误请求)状态代码表示服务器不能或 由于被认为是某种东西,它不会处理请求 客户端错误(例如,格式错误的请求语法,无效请求
消息框架或欺骗性请求路由)。
顺便说一下,您可以看到Python Docs on User-defined Exceptions,因此您可以提供相应的错误消息。这个示例背后的想法是,根据生成它的位置,在BusinessLogicViolation
中使用不同的消息引发my_business_logic_is_violated()
异常。
答案 1 :(得分:5)
HTTP标准中定义了很好的状态代码。你可以找到very readable list on Wikipedia。基本上4XX范围内的错误是客户端发出的错误,即如果他们请求的资源不存在,等等。如果在服务器端遇到错误,则应返回5XX范围内的错误。
关于第3点,您应该在未满足前提条件的情况下选择4XX错误,例如428 Precondition Required
,但在服务器引发语法错误时返回5XX错误。
您的示例的一个问题是除非服务器引发特定异常,否则不会返回任何响应,即当代码正常执行且不引发异常时,消息和状态代码都不会显式发送到客户端。这可以通过finally块来处理,以使代码的那部分尽可能通用。
根据你的例子:
def test_view (request):
try:
# Some code ....
status = 200
msg = 'Everything is ok.'
if my_business_logic_is_violated():
# Here we're handling client side errors, and hence we return
# status codes in the 4XX range
status = 428
msg = 'You violated bussiness logic because a precondition was not met'.
except SomeException as e:
# Here, we assume that exceptions raised are because of server
# errors and hence we return status codes in the 5XX range
status = 500
msg = 'Server error, yo'
finally:
# Here we return the response to the client, regardless of whether
# it was created in the try or the except block
return JsonResponse({'message': msg}, status=status)
但是,正如评论中所述,以相同的方式进行两种验证更有意义,即通过例外,如下所示:
def test_view (request):
try:
# Some code ....
status = 200
msg = 'Everything is ok.'
if my_business_logic_is_violated():
raise MyPreconditionException()
except MyPreconditionException as e:
# Here we're handling client side errors, and hence we return
# status codes in the 4XX range
status = 428
msg = 'Precondition not met.'
except MyServerException as e:
# Here, we assume that exceptions raised are because of server
# errors and hence we return status codes in the 5XX range
status = 500
msg = 'Server error, yo.'
finally:
# Here we return the response to the client, regardless of whether
# it was created in the try or the except block
return JsonResponse({'message': msg}, status=status)