[TOC]
## 第5章 理解WTForms并灵活改造她
WTForms其实是非常强大的验证插件。但很多同学对WTForms的理解仅仅停留在“验证表单”上。那WTForms可以用来做API的参数验证码?完全可以,但这需要你灵活的使用它,对它做出一些“改变”
### 5-1 重写WTForms 一
原因:
* 之前做的表单验证只是将所有验证错误归结于客户端类型错误,并没有具体分到底是客户端传入的哪个数据有问题
* 代码写起来繁琐,我们需要在每一个需要做表单验证的视图函数内写 else 来抛出异常
### 5-2 重写WTForms 二
其实我们在执行 form.validate 的时候内部已经产生了一个异常,只是异常没有抛出来而已,所以我们只需要 wtforms 在执行 form.validate 之后将异常抛出来就可以了。
#### 在 BaseForm 中覆写 Form
首先新建 ginger/app/validators/base.py
~~~
from wtforms import Form
from app.libs.error_code import ParameterException
class BaseForm(Form):
def __init__(self, data):
super(BaseForm, self).__init__(data=data)
def validate_for_api(self):
valid = super(BaseForm, self).validate()
if not valid:
raise ParameterException(self.errors)
~~~
1. 因为 BaseForm 需要接收客户端传过来的数据,所以在构造函数的参数里传入 data
2. 在构造函数内调用父类(wtforms 的 Form)的构造函数进行初始化
3. 我们可以覆写父类的 validate 方法也可以为 api 表单验证写一个单独的 validate\_for\_api 方法
* 在 validate\_for\_api 内调用父类的 validate 方法,就会让 validate\_for\_api 具备与 validate 相同的功能
* 父类 validate 方法执行过程中产生的异常都保存在 forms.errors 属性中
* 其次用 valid 接收父类 validate 的返回结果,将结果中的异常抛出
这样 BaseForm 就具备了抛出异常的能力。
#### 继承 BaseForm
将 ginger/app/validator/forms.py 内的所有表单都继承 BaseForm。
~~~
from app.validators.base import BaseForm as Form
~~~
直接在视图函数内调用 validate\_for\_api 就可以在表单验证遇到异常的时候终止程序并抛出异常信息。
~~~
@api.route('/register', methods=['POST'])
def create_client():
form = ClientForm(data=request.json)
form.validate_for_api()
promise = {
ClientTypeEnum.USER_EMAIL: __register_by_email,
ClientTypeEnum.USER_MOBILE: __register_by_mobile,
ClientTypeEnum.USER_MINA: __register_by_mina,
ClientTypeEnum.USER_WX: __register_by_wx,
}
promise[form.type.data]()
return 'register client success'
~~~
### 5-3 可以接受定义的复杂,但不能接受调用的复杂
1. 优化 data=request.json 将 data=request.json 写入到 BaseForm 中,以后调用就不需要手动传入
~~~
class BaseForm(Form):
def __init__(self):
data = request.json
super(BaseForm, self).__init__(data=data)
~~~
2. 第一步优化好之后,视图函数中的代码如下
~~~
form = ClientForm()
form.validate_for_api()
~~~
这里还能不能优化呢?显然是可以的,可以写成
~~~
form = ClientForm().validate_for_api()
~~~
但是 validate\_for\_api 内部并没有返回 form,所以左边的 form 赋值就会报错,怎么办?强制让 validate\_for\_api 返回 form!因为 validate\_for\_api 是写在 BaseForm 内的,此时的 self 就是 form。
~~~
def validate_for_api(self):
valid = super(BaseForm, self).validate()
if not valid:
raise ParameterException(self.errors)
return self
~~~
### 5-4 已知异常与未知异常
到目前为止客户端传给服务器的都是 JSON 格式的数据,如果不是 JSON 格式的数据呢?
![](https://ws3.sinaimg.cn/large/006tNbRwgy1fyr9tjdz3sj32f30u0n3e.jpg)
异常可以分为:
* 已知异常
* 未知异常
### 5-5 全局异常处理
如何捕捉未知异常呢?这就需要用到 AOP 的思想。不管异常产生在哪里,我只需要在全局捕捉异常并处理就可以解决问题了。
在项目的启动文件内写全局捕捉错误的函数。分类判断该错误是 APIException、HTTPException 还是最基本的 Exception,对应的编写处理方法。
~~~
from werkzeug.exceptions import HTTPException
from app.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):
return e
if isinstance(e, HTTPException):
code = e.code
msg = e.description
error_code = 1007
return APIException(msg, code, error_code)
else:
if app.config['DEBUG']:
raise e
else:
return ServerError()
if __name__ == '__main__':
app.run(debug=True)
~~~