使用Flask-RESTful自定义错误消息的JSON对象

31

使用Flask-RESTful的abort()方法向客户端传播错误消息非常容易,例如:

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
}
7个回答

45

人们往往会过度使用abort(),而事实上生成自己的错误非常简单。您可以编写一个轻松生成自定义错误的函数,这里有一个与您的JSON匹配的示例:

人们倾向于过度使用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...')
    # ...

4
我同意这很简洁干净,但有时确实需要一个中止机制(使用异常)来避免在每个地方都写if+return。 - tokland
2
这太简单了,在嵌套函数中根本没有用。 - Fusion

28

我没有50个声望(reputation)可以评论 @dappiu,所以我只能写一个新答案,但它确实与"Flask-RESTful 管理错误的更清洁方式"相关,可以在这里非常糟糕地记录

这是一篇很糟糕的文档,让我花了一段时间才想出如何使用它。关键是你的自定义异常必须继承自 flask_restful 的 HTTPException。请注意,你不能使用 Python 异常。

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团队在使自定义异常处理变得简单方面做得很好,但文档破坏了这一努力。


7
不必从HTTPException继承,使用Exception也可以正常工作,只需记得将debug设置为FALSE以查看正确的错误响应,否则它将进入调试模式并显示HTML堆栈跟踪错误。 - Sebastian
是的。更明确地说:class UserAlreadyExistsError(Exception)就足够了,无需从flask_restful导入HTTPException。 - colidyre
这样做不会让你发送自定义错误消息,对吧?(我的意思是在异常发生时。) - Kelvin
正如@Sebastian所述 - Flask的调试模式必须被禁用(false)。 - Eido95
1
我可以知道如何从资源代码中引发 UserAlreadyExistsError 吗? - Shankar Guru
我想使用abort和自定义错误返回一个json,这可行吗? - Roy Assis

12

正如 @Miguel 所解释的那样,通常不应该使用异常,而是返回一些错误响应。然而,有时确实需要一个引发异常的中止机制。例如,在过滤器方法中可能会很有用。请注意,flask.abort 接受一个 Response 对象(查看这个 gist):

from flask import abort, make_response, jsonify

json = jsonify(message="Message goes here")
response = make_response(json, 400)
abort(response)

6
我不同意 @Miguel 对于 abort() 的相关性。除非你正在使用 Flask 构建其他类型的应用程序(具有请求/响应范例),否则我认为您应该尽可能使用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'})

3
我必须在我的子类化的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

1

显然已经晚了,但与此同时,Flask-RESTful提供了一种更清晰的处理错误的方式,正如文档所指出的。

另外,为了建议改进,打开的问题也会有所帮助。


5
实际上,文件说明并没有明确地告诉我如何定义自定义错误信息。它没有提供任何使用它的代码示例。 - Francis Davey
@FrancisDavey 将自定义错误传递给 Api 构造函数,就像文档中的示例一样。当需要时,只需 "raise MyCustomError",然后让 Flask-RESTful 完成其余操作。Flask-RESTful 的功能是为您在错误字典中指定的每个异常设置 @app.errorhandler(MyCustomError)。 - dappiu
1
谢谢。不清楚的是,字典中的名称是异常的名称,您还可以通过子类化Exception在其他地方创建它们。至少我认为它应该这样做。正如我所说,没有使用它的示例代码,因此很难确定。仔细重新阅读该页面仍然让我有点困惑。这就是为什么我问这个问题的原因。 - Francis Davey
1
@dappiu - 我遇到了 NameError: global name 'InvalidEmailError' is not defined 的问题。我已经在我的包的 init.py 文件中定义了错误,并且我正在尝试在与我的 init.py 相邻的 views.py 文件中 raise InvalidEmailError,你有任何想法为什么会出现 NameError - Alex Daro
1
我同意Francis的观点——文档中的示例非常简洁,不清楚键是字符串还是真的应该是异常,并且我无法使其正常工作。实际运行的示例代码将不胜感激。 - davidav

0
使用 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等等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接