最近在做SRDP项目的后端开发,但是狗书上面对API相关内容介绍比较少,示例代码中的API部分也不涉及用户登录注册相关内容。不过幸运的是在Github上找到了一个问卷调查项目,通过阅读项目代码发现了在进行API开发时处理异常的一些技巧。
问卷项目地址:https://github.com/yuzhanglong/EasyQuestionnaire-backend
Flask异常
程序执行遇到异常是很正常的现象,在普通的程序中可以用try except
捕获异常并抛出来保证程序的正常执行。但是在web项目中,如果遇到了异常只是在服务器终端中抛出是不行的,有时必须要让用户了解到错误信息。
Flask作为一个web开发框架在设计之初就想到了这点,对异常处理也进行了封装,最典型的例子就是404异常。在默认情况下,如果访问了无效的路由,会看到一个错误页面,这个错误页面就是由Flask内部封装好的异常处理机制发出的。
如果是普通的Flask项目,在程序执行时遇到异常时可以直接把错误渲染在页面上,但是在进行API开发时,返回的数据都应该是json类型,当出现错误时只要把错误类型返回,具体的渲染工作交给前端负责即可。那么应该怎么实现呢,一个很实用的方法是自己定义异常处理类。
这里有一篇文章,详细的介绍了重写异常处理类的思路和方法:Flask开发技巧之异常处理。接下来简单介绍实现思路。
自定义异常
Flask内部的异常都是通过继承HTTPException类来实现的,当抛出这个类时会向发送方发送响应,内部有几个重要的方法:
get_headers
方法定义返回的响应头get_body
方法定义返回的响应体,默认情况下是一段html内容
因为在前后端分离开发时返回类型都是json,所以我们可以通过继承HTTPException这个类来定义我们自己的异常处理类。主要修改的内容就是响应头和响应主体两个部分,将响应头中的类型改为json,并将响应主体也改为json类型。下面是我自己实现的代码:
class ApiException(HTTPException):
code = 404
errorCode = 2000
information = "未知错误"
def __init__(self, code=None, error_code=None, information=None, payload=None):
Exception.__init__(self)
if code:
self.code = code
if error_code:
self.errorCode = error_code
if information:
self.information = information
self.payload = payload
super(ApiException, self).__init__(information, None)
# 重写get_body
def get_body(self, environ=None):
body = dict(self.payload or ())
body['errorCode'] = self.errorCode
body['information'] = self.information
return json.dumps(body)
# 设置返回的响应头
def get_headers(self, environ=None):
return [('Content-Type', 'application/json')]
当抛出上面的异常类时,就会返回包含错误码和错误信息的json格式的响应。http状态码会自动添加到响应当中,也需要注意修改。
在此基础上,我们可以定义自己的各种错误类,并且在合适的地方抛出,比如:
class Success(ApiException):
code = 200
errorCode = 0
class ParameterException(ApiException):
code = 403
errorCode = "validate error"
information = "验证失败"
需要注意的一点是,虽然定义的是错误类,但实际上也可以返回成功的响应,比如上面代码中的Success
。
最后一点技巧是全局捕获异常,推荐去看看我上面放的文章。
from flask import Blueprint
from werkzeug.exceptions import HTTPException
from .errorHandler import ApiException
# 全局错误AOP处理
commonException = Blueprint('common', __name__)
@commonException.app_errorhandler(Exception)
def framework_error(error):
if isinstance(error, ApiException):
return error # 自定义错误直接返回
if isinstance(error, HTTPException):
code = error.code
information = error.description
# error_code = 1007
return ApiException(code=code, information=information) # flask内置错误封装为ApiException返回
else:
return error # 比较合适的方法是在生产环境中返回服务器内部错误 不返回具体信息