Flask-restful - 自定义错误处理

时间:2016-12-14 18:18:01

标签: python flask flask-restful

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

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

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)) 不存在的内容。

目前,我正在做以下事情:

MyResource

使用不存在的id调用时{'message': 'ResourceDoesNotExist: Resource MyModel 5 not found'} 将返回以下JSON:

ResponseEntity<String> entity = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, String.class );
entity.getBody();

这样可以正常使用,但我想使用Flask-restful错误处理。

4 个答案:

答案 0 :(得分:5)

根据the docs

  

Flask-RESTful将在Flask-RESTful路由上发生的任何400或500错误上调用handle_error()函数,并保留其他路由。

您可以利用此功能来实现所需的功能。唯一的缺点是必须创建自定义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))

答案 1 :(得分:2)

我已经使用了蓝图来解决烧瓶不稳定的问题,并且我发现issue上提供的解决方案@billmccord和@cedmt在这种情况下不起作用,因为蓝图不起作用具有handle_exceptionhandle_user_exception函数。

我的解决方法是增强handle_error的功能Api,如果已注册“异常”的“错误处理程序”,请提出该问题,即在应用程序上注册的“错误处理程序”将处理该异常,或者将异常传递给“烧瓶稳定的”受控“自定义错误处理程序”。

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)

顺便说一句,似乎烧瓶不宜使用...

答案 2 :(得分:0)

好吧,我在应用程序逻辑中遇到了一些问题,因为再次捕获了异常并更改了响应内容,所以这似乎很糟糕。现在这对我而言有效。

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  

答案 3 :(得分:0)

我没有将错误指令附加到Api,而是重写Api类的handle_error方法来处理应用程序的异常。

# File: app.py
# ------------

from flask import Blueprint, jsonify
from flask_restful import Api
from werkzeug.http import HTTP_STATUS_CODES
from werkzeug.exceptions import HTTPException

from view import SomeView

class ExtendedAPI(Api):
    """This class overrides 'handle_error' method of 'Api' class ,
    to extend global exception handing functionality of 'flask-restful'.
    """
    def handle_error(self, err):
        """It helps preventing writing unnecessary
        try/except block though out the application
        """
        print(err) # log every exception raised in the application
        # Handle HTTPExceptions
        if isinstance(err, HTTPException):
            return jsonify({
                    'message': getattr(
                        err, 'description', HTTP_STATUS_CODES.get(err.code, '')
                    )
                }), err.code
        # If msg attribute is not set,
        # consider it as Python core exception and
        # hide sensitive error info from end user
        if not getattr(err, 'message', None):
            return jsonify({
                'message': 'Server has encountered some error'
                }), 500
        # Handle application specific custom exceptions
        return jsonify(**err.kwargs), err.http_status_code


api_bp = Blueprint('api', __name__)
api = ExtendedAPI(api_bp)

# Routes
api.add_resource(SomeView, '/some_list')

自定义例外可以保存在单独的文件中,例如:

# File: errors.py
# ---------------


class Error(Exception):
    """Base class for other exceptions"""
    def __init__(self, http_status_code:int, *args, **kwargs):
        # If the key `msg` is provided, provide the msg string
        # to Exception class in order to display
        # the msg while raising the exception
        self.http_status_code = http_status_code
        self.kwargs = kwargs
        msg = kwargs.get('msg', kwargs.get('message'))
        if msg:
            args = (msg,)
            super().__init__(args)
        self.args = list(args)
        for key in kwargs.keys():
            setattr(self, key, kwargs[key])


class ValidationError(Error):
    """Should be raised in case of custom validations"""

在视图中可以引发异常,例如:

# File: view.py
# -------------

from flask_restful import Resource
from errors import ValidationError as VE


class SomeView(Resource):
    def get(self):
        raise VE(
            400, # Http Status code
            msg='some error message', code=SomeCode
        )

在视图中,实际上可以从应用程序中的任何文件引发异常,这些异常将由ExtendedAPI handle_error方法处理。