title: Flask & AJAX: Failed to load resource: the server responded with a status of 400 (BAD REQUEST)
body: <p>I am making a Flask application for sending e-mails. It worked pretty fine until I decided to implement a little bit of AJAX for making it more interactive. </p>
  
  <p>I constantly get 400 ERROR and I tried a lot of combinations, but noting worked.</p>
  
  <p>Some people advised me that the problem is in CSRF token, but when I put <code>@csrf.exempt</code> decorator, it is still not working. Please help!</p>
  
  <p>Here is the source code:</p>
  
  <p><strong>1) Flask:</strong></p>
  
  <pre><code>from flask import Flask, render_template, url_for, redirect, request, jsonify
  from flask_bootstrap import Bootstrap
  from flask_sqlalchemy import SQLAlchemy
  from flask_mail import Mail, Message
  from flask_wtf.csrf import CSRFProtect
  from wtforms import StringField, PasswordField, BooleanField, TextAreaField
  from wtforms.validators import InputRequired, Email, Length, AnyOf, Regexp
  from flask_wtf import FlaskForm
  
  app = Flask(__name__)
  app.config['SECRET_KEY'] = '**********'
  
  mail = Mail(app)
  Bootstrap(app)
  csrf = CSRFProtect(app)
  
  class LoginForm(FlaskForm):
      email = StringField('Email *', _name='email', validators=[InputRequired(), Email(message='I don\'t like your email.')], render_kw={"placeholder": "Please enter your email"})
      password = PasswordField('Password *', _name='password', validators=[InputRequired(), Length(min=5, max=10), AnyOf(['secret', 'password'])], render_kw={"placeholder": "Please enter your password"})
  
  app.config.update(
      DEBUG = True,
      # EMAIL SETTINGS
      MAIL_SERVER = 'smtp.gmail.com',
      MAIL_PORT = 465,
      MAIL_USE_SSL = True,
      MAIL_USERNAME = 'jakov.kusic92@gmail.com',
      MAIL_PASSWORD = '********'
  )
  
  @csrf.exempt
  @app.route('/', methods=['GET', 'POST'])
  def mailForm():
      form = LoginForm()
      return render_template('loginForm.html', form=form)
  
  @csrf.exempt
  @app.route('/process', methods=['POST'])
  def process():
  
      emailInput = request.form['email']
      passwordInput = request.form['password']
  
  
      str = 'Hello'
      msg = Message(
          subject=str,
          sender='jakov.kusic92@gmail.com',
          recipients=['jakov.kusic@gmail.com']
      )
      msg.body = """
      NEW USER INFO:
      Email: {}
      Password: {}
      """.format(emailInput, passwordInput)
  
      if emailInput and passwordInput:
          # mail.send(msg)
          return jsonify({'firstname' : emailInput, 'lastname' : passwordInput})
  
      return jsonify({'error' : 'Missing data!'})
  
  if __name__ == '__main__':
      app.run(debug=True)
  </code></pre>
  
  <p><strong>2) HTML:</strong></p>
  
  <pre><code>{% extends "bootstrap/base.html" %}
  {% import "bootstrap/wtf.html" as wtf %}
  
  {% block title %}
  WTForms
  {% endblock %}
  
  {% block head %}
      {{ super() }}
      &lt;link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"&gt;
      &lt;script src="https://code.jquery.com/jquery-2.2.4.js"&gt;&lt;/script&gt;
      &lt;script src="{{ url_for('static', filename='form.js') }}" type="text/javascript"&gt;&lt;/script&gt;
  
  {% endblock %}
  
  {% block content %}
  &lt;div class="container"&gt;
  
      &lt;br&gt;&lt;br&gt; &lt;!-- br tags are here used for vertical alingment --&gt;
      &lt;div class="col-md-4 col-md-offset-4 col-sm-8 col-sm-offset-2"&gt;
          &lt;div class="well"&gt;
              &lt;br&gt;
              &lt;h1 class="text-center"&gt;Welcome to the database website&lt;/h1&gt;
              &lt;br&gt;
              &lt;h2&gt;Please enter your data below:&lt;/h2&gt;
              &lt;form&gt;
                  &lt;dl&gt;
                      {{ wtf.quick_form(form)}}
                      &lt;input type="submit" value="Login" class="btn btn-info"&gt;
                  &lt;/dl&gt;
              &lt;/form&gt;
              &lt;div class="row"&gt;
                  &lt;div class="col-md-12"&gt;
                      &lt;p class="text-muted"&gt;&lt;strong&gt;*&lt;/strong&gt; These fields are required.&lt;/p&gt;
                  &lt;/div&gt;
              &lt;/div&gt;
              &lt;div id="successAlert" class="alert alert-success" role="alert" style="display:none;"&gt;&lt;/div&gt;
              &lt;div id="errorAlert" class="alert alert-danger" role="alert" style="display:none;"&gt;&lt;/div&gt;
          &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  {% endblock %}
  </code></pre>
  
  <p><strong>3) JS:</strong></p>
  
  <pre><code>$('form').on('submit', function(event) {
  
      $.ajax({
          data : {
              firstnameInput : $('#firstnameInput').val(),
              lastnameInput : $('#lastnameInput').val(),
              emailInput : $('#emailInput').val(),
              passwordInput : $('#passwordInput').val(),
              messageInput : $('#messageInput').val()
          },
          type : 'POST',
          headers: {
              'Cache-Control': 'no-cache, no-store, must-revalidate',
              'Pragma': 'no-cache',
              'Expires': '0'
          },
          url : '/process',
          dataType: "json",
          contentType: "application/json"
      })
      .done(function(data) {
          $('#successAlert').text(data.firstname + data.lastname).show();
          $('#errorAlert').hide();
      });
  
      event.preventDefault();
  
  });
  </code></pre>
  
  <p><strong>THIS IS WHAT I GET ON CHROME DEV TOOLS:</strong></p>
  
  <p><a href="https://i.stack.imgur.com/a3ipq.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/a3ipq.jpg" alt="enter image description here"></a></p>