【问题标题】:Custom error message json object with flask-restful带有flask-restful的自定义错误消息json对象
【发布时间】:2014-03-05 12:35:10
【问题描述】:

通过abort()方法很容易将flask-restful的错误信息传播给客户端,比如

abort(500, message="Fatal error: Pizza the Hutt was found dead earlier today
in the back seat of his stretched limo. Evidently, the notorious gangster
became locked in his car and ate himself to death.")

这将生成以下 json 输出

{
  "message": "Fatal error: Pizza the Hutt was found dead earlier today
       in the back seat of his stretched limo. Evidently, the notorious gangster
       became locked in his car and ate himself to death.", 
  "status": 500
}

有没有办法用其他成员自定义 json 输出?例如:

{
  "sub_code": 42,
  "action": "redirect:#/Outer/Space"
  "message": "You idiots! These are not them! You've captured their stunt doubles!", 
  "status": 500
}

【问题讨论】:

    标签: flask werkzeug flask-restful


    【解决方案1】:

    使用 Flask-RESTful(0.3.8 或更高版本)

    from flask_restful import Api
    customErrors = {
        'NotFound': {
            'message': "The resource that you are trying to access does not exist",
            'status': 404,
            'anotherMessage': 'Another message here'
        },
        'BadRequest': {
            'message': "The server was not able to handle this request",
            'status': 400,
            'anotherMessage': 'Another message here'
        }
    }
    app = Flask(__name__)
    api = Api(app, catch_all_404s=True, errors=customErrors)
    

    诀窍是使用来自Werkzeug Docs 的异常

    例如,如果你想处理一个 400 请求,你应该将 BadRequest 添加到 customErrors json 对象中。

    或者如果你想处理 404 错误,那么在你的 json 对象中使用 NotFound 等等

    【讨论】:

      【解决方案2】:

      正如@Miguel 所示,通常你不应该使用异常,只返回一些错误响应。但是,有时您确实需要一个引发异常的中止机制。例如,这在过滤方法中可能很有用。请注意flask.abort 接受Response 对象(检查此gist):

      from flask import abort, make_response, jsonify
      
      response = make_response(jsonify(message="Message goes here"), 400)
      abort(response)
      

      【讨论】:

        【解决方案3】:

        我不同意@Miguel 关于abort() 的相关性。除非您使用 Flask 构建 HTTP 应用程序以外的东西(使用请求/响应范例),否则我相信您应该尽可能多地使用 HTTPExceptions(请参阅 werkzeug.exceptions 模块)。这也意味着使用中止机制(这只是这些异常的捷径)。相反,如果您选择在视图中显式构建并返回您自己的错误,则会导致您需要使用一系列 if/else/return 检查值的模式,这通常是不必要的。请记住,您的函数很可能在请求/响应管道的上下文中运行。不必在做出决定之前一直返回视图,只需在失败点中止请求并完成它即可。该框架完全理解这种模式并具有偶然性。并且您仍然可以在需要时捕获异常(也许用其他消息补充它,或者挽救请求)。

        因此,类似于@Miguel,但保留了预期的中止机制:

         def json_abort(status_code, data=None):
            response = jsonify(data or {'error': 'There was an error'})
            response.status_code = status_code
            abort(response)
        
        # then in app during a request
        
        def check_unique_username(username):
            if UserModel.by__username(username):
                json_abort(409, {'error': 'The username is taken'})
        
        def fetch_user(user_id): 
            try:
                return UserModel.get(user_id)
            except UserModel.NotFound:
                json_abort(404, {'error': 'User not found'})
        

        【讨论】:

          【解决方案4】:

          我必须为我的子类 HttpException 定义属性 code 才能使此自定义错误处理正常工作:

          from werkzeug.exceptions import HTTPException
          from flask_restful import Api
          from flask import Blueprint
          
          api_bp = Blueprint('api',__name__)
          
          class ResourceAlreadyExists(HTTPException):
              code = 400
          
          errors = {
              'ResourceAlreadyExists': {
                  'message': "This resource already exists.",
                  'status': 409,
              },
          }
          
          api = Api(api_bp, errors=errors)
          

          然后再引发异常

          raise ResourceAlreadyExists
          

          【讨论】:

            【解决方案5】:

            我没有 50 声望来评论@dappiu,所以我只需要写一个新的答案,但它确实与 “Flask-RESTful 设法提供了一种更清洁的方式来处理错误” 作为very poorly documented here

            这是一个糟糕的文档,我花了一段时间才弄清楚如何使用它。关键是你的自定义异常必须继承自flask_restful import HTTPException。请注意,您不能使用 Python Exception。

            from flask_restful import HTTPException
            
            class UserAlreadyExistsError(HTTPException):
                pass
            
            custom_errors = {
                'UserAlreadyExistsError': {
                    'message': "A user with that username already exists.",
                    'status': 409,
                }
            }
            
            api = Api(app, errors=custom_errors)
            

            Flask-RESTful 团队在简化自定义异常处理方面做得很好,但文档毁了这项工作。

            【讨论】:

            • 你不需要从 HTTPException 扩展。使用 Exception 可以正常工作,您只需记住将 debug 设置为 FALSE 以查看正确的错误响应,否则它将进入调试并显示 html stacktrace 错误。
            • 是的。更明确地说:class UserAlreadyExistsError(Exception) 无需从 flask_restful 导入 HTTPException 就足够了
            • 这将不允许您发送自定义错误消息,对吗? (我的意思是在例外的时候。)
            • 正如@Sebastian 所说 - Flask 调试模式 必须禁用 (false)。
            • 我可以知道如何从资源代码中提高UserAlreadyExistsError 吗?
            【解决方案6】:

            显然已经晚了,但与此同时,正如docs 所指出的那样,Flask-RESTful 设法提供了一种更简洁的方式来处理错误。

            还打开了issue 建议改进可以提供帮助。

            【讨论】:

            • 实际上文档并没有明确(至少对我而言)如何定义自定义错误消息。没有使用它的代码示例。
            • @FrancisDavey 将带有自定义错误的 dict 传递给 Api 构造函数,就像文档中的示例一样,然后在需要时“引发 MyCustomError”,然后让 flask-restful 完成其余的工作。 flask-restful 所做的是为您在错误字典中指定的每个异常设置一个 @app.errorhandler(MyCustomError)。
            • 谢谢。不清楚的是字典中的名称是异常的名称,您还可以通过子类化 Exception 在其他地方创建。至少这是我认为它应该做的。正如我所说,没有使用它的示例代码,所以很难说。仔细重新阅读该页面仍然让我有点困惑。这就是我问这个问题的原因。
            • @dappiu - 我收到了NameError: global name 'InvalidEmailError' is not defined。我在我的包的 init.py 文件中定义了我的错误,我试图在我的 init.py 旁边的一个views.py文件中raise InvalidEmailError -知道为什么我会收到NameError 吗?
            • 我同意 Francis 的观点——文档中的示例非常简洁,不清楚键是字符串还是真的应该是异常,我无法让它工作。实际运行的示例代码将不胜感激。
            【解决方案7】:

            人们倾向于过度使用abort(),而实际上很容易产生自己的错误。您可以编写一个轻松生成自定义错误的函数,这是一个与您的 JSON 匹配的函数:

            def make_error(status_code, sub_code, message, action):
                response = jsonify({
                    'status': status_code,
                    'sub_code': sub_code,
                    'message': message,
                    'action': action
                })
                response.status_code = status_code
                return response
            

            那么,不要调用abort(),而是这样做:

            @route('/')
            def my_view_function():
                # ...
                if need_to_return_error:
                    return make_error(500, 42, 'You idiots!...', 'redirect...')
                # ...
            

            【讨论】:

            • 我同意这很好而且很干净,但有时你确实需要一个中止机制(有例外)以避免到处写那些 if+return。
            • 这太简单了,在嵌套函数中毫无用处。
            猜你喜欢
            • 1970-01-01
            • 2018-04-28
            • 2018-09-02
            • 2017-04-30
            • 1970-01-01
            • 2016-05-14
            • 2015-04-10
            • 2019-12-14
            • 1970-01-01
            相关资源
            最近更新 更多