【问题标题】:How to get wtforms to take json and insert data into the form object?如何让 wtforms 获取 json 并将数据插入到表单对象中?
【发布时间】:2021-05-03 06:12:16
【问题描述】:

情况:

我在前端使用 React 和一个 Flask api 服务器。我想将数据从 React 发送到 api,一旦我这样做了,我想在处理数据之前使用 WTForms 对数据运行验证。这个问题可能看起来类似于CSRF Protection with Flask/WTForms and React,但这并不能回答这个问题,请看一下我花了很多精力写了一个好问题。

我有什么

目前,数据作为 json 对象成功发送,其中键与 wtform 结构中的名称匹配,目的是让 wtforms 获取该 json 数据并将其插入对象并从那里正常处理

正在发送的 JSON 对象

{'username': 'tster', 'fullname': 'Tester test', 'phone': '038287827216', 'email': 'test@example.com', 'password': 'Tester1010', 'repeatPassword': 'Tester1010'}

整个概念是从 React 组件发送这个并将其添加到 wtforms 以便进行相应的验证和使用。

Python 代码:

我尝试了多种不同的方法,这是最新的:


    @bp.route('/api/register_user', methods=['POST', 'GET'])
    def register():
        """ End-point to register users """ 
        
        if request.method == 'GET':
            return ('', {'csrf_token': generate_csrf()})
        elif request.method == 'POST':
            req = request.get_json(force=True)
            if validate_csrf(request.headers['X-Csrftoken']):
    
                print('validated')
            return{"message": "posted"}

这个概念是,如果我不能让它与 wtforms 一起工作,我可以手动生成 csrf_token,对其进行验证,一旦验证通过,就可以针对数据运行手动验证,而不是依赖于 wtforms,但它似乎没有相应地验证。这取自https://flask-wtf.readthedocs.io/en/stable/api.html#flask_wtf.csrf.generate_csrf

在此之前我尝试过:


    @bp.route('/api/register_user', methods=['POST', 'GET'])
    def register():
        """ End-point to register users """ 
        
        form = RegistrationForm()
    
        print(request.headers)
        if request.method == 'GET':
            print(form.csrf_token._value())
            return ('', {'csrf_token': form.csrf_token._value()})
        elif request.method == 'POST':
            req = request.get_json(force=True)
            form = RegistrationForm.from_json(req)
            print(form.username)
            return{"message": "posted"}
        else:
            return { 'errors': form.errors }

这背后的想法是像其他情况一样发送 csrf_token,因为在之前的链接中我读到:

生成一个 CSRF 令牌。令牌是为请求缓存的,因此多次调用此函数将生成相同的令牌。

time_limit -- 令牌有效的秒数。默认为 WTF_CSRF_TIME_LIMIT 或 3600 秒(60 分钟)。

阅读本文后,我从逻辑上认为这是有道理的,我基本上可以从标准表单对象中获取 csrf_token,就像您通常使用它们一样,然后当请求方法为 POST 时,我可以重新呈现表单并插入来自请求中 JSON 的数据,我从https://wtforms-json.readthedocs.io/en/latest/得到了这个想法

然而,这遇到了无法访问表单对象中的数据的相同行为,从以下位置接收多个错误:

print(form.username.data) AttributeError: 'tuple' 对象没有属性 'data'

print(form.username)(]})>,)

因此,我什至无法访问我插入到表单对象中的数据。 (如果它像我想象的那样插入的话。)

在此之前:

我尝试了使用 wtforms 的标准操作方式:


    @bp.route('/api/register_user', methods=['POST', 'GET'])
    def register():
        """ End-point to register users """ 
        
        form = RegistrationForm()
    
        print(request.headers)
        if request.method == 'GET':
            print(form.csrf_token._value())
            return ('', {'csrf_token': form.csrf_token._value()})
        elif form.validate_on_submit():
            req = request.get_json(force=True)
            return{"message": "posted"}
        else:
            return { 'errors': form.errors }

现在它从未通过验证,这很奇怪,因为验证器只是 DataRequired()。 然而,在测试时它确实通过了 form.is_submitted(),但我在打印时遇到了同样的错误:

print(form.username.data) AttributeError: 'tuple' 对象没有属性 'data'

print(form.username)(]})>,)

此外,react 代码必须稍微更改才能正常工作,否则它会发回错误的请求,请求必须是:


     handleSubmit = (details) => {
    
            const finalSend =  {
                
                'csrf_token': this.state.csrf_token,
                'username': this.state.usernameInput,
                'fullname': this.state.fullnameInput,
                'phone': this.state.phoneInput,
                'email': this.state.emailInput,
                'password': this.state.passwordInput,
                'repeatPassword': this.state.repeatPasswordInput
    
            }
            axios({
                method: 'post',
                url: '/api/register_user',
                data: finalSend,
                headers: {
                  'content-type': 'application/json'
                }
              })
            .then(res => res.json()).catch(e => console.log(e));
        }

问题

所以毕竟我要问的是:

  • 如何让 wtforms 获取作为 JSON 对象发送的数据?
  • 与 WTForms 分开操作会更好吗?如果可以,如何操作?
  • 如果我执行上述操作,我该如何设置 CSRF 安全性,正如我最近尝试的那样,但它没有按需要工作。

额外需要:

组件发送的JSON格式为:


    {'csrf_token': 'ImQ5MjhlY2VlYzM5Zjg0NmY4ZTg0NDk5ZjNlMjlkNzVlZGM4OGZhY2Ui.YBPbuw.D6BW8XpwEyXySCaBBeS0jIKYabU', 'username': 'charlie', 'fullname': 'charlie char', 'phone': '344444444', 'email': 'cindy@example.com', 'password': 'charlieandcindy', 'repeatPassword': 'charlieandcindy'}

我正在使用的 WTForm:


    class RegistrationForm(FlaskForm):
    
        username = StringField('username', validators=[DataRequired()]), 
        fullname = StringField('fullname', validators=[DataRequired()]), 
        email = BooleanField('email', validators=[DataRequired(), Email()]), 
        phone = StringField('phone', validators=[DataRequired()]), 
        password = StringField('password', validators=[DataRequired()]),
        repeatPassword = StringField('repeatPassword', validators=[DataRequired(), EqualTo('password')])
    
        def validate_username(self, username):
            print("validating username")

我还咨询了以下网站以寻找解决方案: Flask wtforms - 'UnboundField' object is not callable, dynamic field won't init properly https://wtforms.readthedocs.io/en/2.3.x/forms/ https://wtforms-json.readthedocs.io/en/latest/ Flask-WTF set time limit on CSRF token https://flask-wtf.readthedocs.io/en/stable/api.html#flask_wtf.csrf.generate_csrf How do I get the information from a meta tag with JavaScript? https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#uploading_json_data https://flask-wtf.readthedocs.io/en/stable/csrf.html#javascript-requests CSRF Protection with Flask/WTForms and React

再一次,我尽我最大的努力找到了解决方案,并付出了很多努力来写一个好问题,如果您需要更多,请尽管问。

编辑

在这方面进行了一些工作,我又玩了一些 wtforms_json 并让它稍微填充,但是它只填充了 repeatPassword 字段,我不确定这是为什么。

为了做到这一点,我不得不关闭 csrf 验证并在路由中使用以下代码:


    @bp.route('/api/register_user', methods=['POST'])
    def register():
        """ End-point to register users """ 
        json = request.get_json()
        form = RegistrationForm.from_json(json)
        print(request.get_json())
        print(form.data)
        if form.validate():
            print("validated")
            return{"message": "posted"}
        else:
            return { 'errors': form.errors }

打印 form.data 时的结果是设置了 repeatPassword ,但没有其他内容,不知道为什么......这个进步的来源在这里 WTForms-JSON not working with FormFields

【问题讨论】:

    标签: python json flask flask-wtforms wtforms-json


    【解决方案1】:

    这个我也找到了答案。

    为了做到这一点,我最终使用了 json 方法中的 wtforms_json,如下所示:

        @bp.route('/api/register_user', methods=['GET','POST'])
        def register():
            """ End-point to register users """ 
            reg_json = request.get_json()
            form = RegistrationForm.from_json(reg_json)
            
            if form.validate():
                user = Users(
                    username = form.username.data,
                    name = form.fullname.data,
                    phone = form.phone.data,
                    email = form.email.data,
                    password = guard.hash_password(form.password.data),
                    roles = 'User'
                )
                user.save()
                return{"message": "inserted successfully"}
            else:
                return { 'errors': form.errors }
    

    以及不得不调整的表格:

    
        class RegistrationForm(FlaskForm):
        
            username = StringField('username', validators=[DataRequired()]) 
            fullname = StringField('fullname', validators=[DataRequired()])
            email = StringField('email', validators=[DataRequired()])
            phone = StringField('phone', validators=[DataRequired()])
            password = StringField('password', validators=[DataRequired()])
            repeatPassword = StringField('repeatPassword', validators=[DataRequired(), EqualTo('password')])
        
            def validate_username(self, username):
                """ Ensure email isn't in use """
        
                user = Users.objects(username=username.data).first()
                if user != None:
                    raise ValidationError('Please use a different username.')
    
    

    唯一的缺点是我必须关闭配置中的 csrf_token:

    WTF_CSRF_ENABLED = False
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-26
      • 2017-04-16
      • 2020-11-05
      • 1970-01-01
      • 2023-03-29
      • 2016-05-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多