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.');
                }
            }
        });
    });
});
5
  • 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 P.
    Commented Oct 27, 2017 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? Commented Oct 27, 2017 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 P.
    Commented Oct 27, 2017 at 10:11
  • It seems that the form send data using GET instead of POST. Could you change $.ajax to $.post?
    – Romain P.
    Commented Oct 27, 2017 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 Commented Oct 27, 2017 at 11:10

3 Answers 3

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

1
  • 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 Commented Oct 27, 2017 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.');
        }
    }
});
2
  • 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. Commented Oct 30, 2017 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 P.
    Commented Oct 31, 2017 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 and acknowledge you have read our privacy policy.

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