Ah the joys of coding – hitting your head against something which appears so easy but is actually quite fiddly. All the more painful after having some fun with the Google maps Javascript tutorial and feeling Javascript was quite easy.

So what was the something? Making an AJAX POST from some Javascript to a Django service. I was working merrily along following Daniel Stocks’ very readable “AJAX in Django with JQuery” tutorial. My mistake: skipping over his note about CSRF protection.

The code was super simple:

In Javsacript, on a link click, make a call using the ajax post JQuery convenience function, with a callback to display the returned message on the webpage:

 $("#doajaxgetcall").click(function() {
 $.post("http://www.django.unburnout.com/xhr_test/",
 function(data) {
        $("#ajaxtest").append(data);
    });
 });

On the server, check HTTP request and send back a message:

def xhr_test(request):
    if request.is_ajax():
      if request.method == 'GET':
        message = "This is an XHR GET request"
      elif request.method == 'POST':
        message = "This is a XHR POST request"
      return HttpResponse(message)

The result? <Nothing> on the click, a 500 in the JS console, a 500 in the server log, and an arcane error in the Passenger logs

[error] [client 24.4.69.35] ModSecurity:
Output filter: Failed to read bucket (rc 104):
Connection reset by peer

Fix as per the Django project docs on CSRF (thanks Daniel – I’ll read properly next time). You need to manually catch AJAX POSTs and make sure a HTTP header is set to the value of the server-supplied CSRF token. Lots of other people have been reporting this issue … Django need to improve their documentation me feels.

Code to insert in your Javscript (explanatory comments mine, original docs here):

    // intercept AJAX messages
    $(document).ajaxSend(function(event, xhr, settings) {

      ...

      // Main: if message is not using a safe method (e.g. GET)
      // but is from the same origin:
      // set request header to be CSRF cookie value
      if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
	        xhr.setRequestHeader("X-CSRFToken",
                getCookie('csrftoken'));
      }

    });

Oh, and another morale of working with Django. Allegedly you don’t need to restart the server, each time you edit some code. But running on Passenger on Dreamhost you do (sometimes).

touch $PASSENGER_ROOT/tmp/restart.txt