4 Sep 2009 fxn   » (Master)

Rails Tip: Which is the difference between request.xhr? and format.js?

Since Ruby on Rails has respond_to the following has become a common idiom for routing Ajax requests in controllers:


   respond_to |format|
     format.html { ... } # ordinary request
     format.js   { ... } # Ajax request, kinda, keep on reading
   end
Testing for format.js like that often suffices, but strictly speaking this is not testing if the request is Ajax. As everything else, abusing some logic is fine as long as you know what you are doing.

You know an Ajax call is just a fancy name for an ordinary HTTP call that is performed from JavaScript. Ajax requests sent by any of the major JavaScript frameworks include an HTTP header that distinguishes them:


   X_REQUESTED_WITH: XMLHttpRequest
Thus, the proper test to detect Ajax calls checks that header, and this is what request.xhr? does.

On the other hand, Ajax calls do not expect JavaScript necessarily. Remember, they are ordinary HTTP calls, so they may ask for HTML, JSON, whatever. What you ask for goes in the HTTP Accept header. For example Ajax functions in Prototype send by default:


   Accept: text/javascript, text/html, application/xml, text/xml, */*
And that is something format.js tests for (in addition to an optional explicit format parameter somehow encoded in the URL). If the Accept header starts with "text/javascript", or "application/javascript", or "application/x-javascript" you'll get routed into format.js.

In fact, jQuery by default does not send that header and vanilla jQuery calls do not enter format.js. The jRails plugin plays nicely with that idiom.

Note that even link_to_remote with the :update option is routed to format.js. That is fine from the HTTP point of view, because the header says it is OK to send HTML, but is kind of weird to serve HTML from within format.js isn't it?

But you can set whatever Accept header you need, jQuery provides even some shortcuts like "xml", which tells the library to send


   Accept: application/xml, text/xml, */*

So, in practice format.js kind of works, but both tests are not equivalent:

  • An Ajax call asking for "text/xml" is xhr? but won't be routed through format.js.
  • A call triggered by an ordinary SCRIPT tag that reaches your application (perhaps it serves dynamic JavaScript) is routed through format.js, but it is not xhr?.

That being said, if I control the interface and know what's what I am personally fine using format.js. Routing through respond_to is concise and clean, and you may know that in that action the only expected JavaScript calls are Ajax. If that holds you are still not testing for true Ajaxness but are testing a sufficient condition, which is correct anyway.

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!