1

I am using the Contact Form plugin with AJAX. I have used the JS AJAX script supplied in the plugin but i get a 'Bad request: CSRF token could not be verified' error. I have seen many other posts on here regarding AJAX and CSRF but non seem to apply to the Contact Form plugin.

Does anyone know how to get this to work?

<form method="POST" action="" accept-charset="UTF-8" class="request-ring-sizer">
    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="contactForm/sendMessage">
<h2>Order a free ring sizer</h2>
<div class="inputGroup">
        <h3 class="small">Contact details</h3>
        <div class="inputWrap{% if message is defined and message.hasErrors('fromName') %} error{% endif %}">
            <input onkeydown="active(this);" onblur="active(this); validate(this)" onfocus="active(this)" class="required" type="text" id="ring-sizer-fromName" name="fromName" value="{% if message is defined %}{{ message.fromName }}{% endif %}">
            <span class="highlight"></span>
            <span class="bar"></span>
            <label for="ring-sizer-fromName">Your name</label>
            <span class="flash">{% if message is defined and message %}{{ message.getErrors('fromName') }}{% else %}Required{% endif %}</span>
        </div>
        <div class="inputWrap{% if message is defined and message.hasErrors('fromEmail') %} error{% endif %}">
            <input onkeydown="active(this);" onblur="active(this); validate(this)" onfocus="active(this)" class="required" type="email" id="ring-sizer-fromEmail" name="fromEmail" value="{% if message is defined %}{{ message.fromEmail }}{% endif %}">
            <span class="highlight"></span>
            <span class="bar"></span>
            <label for="ring-sizer-fromEmail">Your email</label>
            <span class="flash">{% if message is defined and message %}{{ message.getErrors('fromEmail') }}{% else %}Required{% endif %}</span>
        </div>
    </div>
    <div class="inputGroup">
        <h3 class="small">Your address</h3>
        <div class="inputWrap{% if message is defined and message.hasErrors('message[address1]') %} error{% endif %}">
            <input onkeydown="active(this);" onblur="active(this); validate(this)" onfocus="active(this)" class="required" type="text" id="ring-sizer-message[address1]" name="message[address1]" value="{% if message is defined %}{{ message.message[address1] }}{% endif %}">
            <span class="highlight"></span>
            <span class="bar"></span>
            <label for="ring-sizer-message[address1]">Address line 1</label>
            <span class="flash">{% if message is defined and message %}{{ message.getErrors('message[address1]') }}{% else %}Required{% endif %}</span>
        </div>
        <div class="inputWrap">
            <input onkeydown="active(this);" onblur="active(this); validate(this)" onfocus="active(this)" type="text" id="ring-sizer-message[address2]" name="message[address2]" value="{% if message is defined %}{{ message.message[address2] }}{% endif %}">
            <span class="highlight"></span>
            <span class="bar"></span>
            <label for="ring-sizer-message[address2]">Address line 2</label>
        </div>
        <div class="inputWrap{% if message is defined and message.hasErrors('message[city]') %} error{% endif %}">
            <input onkeydown="active(this);" onblur="active(this); validate(this)" onfocus="active(this)" class="required" type="text" id="ring-sizer-message[city]" name="message[city]" value="{% if message is defined %}{{ message.message[city] }}{% endif %}">
            <span class="highlight"></span>
            <span class="bar"></span>
            <label for="ring-sizer-message[city]">Town or City</label>
            <span class="flash">{% if message is defined and message %}{{ message.getErrors('message[city]') }}{% else %}Required{% endif %}</span>
        </div>
        <div class="inputWrap short{% if message is defined and message.hasErrors('message[zipCode]') %} error{% endif %}">
            <input onkeydown="active(this);" onblur="active(this); validate(this)" onfocus="active(this)" class="required" type="text" id="ring-sizer-message[zipCode]" name="message[zipCode]" value="{% if message is defined %}{{ message.message[zipCode] }}{% endif %}">
            <span class="highlight"></span>
            <span class="bar"></span>
            <label for="ring-sizer-message[zipCode]">Post code</label>
            <span class="flash">{% if message is defined and message %}{{ message.getErrors('message[zipCode]') }}{% else %}Required{% endif %}</span>
        </div>
        <div class="inputWrap{% if message is defined and message.hasErrors('message[countryId]') %} error{% endif %}">
            <select onkeydown="active(this);" onblur="validate(this)" class="address-country" name="message[countryId]" class="required">
                <option selected disabled>Select a Country</option>
                {% for key, option in countries %}
                    <option value="{{ key }}">{{ option }}</option>
                {% endfor %}
            </select>
            <span class="highlight"></span>
            <span class="bar"></span>
            <span class="flash">{% if message is defined and message %}{{ message.getErrors('message[countryId]') }}{% else %}Required{% endif %}</span>
        </div>
        <div class="inputWrap">
            <input onkeydown="active(this);" onblur="active(this); validate(this)" onfocus="active(this)" type="text" id="ring-sizer-message[state]" name="message[state]" value="{% if message is defined %}{{ message.message[state] }}{% endif %}">
            <span class="highlight"></span>
            <span class="bar"></span>
            <label for="ring-sizer-message[state]">State</label>
        </div>
        <input name="preferredJewellery" type="text" style="display:none;">
        <input type="hidden" id="ring-sizer-subject" name="subject" value="I would like to request a free ring sizer">
        </div>
    </div>
    <button type="submit" class="button button--primary button--primary--center page-fade">Submit</button>
    <div class="form-success">
        <p class="center alert alert--success">Thank you. We will let you know when we've dispatched your free ring&nbsp;sizer.</p>
    </div><!-- thanks -->
</form>






$(document).ready(function(){

    $('.request-ring-sizer').submit(function(ev) {
        console.log("ring sizer request submitted");
        // Prevent the form from actually submitting
        ev.preventDefault();

        // Send it to the server
        $.ajax('/', {
            dataType: 'json',
            data: $(this).serialize(),
            success: function(response) {
                if (response.success) {
                    $('.request-ring-sizer .form-success').addClass("visible");
                } else {

                    alert('An error occurred. Please try again.');
                }
            }
        });
    });
});
  • I you are using Craft 3, can you replace {{ getCsrfInput() }} with {{ csrfInput() }}? Alternatively, you could disable CSRF protection by adding 'enableCsrfProtection' => false in the craft/config/general.php config array (craftcms.com/support/csrf-protection). – Romain Poirier Oct 27 '17 at 9:30
  • Craft 2. Didn't think Craft 3 was production ready yet. also, for security purposes surely it wouldn't be good to remove CSRF protection? – ToddPadwick Oct 27 '17 at 9:49
  • You're right, Craft 3 is not yet production ready. And you're right also, remove CSRF protection is not recommended. But 'enableCsrfProtection' => false could help you to make the form working until you find a real solution. – Romain Poirier Oct 27 '17 at 10:11
  • It seems that the form send data using GET instead of POST. Could you change $.ajax to $.post? – Romain Poirier Oct 27 '17 at 11:00
  • That makes sense... I have just updated that. however I now get a new error: Uncaught TypeError: Cannot read property 'success' of undefined – ToddPadwick Oct 27 '17 at 11:10
1

Looks like you need to pass the csrf token through within your data as noted here https://craftcms.com/support/csrf-protection#updating-your-javascript

  • I tried this after seeing another post... but it didn't seem to work. also, it seemed odd that the plugin has csrf included but their ajax snippet supplied doesnt have this. So I assumed they must have included this in the plugin itself? github.com/craftcms/contact-form#ajax-form-submissions – ToddPadwick Oct 27 '17 at 15:19
0

Your form action attribute should not be empty ($('input[name="action"]', form).attr('action')) and your AJAX request should be sent using POST ($.post).

$.post('/', {
    dataType: 'json',
    action: $('input[name="action"]', form).attr('action'),
    data: $(this).serialize(),
    success: function(response) {
        if (response.success) {
            $('.request-ring-sizer .form-success').addClass("visible");
        } else {

            alert('An error occurred. Please try again.');
        }
    }
});
  • His action attribute is not empty and you don't need to use $.post instead of $.ajax, the way you described it sounds like $.ajax will not do the job but yours is just an alternative. – Robin Schambach Oct 30 '17 at 14:07
  • To be more explicit, the default $.ajax method is GET (api.jquery.com/jquery.ajax) and this plugin requires that data are send using POST (github.com/craftcms/contact-form/issues/102). If you want to use $.ajax instead of $.post you can, but you should specify the POST method parameter. – Romain Poirier Oct 31 '17 at 13:39
0

Turns out this was a mistake in the README docs.

"The v1 README uses $.post, so this is only an issue when someone uses the v1 plugin and the v2 ajax instructions. I haven't tested it with v2, but it looks like the SendController requires POST too."

The author of the plugin has now fixed this.

https://github.com/craftcms/contact-form/issues/102

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.