RESTfulapi设计:通过嵌套函数处理异常(python,flask)

RESTful api design: handle exceptions through nested functions (python, flask)

我想通过在设计 API 时更稳健地掌握 tryexceptraise 并减少冗长的代码来改进我的编码风格。

我有嵌套函数,当一个函数捕获到异常时,我将异常传递给另一个函数,依此类推。

但像这样,我可以传播对同一错误的多次检查。 我指的是: [Using try vs if in python 考虑试运行成本。

如何跨嵌套函数只处理一次错误?

例如

我正在做这样的事情:

def f(key):
   try:
     #do something
     return {'data' : 'data_structure'}
   except:
     return {'error': 'there is an error'}

@application.route('/')
def api_f(key):
    data = f(k)
    try:
       # do something on data
       return jsonify(data)
    except:
       return jsonify({'error':'error in key'})

IMO try/except 是处理此用例的最佳方法。每当您想处理异常情况时,请输入 try/except。如果你不能(或不想)以某种理智的方式处理异常,让它冒泡到堆栈的更上层处理。当然,有多种理由采取不同的方法(例如,您并不真正关心错误,可以 return 在不中断正常操作的情况下做其他事情;您希望“异常”情况经常发生;等等。 ),但这里 try/except 似乎最有意义:

在您的示例中,最好将 f() 中的 try/except 保留下来,除非您想...

引发一个不同的错误(小心这个,因为这会重置你的堆栈跟踪):

try:
    ### Do some stuff
except:
    raise CustomError('Bad things')

做一些错误处理(例如记录;清理;等):

try:
    ### Do some stuff
except:
    logger.exception('Bad things')
    cleanup()

    ### Re-raise the same error
    raise

否则,就让错误冒出来吧。

后续功能(例如 g()h())将以相同方式运行。在你的情况下,你可能想要一些 jsonify 辅助函数,在可能的情况下 jsonify,但也处理 non-json 数据:

def handle_json(data):
    try:
        return json.dumps(data)
    except TypeError, e:
        logger.exception('Could not decode json from %s: %s', data, e)

        # Could also re-raise the same error
        raise CustomJSONError('Bad things')

然后,您将让处理程序在堆栈的更上层处理原始错误或自定义错误,以可以处理任何错误的全局处理程序结束。在我的 Flask 应用程序中,我创建了自定义错误 classes,我的全局处理程序能够对其进行解析和处理。当然,全局处理程序也配置为处理意外错误。

例如,我可能有一个基础 class 用于所有 http 错误…

### Not to be raised directly; raise sub-class instances instead
class BaseHTTPError(Exception):
    def __init__(self, message=None, payload=None):
        Exception.__init__(self)
        if message is not None:
            self.message = message
        else:
            self.message = self.default_message

        self.payload = payload

    def to_dict(self):
        """ 
        Call this in the the error handler to serialize the
        error for the json-encoded http response body.
        """
        payload = dict(self.payload or ()) 
        payload['message'] = self.message
        payload['code'] = self.code
        return payload

…针对各种 http 错误进行了扩展:

class NotFoundError(BaseHTTPError):
    code = 404 
    default_message = 'Resource not found'

class BadRequestError(BaseHTTPError):
    code = 400 
    default_message = 'Bad Request'

class NotFoundError(BaseHTTPError):
    code = 500 
    default_message = 'Internal Server Error'

### Whatever other http errors you want

我的全局处理程序看起来像这样(我正在使用 flask_restful,所以这被定义为我的扩展 flask_restful.Api class 上的一个方法):

class RestAPI(flask_restful.Api):
  def handle_error(self, e):
      code = getattr(e, 'code', 500)
      message = getattr(e, 'message', 'Internal Server Error')
      to_dict = getattr(e, 'to_dict', None)

      if code == 500:
          logger.exception(e)

      if to_dict:
          data = to_dict()
      else:
          data = {'code': code, 'message': message}

      return self.make_response(data, code)

使用 flask_restful,您也可以只 define your error classes 并将它们作为字典传递给 flask_restful.Api constructor,但我更喜欢定义我自己的可以添加有效负载数据的处理程序的灵活性动态地。 flask_restful 自动将任何未处理的错误传递给 handle_error。因此,这是我唯一需要将错误转换为 json 数据的地方,因为这是 flask_restful 需要的,以便 return 将 https 状态和有效负载发送给客户端。请注意,即使错误类型未知(例如 to_dict 未定义),我也可以 return 向客户端发送一个正常的 http 状态和有效负载,而不必将错误转换到堆栈的下方。

同样,在您的应用程序的其他地方将错误转换为一些有用的 return 值是有原因的,但对于上述情况,try/except 效果很好。