Home > Mobile >  post request works in browser, gives 400 in terminal
post request works in browser, gives 400 in terminal

Time:10-11

I'm building a flask application and am trying send post requests in the terminal using the requests library, but it is giving a 400 error, but I don't know what's malformed about the request.

Here's the signup page I'm trying to reach in my browser:

enter image description here

And the subsequent result after submitting the post:

enter image description here

However, when I try and recreate this post request and use the same info in the command line I get a 400 status code.

Here's the underlying code I use to generate it:

FORM:

class SignupForm(FlaskForm):
    """Main form for sign up page"""
    email          = EmailField(label       = 'email', validators = [DataRequired(), Email()])
    password       = PasswordField(label    = 'password', 
                                 validators = [DataRequired(), Length(min=9, max=20), 
                                                check_valid_password])
    sec_question   = SelectField(label      = 'Security Question', 
                             validators     = [DataRequired()],
                             choices        = ['What is your favorite band?',
                                               'What is your favorite pets name?',
                                               'What was the last name of your favorite childhoold teacher?',
                                               'In what city were you born?',
                                               'What is your best friends last name?', 
                                               'What is the country you would most like to visit?'])
    sec_answer     = TextField(label='your answer.  not case sensitive', validators=[DataRequired(), Length(4, 200)])

Here's the flask code that handles the incoming request:

@user.route('/signup', methods=['GET', 'POST'])
def signup():
form = SignupForm()

if form.validate_on_submit():
    user_exists = User.query.filter_by(email = request.form.get('email')).first()
    if user_exists:
        flash('User already exists with that e-mail.  Please login instead', 'warning')
        return redirect(url_for('user.signup', form = form))

    user          = User()
    form.populate_obj(user)
    user.password =  User.encrypt_password(request.form.get('password'))
    user.save()

    # send confirmation e-mail using celery
    from app.blueprints.main.tasks import send_email

    token = generate_new_account_token(form.email.data)

    send_email.delay(
        subject     = 'Welcome to CPM!  Please confirm your account',
        recipients  = [form.email.data],
        template    = 'email/user_new_confirm.html',
        confirm_url = url_for('user.confirm_new', token = token, _external=True)
    )

    flash(f'A confirmation e-mail has been sent to {form.email.data}', 'info')
    redirect(url_for('user.signup', form=form))

return render_template('blueprints/user/signup.html', form=form)

I can't for the life of me seem to get this to work inside a post request. It's running on localhost:5000 on my computer.

Here are two examples that illustrate the issue. My successful GET request:

enter image description here

My unsuccessful POST request:

data = {'email': '[email protected]', 'password': 'Passw0rd!', 'sec_question': 'What is your favorite band?', 'sec_answer': 'paul simon'}
requests.post('http://localhost:5000/signup', data = data)

enter image description here

CodePudding user response:

As a security measure, forms need a CSRF token to be sent along with the POST request to prevent CSRF (Cross-Site Request Forgery, sometimes abbreviated XSRF) attacks. Browsers will handle that for you, but in cURL you will have to retrieve and use a CSRF token manually (which is usually a pain in the neck).

For development purposes, you can temporarily disable the CSRF protection. Here is an explanation on how to do that with Flask.

Essentially, just add the @csrf.exempt decorator to your signup method:

from flask_wtf import csrf

@user.route('/signup', methods=['GET', 'POST'])
@csrf.exempt
def signup():
     # The rest of the method

Alternatively, you can quickly and easily disable CSRF for all your views by setting WTF_CSRF_CHECK_DEFAULT to False.

WARNING: Never push a form view to production with CSRF protection disabled.

  • Related