理解WTForms并灵活改造她

WTForms其实是非常强大的验证插件。但很多同学对WTForms的理解仅仅停留在“验证表单”上。那WTForms可以用来做API的参数验证码?完全可以,但这需要你灵活的使用它,对它做出一些“改变”。

默认情况下的form验证出现异常的时候是不直接抛出的,存在于formerrors中,如果我们想直接抛出,就需要更改重写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from flask import request
from wtforms import Form

from app.libs.error_code import ParameterException


class BaseForm(Form):
def __init__(self):
# 这里将silent设置为True表示出现异常不再抛出
data = request.get_json(silent=True)
# 除了上面获得参数 还可以通过 ? url拼接获得
args = request.args.to_dict()
super(BaseForm, self).__init__(data=data, **args)

def validate(self):
# 我们保留原有的validate方法,自己重写一个validate_for_api
pass

def validate_for_api(self):
# 这个方法我们要实现原有的 validate方法的功能,因此要调用一下父类validate
valid = super(BaseForm, self).validate()
if not valid:
# 如果验证出错 所有的错误都存在于form 的errors属性中
raise ParameterException(msg=self.errors)
# 返回form本身
return self

其中的ParameterException就是前一篇我们学习的自定义异常类。

1
2
3
4
class ParameterException(APIException):
code = 400
msg = 'invalid parameter'
error_code = 1000

我们在自定义参数异常类的时候给msg设定了一个默认值。当有具体的错误异常时候,直接传入该值即可。

完成上面的配置之后,我们的自定义Form就完成了,然后在使用到WTForms的地方进行更换。

1
2
3
4
5
6
7
from app.validators.base import BaseForm as Form
# 这里的Form 就是我们的自己定义的Form了

class ClientForm(Form):
account = StringField(validators=[DataRequired(message='不允许为空'), length(
min=5, max=32
)])

在视图函数中使用方式也有所改变。

1
2
3
4
5
@api.route('/register', methods=['POST'])
def create_client():
# 这里直接使用validate_for_api 即可 如果有报错就会抛出 没有报错返回form实例本身
form = ClientForm().validate_for_api()
return Success()

其中的Success()也是我们自定义的一个返回。

1
2
3
4
class Success(APIException):
code = 201
msg = 'ok'
error_code = 0

我们可以接受定义时候的复杂,但是不能接收调用时的复杂。 – 七月老师

因为定义是一次性的,调用是多次性的。

我们在写代码的时候,对于可重用的代码尽量进行封装。

现在我们的代码中有两类异常:可以预知的已知异常,不可预知的未知异常。

对于已知异常我们可以进行指定格式的异常返回,但是对于未知异常怎么办呢?

AOP思想:https://baike.baidu.com/item/AOP/1332219

我们在异常的最终抛出的地方进行拦截未知异常。

我们知道所有的异常类最终继承的都是Exception类。

我们在入口文件进行最终异常捕捉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from werkzeug.exceptions import HTTPException
from app import create_app
from app.libs.error import APIException
from app.libs.error_code import ServerError

app = create_app()

@app.errorhandler(Exception)
def framework_error(e):
if isinstance(e, APIException):
# 首先进行 APIException 判断
return e
if isinstance(e, HTTPException):
# 如果不是 APIException 而是 HTTPException 将异常转化为 APIException
code = e.code
msg = e.description
error_code = 1007
return APIException(msg, code, error_code)
else:
# 我们在调试模式下的时候将异常全部打印,而生产环境抛出我们定义的异常类
if not app.config['DEBUG']:
return ServerError()
else:
raise e

其中的 ServerError为我们自定义的异常类。

经过我们自定义异常类,我们系统里面的异常大致分为三类APIExceptionHTTPExceptionException

我们最想抛出的是APIException,因此我们首先进行APIException判断。

知识就是财富
如果您觉得文章对您有帮助, 欢迎请我喝杯水!