Flask restful-自定义错误处理

2024-05-18 15:46:21 发布

您现在位置:Python中文网/ 问答频道 /正文

我想为Flask restful API定义自定义错误处理。

文档here中建议的方法是执行以下操作:

errors = {
    'UserAlreadyExistsError': {
        'message': "A user with that username already exists.",
        'status': 409,
    },
    'ResourceDoesNotExist': {
        'message': "A resource with that ID no longer exists.",
        'status': 410,
        'extra': "Any extra information you want.",
    },
}
app = Flask(__name__)
api = flask_restful.Api(app, errors=errors)

现在我发现这种格式非常有吸引力,但是当发生异常时,我需要指定更多的参数。例如,当遇到ResourceDoesNotExist时,我想指定什么id不存在。

目前,我正在做以下工作:

app = Flask(__name__)
api = flask_restful.Api(app)


class APIException(Exception):
    def __init__(self, code, message):
        self._code = code
        self._message = message

    @property
    def code(self):
        return self._code

    @property
    def message(self):
        return self._message

    def __str__(self):
        return self.__class__.__name__ + ': ' + self.message


class ResourceDoesNotExist(APIException):
    """Custom exception when resource is not found."""
    def __init__(self, model_name, id):
        message = 'Resource {} {} not found'.format(model_name.title(), id)
        super(ResourceNotFound, self).__init__(404, message)


class MyResource(Resource):
    def get(self, id):
        try:
            model = MyModel.get(id)
            if not model:
               raise ResourceNotFound(MyModel.__name__, id)
        except APIException as e:
            abort(e.code, str(e))

当使用不存在的id调用时MyResource将返回以下JSON:

{'message': 'ResourceDoesNotExist: Resource MyModel 5 not found'}

这可以很好地工作,但我想用烧瓶restful错误处理代替。


Tags: nameselfrestfulidappflaskmessagemodel
3条回答

我使用蓝图来处理烧瓶restful,我发现在issue上提供的解决方案@billmccord和@cedmt不适用于这种情况,因为蓝图没有handle_exceptionhandle_user_exception函数。

我的解决方法是增强Api的函数handle_error,如果“异常”的“错误处理程序”已注册,只需提升它,应用程序上注册的“错误处理程序”将处理该异常,否则该异常将传递给“flask restful”控制的“自定义错误处理程序”。

class ImprovedApi(Api):
    def handle_error(self, e):
        for val in current_app.error_handler_spec.values():
            for handler in val.values():
                registered_error_handlers = list(filter(lambda x: isinstance(e, x), handler.keys()))
                if len(registered_error_handlers) > 0:
                    raise e
        return super().handle_error(e)


api_entry = ImprovedApi(api_entry_bp)

顺便说一句,这瓶酒似乎已经被弃用了。。。

根据the docs

Flask-RESTful will call the handle_error() function on any 400 or 500 error that happens on a Flask-RESTful route, and leave other routes alone.

您可以利用它来实现所需的功能。唯一的缺点是必须创建自定义Api。

class CustomApi(flask_restful.Api):

    def handle_error(self, e):
        flask_restful.abort(e.code, str(e))

如果保留已定义的异常,当发生异常时,将获得与

class MyResource(Resource):
    def get(self, id):
        try:
            model = MyModel.get(id)
            if not model:
               raise ResourceNotFound(MyModel.__name__, id)
        except APIException as e:
            abort(e.code, str(e))

好吧,我的应用程序逻辑有点问题,因为再次捕获异常和更改响应内容,这就是为什么它看起来坏了。现在这对我来说很有效。

from flask import jsonify

class ApiError(Exception):
    def __init__(self, message, payload=None, status_code=400):
        Exception.__init__(self)
        self.message = message
        self.status_code = status_code
        self.payload = payload or ()
        # loggin etc.

    def get_response(self):
        ret = dict(self.payload)
        ret['message'] = self.message
        return jsonify(ret), self.status_code

def create_app():                              
    app = Flask('foo')                                                                   
    # initialising libs. setting up config                                                        
    api_bp = Blueprint('foo', __name__)                                                  
    api = Api(api_bp)

    @app.errorhandler(ApiError)                                                            
    def handle_api_error(error):                                                           
        return error.get_response()                                                        

    app.register_blueprint(api_bp, url_prefix='/api')                                  
    # add resources                                                     
    return app  

相关问题 更多 >

    热门问题