I recently implemented some cross-domain AJAX using jQuery. I wanted to POST
data using JavaScript's XMLHttpRequest
to another site, and this required the use of the new HTTP
access control headers
You can try out the functionality on the new site litepanels.co.nz - simply click the "Contact Us" link in the footer and send us message. That form POST
s data to http://www.lucidsystems.co.nz/company/contact-us/send which is responsible for actually sending us a message.
To make this work, you need to respond to the HTTP OPTIONS
method with appropriate access control headers. When the client web browser tries to send the XMLHttpRequest
, it first initiates an OPTIONS
request to the same URI
, and checks the headers. These headers specify things such as what domains can make XMLHttpRequest
s to this URI
.
Server-side implementation
Here is an example of how to respond to the HTTP
options method using the Utopia framework:
ACCESS_CONTROLS = {
"Access-Control-Allow-Origin" => "*",
"Access-Control-Allow-Headers" => "X-Requested-With",
"Access-Control-Max-Age" => "60"
}
on 'index' do |request, path|
# ...
if request.options?
# Allow AJAX requests from different domains.
return :status => 200, :headers => ACCESS_CONTROLS
end
# ...
end
You can check this using curl -i -X OPTIONS $URI
:
$ curl -i -X OPTIONS http://www.lucidsystems.co.nz/company/contact-us/index
HTTP/1.1 200 OK
Date: Fri, 06 May 2011 02:25:28 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.5
<span class="highlight">Access-Control-Allow-Headers: X-Requested-With</span>
<span class="highlight">Access-Control-Allow-Origin: *</span>
<span class="highlight">Access-Control-Max-Age: 60</span>
Content-Length: 0
Status: 200
Content-Type: text/plain
One problem I encountered when executing XMLHttpRequest
s was the fact that they process 3xx
redirections transparently. So, if you return a 3xx
redirect, it won't actually return this status code to your handler, but instead process the redirectionXMLHttpRequest
s return 2xx
or 4xx
status codes.
It is also important that controllers return the access control headers.
if request.xhr?
# You also need to provide access control headers here.
return :status => :success, :headers => ACCESS_CONTROLS
else
return redirect(params["from"] ? "success" : "success-no-reply")
end
Client-side implementation
On the litepanels.co.nz, I use JavaScript
and jQuery
to serialize the contact form and send this data to http://www.lucidsystems.co.nz/company/contact-us/send:
$(function() {
// The contact form element
var contact = $("#contact");
contact.validate({
rules: {
subject: "required",
from: {
email: true
},
message: "required"
},
submitHandler: function() {
$('#contact-popup .main').slideUp();
$('#contact-popup .sending').slideDown();
$.ajax({
url: contact.attr('action'),
type: 'post',
data: contact.serialize(),
crossDomain: true,
headers: {
"X-Requested-With": "XMLHttpRequest"
},
success: function() {
$('#contact-popup .sending').slideUp();
$('#contact-popup .success').slideDown();
},
error: function(request, status, error) {
$('#contact-popup .sending').slideUp();
$('#contact-popup .error').slideDown();
}
});
}
});
})
jQuery
by default doesn't set X-Requested-With
for cross-domain requests, so you need to do this manually.
Further Reading
HTTP
Access Controls