1. 程式人生 > >flask如何優雅的處理異常

flask如何優雅的處理異常

如果使用flask編寫RESTful風格的程式,不可避免的就是異常的處理問題。flask預設的異常處理方式實在不敢恭維,如果想達到能友好的返回錯誤資訊,自己還要多做不少工作。

什麼算優雅的返回錯誤資訊呢?舉個例子,下面的程式碼是獲取User token的一段程式碼,如果密碼驗證失敗將會向前端反饋錯誤資訊,而反饋是通過丟擲異常:

def get_user_token(self, data):
  valid(get_token, data)

  with get_session() as session:
  if "username" in data:
      user = session.query(User).filter_by(user_name=data['username']).first()
  elif "email" in data:
      user = session.query(User).filter_by(email=data['email']).first()
  else:
    user = None
    if not user or not user.valid_passwd(data['password']):
      raise APIException("get_token_error", "can't get the token with a wrong user password", 400)

  expiration = data['expiration'] if "expiration" in data else 86400
  return {
    "token": user.generate_auth_token(expiration=expiration),
    "id": user.id,
  }

那麼可以在上層邏輯捕獲這個異常,並把異常的資訊包裝一下優雅的反饋給前端,比如這樣處理:

try:
  get_user_token(data)
except APIExcepting as e:
  return json.dumps({
    "id": e.error_id,
    "message": e.message,
    "code": e.code,
  })

當然,這只是最簡單的包裝,而且是http狀態碼寫在了response的body裡,而最好應該直接返回相應狀態碼的response。

每個下層邏輯的異常由上層來處理是很常規的辦法,但是有的異常是可以直接拋到最上層的,比如引數錯誤,密碼錯誤等,這些異常差的只是一個友好的顯示,如果有辦法對這些異常自動封裝一個友好的反饋顯示,那麼就省不少事情。

而利用flask是可以實現這一點的,flask有一個叫errorhandler的東西,它可以捕捉特定的異常,然後根據這個異常進行自定義操作。那麼,我們就可以建立一個可以直接拋到最上層的異常,由errorhandler監聽捕捉這個異常,把捕捉到的異常封裝成友好的response反饋出去就可以了。

首先是寫一個可以被拋到最上層的異常:

class APIException(Base):
    def __init__(self, error_id, message, code=500):
        super(Base, self).__init__()
        self.raw_message = message
        self.error_id = error_id
        self.code = code
        self.message = message

    def to_dict(self):
        result = {
            "id": self.error_id,
            "code": self.code,
            "message": self.message,
        }
        return result

這個異常使用起來也很簡單raise APIException(error_id, message, status_code)就可以了,而to_dict方法可以構建一個符合REST風格的錯誤資訊返回body。下面就差一個錯誤處理控制代碼來把APIEception的例項封裝成友好的response了。

@app.errorhandler(APIException)
def handle_api_exception(error):
    from flask import jsonify
    response = jsonify(error.to_dict())
    response.status_code = error.code
    return response

這樣就有了一個處理APIException的控制代碼,當有這個異常丟擲的話,它便會返回一個友好的body,而且狀態碼也會是在raise時制定的狀態碼。


歡迎到微信裡去當吃瓜群眾