💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[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) ~~~