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:
And the subsequent result after submitting the post:
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:
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)
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.