如何捕获flask_restful应用程序中引发的所有异常

问题描述 投票:0回答:3

我确实有一个简单的 Restful 应用程序,带有 Flask-Restful

from flask import Flask
from flask_restful import Api

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

api.add_resource(ContactList, "/contacts")


if __name__ == '__main__':
    from object_SQLAlchemy import db
    db.init_app(app)
    app.run(port=5000)


class Contact(Resource):
    parser = reqparse.RequestParser()
    parser.add_argument(
        'contact_no',
        type=str,
        required=True,
        help="This field cannot be left blank"
    )

    @throttling.Throttle("10/m", strategy=2)
    def get(self, name):
        contact = Contacts.findbyname(name)
        if contact:
            return contact.json()
        return {"message": "Contact does not exist."}, 404

“get”方法用我的节流实现进行装饰(https://github.com/scgbckbone/RESTAPI/blob/master/resources/utils/throtdling.py)。重要的是节流装饰器在某些情况下会引发异常 - 最重要的是当达到限制时。我希望能够捕获该异常并返回一些合理的 json 消息。

但是以下都不起作用:

from ..app_alchemy import api, app


@api.errorhandler(Exception)
def handle_error(e):
    return {"error": str(e)}


@app.errorhandler(500)
def handle_error_app(e):
    return {"error": str(e.args[0])}


@app.handle_exception(Exception)
def handle_it_app(e):
    return {"error": str(e.args[0])}


@api.handle_exception(Exception)
def handle_it(e):
   return {"error": str(e.args[0])}

我仍然收到默认消息

{"message": "Internal Server Error"}

我是否正确使用了错误处理程序,或者问题是否与装饰器的使用有关?我真的不知道。

python python-3.x flask python-decorators flask-restful
3个回答
3
投票

Flask-Restful 有一个用于处理异常的内置工具,您可以将异常类和响应字段的字典传递给

Api
构造函数:

api = Api(app, errors={
    'Exception': {
        'status': 400,
        'message': 'bad_request',
        'some_description': 'Something wrong with request'
    }
})

状态默认为 500,所有其他字段仅转换为 JSON 并作为响应发送。

有一个主要缺点:您不能使用异常文本作为错误消息。有一个未解决的问题


1
投票

Sentry是一个跨不同平台和框架{包括Python、Django和Flask}捕获异常的好工具。 这个示例 指导您可以将其与 Flask 应用程序集成。

我在生产中使用过它,我最喜欢的功能是它捕获错误的上下文,包括操作系统、浏览器版本等以及其他信息。


0
投票

我重写了flask_restulf.Api并重写了里面的handle_error()和error_router()。 APIException 是 HttpException 的子类,UnknownException 是 APIException 的子类。我也想尝试使用Sentry作为异常记录器,但是Sentry似乎不能很好地与flask_restful配合使用,并且它还会影响多处理部分,所以我仍在寻找记录器的解决方案。

class Api_modified(Api):

def error_router(self, original_handler, e):
    """This function decides whether the error occured in a flask-restful
    endpoint or not. If it happened in a flask-restful endpoint, our
    handler will be dispatched. If it happened in an unrelated view, a UnknownException
    will be returned.
    In the event that the error occurred in a flask-restful endpoint but
    the local handler can't resolve the situation, the router will return a
    UnknownException.

    :param original_handler: the original Flask error handler for the app
    :type original_handler: function
    :param e: the exception raised while handling the request
    :type e: Exception

    """
    if self._has_fr_route():
        try:
            return self.handle_error(e)
        except Exception:
            pass
    else:
        if isinstance(e,APIException):
            return e.get_response()
        else:
            return UnknownException("未知原因错误,具体原因请查询日志").get_response()

def handle_error(self, e):
    """Error handler for the API transforms a raised exception into a Flask
    response, with the appropriate HTTP and body.The status code is always 200, and
    the message and result in the body varies.

    :param e: the raised Exception object
    :type e: Exception

    """
    got_request_exception.send(current_app._get_current_object(), exception=e)

    if not isinstance(e, HTTPException) and current_app.propagate_exceptions:
        exc_type, exc_value, tb = sys.exc_info()
        if exc_value is e:
            raise UnknownException("未知原因错误,具体原因请查询日志")
        else:
            raise UnknownException("未知原因错误,具体原因请查询日志")

    headers = Headers()
    if isinstance(e, HTTPException):
        if e.response is not None:
            # If HTTPException is initialized with a response, then return e.get_response().
            # This prevents specified error response from being overridden.
            # eg. HTTPException(response=Response("Hello World"))
            resp = e.get_response()
            return resp

        code = e.code
        default_data = {
            'message': getattr(e, 'description', http_status_message(code))
        }
        headers = e.get_response().headers
    else:
        resp = UnknownException("未知原因错误,具体原因请查询日志").get_response()
        return resp

    # Werkzeug exceptions generate a content-length header which is added
    # to the response in addition to the actual content-length header
    # https://github.com/flask-restful/flask-restful/issues/534
    remove_headers = ('Content-Length',)

    for header in remove_headers:
        headers.pop(header, None)

    data = getattr(e, 'data', default_data)

    if code and code >= 500:
        exc_info = sys.exc_info()
        if exc_info[1] is None:
            exc_info = None
        current_app.log_exception(exc_info)

    error_cls_name = type(e).__name__
    if error_cls_name in self.errors:
        custom_data = self.errors.get(error_cls_name, {})
        code = custom_data.get('status', 500)
        data.update(custom_data)

    if code == 406 and self.default_mediatype is None:
        # if we are handling NotAcceptable (406), make sure that
        # make_response uses a representation we support as the
        # default mediatype (so that make_response doesn't throw
        # another NotAcceptable error).
        supported_mediatypes = list(self.representations.keys())
        fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"
        resp = self.make_response(
            data,
            code,
            headers,
            fallback_mediatype = fallback_mediatype
        )
    else:
        resp = self.make_response(data, code, headers)

    if code == 401:
        resp = self.unauthorized(resp)
    return resp
© www.soinside.com 2019 - 2024. All rights reserved.