草庐IT

python - Django - 异常处理最佳实践和发送自定义错误消息

coder 2023-05-26 原文

我开始考虑在我的 Django 应用程序中进行适当的异常处理,我的目标是使其尽可能用户友好。通过用户友好性,我暗示用户必须始终详细说明到底出了什么问题。 关注 this post ,最佳做法是

use a JSON response with status 200 for your normal responses and return an (appropriate!) 4xx/5xx response for errors. These can carry JSON payload, too, so your server side can add additional details about the error.

我试图通过这个答案中的关键词进行谷歌搜索,但我脑海中的问题仍然多于答案。

  1. 如何决定返回哪个错误代码(400 或 500)?我的意思是,Django 有许多预定义的错误类型,我该如何实现 Django 异常类型和 400-500 错误代码之间的这种映射,以使异常处理 block 尽可能 DRY 和可重用?
  2. @Reorx 在 the post 中建议的中间件方法可以吗?被认为是可行的? (答案只得到了一个赞成票,因此让我不愿意深入研究细节并在我的项目中实现它
  3. 最重要的是,有时我可能希望提出与业务逻辑相关的错误,而不是不正确的语法或空值之类的标准。例如,如果我的法人实体中没有 CEO,我可能希望禁止用户添加契约(Contract)。在这种情况下应该是什么错误状态,我如何向用户详细解释错误?

让我们从一个简单的角度来考虑它

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)

最佳答案

首先,您应该考虑要暴露哪些错误:

  • 通常会公开 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!"

根据您定义的异常(exception)情况,可能需要不同的代码。转至 Wikipedia并检查 list 。 不要忘记响应也因代码而异。如果你向数据库中添加一些东西,你应该返回一个 201。如果您刚刚获得信息,那么您正在寻找 GET 请求。

回答问题

  1. 如果不处理,Django 异常将返回 500 个错误,因为如果您不知道将要发生异常,那么它就是服务器中的错误。除了 404 和登录要求外,我会为所有内容执行 try catch block 。 (对于 404,您可以提出它,如果您这样做 @login_required 或需要权限,django 将使用适当的代码进行响应,而无需您做任何事情。

  2. 我不完全同意这种方法。正如您所说,错误应该是明确的,因此您应该始终知道假设会发生什么以及如何解释它,并使其依赖于所执行的操作。

  3. 我会说 400 错误是可以的。这是一个糟糕的请求,您只需要解释原因,错误代码适用于您和您的 js 代码,因此请保持一致。

  4. (提供的示例)- 在 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 ):

The 400 (Bad Request) status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request
message framing, or deceptive request routing).

顺便说一下,你可以看到Python Docs on User-defined Exceptions所以你可以给出适当的错误信息。此示例背后的想法是,您根据生成位置在 my_business_logic_is_violated() 中使用不同的消息引发 BusinessLogicViolation异常。

关于python - Django - 异常处理最佳实践和发送自定义错误消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38847441/

有关python - Django - 异常处理最佳实践和发送自定义错误消息的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  5. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  7. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  8. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

    我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

  9. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  10. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

随机推荐